Java 为什么我的Spring@Autowired字段为空?
注意:这是一个常见问题的标准答案 我有一个SpringJava 为什么我的Spring@Autowired字段为空?,java,spring,null,nullpointerexception,autowired,Java,Spring,Null,Nullpointerexception,Autowired,注意:这是一个常见问题的标准答案 我有一个Spring@Service类(MileageFeeCalculator),它有一个@Autowired字段(rateService),但当我尝试使用它时,该字段是null。日志显示,mileageFeeCaulatorbean和mileageGateServicebean都在创建中,但是每当我尝试在我的服务bean上调用mileageCharge方法时,我都会得到一个NullPointerException。为什么Spring没有在现场自动布线 控制器
@Service
类(MileageFeeCalculator
),它有一个@Autowired
字段(rateService
),但当我尝试使用它时,该字段是null
。日志显示,mileageFeeCaulator
bean和mileageGateService
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
}
}
当我尝试获取/里程/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
创建的mileAgeCalculator
的副本,也不知道如何自动连线它
有三个主要的逻辑组件:应用程序可使用的组件(bean)的注册表(称为ApplicationContext
),配置器系统,通过将依赖项与上下文中的bean匹配,将对象的依赖项注入其中,以及一个依赖项解算器,它可以查看许多不同bean的配置,并确定如何按照必要的顺序实例化和配置它们
IoC容器并不神奇,除非您以某种方式通知它,否则它无法了解Java对象。当您调用new
时,JVM会实例化新对象的一个副本,并直接将其交给您——它从未经历过配置过程。有三种方法可以配置bean
我已经发布了所有这些代码,使用SpringBoot启动,在;您可以查看每种方法的完整运行项目,以了解使其工作所需的一切标记为NullPointerException
:
注射你的豆子
最好的选择是让Spring自动连接所有bean;这需要最少的代码,并且是最容易维护的。要使自动布线按您所希望的方式工作,还可以像下面这样自动布线mileagefeeculator
:
@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);
如果您需要为不同的请求创建服务对象的新实例,您仍然可以使用注入
通过注入@mileageCalculator
服务对象工作的标记:
使用@Configurable
如果确实需要使用new
创建的对象进行自动连接,则可以插入对象。这种方法将代码插入到对象的构造函数中,提醒Spring正在创建它,以便Spring可以配置新实例。这需要在构建中进行一些配置(例如使用ajc
进行编译)并启用Spring的运行时配置处理程序(@enablespringconfigulated
,使用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
对象的引用:
@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;
}
}
然后,您的遗留代码可以调用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,请不要使用新操作符创建注释类。
请确保您从以下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();
}
}
另一个解决方案是打电话:
SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this)
要像这样创建里程数计算器构造函数,请执行以下操作:
@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);
@服务
公共类里程数计算器{
@自动连线
private MilWangerateService rateService;//我是Spring新手,但我发现了这个有效的解决方案。请告诉我这是一种不可取的方法
我在这个bean中使用Spring injectapplicationContext
:
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(也可以通过new
)和静态方式您的问题是新的(java风格的对象创建)
通过注释@服务
,@组件
,@配置
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
字段在运行时为空
根本原因是,没有使用Spring IoC容器维护的自动创建bean(其@Autowired
字段确实正确
@Autowired private Settings settings;
<context:component-scan base-package="some.shit"/>
//import org.junit.Test; // JUnit4
import org.junit.jupiter.api.Test; // JUnit5
@SpringBootTest
public class MyTests {
....
@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
}
}
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;
}