spring源码分析IOC容器初始化——查漏补缺

Posted developer_chan

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了spring源码分析IOC容器初始化——查漏补缺相关的知识,希望对你有一定的参考价值。

前言:本文分析InitializingBean和init-method方法,其实该知识点在AbstractAutowireCapableBeanFactory#initializeBean方法中有所提及,这里对其进行详细分析。


InitializingBean

InitializingBean是一个接口,它只包含一个afterPropertiesSet方法:

 1 public interface InitializingBean {
 2 
 3     /**
 4      * 该方法在BeanFactory设置完了所有的属性之后被调用<br/>
 5      * 该方法允许bean实例设置了所有bean属性时执行初始化工作,如果该过程出现了错误,则需要抛出异常<br/>
 6      * Invoked by the containing {@code BeanFactory} after it has set all bean properties
 7      * and satisfied {@link BeanFactoryAware}, {@code ApplicationContextAware} etc.
 8      * <p>This method allows the bean instance to perform validation of its overall
 9      * configuration and final initialization when all bean properties have been set.
10      *
11      * @throws Exception in the event of misconfiguration (such as failure to set an
12      *                   essential property) or if initialization fails for any other reason
13      */
14     void afterPropertiesSet() throws Exception;
15 
16 }

分析:

Spring在完成实例化后,设置完所有属性,进行"Aware"接口和"BeanPostProcessor"前置处理后,会接着检测当前bean对象是否实现了InitializingBean接口,如果是,则会调用其afterPropertiesSet方法进一步调整bean实例对象的状态。

InitializingBean示例

 1 public class UserDefinedInitializingBean implements InitializingBean {
 2 
 3     private String msg;
 4 
 5     public String getMsg() {
 6         return msg;
 7     }
 8 
 9     public void setMsg(String msg) {
10         this.msg = msg;
11     }
12 
13     @Override
14     public void afterPropertiesSet() throws Exception {
15         System.out.println("InitializingBean afterPropertiesSet......");
16         this.msg = "修改了msg,msg=hello initializingBean!!!!!!";
17     }
18 }

进行如下配置:

1 <bean id="userDefinedInitializingBean" class="com.dev.basebean.initializingbean.UserDefinedInitializingBean"
2           p:msg="i am msg!!!"/>

测试:

1 @Test
2     public void initializingBeanTest() {
3         ApplicationContext context = new ClassPathXmlApplicationContext("classpath*:com/dev/config/initializingbean/initializingbean.xml");
4         UserDefinedInitializingBean initializingBean = context.getBean(UserDefinedInitializingBean.class);
5         System.out.println(initializingBean.getMsg());
6     }

运行结果如下:

技术图片

从运行结果来看,msg属性被我们修改了,在afterPropertiesSet方法中,这相当于Spring又提供给我们一种可以改变bean实例对象的方法。

invokeInitMethods

InitializingBean的afterPropertiesSet方法就是在invokeInitMethods方法中被执行的。

AbstractAutowireCapableBeanFactory#invokeInitMethods:

 1 protected void invokeInitMethods(String beanName, final Object bean, @Nullable RootBeanDefinition mbd)
 2             throws Throwable {
 3 
 4         // 首先先检查是否是InitializingBean,如果是,则需要调用afterPropertiesSet()
 5         boolean isInitializingBean = (bean instanceof InitializingBean);
 6         if (isInitializingBean && (mbd == null || !mbd.isExternallyManagedInitMethod("afterPropertiesSet"))) {
 7             if (logger.isDebugEnabled()) {
 8                 logger.debug("Invoking afterPropertiesSet() on bean with name ‘" + beanName + "‘");
 9             }
10             // 安全模式
11             if (System.getSecurityManager() != null) {
12                 try {
13                     AccessController.doPrivileged((PrivilegedExceptionAction<Object>) () -> {
14                         ((InitializingBean) bean).afterPropertiesSet();
15                         return null;
16                     }, getAccessControlContext());
17                 } catch (PrivilegedActionException pae) {
18                     throw pae.getException();
19                 }
20             } else {
21                 // 属性初始化处理
22                 ((InitializingBean) bean).afterPropertiesSet();
23             }
24         }
25 
26         if (mbd != null && bean.getClass() != NullBean.class) {
27             String initMethodName = mbd.getInitMethodName();
28             if (StringUtils.hasLength(initMethodName) &&
29                     !(isInitializingBean && "afterPropertiesSet".equals(initMethodName)) &&
30                     !mbd.isExternallyManagedInitMethod(initMethodName)) {
31                 // 激活用户自定义的初始化方法
32                 invokeCustomInitMethod(beanName, bean, mbd);
33             }
34         }
35     }

分析:

  • 首先检查当前bean是否实现了InitializingBean接口,如果实现了,则调用其afterPropertiesSet方法。
  • 然后再检查是否指定了init-method,如果指定了init-method方法,则通过反射进行调用。

init-method

对init-method进行示例,只需根据上面示例进行一点调整即可。

1 <bean id="userDefinedInitializingBean" class="com.dev.basebean.initializingbean.UserDefinedInitializingBean"
2           p:msg="i am msg!!!" init-method="initMethod"/>

在UserDefinedInitializingBean中增加如下代码:

1 public void initMethod() {
2         System.out.println("通过init-method方法对msg属性进行修改");
3         this.msg = "修改了msg,msg=hello init-method!!!!!!";
4     }

运行结果如下:

技术图片

从结果上可以看到init-method方法是在afterPropertiesSet方法之后,并且达到了同样的效果,对代码无侵入性。

分析到这里其实已经把bean的生命周期都总结出来,下篇文章进行具体总结,这里想来看本篇小结。

总结

从invokeInitMethods方法中,我们知道init-method指定的方法会在afterPropertiesSet方法后执行,如果afterPropertiesSet方法执行过程中出现异常,init-method方法是不会执行的。使用init-method使其对业务代码的侵入降低,虽然init-method是基于xml配置文件的,但我们也可以通过@PostConstruct注解的形式来进行替换。

至此InitializingBean和init-method已分析完毕,对于DisposableBean和destroy-method与init相似,这里不再进行赘述。


by Shawn Chen,2019.05.05,下午。

以上是关于spring源码分析IOC容器初始化——查漏补缺的主要内容,如果未能解决你的问题,请参考以下文章

spring源码分析IOC容器初始化

Spring源码分析总结-IOC容器初始化

spring源码分析IOC容器初始化(总结)

Spring IOC容器初始化流程源码分析

Spring IOC容器初始化流程源码分析

半夜思考之查漏补缺, Spring 中的容器后处理器