java - component - repository null spring
¿Por qué mi campo Spring @Autowired es nulo? (12)
Creo que te has perdido la instrucción de saltar a 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
anotaciones de @Service, @Component
etc. agregan una 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 una anotación deben identificarse con la primavera antes de inyectarlas, @ComponentScan
instruye a las clases marcadas con anotaciones en la primavera. Cuando Spring encuentra @Autowired
, busca el bean relacionado e inyecta la instancia requerida.
Agregando solo anotaciones, no corrige ni facilita la inyección de dependencia, Spring necesita saber dónde buscar.
Nota: se pretende que sea una respuesta canónica para un problema común.
Tengo una clase Spring @Service
( MileageFeeCalculator
) que tiene un campo rateService
( rateService
), pero el campo es null
cuando trato de usarlo. Los registros muestran que tanto el bean MileageFeeCalculator
como el bean MileageRateService
se están creando, pero recibo una NullPointerException
cada vez que trato de llamar al método mileageCharge
en mi bean del servicio. ¿Por qué Spring no está conectando automáticamente 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
}
}
Bean de servicio que debe ser autowired en MileageFeeCalculator
pero no es:
@Service
public class MileageRateService {
public float ratePerMile() {
return 0.565f;
}
}
Cuando intento obtener 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)
...
El campo anotado @Autowired
es null
porque Spring no conoce la copia de MileageFeeCalculator
que creó con la new
y no sabía cómo realizarla 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 en ellos mediante la comparación 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 IoC no es mágico, y no tiene forma de saber acerca de los objetos de Java a menos que de alguna manera se lo informe. Cuando llama a new
, la JVM crea una copia de la copia del nuevo objeto y se lo entrega directamente a usted, nunca pasa por el proceso de configuración. Hay tres formas de configurar tus beans.
He publicado todo este código, utilizando Spring Boot para iniciar, en este proyecto de GitHub ; Puede ver un proyecto en ejecución completa 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 tus beans; 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 quería, también conecte el MileageFeeCalculator
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 beans de Spring .
Etiqueta que funciona al inyectar el objeto de servicio @MileageFeeCalculator
: working-inject-bean
Use @Configurable
Si realmente necesita que los objetos creados con new
se enciendan automáticamente, puede usar la anotación Spring @Configurable
junto con el tejido en tiempo de compilación de AspectJ para inyectar sus objetos. Este enfoque inserta código en el constructor de su 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 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 recomendado
Este enfoque es adecuado solo para interactuar con el código heredado en situaciones especiales. Casi siempre es preferible crear una clase de adaptador singleton al que Spring pueda autowire y que pueda llamar el código heredado, pero es posible pedir directamente al contexto de la aplicación Spring un bean.
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
En realidad, debe utilizar objetos gestionados por JVM o objetos gestionados 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 de conexión automática.
MileageFeeCalculator calc = new MileageFeeCalculator();
así que no funcionará de esa manera.
La solución hace que este MileageFeeCalculator sea un objeto de conexión automática en el propio Controlador.
Cambie su clase de controlador como a continuación.
@Controller
public class MileageFeeController {
@Autowired
MileageFeeCalculator calc;
@RequestMapping("/mileage/{miles}")
@ResponseBody
public float mileageFee(@PathVariable int miles) {
return calc.mileageCharge(miles);
}
}
Otra solución sería poner call: SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this)
Al constructor 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());
}
}
Parece ser un caso raro, pero aquí está lo que me pasó:
Utilizamos @Inject
lugar de @Autowired
que es un estándar javaee soportado por Spring. En todos los lugares 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
Finalmente, descubrimos que el error era que nosotros (en realidad, la función de autocompletado de Eclipse) com.opensymphony.xwork2.Inject
lugar de javax.inject.Inject
!
Entonces, para resumir, asegúrese de que sus anotaciones ( @Autowired
, @Inject
, @Service
, ...) tengan los paquetes correctos.
Si esto sucede en una clase de prueba, asegúrese de no haber olvidado anotar la clase.
Por ejemplo, en Spring Boot :
@RunWith(SpringRunner.class)
@SpringBootTest
public class MyTests {
....
Si no está codificando una aplicación web, asegúrese de que su clase en la que se realiza @Autowiring es un bean spring. Normalmente, el contenedor de primavera no estará al tanto de la clase que podríamos considerar 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 un nuevo operador. Asegúrese de obtenerlo desde el contexto de Appln 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();
}
}
También puede solucionar este problema utilizando la anotación @Service en la clase de servicio y pasando la clase A de bean requerida como un parámetro al otro constructor de classB de beans 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();
}
}
Tu problema es nuevo (creación de objetos en estilo java)
MileageFeeCalculator calc = new MileageFeeCalculator();
Con la anotación @Service
, @Component
, @Configuration
beans se crean en el
contexto de la aplicación de Spring cuando se inicia el servidor. Pero cuando creamos objetos utilizando 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:
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());
}
}
}
}
Una vez me encontré con el mismo problema cuando no estaba acostumbrada a the life in the IoC world
. El campo @Autowired
de uno de mis beans es nulo en el tiempo de ejecución.
La causa principal es que, en lugar de utilizar el bean creado automáticamente mantenido por el contenedor Spring IoC (cuyo campo @Autowired
se inyecta correctamente), estoy newing
mi propia instancia de ese tipo de bean y usándolo. Por supuesto, el campo @Autowired
de @Autowired
es nulo porque Spring no tiene posibilidad de inyectarlo.
Soy nuevo en Spring, pero descubrí esta solución de trabajo. Por favor, dime si es una manera despreciable.
Hago Spring inyectar applicationContext
en este bean:
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;
}
}
Si lo desea, puede poner este código en la clase de aplicación principal.
Otras clases pueden usarlo así:
MyBean myBean = (MyBean)SpringUtils.ctx.getBean(MyBean.class);
De este modo, cualquier objeto en la aplicación puede obtener cualquier bean (también intentado con new
) y de forma estática .
ACTUALIZACIÓN: las personas realmente inteligentes se apresuraron a señalar this respuesta, lo que explica la rareza, que se describe a continuación
RESPUESTA ORIGINAL:
No sé si ayuda a alguien, pero me quedé con el mismo problema, incluso cuando 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"/>
Noté 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 entrar. En un pep-config.xml
solo tenía 2 beans:
<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, la configuración es nula en init (), cuando el elemento <context:component-scan/>
no está presente en absoluto, pero cuando está presente y tiene algunos 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.