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