Java 为什么我的 Spring @Autowired 字段为空?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/19896870/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Why is my Spring @Autowired field null?
提问by chrylis -cautiouslyoptimistic-
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. 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. Why isn't Spring autowiring the field?
我有一个具有字段 ( )的 Spring@Service
类 ( MileageFeeCalculator
) ,但该字段是我尝试使用它时的字段。日志显示bean 和bean 都被创建,但是每当我尝试调用服务 bean 上的方法时,我都会得到一个。为什么 Spring 不自动装配该字段?@Autowired
rateService
null
MileageFeeCalculator
MileageRateService
NullPointerException
mileageCharge
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:
应该自动装配的服务 bean,MileageFeeCalculator
但它不是:
@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)
...
采纳答案by chrylis -cautiouslyoptimistic-
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 不知道MileageFeeCalculator
您创建的副本,new
也不知道自动装配它。
The Spring Inversion of Control (IoC) containerhas 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 控制反转 (IoC) 容器具有三个主要的逻辑组件:ApplicationContext
可供应用程序使用的组件(bean)的注册表(称为上下文中包含 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. 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. There are three ways that you can get your beans configured.
IoC 容器并不神奇,除非您以某种方式通知它,否则它无法了解 Java 对象。当您调用 时new
,JVM 会实例化新对象的副本并将其直接交给您——它永远不会经过配置过程。您可以通过三种方式配置 bean。
I have posted all of this code, using Spring Boot to launch, at this GitHub project; 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
我已经在这个 GitHub 项目中发布了所有这些代码,使用 Spring Boot 启动;您可以查看每种方法的完整运行项目,以了解使其工作所需的一切。标记为NullPointerException
:nonworking
Inject your beans
注入你的豆子
The most preferable option is to let Spring autowire all of your beans; 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:
最可取的选择是让 Spring 自动装配所有 bean;这需要最少的代码并且最易于维护。要使自动装配工作如您所愿,还可以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 范围来使用注入。
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 weavingto inject your objects. 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. 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). This approach is used by the Roo Active Record system to allow new
instances of your entities to get the necessary persistence information injected.
如果你真的需要new
自动装配创建的对象,你可以使用 Spring@Configurable
注释和 AspectJ 编译时编织来注入你的对象。这种方法将代码插入到对象的构造函数中,提醒 Spring 它正在创建,以便 Spring 可以配置新实例。这需要在您的构建中进行一些配置(例如使用 编译ajc
)并打开 Spring 的运行时配置处理程序(@EnableSpringConfigured
使用 JavaConfig 语法)。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 可以自动装配并且遗留代码可以调用的单例适配器类几乎总是更可取的,但是可以直接向 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
回答by Shirish Coolkarni
If you are not coding a web application, make sure your class in which @Autowiring is done is a spring bean. Typically, spring container won't be aware of the class which we might think of as a spring bean. We have to tell the Spring container about our spring classes.
如果您没有编写 Web 应用程序,请确保完成 @Autowiring 的类是 spring bean。通常,spring 容器不会知道我们可能认为是 spring bean 的类。我们必须告诉 Spring 容器我们的 spring 类。
This can be achieved by configuring in appln-contxt or the better wayis to annotate class as @Componentand please do not create the annotated class using new operator. Make sure you get it from Appln-context as below.
这可以通过在 appln-contxt 中配置来实现,或者更好的方法是将类注释为@Component并且请不要使用 new 运算符创建注释类。确保您从 Appln-context 获取它,如下所示。
@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();
}
}
回答by Ondrej Bozek
Another solution would be putting call:
SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this)
To MileageFeeCalculator constructor like this:
另一种解决方案是将调用:
SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(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());
}
}
回答by bluish
I'm new to Spring, but I discovered this working solution. Please tell me if it's a deprecable way.
我是 Spring 的新手,但我发现了这个可行的解决方案。请告诉我这是否是一种可贬低的方式。
I make Spring inject applicationContext
in this bean:
我让 SpringapplicationContext
在这个 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;
}
}
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.
通过这种方式,任何 bean 都可以被应用程序中的任何对象(也使用new
)以静态方式获取。
回答by Deepak
Your problem is new (object creation in java style)
你的问题是新的(Java 风格的对象创建)
MileageFeeCalculator calc = new MileageFeeCalculator();
With annotation @Service
, @Component
, @Configuration
beans are created in the
application context of Spring when server is started. But when we create objects
using new operator the object is not registered in application context which is already created. For Example Employee.java class i have used.
随着注释@Service
,@Component
,@Configuration
在创建豆
春季的应用程序上下文服务器启动时。但是当我们使用 new 操作符创建对象时,对象并没有在已经创建的应用程序上下文中注册。例如,我使用过的 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());
}
}
}
}
回答by smwikipedia
I once encountered the same issue when I was not quite used to the life in the IoC world
. The @Autowired
field of one of my beans is null at runtime.
我曾经在不太习惯的时候遇到过同样的问题the life in the IoC world
。@Autowired
我的一个 bean的字段在运行时为空。
The root cause is, instead of using the auto-created bean maintained by the Spring IoC container (whose @Autowired
field is indeed
properly injected), I am newing
my own instance of that bean type and using it. Of course this one's @Autowired
field is null because Spring has no chance to inject it.
根本原因是,我没有使用 Spring IoC 容器维护的自动创建的 bean(其@Autowired
字段被indeed
正确注入),而是newing
我自己的那个 bean 类型的实例并使用它。当然这个@Autowired
字段是空的,因为 Spring 没有机会注入它。
回答by Alireza Fattahi
It seems to be rare case but here is what happened to me:
这似乎是罕见的情况,但这是发生在我身上的事情:
We used @Inject
instead of @Autowired
which is javaee standard supported by Spring. Every places it worked fine and the beans injected correctly, instead of one place. The bean injection seems the same
我们使用Spring 支持的 javaee 标准@Inject
代替@Autowired
which。它在每个地方都运行良好并且 bean 正确注入,而不是一个地方。bean注入似乎相同
@Inject
Calculator myCalculator
At last we found that the error was that we (actually, the Eclipse auto complete feature) imported com.opensymphony.xwork2.Inject
instead of javax.inject.Inject
!
最后我们发现错误是我们(实际上是 Eclipse 自动完成功能)导入com.opensymphony.xwork2.Inject
而不是javax.inject.Inject
!
So to summarize, make sure that your annotations (@Autowired
, @Inject
, @Service
,... ) have correct packages!
总而言之,请确保您的注释(@Autowired
, @Inject
, @Service
,... )具有正确的包!
回答by Ravi Durairaj
Actually, you should use either JVM managed Objects or Spring-managed Object to invoke methods. from your above code in your controller class, you are creating a new object to call your service class which has an auto-wired object.
实际上,您应该使用 JVM 管理的对象或 Spring 管理的对象来调用方法。从控制器类中的上述代码中,您正在创建一个新对象来调用具有自动连接对象的服务类。
MileageFeeCalculator calc = new MileageFeeCalculator();
so it won't work that way.
所以它不会那样工作。
The solution makes this MileageFeeCalculator as an auto-wired object in the Controller itself.
该解决方案将此 MileageFeeCalculator 作为控制器本身中的自动连接对象。
Change your Controller class like below.
像下面这样更改您的控制器类。
@Controller
public class MileageFeeController {
@Autowired
MileageFeeCalculator calc;
@RequestMapping("/mileage/{miles}")
@ResponseBody
public float mileageFee(@PathVariable int miles) {
return calc.mileageCharge(miles);
}
}
回答by Abhishek
You can also fix this issue using @Service annotation on service class and passing the required bean classA as a parameter to the other beans classB constructor and annotate the constructor of classB with @Autowired. Sample snippet here :
您还可以在服务类上使用 @Service 注释解决此问题,并将所需的 bean classA 作为参数传递给其他 beans classB 构造函数,并使用 @Autowired 注释 classB 的构造函数。示例片段在这里:
@Service
public class ClassB {
private ClassA classA;
@Autowired
public ClassB(ClassA classA) {
this.classA = classA;
}
public void useClassAObjectHere(){
classA.callMethodOnObjectA();
}
}
回答by msucil
I think you have missed to instruct spring to scan classes with annotation.
我认为您错过了指示 spring 扫描带有注释的类。
You can use @ComponentScan("packageToScan")
on the configuration class of your spring application to instruct spring to scan.
您可以@ComponentScan("packageToScan")
在 spring 应用程序的配置类上使用来指示 spring 进行扫描。
@Service, @Component
etc annotations add meta description.
@Service, @Component
等注释添加元描述。
Spring only injects instances of those classes which are either created as bean or marked with annotation.
Spring 只注入这些类的实例,这些类要么是作为 bean 创建的,要么是用注解标记的。
Classes marked with annotation need to be identified by spring before injecting, @ComponentScan
instruct spring look for the classes marked with annotation. When Spring finds @Autowired
it searches for the related bean, and injects the required instance.
注解标记的类在注入前需要通过spring进行识别,@ComponentScan
指示spring查找注解标记的类。当 Spring 找到@Autowired
它时,它会搜索相关的 bean,并注入所需的实例。
Adding annotation only, does not fix or facilitate the dependency injection, Spring needs to know where to look for.
只添加注解,并没有修复或促进依赖注入,Spring 需要知道去哪里寻找。