¿Puedes usar @Autowired con campos estáticos?

Respuestas:

122

En resumen, no. No puede conectar automáticamente o cablear manualmente los campos estáticos en Spring. Tendrás que escribir tu propia lógica para hacer esto.

skaffman
fuente
3
Cuando encuentre que el código antiguo hace esto, es un antipatrón. Entrecierra los ojos, inclina la cabeza y encuentra una mejor manera de resolver el problema. Estaras contento de haberlo hecho.
Joseph Lust
2
esta respuesta también es útil en Spring's@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
fuente
1
¿Alguna idea de cómo puedo usar este enfoque al inicializar un repositorio?
kiedysktos
3
La desventaja: no hay garantía de que someThingse haya inicializado si se accede estáticamente: NewClass.staticMethodWhichUsesSomething();podría arrojar un NPE si se usa antes de la inicialización de la aplicación
Neeraj
¿Se puede evitar la advertencia de Instance methods should not write to "static" fields (squid:S2696)?
user7294900
@ user7294900: deshabilite esta advertencia solo para este caso muy específico.
izogfif
@izogfif sigue siendo un problema si elijo esta solución en casos y clases
generales
67

@Autowired puede usarse con setters para que pueda tener un setter modificando un campo estático.

Solo una sugerencia final ... NO

Victor Hugo
fuente
54
¿Por qué sugieres no hacer esto?
Jon Lorusso
3
Hmmm ... mi sensación acerca de por qué no se recomienda es porque la instancia estática en la clase está más allá del control de la primavera. Una vez inyectado, el campo estático es la referencia para todas las instancias de objetos de la clase correspondiente (circundante). Sin embargo, podría ser exactamente lo que se espera que este comportamiento a suceder, por tanto, podría ser visto como un error o de una pieza ...
Matthaeus
1
Sí @matthaeus, es exactamente la característica que esperaba cuando necesitaba acceder a org.springframework.core.env.Environment:@Component public class SpringAppEnv{ public static Environment _env; @Autowired public void setEnv(Environment env) {_env = env;} }
user1767316
@JonLorusso y todo Porque cuando el cargador de clases carga los valores estáticos, el contexto Spring aún no es necesario cargar. Por lo tanto, el cargador de clases no inyectará correctamente la clase estática en el bean y fallará. Respuesta proporcionada por Andrea T
Jeril Kuruvila
14

Inicie su componente con cable automático en el 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
fuente
¿Se puede evitar la advertencia de Instance methods should not write to "static" fields (squid:S2696)?
user7294900
También puede hacer esto directamente a través del constructor.
Gagarwa
5

Cree un bean que pueda conectar automáticamente que inicializará la variable estática como un efecto secundario.

Jherico
fuente
4

Puede lograr esto utilizando la notación XML y el MethodInvokingFactoryBean. Para un ejemplo mira aquí .

private static StaticBean staticBean;

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

Debe intentar utilizar la inyección de resorte siempre que sea posible, ya que este es el enfoque recomendado pero esto no siempre es posible, ya que estoy seguro de que se puede imaginar, ya que no todo se puede extraer del contenedor de resorte o tal vez se trate de sistemas heredados.

Tenga en cuenta que las pruebas también pueden ser más difíciles con este enfoque.

JARC
fuente
2

Quería agregar a las respuestas que el campo estático de cableado automático (o constante) se ignorará, pero tampoco creará ningún error:

@Autowired
private static String staticField = "staticValue";
usuario7294900
fuente
1

Puedes usar ApplicationContextAware

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

    public AppBeans(){
    }

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

luego

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

Descargo de responsabilidad Esto de ninguna manera es estándar y bien podría haber una mejor manera de hacerlo. Ninguna de las respuestas anteriores aborda los problemas de cableado de un campo estático público.

Quería lograr tres cosas.

  1. Use spring para "Autowire" (estoy usando @Value)
  2. Exponer un valor público estático
  3. Prevenir modificaciones

Mi objeto se ve así

private static String BRANCH = "testBranch";

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

public static String BRANCH() {
    return BRANCH;
}

Ya hemos marcado 1 y 2, ¿cómo evitamos las llamadas al emisor, ya que no podemos ocultarlo?

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

Este aspecto envolverá todos los métodos que comienzan con final y arrojará un error si se llaman.

No creo que esto sea particularmente útil, pero si está ocd y desea mantener los guisantes y las zanahorias separados, esta es una forma de hacerlo de manera segura.

Importante Spring no llama a sus aspectos cuando llama a una función. Lo hice más fácil, para mal, resolví la lógica antes de resolverlo.

jozsef morrissey
fuente
-1
private static UserService userService = ApplicationContextHolder.getContext().getBean(UserService.class);
Amol Kakade
fuente
2
Si bien este código puede resolver la pregunta, incluir una explicación de cómo y por qué esto resuelve el problema realmente ayudaría a mejorar la calidad de su publicación, y probablemente resultaría en más votos positivos. Recuerde que está respondiendo la pregunta para los lectores en el futuro, no solo la persona que pregunta ahora. Por favor, editar su respuesta para agregar explicaciones y dar una indicación de lo que se aplican limitaciones y supuestos.
pitido doble el
Creo que esta respuesta puede no necesitar ninguna explicación en absoluto.
Chaklader Asfak Arefe