Можете ли вы использовать @Autowired со статическими полями?


Ответы:


122

Короче нет. Вы не можете автоматически связывать или вручную связывать статические поля в Spring. Вам придется написать свою собственную логику, чтобы сделать это.


3
Когда вы обнаружите, что старый код делает это, это анти-паттерн. Прищурись, наклони голову и найди лучший способ решения проблемы. Вы будете рады, что сделали.
Джозеф Ласт

2
этот ответ также полезен для Spring's@AutoWired
Кевин Мередит

116
@Component("NewClass")
public class NewClass{
    private static SomeThing someThing;

    @Autowired
    public void setSomeThing(SomeThing someThing){
        NewClass.someThing = someThing;
    }
}

1
Любая идея, как я могу использовать этот подход при инициализации хранилища?
kiedysktos

3
Недостаток: нет гарантии, что someThingбыла инициализирована при статическом доступе: NewClass.staticMethodWhichUsesSomething();может выдать NPE, если используется до инициализации приложения
Neeraj

Можете ли вы избежать предупреждения Instance methods should not write to "static" fields (squid:S2696)?
user7294900

@ user7294900: отключите это предупреждение только для этого особого случая.
Изогфиф

@izogfif все еще проблема, если я выберу это решение в широких случаях и классах
user7294900

67

@Autowired может использоваться с сеттерами, чтобы вы могли иметь сеттер, модифицирующий статическое поле.

Только одно последнее предложение ... НЕ


54
Почему вы предлагаете не делать этого?
Джон Лоруссо

3
Хммм .. я чувствую, почему это не рекомендуется, потому что тогда статический экземпляр в классе находится вне контроля весны. После того, как вводится в поле статического являются ссылками для всех экземпляров объектов соответствующего (окружающий) класса. Но такое поведение может быть именно тем, что ожидается, поэтому может рассматриваться как ошибка или особенность ...
Матфей

1
Да @matthaeus, это именно та функция, которую я ожидал, когда мне нужно было получить доступ к org.springframework.core.env.Environment:@Component public class SpringAppEnv{ public static Environment _env; @Autowired public void setEnv(Environment env) {_env = env;} }
user1767316

@JonLorusso и все потому, что когда загрузчик классов загружает статические значения, контекст Spring еще не загружен. Таким образом, загрузчик классов не будет правильно внедрять статический класс в bean-компонент и завершится ошибкой. Ответ предоставлен Andrea T
Jeril Kuruvila

14

Инициируйте свой автопроводной компонент в методе @PostConstruct

@Component
public class TestClass {
   private static AutowiredTypeComponent component;

   @Autowired
   private AutowiredTypeComponent autowiredComponent;

   @PostConstruct
   private void init() {
      component = this.autowiredComponent;
   }

   public static void testMethod() {
      component.callTestMethod();
   }
}

Можете ли вы избежать предупреждения Instance methods should not write to "static" fields (squid:S2696)?
user7294900

Вы также можете сделать это напрямую через конструктор.
Гагарва

5

Создайте bean-компонент, который вы можете автоматически связать, который будет инициализировать статическую переменную как побочный эффект.


4

Вы можете достичь этого, используя нотацию XML и MethodInvokingFactoryBean. Для примера посмотрите здесь .

private static StaticBean staticBean;

public void setStaticBean(StaticBean staticBean) {
   StaticBean.staticBean = staticBean;
}

Вы должны стремиться использовать пружинный впрыск, где это возможно, так как это рекомендуемый подход, но это не всегда возможно, так как я уверен, что вы можете себе представить, что не все можно извлечь из контейнера с пружиной или вы, возможно, имеете дело с устаревшими системами.

Тестирование заметок также может быть более сложным при таком подходе.


2

Хотел добавить к ответам, что автоматическое связывание статического поля (или константы) будет игнорироваться, но также не создаст никакой ошибки:

@Autowired
private static String staticField = "staticValue";

1

Вы можете использовать ApplicationContextAware

@Component
public class AppContext implements ApplicationContextAware{
    public static ApplicationContext applicationContext;

    public AppBeans(){
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }
}

затем

static ABean bean = AppContext.applicationContext.getBean("aBean",ABean.class);

0

Отказ от ответственности Это ни в коем случае не стандарт, и вполне может быть лучший весенний способ сделать это. Ни один из приведенных выше ответов не решает проблемы подключения открытого статического поля.

Я хотел сделать три вещи.

  1. Используйте пружину для "Autowire" (я использую @Value)
  2. Выставить публичное статическое значение
  3. Предотвратить изменение

Мой объект выглядит так

private static String BRANCH = "testBranch";

@Value("${content.client.branch}")
public void finalSetBranch(String branch) {
    BRANCH = branch;
}

public static String BRANCH() {
    return BRANCH;
}

Мы уже отметили 1 и 2, как мы можем предотвратить вызовы сеттеру, поскольку мы не можем его скрыть.

@Component
@Aspect
public class FinalAutowiredHelper {

@Before("finalMethods()")
public void beforeFinal(JoinPoint joinPoint) {
    throw new FinalAutowiredHelper().new ModifySudoFinalError("");
}

@Pointcut("execution(* com.free.content.client..*.finalSetBranch(..))")
public void finalMethods() {}


public class ModifySudoFinalError extends Error {
    private String msg;

    public ModifySudoFinalError(String msg) {
        this.msg = msg;
    }

    @Override
    public String getMessage() {
        return "Attempted modification of a final property: " + msg;
    }
}

Этот аспект обернет все методы, начинающиеся с final, и выдаст ошибку, если они вызваны.

Я не думаю, что это особенно полезно, но если вы любите держать горох и морковь отдельно, это один из способов сделать это безопасно.

Важно Spring не вызывает ваши аспекты, когда вызывает функцию. Сделал это проще, к сожалению, я разработал логику, прежде чем понять это.


-1
private static UserService userService = ApplicationContextHolder.getContext().getBean(UserService.class);

2
Хотя этот код может решить вопрос, в том числе объяснение того, как и почему это решает проблему, действительно поможет улучшить качество вашего сообщения и, вероятно, получит больше голосов "за". Помните, что вы отвечаете на вопрос для читателей в будущем, а не только для того, кто спрашивает сейчас. Пожалуйста, отредактируйте свой ответ, чтобы добавить объяснения и указать, какие ограничения и предположения применяются.
двойной гудок

Я думаю, что этот ответ может вообще не нуждаться в объяснении.
Chaklader Asfak Arefe
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.