¿Por qué mi campo Spring @Autowired es nulo?


Nota: Esto pretende ser una respuesta canónica para un problema común.

Tengo una clase Spring @Service (MileageFeeCalculator) que tiene un campo @Autowired (rateService), pero el campo es null cuando intento usarlo. Los registros muestran que tanto el frijol MileageFeeCalculator como el frijol MileageRateService se están creando, pero obtengo un NullPointerException cada vez que intento llamar al método mileageCharge en mi frijol de servicio. ¿Por qué no está Spring autowiring el campo?

Clase de controlador:

@Controller
public class MileageFeeController {    
    @RequestMapping("/mileage/{miles}")
    @ResponseBody
    public float mileageFee(@PathVariable int miles) {
        MileageFeeCalculator calc = new MileageFeeCalculator();
        return calc.mileageCharge(miles);
    }
}

Servicio clase:

@Service
public class MileageFeeCalculator {

    @Autowired
    private MileageRateService rateService; // <--- should be autowired, is null

    public float mileageCharge(final int miles) {
        return (miles * rateService.ratePerMile()); // <--- throws NPE
    }
}

Frijol de servicio que debe ser autowired en MileageFeeCalculator pero no lo es:

@Service
public class MileageRateService {
    public float ratePerMile() {
        return 0.565f;
    }
}

Cuando intento GET /mileage/3, obtengo esta excepción: {[18]]}

java.lang.NullPointerException: null
    at com.chrylis.example.spring_autowired_npe.MileageFeeCalculator.mileageCharge(MileageFeeCalculator.java:13)
    at com.chrylis.example.spring_autowired_npe.MileageFeeController.mileageFee(MileageFeeController.java:14)
    ...
Author: Taryn, 2013-11-11

12 answers

El campo anotado @Autowired es null porque Spring no sabe acerca de la copia de MileageFeeCalculator que creaste con new y no sabía autowire.

El contenedor Spring Inversion of Control (IoC) tiene tres componentes lógicos principales: un registro (llamado ApplicationContext) de componentes (beans) que están disponibles para ser utilizados por la aplicación, un sistema configurador que inyecta las dependencias de los objetos en ellos haciendo coincidir las dependencias con beans en el contexto, y solucionador de dependencias que puede ver una configuración de muchos frijoles diferentes y determinar cómo instanciarlos y configurarlos en el orden necesario.

El contenedor IoC no es mágico, y no tiene forma de saber acerca de los objetos Java a menos que de alguna manera le informe de ellos. Cuando llamas a new, la JVM crea una instancia de una copia del nuevo objeto y te la entrega directamente--nunca pasa por el proceso de configuración. Hay tres maneras en las que puedes obtener tus frijoles configurar.

He publicado todo este código, usando Spring Boot para iniciar, en este proyecto GitHub; puedes mirar un proyecto completo en ejecución para cada enfoque para ver todo lo que necesitas para que funcione. Etiqueta con el NullPointerException: nonworking

Inyecte sus frijoles

La opción más preferible es dejar que Spring autowire todos sus frijoles; esto requiere la menor cantidad de código y es el más mantenible. Para hacer que el autowiring funcione como usted se busca, también autowire el MileageFeeCalculator así:

@Controller
public class MileageFeeController {

    @Autowired
    private MileageFeeCalculator calc;

    @RequestMapping("/mileage/{miles}")
    @ResponseBody
    public float mileageFee(@PathVariable int miles) {
        return calc.mileageCharge(miles);
    }
}

Si necesita crear una nueva instancia de su objeto de servicio para diferentes solicitudes, aún puede usar injection usando los ámbitos Spring bean.

Etiqueta que funciona inyectando el objeto de servicio @MileageFeeCalculator : working-inject-bean

Use @Configurable

Si realmente necesita que los objetos creados con new se conecten automáticamente, puede usar la anotación Spring @Configurable junto con AspectJ compile-time weaving para inyectar sus objetos. Este enfoque inserta código en el constructor del objeto que alerta a Spring de que se está creando para que Spring pueda configurar la nueva instancia. Esto requiere un poco de configuración en su compilación (como compilar con ajc) y activar los manejadores de configuración de tiempo de ejecución de Spring (@EnableSpringConfigured con la sintaxis JavaConfig). Este enfoque es utilizado por el sistema Roo Active Record para permitir que new las instancias de sus entidades obtengan lo necesario información de persistencia inyectada.

@Service
@Configurable
public class MileageFeeCalculator {

    @Autowired
    private MileageRateService rateService;

    public float mileageCharge(final int miles) {
        return (miles * rateService.ratePerMile());
    }
}

Etiqueta que funciona usando @Configurable en el objeto de servicio: working-configurable

Búsqueda manual de frijoles: no se recomienda

Este enfoque solo es adecuado para interactuar con código heredado en situaciones especiales. Casi siempre es preferible crear una clase de adaptador singleton que Spring pueda conectar automáticamente y que el código heredado pueda llamar, pero es posible preguntar directamente al contexto de la aplicación Spring frijol.

