本文翻译自:Why is my Spring @Autowired field null?
Note: This is intended to be a canonical answer for a common problem. 注意:这旨在作为常见问题的规范答案。
I have a Spring @Service
class ( MileageFeeCalculator
) that has an @Autowired
field ( rateService
), but the field is null
when I try to use it. 我有一个Spring @Service
类( MileageFeeCalculator
),它具有一个@Autowired
字段( rateService
),但是当我尝试使用它时,该字段为null
。 The logs show that both the MileageFeeCalculator
bean and the MileageRateService
bean are being created, but I get a NullPointerException
whenever I try to call the mileageCharge
method on my service bean. 日志显示同时创建了MileageFeeCalculator
Bean和MileageRateService
Bean,但是每当尝试在服务Bean上调用mileageCharge
方法时,都会收到NullPointerException
。 Why isn't Spring autowiring the field? Spring为什么不自动接线该领域?
Controller class: 控制器类:
@Controller
public class MileageFeeController {
@RequestMapping("/mileage/{miles}")
@ResponseBody
public float mileageFee(@PathVariable int miles) {
MileageFeeCalculator calc = new MileageFeeCalculator();
return calc.mileageCharge(miles);
}
}
Service class: 服务等级:
@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 that should be autowired in MileageFeeCalculator
but it isn't: 应该在MileageFeeCalculator
自动MileageFeeCalculator
服务bean,但不是:
@Service
public class MileageRateService {
public float ratePerMile() {
return 0.565f;
}
}
When I try to GET /mileage/3
, I get this exception: 当我尝试GET /mileage/3
,出现以下异常:
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)
...
#1楼
参考:https://stackoom.com/question/1LU5G/为什么我的Spring-Autowired字段为空
#2楼
The field annotated @Autowired
is null
because Spring doesn't know about the copy of MileageFeeCalculator
that you created with new
and didn't know to autowire it. 注释为@Autowired
的字段为null
因为Spring不知道您使用new
创建的MileageFeeCalculator
的副本,也不知道自动对其进行MileageFeeCalculator
。
The Spring Inversion of Control (IoC) container has three main logical components: a registry (called the ApplicationContext
) of components (beans) that are available to be used by the application, a configurer system that injects objects' dependencies into them by matching up the dependencies with beans in the context, and a dependency solver that can look at a configuration of many different beans and determine how to instantiate and configure them in the necessary order. Spring Inversion of Control(IoC)容器具有三个主要的逻辑组件:组件(bean)的注册表(称为ApplicationContext
)可供应用程序使用,配置程序系统通过匹配将对象的依赖项注入对象上下文中具有bean的依赖关系,以及一个依赖关系求解程序,它可以查看许多不同bean的配置,并确定如何以必要的顺序实例化和配置它们。
The IoC container isn't magic, and it has no way of knowing about Java objects unless you somehow inform it of them. IoC容器不是魔术,除非您以某种方式告知Java对象,否则它无法了解Java对象。 When you call new
, the JVM instantiates a copy of the new object and hands it straight to you--it never goes through the configuration process. 当您调用new
,JVM会实例化新对象的副本并将其直接交给您-它从未经历配置过程。 There are three ways that you can get your beans configured. 您可以通过三种方式配置bean。
I have posted all of this code, using Spring Boot to launch, at this GitHub project ; 我已经在GitHub项目上使用Spring Boot启动了所有这些代码; you can look at a full running project for each approach to see everything you need to make it work. 您可以针对每种方法查看一个正在运行的项目,以查看使其工作所需的一切。 Tag with the NullPointerException
: nonworking
带有NullPointerException
标记: nonworking
Inject your beans 注入你的豆子
The most preferable option is to let Spring autowire all of your beans; 最可取的选择是让Spring自动连接所有bean。 this requires the least amount of code and is the most maintainable. 这需要最少的代码量,并且最易于维护。 To make the autowiring work like you wanted, also autowire the MileageFeeCalculator
like this: 要使自动装配工作如您所愿,还可以如下方式自动连接MileageFeeCalculator
:
@Controller
public class MileageFeeController {
@Autowired
private MileageFeeCalculator calc;
@RequestMapping("/mileage/{miles}")
@ResponseBody
public float mileageFee(@PathVariable int miles) {
return calc.mileageCharge(miles);
}
}
If you need to create a new instance of your service object for different requests, you can still use injection by using the Spring bean scopes . 如果您需要为不同的请求创建服务对象的新实例,则仍可以通过Spring bean scopes使用注入。
Tag that works by injecting the @MileageFeeCalculator
service object: working-inject-bean
通过注入@MileageFeeCalculator
服务对象而起作用的标记: working-inject-bean
Use @Configurable 使用@Configurable
If you really need objects created with new
to be autowired, you can use the Spring @Configurable
annotation along with AspectJ compile-time weaving to inject your objects. 如果确实需要自动创建使用new
创建的对象,则可以使用Spring @Configurable
批注以及AspectJ编译时编织来注入对象。 This approach inserts code into your object's constructor that alerts Spring that it's being created so that Spring can configure the new instance. 这种方法将代码插入到对象的构造函数中,以警告Spring正在创建它,以便Spring可以配置新实例。 This requires a bit of configuration in your build (such as compiling with ajc
) and turning on Spring's runtime configuration handlers ( @EnableSpringConfigured
with the JavaConfig syntax). 这需要在构建中进行一些配置(例如,使用ajc
编译)并打开Spring的运行时配置处理程序(使用JavaConfig语法的@EnableSpringConfigured
)。 This approach is used by the Roo Active Record system to allow new
instances of your entities to get the necessary persistence information injected. Roo Active Record系统使用这种方法来允许实体的new
实例获取注入的必要持久性信息。
@Service
@Configurable
public class MileageFeeCalculator {
@Autowired
private MileageRateService rateService;
public float mileageCharge(final int miles) {
return (miles * rateService.ratePerMile());
}
}
Tag that works by using @Configurable
on the service object: working-configurable
通过在服务对象上使用@Configurable
起作用的标记: working-configurable
Manual bean lookup: not recommended 手动查找bean:不建议
This approach is suitable only for interfacing with legacy code in special situations. 这种方法仅适用于在特殊情况下与遗留代码接口。 It is nearly always preferable to create a singleton adapter class that Spring can autowire and the legacy code can call, but it is possible to directly ask the Spring application context for a bean. 几乎总是最好创建一个Spring可以自动装配并且遗留代码可以调用的Singleton适配器类,但是可以直接向Spring应用程序上下文请求一个bean。
To do this, you need a class to which Spring can give a reference to the ApplicationContext
object: 为此,您需要一个Spring可以引用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;
}
}
Then your legacy code can call getContext()
and retrieve the beans it needs: 然后,您的旧代码可以调用getContext()
并检索所需的bean:
@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);
}
}
Tag that works by manually looking up the service object in the Spring context: working-manual-lookup
通过在Spring上下文中working-manual-lookup
服务对象而起作用的标记: working-manual-lookup
#3楼
If you are not coding a web application, make sure your class in which @Autowiring is done is a spring bean. 如果您不编写Web应用程序的代码,请确保完成@Autowiring的类是spring bean。 Typically, spring container won't be aware of the class which we might think of as a spring bean. 通常,spring容器不会意识到我们可能将其视为spring bean的类。 We have to tell the Spring container about our spring classes. 我们必须告诉Spring容器我们的Spring类。
This can be achieved by configuring in appln-contxt or the better way is to annotate class as @Component and please do not create the annotated class using new operator. 这可以通过在appln-contxt中配置来实现,或者更好的方法是将类注释为@Component ,请不要使用new运算符创建带注释的类。 Make sure you get it from Appln-context as below. 确保从Appln上下文中获取它,如下所示。
@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();
}
}
#4楼
Another solution would be putting call: SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this)
另一个解决方案是调用: SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this)
To MileageFeeCalculator constructor like this: 对于MileageFeeCalculator构造函数,如下所示:
@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());
}
}
#5楼
I'm new to Spring, but I discovered this working solution. 我是Spring的新手,但是我发现了这个可行的解决方案。 Please tell me if it's a deprecable way. 请告诉我这是否是可弃的方法。
I make Spring inject applicationContext
in this bean: 我在这个bean中使Spring注入applicationContext
:
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;
}
}
You can put this code also in the main application class if you want. 如果需要,您也可以将此代码放入主应用程序类。
Other classes can use it like this: 其他类可以这样使用它:
MyBean myBean = (MyBean)SpringUtils.ctx.getBean(MyBean.class);
In this way any bean can be obtained by any object in the application (also intantiated with new
) and in a static way . 这样,应用程序中的任何对象 (也可以通过new
实例化) 都可以以静态方式 获取任何bean 。
#6楼
Your problem is new (object creation in java style) 您的问题是新的(以Java风格创建对象)
MileageFeeCalculator calc = new MileageFeeCalculator();
With annotation @Service
, @Component
, @Configuration
beans are created in the 使用注解@Service
@Component
, @Service
, @Component
@Configuration
Bean在
application context of Spring when server is started. 服务器启动时Spring的应用程序上下文。 But when we create objects using new operator the object is not registered in application context which is already created. 但是,当我们使用new运算符创建对象时,该对象未在已创建的应用程序上下文中注册。 For Example Employee.java class i have used. 例如我使用的Employee.java类。
Check this out: 看一下这个:
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());
}
}
}
}