JAVA && Spring && SpringBoot2.x — 学习目录
与容器的生命周期交互,即Spring容器启动后和容器销毁前对每个bean执行操作。
下文所说的声明周期方法,均是在创建bean时被调用。
创建bean的源码
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
throws BeanCreationException {
....
Object exposedObject = bean;
try {
//依赖注入,填充属性
populateBean(beanName, mbd, instanceWrapper);
//初始化的方法(在创建bean的时候被执行)
exposedObject = initializeBean(beanName, exposedObject, mbd);
}
...
return exposedObject;
}
protected Object initializeBean(final String beanName, final Object bean, @Nullable RootBeanDefinition mbd) {
...
Object wrappedBean = bean;
if (mbd == null || !mbd.isSynthetic()) {
//CommandAnnotationBeanPostProcessor会回调@PostConstrut方法
wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
}
try {
//回调方法
invokeInitMethods(beanName, wrappedBean, mbd);
}
...
if (mbd == null || !mbd.isSynthetic()) {
//创建代理对象
wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
}
return wrappedBean;
}
protected void invokeInitMethods(String beanName, final Object bean, @Nullable RootBeanDefinition mbd)
throws Throwable {
//判断是否实现InitializingBean接口,调用afterPropertiesSet()方法
boolean isInitializingBean = (bean instanceof InitializingBean);
if (isInitializingBean && (mbd == null || !mbd.isExternallyManagedInitMethod("afterPropertiesSet"))) {
...
else {
((InitializingBean) bean).afterPropertiesSet();
}
}
//判断是否含有Init方法,通过反射回调。
if (mbd != null && bean.getClass() != NullBean.class) {
String initMethodName = mbd.getInitMethodName();
if (StringUtils.hasLength(initMethodName) &&
!(isInitializingBean && "afterPropertiesSet".equals(initMethodName)) &&
!mbd.isExternallyManagedInitMethod(initMethodName)) {
invokeCustomInitMethod(beanName, bean, mbd);
}
}
}
1. 初始化方法
1.1 CommandLineRunner、ApplicationRunner接口
CommandLineRunner、ApplicationRunner接口是在容器成功启动后的最后一步回调。
在官方doc中:
在一个spring上下文中,多个CommandLineRunner可以被同时执行,并且执行顺序按照@Order
注解的参数保持一致。
若是需要访问ApplicationArguments去代替掉字符串数组,可以考虑使用ApplicationRunner类。
//@Order(2)
@Component
public class ServerStartedReport implements CommandLineRunner{
@Override
public void run(String... args) throws Exception {
System.out.println("===========ServerStartedReport启动====="+ LocalDateTime.now());
}
}
1.2 InitializingBean接口
可以使用spring的InitializingBean接口的afterPropertiesSet来完成初始化。这个方法将在所有的属性被初始化后被调用。但是会在init方法前调用。
但如果是延迟加载的话,即使用@Lazy
注解,可以让用户在首次使用的时候进行初始化。afterPropertiesSet也会在首次使用的时候进行初始化。
@Component
@Lazy
public class EventImpl implements InitializingBean {
private String id;
private String name;
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("来了!!!=========================================================");
}
}
1.3 @PostConstruct注解
如果想在生成对象之后完成某些初始化操作,而偏偏这些初始化操作又依赖于依赖注入,那么无法在构造函数中实现,为此,可以使用@PostConstruct注解一个方法来完成初始化操作。
被@PostConstruct修饰的方法会在服务器加载Servlet(bean)的时候运行,并且只会被服务器调用一次,但是注意的是,该方法会在构造方法、属性依赖注入之后,init()方法之前运行。
1.4 init()方法
在Spring中,我们将对象放入容器的方法一般有两种。
- 注解法,使用@Component注解等;
- 配置法,将bean注册到spring.xml中;
在SpringBoot中,我们可以使用@Configuration
和@Bean
注解,将Bean放入到容器中。我们也可以指定init方法。初始化对象的时候,便可自动调用init方法执行。
@Configuration
public class DemoBeanConfiguration {
@Bean(initMethod = "init",destroyMethod = "destroy")
public DemoBean getBean(){
DemoBean demoBean = new DemoBean();
demoBean.setId("001");
demoBean.setName("002");
return demoBean;
}
}
public class DemoBean implements InitializingBean {
private String id;
private String name;
public DemoBean() {
System.out.println("初始化——构造方法!");
}
private void init(){
System.out.println("初始化——init方法!");
}
private void destroy() {
System.out.println("初始化——destroy方法!");
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("初始化——InitializingBean接口");
}
@PostConstruct
private void postConstruct(){
System.out.println("初始化——@PostConstruct方法");
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
在容器初始化过程中便会按照如下的顺序执行初始化对象的操作。
初始化——构造方法!
初始化——@PostConstruct方法
初始化——InitializingBean接口
初始化——init方法!
---容器启动完毕后...
容器启动—CommandLineRunner接口方法!
1.4 SmartInitializingSingleton接口
实现SmartInitializingSingleton的接口后,当所有单例 bean 都初始化完成以后, Spring的IOC容器会回调该接口的 afterSingletonsInstantiated()方法。
使用方式:
import org.springframework.beans.factory.ListableBeanFactory;
import org.springframework.beans.factory.SmartInitializingSingleton;
import org.springframework.stereotype.Component;
@Component
public class MyRegister implements SmartInitializingSingleton {
private ListableBeanFactory beanFactory;
public MyRegister(ListableBeanFactory beanFactory) {
this.beanFactory = beanFactory;
}
@Override
public void afterSingletonsInstantiated() {
String[] beanNames = beanFactory.getBeanNamesForType(IPerson.class);
for (String s : beanNames) {
System.out.println(s);
}
}
2. 销毁方法
和初始化方法对应,Spring也是提供了如下销毁对象的回调方法。
文章参考: