【spring源碼分析】IOC容器初始化——查漏補缺(三)

前言:本文分析InitializingBean和init-method方法,其實該知識點在AbstractAutowireCapableBeanFactory#initializeBean方法中有所說起,這裏對其進行詳細分析。安全


InitializingBean

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

 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實例對象的狀態。this

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 }

進行以下配置:spa

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

測試:debug

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     }

運行結果以下:3d

從運行結果來看,msg屬性被咱們修改了,在afterPropertiesSet方法中,這至關於Spring又提供給咱們一種能夠改變bean實例對象的方法。code

invokeInitMethods

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

AbstractAutowireCapableBeanFactory#invokeInitMethods:xml

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

相關文章
相關標籤/搜索