Para hacer esto, necesita una clase a la que Spring pueda dar una referencia al objeto ApplicationContext:

@Component
public class ApplicationContextHolder implements ApplicationContextAware {
    private static ApplicationContext context;

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

    public static ApplicationContext getContext() {
        return context;
    }
}

Entonces su código heredado puede llamar a getContext() y recuperar los frijoles que necesita:

@Controller
public class MileageFeeController {    
    @RequestMapping("/mileage/{miles}")
    @ResponseBody
    public float mileageFee(@PathVariable int miles) {
        MileageFeeCalculator calc = ApplicationContextHolder.getContext().getBean(MileageFeeCalculator.class);
        return calc.mileageCharge(miles);
    }
}

Etiqueta que funciona al buscar manualmente el objeto de servicio en el contexto Spring: working-manual-lookup

 517
Author: chrylis,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-02-28 19:40:42

Si no está codificando una aplicación web, asegúrese de que su clase en la que se realiza @Autowiring sea un frijol de primavera. Por lo general, spring container no será consciente de la clase que podríamos pensar como un frijol de primavera. Tenemos que decirle al contenedor de primavera sobre nuestras clases de primavera.

Esto se puede lograr configurando en appln-contxt o la mejor manera es anotar la clase como @Component y por favor no cree la clase anotada usando el nuevo operador. Asegúrate de conseguirlo de Appln-contexto como abajo.

@Component
public class MyDemo {


    @Autowired
    private MyService  myService; 

    /**
     * @param args
     */
    public static void main(String[] args) {
        // TODO Auto-generated method stub
            System.out.println("test");
            ApplicationContext ctx=new ClassPathXmlApplicationContext("spring.xml");
            System.out.println("ctx>>"+ctx);

            Customer c1=null;
            MyDemo myDemo=ctx.getBean(MyDemo.class);
            System.out.println(myDemo);
            myDemo.callService(ctx);


    }

    public void callService(ApplicationContext ctx) {
        // TODO Auto-generated method stub
        System.out.println("---callService---");
        System.out.println(myService);
        myService.callMydao();

    }

}
 46
Author: Shirish Coolkarni,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-01-02 15:01:05

En realidad, debe usar objetos administrados por JVM o Objetos administrados por Spring para invocar métodos. a partir de su código anterior en su clase controller, está creando un nuevo objeto para llamar a su clase de servicio que tiene un objeto cableado automático.

MileageFeeCalculator calc = new MileageFeeCalculator();

Así que no funcionará de esa manera.

La solución hace que este MileageFeeCalculator sea un objeto auto-cableado en el propio Controlador.

Cambie su clase de Controlador como abajo.

@Controller
public class MileageFeeController {

    @Autowired
    MileageFeeCalculator calc;  

    @RequestMapping("/mileage/{miles}")
    @ResponseBody
    public float mileageFee(@PathVariable int miles) {
        return calc.mileageCharge(miles);
    }
}
 22
Author: Ravi Durairaj,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-07-26 19:21:07

Una vez me encontré con el mismo problema cuando no estaba muy acostumbrado a the life in the IoC world. El campo @Autowired de uno de mis beans es nulo en tiempo de ejecución.

La causa raíz es, en lugar de usar el bean auto-creado mantenido por el contenedor Spring IoC (cuyo campo @Autowired es indeed correctamente inyectado), soy newing mi propia instancia de ese tipo de bean y lo uso. Por supuesto, el campo @Autowired de este es nulo porque Spring no tiene posibilidad de inyectarlo.

 21
Author: smwikipedia,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-05-03 08:46:48

Su problema es nuevo (creación de objetos en estilo java)

MileageFeeCalculator calc = new MileageFeeCalculator();

Con anotación @Service, @Component, @Configuration habas se crean en el
contexto de la aplicación de Spring cuando se inicia el servidor. Pero cuando creamos objetos usando operador nuevo el objeto no se registra en el contexto de la aplicación que ya está creado. Por Ejemplo Empleado.clase java que he usado.

Mira esto:

public class ConfiguredTenantScopedBeanProcessor implements BeanFactoryPostProcessor {

@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
    String name = "tenant";
    System.out.println("Bean factory post processor is initialized"); 
    beanFactory.registerScope("employee", new Employee());

    Assert.state(beanFactory instanceof BeanDefinitionRegistry,
            "BeanFactory was not a BeanDefinitionRegistry, so CustomScope cannot be used.");
    BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;

    for (String beanName : beanFactory.getBeanDefinitionNames()) {
        BeanDefinition definition = beanFactory.getBeanDefinition(beanName);
        if (name.equals(definition.getScope())) {
            BeanDefinitionHolder proxyHolder = ScopedProxyUtils.createScopedProxy(new BeanDefinitionHolder(definition, beanName), registry, true);
            registry.registerBeanDefinition(beanName, proxyHolder.getBeanDefinition());
        }
    }
}

}
 17
Author: Deepak,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-04-12 10:43:22

Parece ser un caso raro, pero esto es lo que me pasó:

