IOC之InitializingBean 和 init-method
Spring在初始化时进行三个检测扩展:之前说到的Aware
和BeanPostProcessor
,还有本篇InitializingBean
接口和init-method
。
因为DisposableBean
接口 和 destroy-method
与初始化的时候逻辑一样,故只分析InitializingBean
接口和init-method
。
InitializingBean
接口
InitializingBean
接口只提供了一个方法afterPropertiesSet()
,用来为bean提供定义初始化方法。
1 2 3 4 5
| public interface InitializingBean {
void afterPropertiesSet() throws Exception; }
|
Spring 在完成实例化后,设置完所有属性,进行 Aware
接口和 BeanPostProcessor
前置处理之后,会接着检测当前 bean 对象是否实现了 InitializingBean
接口。如果是,则会调用其 afterPropertiesSet()
方法,进一步调整 bean 实例对象的状态,最后会执行BeanPostProcessor
后置处理。
实例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| public class TestInitializingBean implements InitializingBean { private String name; @Override public void afterPropertiesSet() throws Exception { System.out.println("InitializingBean init..."); this.name = "jievhahaTest"; }
public String getName() { return name; }
public void setName(String name) { this.name = name; } }
|
1 2 3 4 5 6 7
| public class MyApplication { public static void main(String[] args) { ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml"); TestInitializingBean testInitializingBean = (TestInitializingBean) applicationContext.getBean("testInitializingBean"); System.out.println(testInitializingBean.getName()); } }
|
1 2 3 4 5 6 7 8
| <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="testInitializingBean" class="com.jievhaha.TestInitializingBean"> <property name="name" value="test"/> </bean> </beans>
|
最后执行结果为:
1 2 3 4
| Bean[testInitializingBean]开始初始化 InitializingBean init... Bean[testInitializingBean]完成初始化 jievhahaTest //假如在afterPropertiesSet中没有设置,最后结果为test
|
invokeInitMethods
在Bean
初始化阶段, Spring 容器会主动检查当前 bean 是否已经实现了 InitializingBean 接口,如果实现了,则会掉用其 afterPropertiesSet()
方法。这个主动检查、调用的动作是由 invokeInitMethods(String beanName, final Object bean, @Nullable RootBeanDefinition mbd)
方法来完成的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
|
protected void invokeInitMethods(String beanName, final Object bean, @Nullable RootBeanDefinition mbd) throws Throwable { boolean isInitializingBean = (bean instanceof InitializingBean); if (isInitializingBean && (mbd == null || !mbd.isExternallyManagedInitMethod("afterPropertiesSet"))) { if (logger.isTraceEnabled()) { logger.trace("Invoking afterPropertiesSet() on bean with name '" + beanName + "'"); } if (System.getSecurityManager() != null) { try { AccessController.doPrivileged((PrivilegedExceptionAction<Object>) () -> { ((InitializingBean) bean).afterPropertiesSet(); return null; }, getAccessControlContext()); } catch (PrivilegedActionException pae) { throw pae.getException(); } } else { ((InitializingBean) bean).afterPropertiesSet(); } }
if (mbd != null && bean.getClass() != NullBean.class) { String initMethodName = mbd.getInitMethodName(); if (StringUtils.hasLength(initMethodName) && !(isInitializingBean && "afterPropertiesSet".equals(initMethodName)) && !mbd.isExternallyManagedInitMethod(initMethodName)) { invokeCustomInitMethod(beanName, bean, mbd); } } }
|
- 首先,检测当前 bean 是否实现了 InitializingBean 接口,如果实现了则调用其
afterPropertiesSet()
方法。
- 然后,再检查是否也指定了
init-method
,如果指定了则通过反射机制调用指定的 init-method
方法。
init-method
属性
直接实现InitializingBean
接口破坏了Spring的核心理念:无侵入性,故有另一种实现方式:init-method
。
即spring配置文件注册bean时,给<bean>
标签添加init-method
属性,也可以直接使用<beans>
标签的default-init-method
属性来统一指定初始化方法,不用每个<bean>
标签指定init-method
属性。
总结
从 invokeInitMethods(...)
方法中,我们知道 init-method
指定的方法会在 afterPropertiesSet()
方法之后执行,如果 afterPropertiesSet()
方法的执行的过程中出现了异常,则 init-method
是不会执行的,而且由于 init-method
采用的是反射执行的方式,所以 afterPropertiesSet()
方法的执行效率一般会高些,但是并不能排除我们要优先使用 init-method
,主要是因为它消除了 bean 对 Spring 的依赖,Spring 没有侵入到我们业务代码,这样会更加符合 Spring 的理念。诚然,init-method
是基于 xml 配置文件的,就目前而言,我们的工程几乎都摒弃了配置,而采用注释的方式,那么 @PreDestory
可能比较适合。