• 【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,下午。

  • 相关阅读:
    CommonJS和AMD/CMD
    map 有{}的时候需要有return 没有{}的时候不需要有return
    sublime3添加插件
    C++—模板(1)模板与函数模板
    函数的调用过程(栈帧)
    Linux-进程描述(5)之进程环境
    Linux-进程描述(4)之进程优先级与进程创建执行
    多态(2)纯虚函数与重载、重写(覆盖)、重定义(隐藏)
    多态(1)静态多态与动态多态以及虚函数相关
    Linux-进程描述(3)之进程状态僵尸进程与孤儿进程
  • 原文地址:https://www.cnblogs.com/developer_chan/p/10812172.html
Copyright © 2020-2023  润新知