引言
现在要研究的问题是关于Spring框架中各种初始化方法的执行顺序。常见的几种方法,比如static final和final static的区别,构造方法,@Autowired,@PostConstruct,afterPropertiesSet(需要实现InitializingBean接口),还有init-method配置,或者通过@Bean的initMethod属性指定的初始化方法。这些方法的执行顺序是怎样的呢?
首先,回忆一下Spring bean的生命周期。当容器启动时,Spring会实例化bean,然后进行依赖注入,接着执行一些初始化方法,最后bean就可以被使用了。而销毁的时候也有相应的方法,比如@PreDestroy,DisposableBean的destroy方法,或者destroy-method配置。
但这里的问题主要集中在初始化阶段的各个方法的执行顺序。那具体来说,当一个bean被创建时,这些方法的执行顺序是怎样的呢?
首先,构造方法(Constructor)应该是最先被调用的,因为对象实例化的时候首先要调用构造方法。构造方法执行完毕后,Spring会进行依赖注入,这时候@Autowired注解的字段或方法会被处理。所以构造方法之后是@Autowired的处理。
接下来,可能涉及到@PostConstruct注解的方法,还有InitializingBean接口的afterPropertiesSet方法,以及通过init-method配置的方法。那这三个的顺序是什么呢?
根据之前学过的知识,@PostConstruct注解的方法会在依赖注入完成后被调用,也就是在构造方法和@Autowired之后执行。而实现了InitializingBean接口的afterPropertiesSet方法会在@PostConstruct之后执行吗?然后才是init-method指定的方法?
或者有没有可能它们的顺序是:@PostConstruct → afterPropertiesSet → init-method?
Spring的官方文档中提到,初始化方法的执行顺序是:
1. @PostConstruct 注解的方法
2. InitializingBean的afterPropertiesSet()
3. 自定义的init-method方法
那这样的话,这三种初始化方法的顺序就是这样的。例如,如果一个bean同时有这三种方法,它们的执行顺序应该是先执行@PostConstruct方法,然后执行afterPropertiesSet,最后执行init-method指定的方法。
接下来,可以看static final和final static的区别,实际上,在Java中,static final和final static是等价的,它们的顺序不影响变量的声明,只是风格上的差异。所以这可能是一个干扰信息,和初始化顺序无关。构造方法自然是第一步,因为对象必须先实例化。然后是依赖注入,也就是@Autowired的处理,这可能是在构造方法之后,但如果是通过构造方法注入的话,@Autowired可能会在构造方法上使用,这时候构造方法的参数由Spring注入。这时候,构造方法的执行可能是在依赖注入的过程中进行的。例如,如果使用构造器注入,那么构造方法会被Spring调用,并传入所需的依赖项。此时,@Autowired可能标注在构造方法上,不过从Spring 4.3开始,如果类只有一个构造方法,那么不需要显式添加@Autowired注解,Spring会自动使用该构造方法。如果有多个构造方法,则需要指定哪一个用@Autowired。
接下来,当依赖注入完成后,即所有@Autowired的字段或方法都被处理之后,初始化方法开始执行。这时候,首先执行的是@PostConstruct注解的方法,然后是InitializingBean的afterPropertiesSet()方法,最后是init-method指定的方法。
所以,综合起来,整个顺序应该是:
1. 构造方法(Constructor)
2. @Autowired 注入依赖
3. @PostConstruct 注解的方法
4. afterPropertiesSet() (实现了InitializingBean接口)
5. init-method 指定的方法(通过XML配置或者@Bean的initMethod属性)
不过,需要注意的是,构造方法和@Autowired的顺序可能因注入方式的不同而有所变化。例如,如果是构造器注入,那么构造方法是在实例化的时候调用的,此时参数由Spring注入,这时候@Autowired(如果用在构造方法上)是在构造方法执行的时候处理的。而如果是字段注入或者setter方法注入,那么构造方法执行完毕后,Spring再进行依赖注入。
但不管怎样,@PostConstruct、afterPropertiesSet、init-method这三个初始化方法的执行顺序是确定的,即按照上述顺序执行。
为了验证这个顺序,可以举一个例子,以下涉及了java中各种初始化方法的执行顺序,我们创建一个springboot测试项目来观察加载顺序。
1.创建application.yml
server:
port: 8080
2.创建Application.java启动类
package com.boot;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class Application {
public static void main(String[] args) throws Exception {
SpringApplication.run(Application.class, args);
}
}
3.创建SomeDependency.java(用于测试@Autowired)
package com.boot.util;
import org.springframework.stereotype.Component;
@Component
public class SomeDependency {
public void check() {
System.out.println("SomeDependency 已注入成功!");
}
}
4.创建AppConfig.java(用于测试@Bean)
package com.boot.util;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ComponentScan;
@Configuration
@ComponentScan(basePackages = "com.boot.util") // 确保扫描到SomeDependency
public class AppConfig {
@Bean(initMethod = "initMethod")
public MyBean myBean() {
return new MyBean();
}
}
5.创建测试类MyBean.java
package com.boot.util;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import javax.annotation.PostConstruct;
public class MyBean implements InitializingBean {
// ---------------------
// 静态字段初始化(类加载时执行)
// ---------------------
public static final String STATIC_FINAL_FIELD = "Static Final Field";
public static String staticField = (STATIC_FINAL_FIELD=="" ? "Static Final 未加载":"Static Final 已加载") +"\nstatic已加载";
static {
System.out.println("静态块初始化: staticField = " + staticField);
}
// ---------------------
// 实例字段和依赖注入
// ---------------------
@Autowired
private SomeDependency dependency;
public MyBean() {
System.out.println("构造方法调用");
try{
dependency.check(); // 验证依赖是否注入
}catch (Throwable t){
System.out.println("SomeDependency 还未注入");
}
}
@PostConstruct
public void postConstruct() {
System.out.println("@PostConstruct方法调用");
dependency.check(); // 验证依赖是否注入
}
@Override
public void afterPropertiesSet() {
System.out.println("InitializingBean的afterPropertiesSet()调用");
}
public void initMethod() {
System.out.println("自定义init-method调用");
}
}
6.启动项目,查看控制台输出的结果
7.结论
以下按照加载顺序排列:
- static final 或 final static
- static
- static静态块
- 构造方法
- @Autowired
- @postConstruct
- afterpropertiesset()方法,需要实现initializingbean
- intmethod方法,@Bean(initMethod = "init") ,等价于 init-method,<bean />配置
<bean id="myBean" class="com.boot.util.Mybean" init-method="createPool" />