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

Ответы:

122

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

skaffman
источник
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();
   }
}
ак-J
источник
Можете ли вы избежать предупреждения Instance methods should not write to "static" fields (squid:S2696)?
user7294900
Вы также можете сделать это напрямую через конструктор.
Гагарва
5

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

Jherico
источник
4

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

private static StaticBean staticBean;

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

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

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

Jarc
источник
2

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

@Autowired
private static String staticField = "staticValue";
user7294900
источник
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