Você pode usar @Autowired com campos estáticos?

Respostas:

122

Em suma, não. Você não pode conectar automaticamente ou conectar manualmente os campos estáticos no Spring. Você terá que escrever sua própria lógica para fazer isso.

skaffman
fonte
3
Quando você encontra código antigo fazendo isso, é um anti-padrão. Aperte os olhos, incline a cabeça e encontre uma maneira melhor de resolver o problema. Você ficará feliz por ter feito.
Joseph Lust
2
esta resposta também é útil para Spring@AutoWired
Kevin Meredith
116
@Component("NewClass")
public class NewClass{
    private static SomeThing someThing;

    @Autowired
    public void setSomeThing(SomeThing someThing){
        NewClass.someThing = someThing;
    }
}
Sedat Başar
fonte
1
alguma idéia de como posso usar essa abordagem ao inicializar um repositório?
Kryysktos 8/03
3
A desvantagem: Não há nenhuma garantia de que someThingfoi inicializado se acessado estaticamente: NewClass.staticMethodWhichUsesSomething();pode lançar uma NPE se usado antes da inicialização aplicativo
Neeraj
Você pode evitar o aviso de Instance methods should not write to "static" fields (squid:S2696)?
user7294900
@ user7294900: desative este aviso apenas neste caso muito específico.
Izogfif 6/06/19
@izogfif ainda um problema se eu optar por esta solução em casos gerais e classes
user7294900
67

@Autowired pode ser usado com setters para que você possa ter um setter modificando um campo estático.

Apenas uma sugestão final ... NÃO

Victor Hugo
fonte
54
Por que você sugere não fazer isso?
Jon Lorusso 17/07
3
Hmmm .. meu sentimento sobre por que não é recomendado é, porque a instância estática na classe está além do controle da primavera. Uma vez injetado, o campo estático é a referência para todas as instâncias de objetos da classe correspondente (ao redor). Mas, este comportamento pode ser exatamente o que se espera que aconteça, portanto, pode ser visto como um bug ou um recurso ...
matthaeus
1
Sim, @matthaeus, é exatamente o recurso que eu esperava quando precisava acessar org.springframework.core.env.Environment:@Component public class SpringAppEnv{ public static Environment _env; @Autowired public void setEnv(Environment env) {_env = env;} }
user1767316:
@JonLorusso e tudo Porque quando o carregador de classes carrega os valores estáticos, o contexto do Spring ainda não é necessário. Portanto, o carregador de classes não injeta corretamente a classe estática no bean e falha. Resposta fornecida por Andrea T
Jeril Kuruvila
14

Inicie seu componente autowired no método @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();
   }
}
ak-j
fonte
Você pode evitar o aviso de Instance methods should not write to "static" fields (squid:S2696)?
user7294900
Você também pode fazer isso diretamente através do construtor.
gagarwa 4/06
5

Crie um bean que você possa conectar automaticamente, que inicializará a variável estática como efeito colateral.

Jherico
fonte
4

Você pode conseguir isso usando a notação XML e o MethodInvokingFactoryBean. Para um exemplo, veja aqui .

private static StaticBean staticBean;

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

Você deve usar a injeção de mola sempre que possível, pois esta é a abordagem recomendada, mas isso nem sempre é possível, pois tenho certeza de que você pode imaginar que nem tudo pode ser retirado do contêiner de mola ou talvez você esteja lidando com sistemas legados.

Nota: o teste também pode ser mais difícil com essa abordagem.

JARC
fonte
1

Você pode usar o ApplicationContextAware

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

    public AppBeans(){
    }

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

então

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

Queria adicionar às respostas que o campo estático (ou constante) da fiação automática será ignorado, mas também não criará nenhum erro:

@Autowired
private static String staticField = "staticValue";
user7294900
fonte
0

Isenção de responsabilidade Isso não é de forma alguma padrão e poderia muito bem haver uma maneira melhor de fazer isso. Nenhuma das respostas acima aborda os problemas de conectar um campo estático público.

Eu queria realizar três coisas.

  1. Use spring para "Autowire" (estou usando @Value)
  2. Expor um valor estático público
  3. Impedir modificações

Meu objeto fica assim

private static String BRANCH = "testBranch";

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

public static String BRANCH() {
    return BRANCH;
}

Já marcamos 1 e 2 agora, como evitamos chamadas para o setter, pois não podemos ocultá-lo.

@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;
    }
}

Esse aspecto envolve todos os métodos que começam com final e gera um erro se eles forem chamados.

Eu não acho que isso seja particularmente útil, mas se você é ocd e gosta de manter as ervilhas e as cenouras separadas, essa é uma maneira de fazê-lo com segurança.

Importante O Spring não chama seus aspectos quando chama uma função. Tornou isso mais fácil, até que eu trabalhei na lógica antes de descobrir isso.

jozsef morrissey
fonte
-1
private static UserService userService = ApplicationContextHolder.getContext().getBean(UserService.class);
Amol Kakade
fonte
2
Embora esse código possa resolver a questão, incluindo uma explicação de como e por que isso resolve o problema realmente ajudaria a melhorar a qualidade da sua postagem e provavelmente resultará em mais votos positivos. Lembre-se de que você está respondendo à pergunta dos leitores no futuro, não apenas à pessoa que está perguntando agora. Por favor edite sua resposta para adicionar explicações e dar uma indicação do que limitações e premissas se aplicam.
bip duplo
Eu acho que essa resposta pode não precisar de nenhuma explicação.
Chaklader Asfak Arefe