Java 为什么我的Spring@Autowired字段为空?
注意:这是一个常见问题的标准答案 我有一个Spring@Service类MileageFeeCalculator,它有一个@Autowired field rateService,但当我尝试使用它时,该字段为空。日志显示MileAgeeCalculator bean和MileAgeateService bean都在创建中,但是每当我尝试在我的服务bean上调用mileageCharge方法时,我都会得到一个NullPointerException。为什么Spring没有在现场自动布线 控制器类:Java 为什么我的Spring@Autowired字段为空?,java,spring,null,nullpointerexception,autowired,Java,Spring,Null,Nullpointerexception,Autowired,注意:这是一个常见问题的标准答案 我有一个Spring@Service类MileageFeeCalculator,它有一个@Autowired field rateService,但当我尝试使用它时,该字段为空。日志显示MileAgeeCalculator bean和MileAgeateService bean都在创建中,但是每当我尝试在我的服务bean上调用mileageCharge方法时,我都会得到一个NullPointerException。为什么Spring没有在现场自动布线 控制器类:
@Controller
public class MileageFeeController {
@RequestMapping("/mileage/{miles}")
@ResponseBody
public float mileageFee(@PathVariable int miles) {
MileageFeeCalculator calc = new MileageFeeCalculator();
return calc.mileageCharge(miles);
}
}
服务类别:
@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
}
}
当我尝试获取/miliety/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)
...
带注释的@Autowired字段为null,因为Spring不知道您使用new创建的MileageFeeCalculator的副本,也不知道如何自动关联它
有三个主要的逻辑组件:一个称为ApplicationContext的注册表,其中包含可供应用程序使用的组件bean;一个配置器系统,通过将依赖项与上下文中的bean进行匹配,将对象的依赖项注入其中,以及一个依赖项解算器,它可以查看许多不同bean的配置,并确定如何按照必要的顺序实例化和配置它们
IoC容器并不神奇,除非您以某种方式通知它,否则它无法了解Java对象。当您调用new时,JVM实例化新对象的一个副本,并直接将其交给您,因为它从未经过配置过程。有三种方法可以配置bean
我已经发布了所有这些代码,使用SpringBoot启动,在;您可以查看每种方法的完整运行项目,以了解使其工作所需的一切。带有NullPointerException的标记:
注射你的豆子
最好的选择是让Spring自动连接所有bean;这需要最少的代码,并且是最容易维护的。要使自动布线按您所希望的方式工作,还可以像下面这样自动布线里程数计算器:
@Controller
public class MileageFeeController {
@Autowired
private MileageFeeCalculator calc;
@RequestMapping("/mileage/{miles}")
@ResponseBody
public float mileageFee(@PathVariable int miles) {
return calc.mileageCharge(miles);
}
}
@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());
}
}
MyBean myBean = (MyBean)SpringUtils.ctx.getBean(MyBean.class);
ApplicationContext context =
new ClassPathXmlApplicationContext(new String[] {
"common.xml",
"token.xml",
"pep-config.xml" });
TokenInitializer ti = context.getBean(TokenInitializer.class);
如果您需要为不同的请求创建服务对象的新实例,您仍然可以使用注入
通过注入@MileageFeeCalculator服务对象工作的标记:
使用@Configurable
如果确实需要使用“新建”创建的对象进行自动关联,则可以插入对象。这种方法将代码插入到对象的构造函数中,提醒Spring正在创建它,以便Spring可以配置新实例。这需要在构建中进行一些配置,例如使用ajc编译并启用Spring的运行时配置处理程序@EnableSpring,并使用JavaConfig语法进行配置。Roo活动记录系统使用这种方法来允许实体的新实例获得必要的持久性信息
@Service
@Configurable
public class MileageFeeCalculator {
@Autowired
private MileageRateService rateService;
public float mileageCharge(final int miles) {
return (miles * rateService.ratePerMile());
}
}
通过在服务对象上使用@Configurable工作的标记:
手动bean查找:不推荐
这种方法仅适用于在特殊情况下与遗留代码接口。几乎总是最好创建一个单例适配器类,Spring可以自动连接,遗留代码可以调用,但也可以直接向Spring应用程序上下文请求bean
为此,您需要一个Spring可以引用ApplicationContext对象的类:
然后,您的遗留代码可以调用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);
}
}
通过在Spring上下文中手动查找服务对象工作的标记:如果您没有编写web应用程序,请确保完成@Autowiring的类是Springbean。通常,spring容器不会知道我们可能认为是Springbean的类。我们必须告诉Spring容器我们的Spring课程 这可以通过在appln Context中配置来实现,或者更好的方法是将类注释为@Component,并且请不要使用new运算符创建注释的类。 请确保您从以下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();
}
}
另一个解决方案是打电话: SpringBeanAuthoringSupport.processInjectionBasedOnCurrentContextthis 要像这样创建里程数计算器构造函数,请执行以下操作:
@Controller
public class MileageFeeController {
@Autowired
private MileageFeeCalculator calc;
@RequestMapping("/mileage/{miles}")
@ResponseBody
public float mileageFee(@PathVariable int miles) {
return calc.mileageCharge(miles);
}
}
@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());
}
}
MyBean myBean = (MyBean)SpringUtils.ctx.getBean(MyBean.class);
ApplicationContext context =
new ClassPathXmlApplicationContext(new String[] {
"common.xml",
"token.xml",
"pep-config.xml" });
TokenInitializer ti = context.getBean(TokenInitializer.class);
我刚接触Spring,但我发现了这个有效的解决方案。请告诉我这是不是一条不可取的路
我在这个bean中创建Spring Injection 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;
}
}
如果需要,也可以将此代码放在主应用程序类中
其他类可以这样使用它:
@Controller
public class MileageFeeController {
@Autowired
private MileageFeeCalculator calc;
@RequestMapping("/mileage/{miles}")
@ResponseBody
public float mileageFee(@PathVariable int miles) {
return calc.mileageCharge(miles);
}
}
@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());
}
}
MyBean myBean = (MyBean)SpringUtils.ctx.getBean(MyBean.class);
ApplicationContext context =
new ClassPathXmlApplicationContext(new String[] {
"common.xml",
"token.xml",
"pep-config.xml" });
TokenInitializer ti = context.getBean(TokenInitializer.class);
通过这种方式,应用程序中的任何对象都可以以静态的方式获取任何bean。您的问题是java风格的新对象创建
MileageFeeCalculator calc = new MileageFeeCalculator();
使用注释@Service、@Component、@配置bean在
服务器启动时Spring的应用程序上下文。但是当我们创建对象时
使用新运算符时,对象未在已创建的应用程序上下文中注册。例如,我使用的Employee.java类
看看这个:
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());
}
}
}
}
我曾经遇到过
当我不太习惯国际奥委会的生活时,我也遇到了同样的问题。我的一个bean的@Autowired字段在运行时为空
根本原因是,我没有使用springioc容器维护的自动创建的bean,它的@Autowired字段确实被正确注入,而是更新了我自己的bean类型实例并使用它。当然,这个@Autowired字段是空的,因为Spring没有机会注入它。这似乎是罕见的情况,但下面是发生在我身上的情况: 我们使用@Inject而不是@Autowired,后者是Spring支持的javaee标准。每个地方都很好,豆子注射正确,而不是一个地方。豆子注射剂似乎是一样的
@Inject
Calculator myCalculator
最后我们发现错误在于,实际上,Eclipse自动完成功能导入了com.opensymphony.xwork2.Inject而不是javax.Inject.Inject
总之,请确保您的注释@Autowired、@Inject、@Service、,。。。有正确的包装 实际上,您应该使用JVM托管对象或Spring托管对象来调用方法。 根据控制器类中的上述代码,您正在创建一个新对象来调用具有自动连接对象的服务类
MileageFeeCalculator calc = new MileageFeeCalculator();
所以它不会那样工作
该解决方案将此MileageFeeCalculator作为控制器本身的自动连线对象
更改控制器类,如下所示
@Controller
public class MileageFeeController {
@Autowired
MileageFeeCalculator calc;
@RequestMapping("/mileage/{miles}")
@ResponseBody
public float mileageFee(@PathVariable int miles) {
return calc.mileageCharge(miles);
}
}
您还可以使用服务类上的@Service annotation修复此问题,并将所需的bean classA作为参数传递给另一个bean classB构造函数,并使用@Autowired注释classB的构造函数。此处是示例代码段:
@Service
public class ClassB {
private ClassA classA;
@Autowired
public ClassB(ClassA classA) {
this.classA = classA;
}
public void useClassAObjectHere(){
classA.callMethodOnObjectA();
}
}
我认为您没有指示spring扫描带有注释的类 您可以在spring应用程序的配置类上使用@ComponentScanpackageToScan来指示spring进行扫描 @服务、@Component等注释添加元描述。 Spring只注入那些创建为bean或用注释标记的类的实例 使用注释标记的类需要在注入之前由spring标识,@ComponentScan指示spring查找使用注释标记的类。当Spring找到@Autowired时,它会搜索相关的bean,并注入所需的实例 只添加注释,不能修复或促进依赖注入,Spring需要知道在哪里寻找。更新:真正聪明的人很快就找到了答案,这解释了下面描述的奇怪之处 原始答复: 我不知道这是否对任何人都有帮助,但即使在做一些看似正确的事情时,我也遇到了同样的问题。在我的主要方法中,我有如下代码:
@Controller
public class MileageFeeController {
@Autowired
private MileageFeeCalculator calc;
@RequestMapping("/mileage/{miles}")
@ResponseBody
public float mileageFee(@PathVariable int miles) {
return calc.mileageCharge(miles);
}
}
@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());
}
}
MyBean myBean = (MyBean)SpringUtils.ctx.getBean(MyBean.class);
ApplicationContext context =
new ClassPathXmlApplicationContext(new String[] {
"common.xml",
"token.xml",
"pep-config.xml" });
TokenInitializer ti = context.getBean(TokenInitializer.class);
在token.xml文件中,我有一行
<context:component-scan base-package="package.path"/>
SomeAbac类有一个声明为
@Autowired private Settings settings;
由于一些未知的原因,当元素根本不存在时,init中的设置为null,但当元素存在并且有一些bs作为basePackage时,一切正常。这一行现在看起来像这样:
<context:component-scan base-package="some.shit"/>
它是有效的。可能有人可以提供解释,但对我来说现在就足够了如果这是在测试类中发生的,请确保您没有忘记注释该类 例如,在Spring Boot中: @RunWithSpringRunner.class @春靴测试 公共类MyTests{ .... 一段时间过去了。。。 如果使用正确版本的JUnit,则不再需要使用@RunWith 要使@SpringBootTest独立工作,您需要使用@Test from
如果您的配置错误,您的测试将被编译,但是@Autowired和@Value字段(例如)将为null。由于Spring Boot是通过魔术操作的,因此您可能没有直接调试此故障的途径。还请注意,如果出于任何原因,您将@Service中的一个方法设为final,那么您将访问自动连线beanom它将始终为空。这是给出NullPointerException MileageFeeCalculator calc=new MileageFeeCalculator的罪魁祸首;我们使用Spring-不需要手动创建对象。对象创建将由IoC容器负责。本文中未提及的内容将在执行顺序的段落中描述离子 在了解到我必须用@Component或派生@Service或@Repository注释一个类之后,我想还有更多,要自动连接其中的其他组件,我突然意识到这些其他组件在父组件的构造函数中仍然为空 使用@PostConstruct可以解决以下问题:
@SpringBootApplication
public class Application {
@Autowired MyComponent comp;
}
以及:
简而言之,@Autowired字段为空主要有两个原因 你的班级不是一个SpringBean 这个字段不是一个BEAN
这仅在单元测试的情况下有效 我的服务类有一个服务注释,它是@autowired另一个组件类。当我测试该组件类时,该组件类为空。因为对于服务类,我是creatin 使用新的 若您正在编写单元测试,请确保您并没有使用新对象创建对象。改用mock
这解决了我的问题。这里有一个有用的与问题不完全相关,但是如果字段注入为null,则基于构造函数的注入仍然可以正常工作
private OrderingClient orderingClient;
private Sales2Client sales2Client;
private Settings2Client settings2Client;
@Autowired
public BrinkWebTool(OrderingClient orderingClient, Sales2Client sales2Client, Settings2Client settings2Client) {
this.orderingClient = orderingClient;
this.sales2Client = sales2Client;
this.settings2Client = settings2Client;
}
以下其中一项将起作用:
您使用@Autowired的类不是一个Bean,我相信您可能在某个地方使用了new
在SpringConfig类中,您没有提到Spring应该查找@Component的包,我这里说的是@ComponentScanBasePackages
如果以上两项都不起作用。。。。开始放置System.out.println并找出错误所在。如果您使用的是私有方法,它将为空,请尝试在controller中将私有更改为公共。我发现了类似的帖子
错误的根本原因可以在Spring参考文档中解释,如下所示:
自动连线字段
字段在构建bean之后,在任何
配置方法被调用
但是SpringDoc中这条语句背后的真正原因是Spring中Bean的生命周期。这是Spring设计理念的一部分
这是:
Bean需要先初始化,然后才能注入诸如field之类的属性。这就是bean的设计方式,所以这才是真正的原因
我希望这个答案对你有帮助 此外,不要注入到静态成员,它将为null。另一件要注意的事情是在@Configuration bean中为bean创建对象,其中创建特定bean类实例的方法用@bean注释。@DonalFellows我不完全确定您所说的创建是不明确的。你是说在使用Spring代理AOP时多次调用@Bean方法会出现问题吗?您好,我遇到了类似的问题,但是当我使用你的第一个建议时,我的应用程序在调用mileageFee方法时认为calc为null。这就好像它从未初始化@Autowired MileageFeeCalculator calc。有什么想法吗?我认为您应该在答案的顶部添加一个条目,解释如何通过ApplicationContext检索第一个bean,即执行所有操作的根。我关闭为重复用户的某些用户不理解这一点。如果我错了,请纠正我,但在MilegageFeeCalculator上同时指定@Service和@Configurable注释可能不正确,根据SpringAOP:…确保不要在bean类上使用@Configurable,这些bean类在容器中注册为常规SpringBean:否则会得到双重初始化,一次通过容器,一次通过方面。所以本质上,你应该只选择其中一个。嗨,我已经看过你的解决方案了,这是正确的。在这里我想知道为什么我们不使用new操作符创建带注释类的实例,我想知道这背后的原因。如果你使用new创建对象,你将处理bean的生命周期,这与IOC的概念相矛盾。我们需要让容器来做这件事,它以一种更好的方式来做这件事。这使用了不安全的发布。这种模式对于使SpringBean可以访问遗留代码是必要的,但在新代码中应该避免。在我的例子中,我需要这样做,因为第三方类很少。春季国际奥委会对他们没有控制权。这些类从未从我的spring boot应用程序调用过。我遵循了这种方法,它对我有效。另一种情况是,在另一个bean的构造函数中调用bean F。在这种情况下,将所需的bean F作为参数传递给另一个bean的构造函数,并用@Autowire注释S的构造函数。记住用@Component注释第一个bean F的类。我在这里使用Gradle编写了一些与此非常类似的示例:。希望有人会觉得它有用。这就是答案。因为您要自己实例化一个新的MilageFeeCalculator,所以Spring不参与实例化,所以Spring不知道对象是否存在。因此,它不能对它做任何事情,比如注入依赖项。这就是解释。隐式启用@Autowired工作所需的功能。这对我来说很有效,但你能详细说明一下这是如何解决问题的吗?@crueengine,看这是构造函数注入,在这里你显式设置一个对象,而不是仅仅使用字段注入。这主要是由spring配置完成的。所以,如果您正在使用new operator创建ClassB的对象,那么它将不可见,也不会为ClassA设置自动连线。因此,在调用classB.UseClassaObject时,如果您只声明字段注入,则会抛出NPE,因为classA对象不是自动连接的。阅读chrylis试图解释同样的问题。这就是为什么构造函数注入比字段注入更受推荐。现在有意义了吗?当我忘记添加到beans.xml文件时遇到了这个问题
另外:注意:@Value在与静态字段一起使用时将为null。Spring提供了许多失败的方法,没有编译器的帮助。当事情出错时,你最好还是回到原点——只使用你知道会一起工作的注释组合。作为一个不得不快速学习Spring的人,没有时间练习lol,这对我帮助很大!非常感谢。谢谢,有没有一个很好的解决方案可以将新建一个实例和使用@Autowired结合起来?我在代码中使用了新的,这就导致了问题。我需要用那个新的。但也需要在新类中使用@Autowire。如何做到这一点。你不需要做新的!您只需声明类变量并在其上方使用@Autowire即可。您还必须确保在要自动连线的类ABC{…}之上包含@Component。它将在以下方面发挥作用: