¿Por qué es mi campo de primavera con cableado automático nulo

java spring null nullpointerexception autowired


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

Tengo un resorte @Service clase ( MileageFeeCalculator ) que tiene un @Autowired campo ( rateService ), pero el campo es null cuando trato de usarlo. Los registros muestran que se están creando tanto el bean MileageFeeCalculator como el bean MileageRateService , pero obtengo una NullPointerException cada vez que intento llamar al método mileageCharge en mi bean de servicio. ¿Por qué no 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);
    }
}

Clase de servicio:

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

Service bean que debe conectarse automáticamente en MileageFeeCalculator pero no lo es:

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

Cuando trato de GET /mileage/3 , obtengo esta excepción:

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)
    ...



Answer 1 chrylis -on strike-


El campo anotado @Autowired es null porque Spring no sabe acerca de la copia de MileageFeeCalculator que creó con new y no sabía cómo conectarlo automáticamente.

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 haciendo coincidirlos las dependencias con beans en el contexto, y un solucionador de dependencias que puede ver una configuración de muchos beans diferentes y determinar cómo instanciarlos y configurarlos en el orden necesario.

El contenedor de IoC no es mágico, y no tiene forma de conocer los objetos Java a menos que de alguna manera le informe de ellos. Cuando llama a new , la JVM crea una copia del nuevo objeto y se lo entrega directamente; nunca pasa por el proceso de configuración. Hay tres formas de configurar los beans.

He publicado todo este código, usando Spring Boot para el lanzamiento, en este proyecto de GitHub ; puede ver un proyecto en ejecución completo para cada enfoque para ver todo lo que necesita para que funcione. Etiqueta con la NullPointerException : nonworking

Inyecta tus frijoles

La opción más preferible es dejar que Spring conecte automáticamente todos sus frijoles; esto requiere la menor cantidad de código y es el más fácil de mantener. Para hacer que el cableado automático funcione como usted desea, también cablee automáticamente el MileageFeeCalculator de esta manera:

@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 la inyección utilizando los ámbitos de Spring Bean .

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

Usar @Configurable

Si realmente necesita que los objetos creados con new se conecten automáticamente, puede usar la anotación Spring @Configurable junto con el tejido de tiempo de compilación AspectJ para inyectar sus objetos. Este enfoque inserta código en el constructor de su objeto que alerta a Spring de que está siendo creado 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 controladores de configuración de tiempo de ejecución de Spring ( @EnableSpringConfigured con la sintaxis JavaConfig). El sistema Roo Active Record utiliza este enfoque para permitir que las new instancias de sus entidades obtengan la información de persistencia necesaria inyectada.

@Service
@Configurable
public class MileageFeeCalculator {

    @Autowired
    private MileageRateService rateService;

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

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

Búsqueda manual de frijoles:no se recomienda

Este enfoque sólo es adecuado para la interfaz con el código heredado en situaciones especiales.Casi siempre es preferible crear una clase de adaptador simple que Spring pueda autocablear y que el código heredado pueda llamar,pero es posible pedir directamente al contexto de la aplicación de Spring un 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;
    }
}

Luego, su código heredado puede llamar a getContext() y recuperar los beans 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 de Spring: working-manual-lookup




Answer 2 Shirish Coolkarni


Si no estás codificando una aplicación web,asegúrate de que tu clase en la que se hace el @Autocableado es un frijolito.Típicamente,el contenedor de resortes 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 no cree la clase anotada con un nuevo operador. Asegúrese de obtenerlo de Appln-context como se muestra a continuación.

@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();

    }

}



Answer 3 Ravi Durairaj


En realidad,deberías usar tanto los objetos gestionados por la JVM como los gestionados por Spring para invocar métodos.Desde el código anterior en tu clase de controlador,estás creando un nuevo objeto para llamar a tu clase de servicio que tiene un objeto auto-cableado.

MileageFeeCalculator calc = new MileageFeeCalculator();

así que no funcionará de esa manera.

La solución hace que esta MileageFeeCalculator como 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);
    }
}



Answer 4 smwikipedia


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

La raíz del problema es, en lugar de utilizar el grano de auto-creado mantenida por el contenedor de primavera de la COI (cuya @Autowired campo está indeed correctamente inyectado), estoy newing mi propia instancia de ese tipo de grano y su uso. Por supuesto, este campo @Autowired es nulo porque Spring no tiene oportunidad de inyectarlo.




Answer 5 Deepak


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

MileageFeeCalculator calc = new MileageFeeCalculator();

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

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());
        }
    }
}

}