我们通过spring容器帮我们实例化并且维护bean的时候,有时候我们需要在bean在实例化完成的时候,帮我们做一些事情,这个时候我们就会使用到bean的初始化方法。举个例子,比如我们创建一个电脑,那么我们肯定就需要先安装系统,不然不能使用,此时我们就能把安装系统的过程封装到初始化方法中。我们今天主要来看我们常见的三种初始化的方法,并且分析一下他们的源码。
我们这里分析的三个初始化方法如下,如果已经比较了解如何使用以及原理,可以跳过该文章
1.实现InitializingBean接口
2.使用@PostConstruct
3.使用@Bean(initMethod = “xxx”)
这篇文章相对来说比较简单,但是还是希望大家对spring中的beanPostProcessor和bean创建的过程有了解。如果你不了解也没关系,相信这篇文章还是能给你带来收获
一.使用方式
使用方式就超级简单,下面简单的贴下代码
public class Computer implements InitializingBean {
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("调用afterPropertiesSet 完成初始化...安装系统");
}
@PostConstruct
public void initByPostConstruct(){
System.out.println("调用initByPostConstruct 完成初始化...安装系统");
}
public void init(){
System.out.println("调用init 完成初始化...安装系统");
}
}
另外,还有在config类里面通过@Bean实例化bean
@Configuration
@EnableAspectJAutoProxy
public class MainConfig {
@Bean(initMethod = "init")
public Computer computer(){
return new Computer();
}
}
上面同时使用了三种方式,正常情况下选择一种方式使用即可,我们看下执行打印的结果,顺便看下这三个是哪个先执行呢?
使用就到这里结束了,非常简单
二.源码分析
现在我们来分析他们三个实现的原理,第一可以更加深入的理解源码,第二万一面试被问到也不虚。这里就不再介绍bean创建的全过程了,如果感兴趣的可以参考我这篇文章(spring源码分析之如何解决循环依赖),这篇文章写的比较详细,我们直接从bean创建后,开始初始化的地方开始介绍
//AbstractAutowireCapableBeanFactory.java
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
throws BeanCreationException {
//....省略代码
try {
//属性赋值
populateBean(beanName, mbd, instanceWrapper);
//实现初始化的逻辑入口在这里
exposedObject = initializeBean(beanName, exposedObject, mbd);
}
//省略代码
return exposedObject;
}
上面直接从AbstractAutowireCapableBeanFactory.java开始分析,注释写的比较清楚,我们直接看initializeBean()
protected Object initializeBean(final String beanName, final Object bean, @Nullable RootBeanDefinition mbd) {
if (System.getSecurityManager() != null) {
AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
invokeAwareMethods(beanName, bean);
return null;
}, getAccessControlContext());
}
else {
invokeAwareMethods(beanName, bean);
}
Object wrappedBean = bean;
//1.这里实现了@PostConstruct原理
if (mbd == null || !mbd.isSynthetic()) {
wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
}
try {
//2.这里调用初始化方法,这里实现了InitializingBean和@Bean(initMethod="xx")的原理
invokeInitMethods(beanName, wrappedBean, mbd);
}
catch (Throwable ex) {
throw new BeanCreationException(
(mbd != null ? mbd.getResourceDescription() : null),
beanName, "Invocation of init method failed", ex);
}
if (mbd == null || !mbd.isSynthetic()) {
wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
}
return wrappedBean;
}
我们先来看注释2的invokeInitMethods,看看他是如何实现初始化方法的
protected void invokeInitMethods(String beanName, final Object bean, @Nullable RootBeanDefinition mbd)
throws Throwable {
//1.实现InitializingBean原理
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-1
((InitializingBean) bean).afterPropertiesSet();
return null;
}, getAccessControlContext());
}
catch (PrivilegedActionException pae) {
throw pae.getException();
}
}
else {
//1-2
((InitializingBean) bean).afterPropertiesSet();
}
}
//2.实现了@Bean(initMethod="xx")的原理
if (mbd != null && bean.getClass() != NullBean.class) {
//2-1这里会读取到initMethod方法,然后下面通过反射调用
String initMethodName = mbd.getInitMethodName();
if (StringUtils.hasLength(initMethodName) &&
!(isInitializingBean && "afterPropertiesSet".equals(initMethodName)) &&
!mbd.isExternallyManagedInitMethod(initMethodName)) {
//具体的调用过程看这里,不做过多的解释
invokeCustomInitMethod(beanName, bean, mbd);
}
}
}
注释1通过实现InitializingBean方式来实现初始化的非常的简单,不做过多的介绍,注释2通过@Bean的方式稍微介绍一下。
当我们通过@Bean方式还是之前通过xml配置bean时都能够指定initMethod属性,此时就会将这个bean先封装成BeanDefinition,同时配置的属性也会设置到BeanDefinition中,所以在这里我们是可以取出initMethod属性对应的方法,拿到之后然后实例耶创建出来了,自然就能通过反射调用。bean创建的过程都是先封装成beanDefinition,如果这一块不了解的可以先阅读相关文章
上面我们已经知道InitializingBean和@Bean(initMethod=“xx”)来实现初始化方法的原理,下面我们来看注释1通过@PostConstruct,直接对应下面方法
public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName)
throws BeansException {
Object result = existingBean;
for (BeanPostProcessor processor : getBeanPostProcessors()) {
//beanPostProcessor调用方法
Object current = processor.postProcessBeforeInitialization(result, beanName);
if (current == null) {
return result;
}
result = current;
}
return result;
}
这其实就是beanPostProcessor,我们知道在spring中使用了大量的beanPostProcessor(我们自己也可以实现),通过它达到在spring创建过程中拦截的作用,在某个动作之前/之后做一些感兴趣的事情,达到“增强”的目的
这里其实就是用到了一个InitDestroyAnnotationBeanPostProcessor来实现初始化和销毁的过程,一定会执行postProcessBeforeInitialization
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
//找到要执行的方法
LifecycleMetadata metadata = findLifecycleMetadata(bean.getClass());
try {
//反射调用
metadata.invokeInitMethods(bean, beanName);
}
catch (InvocationTargetException ex) {
throw new BeanCreationException(beanName, "Invocation of init method failed", ex.getTargetException());
}
catch (Throwable ex) {
throw new BeanCreationException(beanName, "Failed to invoke init method", ex);
}
return bean;
}
这里找到@PostConstruct的方法也很简单,无非就是获取方法的注释,并且判断是否是@PostConstruct,如果是则就算找到了,具体的细节感兴趣的可以自行阅读。
总的来说这篇文章还是比较简单,其实相对应的销毁方法也是一样的,感兴趣的可以自行阅读
发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/111201.html原文链接:https://javaforall.cn
【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛
【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...