今天在做完成一个业务功能开发时,发现需要根据数据状态进行不同的方法进行操作,代码中会有大量if判断条件的代码,于是乎用到了策略模式+简单工厂模式+注解用以消除if判断。但在开发完成进行测试发现以下问题:
java.lang.NullPointerException: null
at com.wondersgroup.inspectionreport.utils.ReportFactory.<init>(ReportFactory.java:40)
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
at org.springframework.beans.BeanUtils.instantiateClass(BeanUtils.java:172)
... 42 common frames omitted
对应的类如下:
@Component
public class ReportFactory {
@Autowired
SpringContextUtil springContextUtil;
private static Map<InspectionReportType, SampleTable> reportTypeMap = Maps.newConcurrentMap();
// @PostConstruct()
// public void InitReportFactory() {
// Map<String, Object> beanMap = springContextUtil.getContext().getBeansWithAnnotation(ReportTypeAnnotation.class);
// for (Object report : beanMap.values()) {
// ReportTypeAnnotation reportTypeAnnotation = report.getClass().getAnnotation(ReportTypeAnnotation.class);
// reportTypeMap.put(reportTypeAnnotation.value(), (SampleTable) report);
// }
// }
public ReportFactory() {
Map<String, Object> beanMap = springContextUtil.getContext().getBeansWithAnnotation(ReportTypeAnnotation.class);
for (Object report : beanMap.values()) {
ReportTypeAnnotation reportTypeAnnotation = report.getClass().getAnnotation(ReportTypeAnnotation.class);
reportTypeMap.put(reportTypeAnnotation.value(), (SampleTable) report);
}
}
public static SampleTable createSampleTable(InspectionReportType inspectionReportType) {
return reportTypeMap.get(inspectionReportType);
}
}
@Component
public class SpringContextUtil implements ApplicationContextAware {
private ApplicationContext context;
public ApplicationContext getContext() {
return context;
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.context = applicationContext;
}
}
我在ReportFactory类中,期望通过在项目启动时,Spring加载Component注解的Bean时会调用该类的构造方法,以达到加载带有ReportAnnotation注解的所有类至beanMap中。但在测试时却报出空指针问题。经过分析后,得出结论,应该是springContextUtil类没有加载,那么为什么会有这个问题呢?究其原因,也就引入了今天要讲的Spring中@PostConstruct 、@Autowired和Construct构造方法加载顺序。
我们从依赖注入字面意思理解,要将对象S注入对象R,就必须先生成这两个对象,才能将S通过@Autowired注解注入R对象,所以@Autowired注解,是发生在对象初始化之后。而在上述代码中,我想在构造方法中,使用了@Autowired注入的对象springContextUtil,显然,由于Construct构造方法先执行,此时,Autowired注入的对象还未进行注入,因此。沟通方法中的spirngContextUtil肯定为空。那么如果我们想要在类加载完后就初始化某些数据该如何操作呢?这时我们就引入了PostConstruct注解。被PostConstruct标记的方法,将会在完成依赖注入后,自动调用。即最终解决方案为,上述ReportFactory注释的代码
@PostConstruct()
public void InitReportFactory() {
Map<String, Object> beanMap = springContextUtil.getContext().getBeansWithAnnotation(ReportTypeAnnotation.class);
for (Object report : beanMap.values()) {
ReportTypeAnnotation reportTypeAnnotation = report.getClass().getAnnotation(ReportTypeAnnotation.class);
reportTypeMap.put(reportTypeAnnotation.value(), (SampleTable) report);
}
}
通过解决这个问题,我们也得出结论在Spring中执行Construct,@Autowired,@PostConstruct顺序为:
Construct >> Autowired >> PostConstruct