Springboot扩展点系列实现方式、工作原理集合:
Springboot扩展点之ApplicationContextInitializer
Springboot扩展点之BeanFactoryPostProcessor
Springboot扩展点之BeanDefinitionRegistryPostProcessor
Springboot扩展点之BeanPostProcessor
Springboot扩展点之InstantiationAwareBeanPostProcessor
Springboot扩展点之SmartInstantiationAwareBeanPostProcessor
Springboot扩展点之ApplicationContextAwareProcessor
Springboot扩展点之@PostConstruct
Springboot扩展点之InitializingBean
Springboot扩展点之SmartInitializingSingleton
Springboot扩展点之CommandLineRunner和ApplicationRunner
Springboot扩展点之FactoryBean
Springboot扩展点之DisposableBean
Springboot扩展点系列之终结篇:Bean的生命周期
InitializingBean这个扩展点,其实在Springboot扩展点之BeanPostProcessor中已经简单有所涉及,而这篇文章的将重点分析其功能特性、实现方式和工作原理。
1、Spring中提供了InitializingBean接口,帮助用户实现一些自定义的初始化操作;
在bean实例化、属性注入后的提供了一个扩展方法afterPropertiesSet();
2、其实现方式很简单,需要bean实现InitializingBean接口并且重写afterPropertiesSet(),且bean要注册到Spring容器中,那么bean在实例化、属性注入后,重写的afterPropertiesSet()就会触发执行;
3、与InitializingBean#afterPropertiesSet()类似效果的是init-method,但是需要注意的是InitializingBean#afterPropertiesSet()执行时机要略早于init-method;
4、InitializingBean#afterPropertiesSet()的调用方式是在bean初始化过程中真接调用bean的afterPropertiesSet();
5、bean自定义属性init-method是通过java反射的方式进行调用 ;
6、InitializingBean#afterPropertiesSet()与init-method的执行时机是在BeanPostProcessor#postProcessBeforeInitialization()和BeanPostProcessor#postProcessAfterInitialization()之间;
注:关于BeanPostProcessor接口的相关功能特性、实现方式及工作原理可移步Springboot扩展点之BeanPostProcessor。
1、定义Dog类,注入Food类型的属性,定义一个myInitMethod,且实现InitializingBean,用于观察Dog类的实例化时机、属性注入时机、afterPropertiesSet()和init-method定义方法的执行时机
@Slf4j
public class Dog implements InitializingBean {
private String name = "wang cai";
private Food food;
public Dog() {
log.info("----Dog的无参构造方法被执行");
}
@Autowired
public void setFood(Food food) {
this.food = food;
log.info("----dog的food属性被注入");
}
@Override
public void afterPropertiesSet() throws Exception {
log.info("----com.fanfu.entity.Dog.afterPropertiesSet触发执行");
}
public void myInitMethod() {
log.info("----com.fanfu.entity.Dog.myInitMethod触发执行");
}
}
2、定义Food类,用于注入到Dog类中;
public class Food {
private String name = "大骨头";
public Food() {
log.info("----Food的无参数构造方法被执行");
}
}
3、把定义好的Dog、Food类,使用Configuriation配置类的方式注入到Spring容器中,并定义Dog类中的init-method方法为Dog#myInitMethod();
@Configuration
public class SpringConfig {
@Bean(initMethod = "myInitMethod")
public Dog dog(){
Dog dog = new Dog();
return dog;
}
@Bean
public Food food(){
Food food = new Food();
return food;
}
}
4、定义MyBeanPostProcessor类,并实现BeanPostProcessor接口,重写postProcessBeforeInitialization()和postProcessAfterInitialization(),用于观察InitializingBean#afterPropertiesSet()、init-method、postProcessBeforeInitialization()和postProcessAfterInitialization()的执行时机;
@Component
@Slf4j
public class MyBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if (beanName.equals("dog")) {
log.info("----postProcessBeforeInitialization---" + beanName);
//如果特定的bean实例化完成后,还未执行InitializingBean.afterPropertiesSet()方法之前,有一些其他操作,可以在这里实现
}
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (beanName.equals("dog")) {
log.info("----postProcessAfterInitialization---" + beanName);
//如果特定的bean实例化完成,InitializingBean.afterPropertiesSet()方法执行后,有一些其他操作,可以在这里实现
}
return bean;
}
}
5、单元测试
@Test
public void test4(){
log.info("----单元测试执行开始");
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext("com.fanfu");
log.info("----单元测试执行完毕");
}
验证结果:
从实现方式示例的执行结果来看,也验证了其功能特性,那么InitializingBean#afterPropertiesSet方法和init-method是什么时候开始调用?是用什么方式来调用的呢?下面从源码的角度来研究一下:
在Dog类的afterPropertiesSet()方法打上断点,让程序执行到断点处,然后观察其方法执行栈(方法执行栈按顺序来看是从下往上看的哦):
1、AbstractApplicationContext#refresh是Spring容器启动的关键一步,属于最外层的入口了;
2、AbstractApplicationContext#finishBeanFactoryInitialization,是Spring容器启动最后一大步了,前面关于容器必需的组件基本上已经创建好了,这里主要是把需要Spring管理的单例bean实例化注册到Spring容器里;
3、DefaultListableBeanFactory#preInstantiateSingletons,从名字上看更可以看出来是要实例化单例类型的bean了;
4、AbstractBeanFactory#getBean(java.lang.String)-->AbstractBeanFactory#doGetBean
-->DefaultSingletonBeanRegistry#getSingleton(String,ObjectFactory),
要调用getSingleton(String,ObjectFactory)时,这里要注意一下ObjectFactory是一个函数式接口,使用了java8的lambda表达式的写法,在进入到getSingleton()方法内,执行到ObjectFactory#getObject()方法时,才会触发createBean()方法执行。(这一段看不太懂的小伙伴要稍微注意一下,顺便可以学习一下lambda表达式是怎么使用的)
if (mbd.isSingleton()) {
//java8的lambda表达式
sharedInstance = getSingleton(beanName, () -> {
try {
return createBean(beanName, mbd, args);
}
catch (BeansException ex) {
destroySingleton(beanName);
throw ex;
}
});
bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
@FunctionalInterface
public interface ObjectFactory {
T getObject() throws BeansException;
}
5、AbstractAutowireCapableBeanFactory#createBeanString,RootBeanDefinition, Object[])
-->AbstractAutowireCapableBeanFactory#doCreateBean,这时完成了Bean的实例化,属性还未注入;
6、AbstractAutowireCapableBeanFactory#createBeanString,RootBeanDefinition, Object[])
-->AbstractAutowireCapableBeanFactory#populateBean,完成属性的注入;
7、顺着AbstractAutowireCapableBeanFactory#populateBean往下执行到initializeBean(),(如果看过之前分享的Springboot扩展点之BeanPostProcessor,那么对initializeBean()就比较熟了),BeanPostProcessor的postProcessBeforeInitialization()和postProcessAfterInitialization()就是在这个方法中执行的;
protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) {
if (System.getSecurityManager() != null) {
AccessController.doPrivileged((PrivilegedAction
8、从最外层以剥洋葱的方式一层一层分析到这里,AbstractAutowireCapableBeanFactory#initializeBean()--->invokeInitMethods()才正式走入高潮部分,InitializingBean#afterPropertiesSet和initMethod方法的执行时机就在这里;
protected void invokeInitMethods(String beanName, Object bean, @Nullable RootBeanDefinition mbd)
throws Throwable {
//判断bean是否实现了InitializingBean
boolean isInitializingBean = (bean instanceof InitializingBean);
if (isInitializingBean && (mbd == null || !mbd.isExternallyManagedInitMethod("afterPropertiesSet"))) {
if (logger.isTraceEnabled()) {
logger.trace("Invoking afterPropertiesSet() on bean with name '" + beanName + "'");
}
if (System.getSecurityManager() != null) {
try {
AccessController.doPrivileged((PrivilegedExceptionAction
9、进入到invokeCustomInitMethod()内,使用java反射来执行自定义的init-method方法;
到这里InitializingBean#afterPropertiesSet方法和init-method是什么时候开始调用,是用什么方式来调用的呢,已经分析清楚了,同时还了解到InitializingBean#afterPropertiesSet方法和init-method有类似的效果,执行时机也比较接近,但是是两个完全不同的东西,且InitializingBean#afterPropertiesSet方法的执行时机要稍早于init-method。
这个扩展点其实是比较有用的一个扩展点,可以用于修改默认设置的属性、添加补充额外的属性值,或者针对关键属性做校验。而Spring内部也有比较经典的实现,有些经常用到,但是可能没有觉察到,如Spring MVC中的RequestMappingHandlerMapping,完成了URL与controller的映射工作。
@Override
@SuppressWarnings("deprecation")
public void afterPropertiesSet() {
this.config = new RequestMappingInfo.BuilderConfiguration();
this.config.setUrlPathHelper(getUrlPathHelper());
this.config.setPathMatcher(getPathMatcher());
this.config.setSuffixPatternMatch(useSuffixPatternMatch());
this.config.setTrailingSlashMatch(useTrailingSlashMatch());
this.config.setRegisteredSuffixPatternMatch(useRegisteredSuffixPatternMatch());
this.config.setContentNegotiationManager(getContentNegotiationManager());
super.afterPropertiesSet();
}
Springboot扩展点之ApplicationContextInitializer
Springboot扩展点之BeanDefinitionRegistryPostProcessor
Springboot扩展点之BeanFactoryPostProcessor
Springboot扩展点之BeanPostProcessor
Springboot扩展点之InstantiationAwareBeanPostProcessor
Springboot扩展点之SmartInstantiationAwareBeanPostProcessor
Springboot扩展点之ApplicationContextAwareProcessor
Springboot扩展点之@PostConstruct
Springboot扩展点之InitializingBean