Usamos @Inject en lugar de @Autowired que es el estándar javaee soportado por Spring. En cada lugar funcionó bien y los frijoles se inyectaron correctamente, en lugar de un solo lugar. La inyección de frijol parece la misma

@Inject
Calculator myCalculator

Por fin encontramos que el error fue que nosotros (en realidad, la función Eclipse auto complete) importamos com.opensymphony.xwork2.Inject en lugar de javax.inject.Inject!

Entonces, para resumir, asegúrese de que sus anotaciones (@Autowired, @Inject, @Service ,... ) tener paquetes correctos!

 8
Author: Alireza Fattahi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-04-20 13:07:20

Soy nuevo en la primavera, pero descubrí esta solución de trabajo. Por favor, dime si es una forma obsoleta.

Hago Spring inject applicationContext en este frijol:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;

@Component
public class SpringUtils {

    public static ApplicationContext ctx;

    /**
     * Make Spring inject the application context
     * and save it on a static variable,
     * so that it can be accessed from any point in the application. 
     */
    @Autowired
    private void setApplicationContext(ApplicationContext applicationContext) {
        ctx = applicationContext;       
    }
}

Puede poner este código también en la clase de aplicación principal si lo desea.

Otras clases pueden usarlo así:

MyBean myBean = (MyBean)SpringUtils.ctx.getBean(MyBean.class);

De esta manera cualquier frijol puede ser obtenido por cualquier objeto en la aplicación (también intanciado con new) y de una manera estática.

 7
Author: bluish,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-05-14 12:44:17

Creo que se ha perdido para instruir a spring para escanear las clases con anotación.

Puede usar @ComponentScan("packageToScan") en la clase de configuración de su aplicación spring para indicar a spring que escanee.

@Service, @Component etc anotaciones añadir meta descripción.

Spring solo inyecta instancias de aquellas clases que se crean como bean o se marcan con anotación.

Las clases marcadas con anotación deben ser identificadas por spring antes de inyectar, @ComponentScan instruir spring look para las clases marcadas con anotación. Cuando Spring encuentra @Autowired busca el bean relacionado e inyecta la instancia requerida.

Agregar solo anotación, no arregla ni facilita la inyección de dependencias, Spring necesita saber dónde buscar.

 3
Author: msucil,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-01-10 19:34:07

Otra solución sería poner llamada: SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this)
Para el constructor de MileageFeeCalculator como este:

@Service
public class MileageFeeCalculator {

    @Autowired
    private MileageRateService rateService; // <--- will be autowired when constructor is called

    public MileageFeeCalculator() {
        SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this)
    }

    public float mileageCharge(final int miles) {
        return (miles * rateService.ratePerMile()); 
    }
}
 2
Author: Ondrej Bozek,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-04-21 11:42:09

También puede solucionar este problema utilizando la anotación @Service en la clase service y pasando el bean ClassA requerido como parámetro al otro constructor beans ClassB y anotar el constructor de ClassB con @Autowired. Fragmento de muestra aquí:

@Service
public class ClassB {

    private ClassA classA;

    @Autowired
    public ClassB(ClassA classA) {
        this.classA = classA;
    }

    public void useClassAObjectHere(){
        classA.callMethodOnObjectA();
    }
}
 2
Author: Abhishek Pandey,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-10-13 18:56:38

ACTUALIZACIÓN: Las personas realmente inteligentes se apresuraron a señalar esta respuesta, que explica la rareza, descrita a continuación

RESPUESTA ORIGINAL:

No se si ayuda a alguien, pero estaba atascado con el mismo problema incluso mientras hacía las cosas aparentemente bien. En mi método Principal, tengo un código como este:

ApplicationContext context =
    new ClassPathXmlApplicationContext(new String[] {
        "common.xml",
        "token.xml",
        "pep-config.xml" });
    TokenInitializer ti = context.getBean(TokenInitializer.class);

Y en un archivo token.xml he tenido una línea

<context:component-scan base-package="package.path"/>

Me di cuenta de que el paquete.el camino ya no existe, así que acabo de caer la línea para siempre.

Y después de eso, NPE comenzó a entrar. En un pep-config.xml solo tenía 2 frijoles:

<bean id="someAbac" class="com.pep.SomeAbac" init-method="init"/>
<bean id="settings" class="com.pep.Settings"/>

Y la clase SomeAbac tiene una propiedad declarada como

@Autowired private Settings settings;

Por alguna razón desconocida, settings es null en init(), cuando el elemento <context:component-scan/> no está presente en absoluto, pero cuando está presente y tiene algunas bs como paquete base, todo funciona bien. Esta línea ahora se ve así:

<context:component-scan base-package="some.shit"/>

Y funciona. Puede ser que alguien pueda dar una explicación, pero para mí es suficiente ahora mismo )

 2
Author: 62mkv,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-04-12 10:42:27

Si esto está sucediendo en una clase de prueba, asegúrese de que no ha olvidado anotar la clase.

Por ejemplo, en Spring Boot :

@RunWith(SpringRunner.class)
@SpringBootTest
public class MyTests {
    ....
 1
Author: nobar,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-04-12 18:25:12