记录一下本人在项目遇到过的一个实例初始化顺序先后引起了项目启动失败的问题。
有两个需要实例化的Bean,我就写作DemoTest1和DemoTest2。两个bean都实现了InitializingBean接口。还有一个工具类DemoUtil。
DemoTest2类中定义了自己的实例对象为null,并且提供了一个获取实例的方法。
DemoTest1类中的实例初始化时调用工具类DemoUtil来输出信息
DemoUtil 工具了在一个静态方法中 通过类DemoTest2来获取实例并打印出DemoTest2中的成员变量name。
这时在项目启动的时候,就会报工具类DemoUtil的方法出现空指针。
原因分析:DemoTest2的实例化比DemoTest1实例化要晚,所以在DemoTest1实例化时,调用工具类DemoUtil输出DemoTest2的变量时,获取不到DemoTest2的实例对象,所以报空指针。
代码:DemoTest1
@Component
public class DemoTest1 implements InitializingBean{
public DemoTest1() {
System.out.println("@@@@@@init DemoTest1@@@@@");
}
@Override
public void afterPropertiesSet() throws Exception {
// TODO Auto-generated method stub
DemoUtil.dosomething();
}
}
代码:DemoTest2
@Component
public class DemoTest2 implements InitializingBean{
private static DemoTest2 demoTest2 = null;
private String name="123123";
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public static DemoTest2 getDemoTest2() {
return demoTest2;
}
@Override
public void afterPropertiesSet() throws Exception {
// TODO Auto-generated method stub
System.out.println("!!!!!afterPropertiesSet!!!!");
demoTest2 = this;
}
}
代码DemoUtil
public class DemoUtil {
public static void dosomething() {
System.out.println(DemoTest2.getDemoTest2().getName());
}
}
解决办法有两个:
1、是通过在DemoTest1中添加DemoTest2的对象初始化来解决:
@Autowired
DemoTest2 demoTest2
@Component
public class DemoTest1 implements InitializingBean{
@Autowired
DemoTest2 demoTest2;
public DemoTest1() {
System.out.println("@@@@@@init DemoTest1@@@@@");
}
@Override
public void afterPropertiesSet() throws Exception {
// TODO Auto-generated method stub
DemoUtil.dosomething();
}
}
2、在DemoTest1类的注解上,添加初始化依赖关系的注解@DependsOn来指定DemoTest1依赖于DemoTest2来初始化,也就是必须是DemoTest2先初始化,然后DemoTest1才能初始化:
@DependsOn("demoTest2")
@Component
public class DemoTest1 implements InitializingBean{
public DemoTest1() {
System.out.println("@@@@@@init DemoTest1@@@@@");
}
@Override
public void afterPropertiesSet() throws Exception {
// TODO Auto-generated method stub
DemoUtil.dosomething();
}
}
由此问题,还查了下springboot中各种类初始化的接口以及先后顺序:
在SpringBoot开发中想要在项目启动时执行一段代码,我们通常的做法有如下几种:
1、实现接口ApplicationRunner
@Component
public class TestApplicationRunner implements ApplicationRunner {
@Override
public void run(ApplicationArguments args) throws Exception {
// TODO Auto-generated method stub
System.out.println("****init TestApplicationRunner");
}
}
2、实现接口CommandLineRunner
@Component
public class TestCommandLineRunner implements CommandLineRunner {
@Override
public void run(String... args) throws Exception {
// TODO Auto-generated method stub
System.out.println("****init TestCommandLineRunner");
}
}
3、实现接口InitializingBean
@Component
public class TestInitializingBean implements InitializingBean {
@Override
public void afterPropertiesSet() throws Exception {
// TODO Auto-generated method stub
System.out.println("****init TestInitializingBean");
}
}
4、实现接口ServletContextListener
@Component
public class TestServletContextListener implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent sce) {
// TODO Auto-generated method stub
System.out.println("****init TestServletContextListener");
}
@Override
public void contextDestroyed(ServletContextEvent sce) {
// TODO Auto-generated method stub
System.out.println("****destroyed TestServletContextListener");
}
}
5、使用注解@PostConstruct
@Component
public class TestPostConstruct {
@PostConstruct
public void init(){
System.out.println("*****init TestPostConstruct");
}
}
已验证这五种方法初始化的先后顺序如下:
ServletContextListener>InitializingBean>PostConstruct>ApplicationRunner>CommandLineRunner