❝上周因为出去跑了一个
❞30km
导致文章没有写,今天在写的时候,尽然感觉有点小难受???坚持了好久的事情,不能这么轻易的放弃。有些事情我真的很怕到最后给自己的答案是:这个事情本来我可以,但是...(写在前面给自己打打气)
上一篇文章,因为要做一个分享所以简单的总结了一下 Spring IoC
相关的知识点。今天这篇文章还是接着之前没写完的 IoC
的部分继续往下写。在上上篇文章 Spring 对象注入循环依赖框架是怎么帮我们做的? 分析了 IOC
总循环依赖的处理(掘金的图片显示不出来,这里是本人微信公众号里面的文章)。在Spring 注入对象处理过程中详细分析了对象的注入,其中涉及到一个非常重要的方法populateBean()
在该方法中完成了 Bean
依赖关系的处理。今天分析在对象之间的依赖关系处理完成之后,Spring是如何初始化 Bean
的。
Bean的初始化
Bean
的初始化方法入口如下: 首先简单的总结一下这个方法的处理过程,分为如下几个步骤:
Aware
系列接口的回调- 执行
BeanPostProcessors
的applyBeanPostProcessorsBeforeInitialization
方法 - 激活用户自定义的
init
方法 - 执行
BeanPostProcessors
的applyBeanPostProcessorsAfterInitialization
方法
方法实现如下
protected Object initializeBean(final String beanName, final Object bean, @Nullable RootBeanDefinition mbd) {
if (System.getSecurityManager() != null) {
AccessController.doPrivileged((PrivilegedAction) () -> {
invokeAwareMethods(beanName, bean);return null;
}, getAccessControlContext());
}else {/**
* 对特殊的bean处理:Aware,BeanClassLoaderAware,BeanFactoryAware
*/
invokeAwareMethods(beanName, bean);
}
Object wrappedBean = bean;if (mbd == null || !mbd.isSynthetic()) {/** 执行BeanPostProcessors的before 方法*/
wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
}try {/**
* 激活用户自定义的 init 方法
*/
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()) {/** 执行BeanPostProcessors的after 方法*/
wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
}return wrappedBean;
}
1. Aware 系列接口的回调
在上述代码中,Aware
接口的回调是通过 invokeAwareMethods(beanName, bean);
来完成的。在这个方法中,对特殊的bean处理:Aware
,BeanClassLoaderAware
,BeanFactoryAware
等。
private void invokeAwareMethods(final String beanName, final Object bean) {
if (bean instanceof Aware) {
if (bean instanceof BeanNameAware) {
((BeanNameAware) bean).setBeanName(beanName);
}
if (bean instanceof BeanClassLoaderAware) {
ClassLoader bcl = getBeanClassLoader();
if (bcl != null) {
((BeanClassLoaderAware) bean).setBeanClassLoader(bcl);
}
}
if (bean instanceof BeanFactoryAware) {
((BeanFactoryAware) bean).setBeanFactory(AbstractAutowireCapableBeanFactory.this);
}
}
}
Aware 系列的接口
![0963bc57c8879526e129c8ec092ddccd.png](https://i-blog.csdnimg.cn/blog_migrate/6653d478d44ec5e13d961ca64f416ff1.jpeg)
「常用的Aware相关接口作用说明」
名称 | 作用 |
---|---|
ApplicationContextAware | 获取spring 上下文环境的对象 |
BeanNameAware | 获取该bean在BeanFactory配置中的名字 |
BeanFactoryAware | 创建它的BeanFactory实例 |
以 ApplicationContextAware
为例简单做一个使用说明,在 Spring
官网中主要介绍了这个类,官方文档还是要多看看的...测试代码如下:
「启动类:」
public class MyTestStart {
public static void main(String[] args) {
// Aware 使用测试
ApplicationContext ann = new AnnotationConfigApplicationContext(MyConfig.class);
MyAwareTestService service = (MyAwareTestService) ann.getBean("myAwareTestService");
service.myTest();
service.myTest();
}
}
「单例的Bean
:」
@Service("myAwareTestService")
public class MyAwareTestService {
@Autowired
Test test;
@Autowired
TestAware1 testAware1;
public void myTest(){
testAware1.test();
test.test();
}
}
「原型 Bean
:」
@Service
@Scope("prototype")
public class Test {
public void test(){
System.out.println("Test ->" + this);
}
}
注意,知识点来了:如果一个单例的
Bean
依赖了一个原型的 Bean
,那么依赖的这个 Bean
是单例的还是原型的?如果是单例的,那么怎么才能拿到原型的?
答案:其实 Spring
在准备环境的时候,会实例化所有的(除去懒加载) Bean
,然后缓存起来(还记得 Spring
当中的三个 Map
吗?),在 getBean()
的时候如果发现缓存中已经有了,就会直接拿到不会创建了。因此,如果不做特殊处理的话,单例 Bean
中依赖原型的 Bean
拿到的也是同一个 Bean
。那么问题来了,这个有解决方法吗?自然是有的,但是为了不偏离本文的测重点,这里只给出方案,不做原理上的深究。其实解决这个问题,可以通过以下三种途径来解决:
- 通过上述的
ApplicationContextAware
。实现该接口,拿到ApplicationContext
,然后通过getBean()
获取对象,然后在使用。 - 在原型
Bean
中注入ApplicationContext
,然后通过getBean()
获取对象,然后在使用。
@Service
@Scope("prototype")
public class Test {
@Autowired
ApplicationContext applicationContext;
public void test(){
System.out.println("Test ->" + applicationContext.getBean("test"));
}
}
- 使用@Lookup注解。
Service
@Scope("prototype")
public abstract class Test1 {
@Lookup
protected abstract Test1 methodInject();
public void test(){
Test1 test1 = methodInject();
System.out.println("Test ->" + test1);
}
}
运行结果:
2. applyBeanPostProcessorsBeforeInitialization 方法的执行
public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName)throws BeansException {
Object result = existingBean;
for (BeanPostProcessor processor : getBeanPostProcessors()) {
Object current = processor.postProcessBeforeInitialization(result, beanName);
if (current == null) {
return result;
}
result = current;
}
return result;
}
容器初始化之前做最后的检查,检查 BeanPostProcessor
对 Bean
做相应的处理。
3. 激活用户自定义的 init
方法
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) () -> {
((InitializingBean) bean).afterPropertiesSet();return null;
}, getAccessControlContext());
}
catch (PrivilegedActionException pae) {
throw pae.getException();
}
}else {
// 属性 初始化后的处理,调用 afterPropertiesSet()方法
((InitializingBean) bean).afterPropertiesSet();
}
}
/**
* 从代码看出:
* afterPropertiesSet() 和 init-method() 都是在初始化bean时执行,
* 执行顺序是 先 afterPropertiesSet()方法执行,
* 然后 init-method() 方法执行
*/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);
}
}
}
这里是处理实现了 InitializingBean
接口,被重写了的 afterPropertiesSet()
方法;以及 init-method()
方法的处理。通过下面的 invokeCustomInitMethod()
方法对 @Bean
中的 initMethod
属性或 xml
中 init-Method
进行相应的处理。
protected void invokeCustomInitMethod(String beanName, final Object bean, RootBeanDefinition mbd)
throws Throwable {
/**
* 获取 Bean 定义的 @Bean 中的 initMethod 属性 或xml中init-Method
*/
String initMethodName = mbd.getInitMethodName();
Assert.state(initMethodName != null, "No init method set");
Method initMethod = (mbd.isNonPublicAccessAllowed() ?
BeanUtils.findMethod(bean.getClass(), initMethodName) :
ClassUtils.getMethodIfAvailable(bean.getClass(), initMethodName));
if (initMethod == null) {
if (mbd.isEnforceInitMethod()) {
throw new BeanDefinitionValidationException("Could not find an init method named '" +
initMethodName + "' on bean with name '" + beanName + "'");
}
else {
if (logger.isTraceEnabled()) {
logger.trace("No default init method named '" + initMethodName +
"' found on bean with name '" + beanName + "'");
}
// Ignore non-existent default lifecycle methods.
return;
}
}
if (logger.isTraceEnabled()) {
logger.trace("Invoking init method '" + initMethodName + "' on bean with name '" + beanName + "'");
}
Method methodToInvoke = ClassUtils.getInterfaceMethodIfPossible(initMethod);
if (System.getSecurityManager() != null) {
AccessController.doPrivileged((PrivilegedAction) () -> {
ReflectionUtils.makeAccessible(methodToInvoke);return null;
});
try {
AccessController.doPrivileged((PrivilegedExceptionAction) () ->
methodToInvoke.invoke(bean), getAccessControlContext());
}
catch (PrivilegedActionException pae) {
InvocationTargetException ex = (InvocationTargetException) pae.getException();
throw ex.getTargetException();
}
}else {
try {
ReflectionUtils.makeAccessible(methodToInvoke);
/**
* 通过 JDk 的反射机制得到 Method 对象
* 直接调用在 Bean 中定义声明的初始化方法
*/
methodToInvoke.invoke(bean);
}
catch (InvocationTargetException ex) {
throw ex.getTargetException();
}
}
4. applyBeanPostProcessorsAfterInitialization
这里就是对 BeanPostProcessor
中的另一个方法的处理过程。之前的文章多次提到过,这里就不在赘述了。
5. 总结
这篇文章中分析了依赖关系处理完成之后,Bean
的初始化的过程,通过上述的4个方面做了简单的分析。从中也了解了Spring
中设计的 Aware
接口,通过一道面试题分析简单的使用了一下 ApplicationContextAware
。
其实到这里,doCreateBean()
方法已经分析完了,在这个方法中主要完成了一下几件事情 ①:如果是单例首先需要清除缓存;②:实例化 bean
,将 BeanDefinition
转化为 BeanWrapper
;③:MergedBeanDefinitionPostProcessor
的应用;④:依赖处理;⑤:属性填充;⑥:循环依赖处理;⑦:注册 DisposableBean
;⑧:完成创建并返回。也就是说 Bean
的创建完成了。
其实到这里,doCreateBean()
方法完成之后,doGetBean()
方法中的流程也已经基本完成了,最后剩了一个 「类型转换」,程序运行到这里bean的初始化基本结束,requiredType
通常是为空的,但是可能存在这样的情况,返回的 Bean
是个 String
,但是 requiredType
是 Integer
,这个时候这个步骤就有作用了:将返回的 Bean
转换为 requiredType
所指定的类型。
关于 doGetBean()
我会在后面的文章中,做一个详细的总结。