Spring Bean Lifecycle 中initMethod和@PostConstruct 优先级 和 内部执行
如题,我们来看看这两者的到底谁先执行,这两个方法都是用来对bean进行初始化操作的。
我们先创建一个User类,并添加init方法和@PostConstruct注解方法,部分代码如下:
@Bean(initMethod = "init") User user(){ return new User(); }
class User {
public String userName;
public void init(){
this.userName = "initName";
System.out.println(" user init. ....");
}
@PostConstruct
public void postConstruct(){
System.out.println(" postConstruct. ....");
}
@Override public String toString() { return "user:"+this.userName; } }
分别在postConstruct()方法和init() 方法内部打断点,启动程序,发现优先在postConstruct方法中停住了,所以是@postConstruct注解方法比@Bean(initMethod = “”)方式的initMethod方法优先执行 。
spring 官方文档也有说明:
我们来分析下其原因,
@PostConstruct 解析和执行分析
我们先看下断点的线程调用栈信息:
发现其实@PostConstruct注解对应的方法是被InitDestroyAnnotationBeanPostProcessor 调用了,所以说就是BeanPostProcessor的机制了。
那么我们来看下InitDestroyAnnotationBeanPostProcessor 是如何解析@PostConstruct注解方法并调用的呢?根据线程调用栈信息:
我们可以得出org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor#findLifecycleMetadata 方法解析了生命周期方法并做了缓存处理,真正解析的方法是:org.springframework.beans.factory.annotation.InitDestroyAnnotationBeanPostProcessor#buildLifecycleMetadata 部分源码:
ReflectionUtils.doWithLocalMethods(targetClass, method -> { if (this.initAnnotationType != null && method.isAnnotationPresent(this.initAnnotationType)) { LifecycleElement element = new LifecycleElement(method); currInitMethods.add(element); if (logger.isTraceEnabled()) { logger.trace("Found init method on class [" + clazz.getName() + "]: " + method); } } if (this.destroyAnnotationType != null && method.isAnnotationPresent(this.destroyAnnotationType)) { currDestroyMethods.add(new LifecycleElement(method)); if (logger.isTraceEnabled()) { logger.trace("Found destroy method on class [" + clazz.getName() + "]: " + method); } } }); initMethods.addAll(0, currInitMethods); destroyMethods.addAll(currDestroyMethods); targetClass = targetClass.getSuperclass();
其中initAnnotationType 和 destroyAnnotationType 值就是PostConstruct.class , PreDestroy.class ,可以通过反向引用查找:
public CommonAnnotationBeanPostProcessor() { setOrder(Ordered.LOWEST_PRECEDENCE - 3); setInitAnnotationType(PostConstruct.class); setDestroyAnnotationType(PreDestroy.class); ignoreResourceType("javax.xml.ws.WebServiceContext"); }
这里,我们看到了CommonAnnotationBeanPostProcessor类,其继承了InitDestroyAnnotationBeanPostProcessor类,原来是在CommonAnnotationBeanPostProcessor实例化的时候设置进去的。 那么这是在什么时候实例化的吗? 初看是个BeanPostProcessor,要是springboot的话,一般实例化方式都是在spring.factories中配置实现的。可是CommonAnnotationBeanPostProcessor类是spring-beans下的,是spring的核心模块,就没有这个功能了。我们直接利用IDEA的强大引用查找,如图:
直觉告诉我,红框的工具类利用了RootBeanDefinition bean类定义的方式加载实例化了CommonAnnotationBeanPostProcessor,进来一窥究竟:
这里还对jsr250做了检测:
那又有个疑问了,org.springframework.context.annotation.AnnotationConfigUtils#registerAnnotationConfigProcessors(org.springframework.beans.factory.support.BeanDefinitionRegistry, java.lang.Object) 这个方法又是谁调的呢? 打断点执行:
发现是org.springframework.context.annotation.AnnotationConfigApplicationContext#AnnotationConfigApplicationContext() 这就是spring 注解容器了,由org.springframework.boot.SpringApplication#run(java.lang.String…) 内部调用的:
context = createApplicationContext();
@Bean的initMethod 解析和执行分析
首先我们可以看下其javadoc注释说明:
/** * The optional name of a method to call on the bean instance during initialization. * Not commonly used, given that the method may be called programmatically directly * within the body of a Bean-annotated method. * <p>The default value is {@code ""}, indicating no init method to be called. * @see org.springframework.beans.factory.InitializingBean * @see org.springframework.context.ConfigurableApplicationContext#refresh() */ String initMethod() default "";
说明是不常用的,然后我们看到org.springframework.beans.factory.InitializingBean ,而InitializingBean就是用来初始化bean的,在当所有的propproperties设置完成后调用的。直觉跟实现InitializingBean接口的效果一样,我们先保留这个直觉好了。
现在我们应该找出@Bean(initMethod = “init”) 是怎么被解析的? 我们肯定不知道是哪个类去解析的,那么我们先在执行方法打个断点,通过查看线程调用栈信息来找出具体是在哪个类中执行我们的init方法的,通过找到这个类,然后再找是怎样解析获取init方法元信息的。
这里我们还可以确定之前的直接是正确的,看图:
这里实现InitializingBean的方法afterPropertiesSet比设置@Bean(initMethod=“”)方法先调用。
我们看到initMethod是被存储在了RootBeanDefinition mbd中:
String initMethodName = mbd.getInitMethodName();
那么就是说在Bean解析的时候,也解析获取了initMethod方法。
最外部的调用方法为:org.springframework.context.support.AbstractApplicationContext#refresh 的
// Invoke factory processors registered as beans in the context. invokeBeanFactoryPostProcessors(beanFactory);
而执行init方法的最外部调用方法为:org.springframework.context.support.AbstractApplicationContext#refresh 的
// Instantiate all remaining (non-lazy-init) singletons. finishBeanFactoryInitialization(beanFactory);
自学咖网 » Spring Bean Lifecycle 中initMethod和@PostConstruct 优先级 和 内部执行