注意:这是一个常见问题的规范答案。
我有一个Spring @Service类(MileageFeeCalculator),它有一个@Autowired字段(rateService),但当我尝试使用它时,该字段为空。日志显示正在创建MileageFeeCalculator bean和MileageRateService 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
}
}
服务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)
...
更新:真正聪明的人很快就指出了这个答案,这解释了下面描述的奇怪之处
最初的回答:
我不知道这是否对任何人都有帮助,但我被同样的问题困住了,即使我做的事情看起来是正确的。在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"/>
我注意到这个包装。路径已经不存在了,所以我干脆把这行删掉了。
在那之后,NPE开始进入。在pep-config.xml中,我只有2个bean:
<bean id="someAbac" class="com.pep.SomeAbac" init-method="init"/>
<bean id="settings" class="com.pep.Settings"/>
SomeAbac类有一个属性声明为
@Autowired private Settings settings;
由于一些未知的原因,init()中的settings为空,当<context:component-scan/>元素根本不存在时,但是当它存在并且有一些bs作为basePackage时,一切都能正常工作。这条线现在看起来是这样的:
<context:component-scan base-package="some.shit"/>
这很有效。也许有人可以提供一个解释,但对我来说,现在已经足够了)
我是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);
通过这种方式,应用程序中的任何对象(也用new实例化)都可以以静态方式获取任何bean。