Nota: Esto pretende ser una respuesta canónica para un problema común.
Tengo una @Service
clase Spring ( MileageFeeCalculator
) que tiene un @Autowired
campo ( rateService
), pero el campo es null
cuando trato de usarlo. Los registros muestran que tanto el MileageFeeCalculator
bean como el MileageRateService
bean se están creando, pero recibo un NullPointerException
mensaje cada vez que intento llamar al mileageCharge
método 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 MileageFeeCalculator
pero no lo es:
@Service
public class MileageRateService {
public float ratePerMile() {
return 0.565f;
}
}
Cuando intento 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)
...
F
se llama a bean dentro del constructor de otro beanS
. En este caso, pase el bean requeridoF
como parámetro al otroS
constructor de beans y anote el constructor deS
with@Autowire
. Recuerde anotar la clase del primer beanF
con@Component
.Respuestas:
El campo anotado
@Autowired
senull
debe a que Spring no sabe acerca de la copia de laMileageFeeCalculator
que creónew
y no sabía cómo conectarla 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 coincidir los 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 de Java a menos que de alguna manera le informe de ellos. Cuando llama
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 el
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 realice el cableado automático de
MileageFeeCalculator
esta manera:Si necesita crear una nueva instancia de su objeto de servicio para diferentes solicitudes, aún puede usar la inyección usando los ámbitos de Spring Bean .
Etiqueta que funciona inyectando el
@MileageFeeCalculator
objeto de servicio:working-inject-bean
Use @Configurable
Si realmente necesita objetos creados con
new
autoconexión, puede usar la@Configurable
anotación Spring 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 conajc
) y activar los controladores 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 lasnew
instancias de sus entidades obtengan la información de persistencia necesaria inyectada.Etiqueta que funciona mediante el uso
@Configurable
en el objeto de servicio:working-configurable
Búsqueda manual de frijoles: no recomendado
Este enfoque es adecuado solo para interactuar con código heredado en situaciones especiales. Casi siempre es preferible crear una clase de adaptador singleton que Spring pueda autoalambrar y que el código heredado pueda llamar, pero es posible solicitar directamente un bean en el contexto de la aplicación Spring.
Para hacer esto, necesita una clase a la que Spring pueda dar una referencia al
ApplicationContext
objeto:Luego, su código heredado puede llamar
getContext()
y recuperar los beans que necesita:Etiqueta que funciona buscando manualmente el objeto de servicio en el contexto Spring:
working-manual-lookup
fuente
@Configuration
bean, donde se anota el método para hacer una instancia de una clase de bean particular@Bean
.@Bean
métodos cuando usas Spring Proxy AOP?@Autowired MileageFeeCalculator calc
. ¿Alguna idea?ApplicationContext
. Algunos usuarios (para los cuales he cerrado como duplicados) no entienden esto.Si no está codificando una aplicación web, asegúrese de que su clase en la que se realiza @Autowiring sea un bean de primavera. Por lo general, el contenedor de primavera no se dará cuenta de la clase que podríamos considerar un grano de primavera. Tenemos que decirle al contenedor Spring acerca de 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.
fuente
En realidad, debe usar Objetos administrados por JVM u Objeto administrado por Spring para invocar métodos. a partir de su código anterior en su clase de controlador, está creando un nuevo objeto para llamar a su clase de servicio que tiene un objeto con cableado automático.
entonces no funcionará de esa manera.
La solución hace que este MileageFeeCalculator sea un objeto con cableado automático en el Controlador mismo.
Cambie su clase de controlador como a continuación.
fuente
Una vez me encontré con el mismo problema cuando no estaba acostumbrado
the life in the IoC world
. El@Autowired
campo de uno de mis beans es nulo en tiempo de ejecución.La causa raíz es que, en lugar de usar el bean creado automáticamente mantenido por el contenedor Spring IoC (cuyo
@Autowired
campo seindeed
inyectó correctamente), soynewing
mi propia instancia de ese tipo de bean y lo uso. Por supuesto, este@Autowired
campo es nulo porque Spring no tiene oportunidad de inyectarlo.fuente
Su problema es nuevo (creación de objetos en estilo java)
Con la anotación
@Service
,@Component
,@Configuration
habas se crean en elcontexto de la aplicación de la primavera 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, la clase Employee.java que he usado.
Mira esto:
fuente
Soy nuevo en Spring, pero descubrí esta solución de trabajo. Por favor dime si es una forma despreciable.
Hago Spring inject
applicationContext
en este bean:Puede poner este código también en la clase de aplicación principal si lo desea.
Otras clases pueden usarlo así:
De esta forma, cualquier objeto de la aplicación puede obtener cualquier bean (también se puede identificar
new
) y de forma estática .fuente
Parece ser un caso raro, pero esto es lo que me pasó:
Usamos en
@Inject
lugar de@Autowired
cuál es el estándar Java soportado por Spring. Todos los lugares funcionaron bien y los frijoles se inyectaron correctamente, en lugar de un solo lugar. La inyección de frijol parece igual¡Por fin descubrimos que el error fue que nosotros (en realidad, la función de autocompletar Eclipse) importamos en
com.opensymphony.xwork2.Inject
lugar dejavax.inject.Inject
!Entonces, para resumir, asegúrese de que sus anotaciones (
@Autowired
,@Inject
,@Service
, ...) tienen paquetes correctos!fuente
Creo que te has perdido para indicarle a Spring que escanee las clases con anotaciones.
Puede usar
@ComponentScan("packageToScan")
en la clase de configuración de su aplicación Spring para indicarle a Spring que escanee.@Service, @Component
las anotaciones etc. agregan meta descripción.Spring solo inyecta instancias de esas clases que se crean como bean o se marcan con anotaciones.
Las clases marcadas con anotación deben identificarse por resorte antes de inyectar,
@ComponentScan
instruir la búsqueda de primavera para las clases marcadas con anotación. Cuando Spring lo encuentra@Autowired
, busca el bean relacionado e inyecta la instancia requerida.Agregando solo anotaciones, no repara ni facilita la inyección de dependencia, Spring necesita saber dónde buscar.
fuente
<context:component-scan base-package="com.mypackage"/>
a mibeans.xml
archivoSi esto sucede en una clase de prueba, asegúrese de no haber olvidado anotar la clase.
Por ejemplo, en Spring Boot :
Transcurre algún tiempo ...
Spring Boot continúa evolucionando . Ya no es necesario usarlo
@RunWith
si usa la versión correcta de JUnit .Para
@SpringBootTest
trabajar de forma independiente, debe usar@Test
desde JUnit5 en lugar de JUnit4 .Si se consigue este mal configuración de sus pruebas se compilan, pero
@Autowired
y@Value
campos (por ejemplo) seránnull
. Dado que Spring Boot funciona por arte de magia, es posible que tenga pocas vías para depurar directamente este error.fuente
@Value
será nulo cuando se use constatic
campos.Otra solución sería poner call:
SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this)
al constructor MileageFeeCalculator así:
fuente
ACTUALIZACIÓN: las personas realmente inteligentes se apresuraron a señalar esta respuesta, que explica la rareza, que se describe a continuación
RESPUESTA ORIGINAL:
No sé si ayuda a alguien, pero me quedé atrapado con el mismo problema incluso mientras hacía las cosas aparentemente bien. En mi método Main, tengo un código como este:
y en un
token.xml
archivo he tenido una líneaMe di cuenta de que el package.path ya no existe, por lo que acabo de dejar la línea para siempre.
Y después de eso, NPE comenzó a llegar. En un
pep-config.xml
solo tenía 2 frijoles:y la clase SomeAbac tiene una propiedad declarada como
por alguna razón desconocida, la configuración es nula en init (), cuando el
<context:component-scan/>
elemento no está presente en absoluto, pero cuando está presente y tiene algunos bs como basePackage, todo funciona bien. Esta línea ahora se ve así:y funciona. Puede ser que alguien pueda dar una explicación, pero para mí es suficiente en este momento)
fuente
<context:component-scan/>
implícitamente habilita lo<context:annotation-config/>
necesario para@Autowired
que funcione.Este es el culpable de dar NullPointerException
MileageFeeCalculator calc = new MileageFeeCalculator();
Estamos usando Spring, no es necesario crear objetos manualmente. La creación de objetos será atendida por el contenedor IoC.fuente
También puede solucionar este problema utilizando la anotación @Service en la clase de servicio y pasando la clase de bean A requerida como parámetro al otro constructor de clase B de beans y anote el constructor de la clase B con @Autowired. Fragmento de muestra aquí:
fuente
Lo que no se ha mencionado aquí se describe en este artículo en el párrafo "Orden de ejecución".
Después de "aprender" que tenía que anotar una clase con @Component o los derivados @Service o @Repository (supongo que hay más), para conectar automáticamente otros componentes dentro de ellos, me sorprendió que estos otros componentes aún fueran nulos dentro del constructor del componente padre.
El uso de @PostConstruct resuelve que:
y:
fuente
Esto solo es válido en caso de prueba unitaria.
Mi clase de servicio tenía una anotación de servicio y era
@autowired
otra clase de componente. Cuando probé, la clase de componente se estaba volviendo nula. Porque para la clase de servicio estaba creando el objeto usandonew
Si está escribiendo una prueba unitaria, asegúrese de no crear objetos utilizando
new object()
. Utilice en su lugar injectMock.Esto solucionó mi problema. Aquí hay un enlace útil
fuente
También tenga en cuenta que si, por cualquier razón, realiza un método en un
@Service
asfinal
, los beans con conexión automática a los que accederá siempre seránnull
.fuente
En palabras simples, existen principalmente dos razones para que un
@Autowired
campo seanull
Tu clase no es un frijol de primavera.
El campo no es un frijol.
fuente
No está completamente relacionado con la pregunta, pero si la inyección de campo es nula, la inyección basada en el constructor seguirá funcionando bien.
fuente