Spring Bean Lifecycle 中initMethod和@PostConstruct 优先级 和 内部执行

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);

 

 

 

 

hmoban主题是根据ripro二开的主题,极致后台体验,无插件,集成会员系统
自学咖网 » Spring Bean Lifecycle 中initMethod和@PostConstruct 优先级 和 内部执行