我们在spring初始化bean的过程中在最后遗留了一个问题,本篇来说,问题是在#InitializingBean(...)中首先是对bean的判断,可以通过#afterPropertiesSet()方法来处理,具体详情请看spring容器之创建bean的终结篇,当然还有我们在配置文件中配置的init-method方法等
InitializingBean
该接口位于org.springframework.beans.factory包下,只有一个afterPropertiesSet()方法,其主要的作用是对bean自定义的实现过程,属性设置的检查等操作.
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()是在实例化时进行对bean的属性的检查,前提是我们此刻的bean是已经完成了Aware和BeanPostProcesser的设置过程,接下来我们简单的写个Demo来了解下该方法的作用:
case
定义一个类实现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("实例化bean开始了....");
this.name = "9527";
}
代码简单,我们直接给name属性赋值,将该类交给spring容器来创建:
<?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容器帮我们创建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());
}
}
运行结果:
看到结果的时候我也蒙了,奥明白了,该方法原来是spring给我们提供了一种在初始化时,动态修改bean的属性的途径,使得我们更加随心操作bean的一种手段.
初始过程
既然该方法是在初始化时来操作bean的属性的,我们来看一下,初始化过程#invokeInitMethods(...)的过程,该方法调用的前提是判断我们的bean是否是实现了InitializingBean接口,来看代码:
protected void invokeInitMethods(String beanName, final Object bean, @Nullable RootBeanDefinition mbd)
throws Throwable {
//首先是检查是否是InitializingBean,如果是需要调用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 + "'");
}
//在系统安全管理器的环境下
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(),则再调用指定的init-method
String initMethodName = mbd.getInitMethodName();
if (StringUtils.hasLength(initMethodName) &&
!(isInitializingBean && "afterPropertiesSet".equals(initMethodName)) &&
!mbd.isExternallyManagedInitMethod(initMethodName)) {
//2.激活自定义方法的入口
// 利用反射机制执行
invokeCustomInitMethod(beanName, bean, mbd);
}
}
}
简单的来看下流程:
- 首先检查bean是否是实现了InitializingBean接口,如果实现了则调用afterPropertiesSet方法
- 同时也检查是否指定了init-method,如果指定了,则通过反射机制调用指定的init-method
init-method是spring提供的另外一种方法,我们来看
init-method
还记得我们在spring容器之Bean标签的解析文章中说过关于init-method标签,该标签在这里体现了它的作用,主要是在bean初始化的过程中调用指定的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("调用了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());
}
结果:
可以看到的是跟我们之前实现InitializingBean的结果是一样的,只是我们这里自定义方法,交给spring通过反射机制去调用方法,完全替代了,InitializingBean接口,实际上效率要高一些,到这里InitializingBean和init-method 已经分析完了