我們在spring初始化bean的過程中在最后遺留了一個問題,本篇來說,問題是在#InitializingBean(...)中首先是對bean的判斷,可以通過#afterPropertiesSet()方法來處理,具體詳情請看spring容器之創(chuàng)建bean的終結(jié)篇,當(dāng)然還有我們在配置文件中配置的init-method方法等
InitializingBean
該接口位于org.springframework.beans.factory包下,只有一個afterPropertiesSet()方法,其主要的作用是對bean自定義的實(shí)現(xiàn)過程,屬性設(shè)置的檢查等操作.
public interface InitializingBean {
/**
* Invoked by the containing {@code BeanFactory} after it has set all bean properties
* and satisfied {@link BeanFactoryAware}, {@code ApplicationContextAware} etc.
* <p>This method allows the bean instance to perform validation of its overall
* configuration and final initialization when all bean properties have been set.
* @throws Exception in the event of misconfiguration (such as failure to set an
* essential property) or if initialization fails for any other reason
*/
void afterPropertiesSet() throws Exception;
大概的可以了解到afterPropertiesSet()是在實(shí)例化時進(jìn)行對bean的屬性的檢查,前提是我們此刻的bean是已經(jīng)完成了Aware和BeanPostProcesser的設(shè)置過程,接下來我們簡單的寫個Demo來了解下該方法的作用:
case
定義一個類實(shí)現(xiàn)InitializingBean接口
public class InitializingBeanCase implements InitializingBean {
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
private String name;
public void afterPropertiesSet() throws Exception {
System.out.println("實(shí)例化bean開始了....");
this.name = "9527";
}
代碼簡單,我們直接給name屬性賦值,將該類交給spring容器來創(chuàng)建:
<?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="initializingBeanCase" class="com.sgcc.InitializingBean.InitializingBeanCase">
<property name="name" value="9528"/>
</bean>
</beans>
在配置文件中我們是想讓spring容器幫我們創(chuàng)建name為9528的bean
測試代碼:
public class App {
public static void main(String[] args) {
ClassPathResource resource = new ClassPathResource("InitializingBeanCaseConfig.xml");
DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory);
reader.loadBeanDefinitions(resource);
InitializingBeanCase initializingBeanCase = (InitializingBeanCase) factory.getBean("initializingBeanCase");
System.out.println("name:"+initializingBeanCase.getName());
}
}
運(yùn)行結(jié)果:
看到結(jié)果的時候我也蒙了,奧明白了,該方法原來是spring給我們提供了一種在初始化時,動態(tài)修改bean的屬性的途徑,使得我們更加隨心操作bean的一種手段.
初始過程
既然該方法是在初始化時來操作bean的屬性的,我們來看一下,初始化過程#invokeInitMethods(...)的過程,該方法調(diào)用的前提是判斷我們的bean是否是實(shí)現(xiàn)了InitializingBean接口,來看代碼:
protected void invokeInitMethods(String beanName, final Object bean, @Nullable RootBeanDefinition mbd)
throws Throwable {
//首先是檢查是否是InitializingBean,如果是需要調(diào)用afterPropertiesSet
boolean isInitializingBean = (bean instanceof InitializingBean);
if (isInitializingBean && (mbd == null || !mbd.isExternallyManagedInitMethod("afterPropertiesSet"))) {
if (logger.isTraceEnabled()) {
logger.trace("Invoking afterPropertiesSet() on bean with name '" + beanName + "'");
}
//在系統(tǒng)安全管理器的環(huán)境下
if (System.getSecurityManager() != null) {
try {
AccessController.doPrivileged((PrivilegedExceptionAction<Object>) () -> {
//1.初始化屬性
((InitializingBean) bean).afterPropertiesSet();
return null;
}, getAccessControlContext());
}
catch (PrivilegedActionException pae) {
throw pae.getException();
}
}
else {
//同上
((InitializingBean) bean).afterPropertiesSet();
}
}
if (mbd != null && bean.getClass() != NullBean.class) {
//判斷是否指定了 init-method()
//如果指定了 init-method()淹辞,則再調(diào)用指定的init-method
String initMethodName = mbd.getInitMethodName();
if (StringUtils.hasLength(initMethodName) &&
!(isInitializingBean && "afterPropertiesSet".equals(initMethodName)) &&
!mbd.isExternallyManagedInitMethod(initMethodName)) {
//2.激活自定義方法的入口
// 利用反射機(jī)制執(zhí)行
invokeCustomInitMethod(beanName, bean, mbd);
}
}
}
簡單的來看下流程:
- 首先檢查bean是否是實(shí)現(xiàn)了InitializingBean接口,如果實(shí)現(xiàn)了則調(diào)用afterPropertiesSet方法
- 同時也檢查是否指定了init-method,如果指定了,則通過反射機(jī)制調(diào)用指定的init-method
init-method是spring提供的另外一種方法,我們來看
init-method
還記得我們在spring容器之Bean標(biāo)簽的解析文章中說過關(guān)于init-method標(biāo)簽,該標(biāo)簽在這里體現(xiàn)了它的作用,主要是在bean初始化的過程中調(diào)用指定的init-method方法來替代InitializingBean接口,我們通過案例來看:
init-method案例
我們自定義一個類,其中自定義一個方法:
public class InitMethodCase {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
//自定義方法setInitMethod
public void setInitMethod(){
System.out.println("調(diào)用了init的初始化過程......");
this.name = "9527";
}
來看配置文件:
<?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="initMethodCase" class="com.sgcc.initMethod.InitMethodCase" init-method="setInitMethod">
<property name="name" value="9529"/>
</bean>
</beans>
測試代碼:
public class App {
public static void main(String[] args) {
ClassPathResource resource = new ClassPathResource("InitMethodCaseConfig.xml");
DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory);
reader.loadBeanDefinitions(resource);
InitMethodCase initMethodCase = (InitMethodCase) factory.getBean("initMethodCase");
System.out.println("name="+initMethodCase.getName());
}
結(jié)果:
可以看到的是跟我們之前實(shí)現(xiàn)InitializingBean的結(jié)果是一樣的,只是我們這里自定義方法,交給spring通過反射機(jī)制去調(diào)用方法,完全替代了医舆,InitializingBean接口,實(shí)際上效率要高一些,到這里InitializingBean和init-method 已經(jīng)分析完了