提问人:chrylis -cautiouslyoptimistic- 提问时间:11/11/2013 最后编辑:Tarynchrylis -cautiouslyoptimistic- 更新时间:4/12/2023 访问量:727938
为什么我的 Spring @Autowired 字段为空?
Why is my Spring @Autowired field null?
问:
注意:这是常见问题的规范答案。
我有一个 Spring 类 () 有一个字段 (),但该字段是当我尝试使用它时。日志显示正在创建 Bean 和 Bean,但每当我尝试在我的服务 Bean 上调用该方法时,我都会得到一个。为什么 Spring 不自动布线?@Service
MileageFeeCalculator
@Autowired
rateService
null
MileageFeeCalculator
MileageRateService
NullPointerException
mileageCharge
控制器类:
@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
}
}
应该自动连接但它不是的服务 bean:MileageFeeCalculator
@Service
public class MileageRateService {
public float ratePerMile() {
return 0.565f;
}
}
当我尝试时,我得到这个异常: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)
...
答:
注释的字段是因为 Spring 不知道您创建的副本,也不知道自动连接它。@Autowired
null
MileageFeeCalculator
new
Spring 控制反转 (IoC) 容器有三个主要的逻辑组件:可供应用程序使用的组件 (bean) 的注册表(称为)、配置器系统,该系统通过将依赖关系与上下文中的 bean 匹配来将对象的依赖关系注入其中,以及依赖关系求解器,它可以查看许多不同 bean 的配置并确定如何以必要的顺序实例化和配置它们。ApplicationContext
IoC 容器并不神奇,除非你以某种方式通知它,否则它无法了解 Java 对象。当您调用 时,JVM 会实例化新对象的副本并将其直接交给您 - 它永远不会经过配置过程。有三种方法可以配置 bean。new
我已经在这个 GitHub 项目中发布了所有这些代码,使用 Spring Boot 启动;您可以查看每种方法的完整运行项目,以了解使其工作所需的一切。带有 NullPointerException
的标记:nonworking
注入你的豆子
最可取的选择是让 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);
}
}
如果需要为不同的请求创建服务对象的新实例,您仍然可以使用 Spring Bean 作用域使用注入。
通过注入 @MileageFeeCalculator
服务对象起作用的标签:working-inject-bean
使用@Configurable
如果你真的需要自动连接创建的对象,你可以使用 Spring @Configurable
注解和 AspectJ 编译时编织来注入你的对象。这种方法将代码插入到对象的构造函数中,以提醒 Spring 正在创建它,以便 Spring 可以配置新实例。这需要在构建中进行一些配置(例如使用 编译)并打开 Spring 的运行时配置处理程序(使用 JavaConfig 语法)。Roo Active Record 系统使用此方法,以允许实体的实例获取注入的必要持久性信息。new
ajc
@EnableSpringConfigured
new
@Service
@Configurable
public class MileageFeeCalculator {
@Autowired
private MileageRateService rateService;
public float mileageCharge(final int miles) {
return (miles * rateService.ratePerMile());
}
}
在服务对象上使用@Configurable
的标记:working-configurable
手动 Bean 查找:不推荐
此方法仅适用于在特殊情况下与遗留代码进行交互。创建一个 Spring 可以自动连接并且遗留代码可以调用的单例适配器类几乎总是更可取的,但可以直接向 Spring 应用程序上下文请求 bean。
为此,您需要一个 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;
}
}
然后,您的遗留代码可以调用和检索它需要的 bean:getContext()
@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 上下文中手动查找服务对象来工作的标签:working-manual-lookup
评论
@Configuration
@Bean
@Bean
@Autowired MileageFeeCalculator calc
ApplicationContext
@Configurable
: 否则,您将获得双重初始化,一次通过容器,一次通过方面。所以从本质上讲,你应该只选择其中之一。@Service
@Configurable
MilegageFeeCalculator
如果您不是编写 Web 应用程序,请确保完成@Autowiring的类是 spring bean。通常,spring container 不会意识到我们可能认为是 spring bean 的类。我们必须告诉 Spring 容器我们的春季课程。
这可以通过在 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();
}
}
评论
另一种解决方案是像这样调用
构造函数: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());
}
}
评论
我是 Spring 的新手,但我发现了这个可行的解决方案。请告诉我这是否是一种可弃用的方式。
我让 Spring 注入这个 bean: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;
}
}
如果需要,也可以将此代码放在主应用程序类中。
其他类可以像这样使用它:
MyBean myBean = (MyBean)SpringUtils.ctx.getBean(MyBean.class);
这样,任何 Bean 都可以由应用程序中的任何对象(也用 指定)以静态方式获得。new
评论
您的问题是新的(以 java 样式创建对象)
MileageFeeCalculator calc = new MileageFeeCalculator();
使用注解 , ,当服务器启动时,将在 Spring
的应用程序上下文中创建 bean。但是当我们创建对象时
使用 new 运算符时,对象不会在已创建的应用程序上下文中注册。例如,我使用的 Employee.java 类。@Service
@Component
@Configuration
看看这个:
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());
}
}
}
}
当我不太习惯 IoC 世界的生活时,我曾经遇到过同样的问题。我的一个 Bean 的字段在运行时为 null。@Autowired
根本原因是,我没有使用由 Spring IoC 容器维护的自动创建的 bean(其字段确实被正确注入),而是正在构建我自己的该 bean 类型的实例并使用它。当然,这个字段是空的,因为 Spring 没有机会注入它。@Autowired
new
@Autowired
评论
new
这似乎很少见,但这是发生在我身上的事情:
我们用的是 Spring 支持的 javaee 标准。每个地方都工作正常,豆子注射正确,而不是一个地方。豆子注射似乎是一样的@Inject
@Autowired
@Inject
Calculator myCalculator
最后我们发现错误是我们(实际上是 Eclipse 自动完成功能)导入而不是 !com.opensymphony.xwork2.Inject
javax.inject.Inject
所以总而言之,确保你的注解(、、,...)有正确的包!@Autowired
@Inject
@Service
实际上,您应该使用 JVM 管理的对象或 Spring 管理的对象来调用方法。 从控制器类中的上述代码中,您将创建一个新对象来调用具有自动连接对象的服务类。
MileageFeeCalculator calc = new MileageFeeCalculator();
所以它不会那样工作。
该解决方案使此 MileageFeeCalculator 成为控制器本身中的自动连线对象。
更改 Controller 类,如下所示。
@Controller
public class MileageFeeController {
@Autowired
MileageFeeCalculator calc;
@RequestMapping("/mileage/{miles}")
@ResponseBody
public float mileageFee(@PathVariable int miles) {
return calc.mileageCharge(miles);
}
}
评论
您还可以使用对服务类进行@Service注释,并将所需的 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 应用程序的配置类上使用 Spring 来指示 spring 进行扫描。@ComponentScan("packageToScan")
@Service, @Component
etc 注解添加元描述。
Spring 只注入那些被创建为 bean 或用注解标记的类的实例。
标有注解的类在注入前需要用spring标识,指示spring寻找标有注解的类。当 Spring 找到它时,它会搜索相关的 bean,并注入所需的实例。@ComponentScan
@Autowired
仅添加注解,并不能修复或促进依赖注入,Spring 需要知道在哪里寻找。
评论
<context:component-scan base-package="com.mypackage"/>
beans.xml
更新:真正聪明的人很快就指出了这个答案,这解释了下面描述的奇怪之处
原答案:
我不知道它是否对任何人有帮助,但即使做看似正确的事情,我也遇到了同样的问题。在我的 Main 方法中,我有这样的代码:
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"/>
我注意到package.path不再存在,所以我刚刚永远删除了这一行。
在那之后,NPE开始进来。在一个 我只有 2 颗豆子:pep-config.xml
<bean id="someAbac" class="com.pep.SomeAbac" init-method="init"/>
<bean id="settings" class="com.pep.Settings"/>
和 SomeAbac 类有一个声明为
@Autowired private Settings settings;
由于某种未知原因,当元素根本不存在时,init() 中的设置为 null,但是当它存在并且有一些 bs 作为 basePackage 时,一切正常。此行现在如下所示:<context:component-scan/>
<context:component-scan base-package="some.shit"/>
它起作用了。也许有人可以提供解释,但对我来说现在已经足够了:)
评论
<context:component-scan/>
<context:annotation-config/>
@Autowired
如果这种情况发生在测试类中,请确保您没有忘记为类添加注释。
例如,在 Spring Boot 中:
@RunWith(SpringRunner.class)
@SpringBootTest
public class MyTests {
....
一段时间过去了......
Spring Boot 不断发展。如果您使用正确版本的 JUnit,则不再需要使用它。@RunWith
要独立工作,您需要使用 JUnit5 而不是 JUnit4。@SpringBootTest
@Test
//import org.junit.Test; // JUnit4
import org.junit.jupiter.api.Test; // JUnit5
@SpringBootTest
public class MyTests {
....
如果此配置错误,则测试将编译,但字段(例如)将为 .由于 Spring Boot 的运行方式很神奇,因此您可能几乎没有直接调试此故障的途径。@Autowired
@Value
null
评论
@Value
static
另请注意,如果出于某种原因,您在 as 中创建了一个方法,您将从中访问的自动连线 bean 将始终是 。@Service
final
null
评论
这是导致 NullPointerException 的罪魁祸首 我们正在使用 Spring - 不需要手动创建对象。对象创建将由 IoC 容器处理。MileageFeeCalculator calc = new MileageFeeCalculator();
在“学习”我必须用@Component或导数@Service或@Repository(我想还有更多)注释一个类之后,自动连接它们内部的其他组件,令我震惊的是,这些其他组件在父组件的构造函数中仍然是空的。
使用 @PostConstruct 可以解决以下问题:
@SpringBootApplication
public class Application {
@Autowired MyComponent comp;
}
和:
@Component
public class MyComponent {
@Autowired ComponentDAO dao;
public MyComponent() {
// dao is null here
}
@PostConstruct
public void init() {
// dao is initialized here
}
}
简单来说,一个字段主要有两个原因@Autowired
null
- 你的班级不是春豆。
定义注释的类不是 spring bean。因此,弹簧不会自动连接杆件。@Autowire
- 田地不是豆子。
在 Spring 应用程序上下文或注册表中尚不存在您在字段中指定的类型或层次结构中的类型的 bean@Autowired
这仅在单元测试的情况下有效。
我的 Service 类有一个 service 的注释,它是另一个组件类。当我测试时,组件类为 null。因为对于服务类,我使用@autowired
new
如果您正在编写单元测试,请确保您没有使用 创建对象。请改用 injectMock。new object()
这解决了我的问题。这是一个有用的链接
与问题不完全相关,但如果字段注入为 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查找的软件包(我说的是@ComponentScan(basePackages“here”))
如果以上两个不起作用....开始放置 System.out.println() 并找出它出错的地方。
评论
如果您使用的是某个方法,它将是 ,尝试更改为 In Controller。private
null
private
public
评论
另外,不要注入到成员身上,它会是.static
null
评论
F
S
F
S
S
@Autowire
F
@Component