Spring Ioc源码分析 之 Bean的加载(八):初始化

在上篇文章中,我们详细分析了doCreateBean()中的第5步:属性填充,本文接着分析doCreateBean()的第6步——初始化 bean 实例对象

首先回顾下CreateBean的主流程:

  1. 如果是单例模式,从factoryBeanInstanceCache 缓存中获取BeanWrapper 实例对象并删除缓存
  2. 调用 createBeanInstance() 实例化 bean
  3. 后置处理
  4. 单例模式的循环依赖处理
  5. 属性填充
  6. 初始化 bean 实例对象
  7. 依赖检查
  8. 注册bean的销毁方法

一、初始化

Spring在对Bean进行属性填充之后,会对Bean进行初始化,代码如下:

//AbstractAutowireCapableBeanFactory.java

protected Object initializeBean(final String beanName, final Object bean, @Nullable RootBeanDefinition mbd) {
	//JDK的安全机制验证权限
	if (System.getSecurityManager() != null) {
		// <1> 激活 Aware 方法,对特殊的 bean 处理:Aware、BeanClassLoaderAware、BeanFactoryAware
		AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
			invokeAwareMethods(beanName, bean);
			return null;
		}, getAccessControlContext());
	}
	else {
		// <1> 激活 Aware 方法,对特殊的 bean 处理:Aware、BeanClassLoaderAware、BeanFactoryAware
		invokeAwareMethods(beanName, bean);
	}

	Object wrappedBean = bean;
	// <2> 后置处理器,before
	if (mbd == null || !mbd.isSynthetic()) {
		wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
	}

	// <3> 激活用户自定义的 init 方法
	try {
		invokeInitMethods(beanName, wrappedBean, mbd);
	}
	catch (Throwable ex) {
		throw new BeanCreationException(
				(mbd != null ? mbd.getResourceDescription() : null),
				beanName, "Invocation of init method failed", ex);
	}
	// <2> 后置处理器,after
	if (mbd == null || !mbd.isSynthetic()) {
		wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
	}

	return wrappedBean;
	}
复制代码

初始化 bean 的方法其实就是三个步骤的处理,而这三个步骤主要还是根据用户设定的来进行初始化,这三个过程为:

  • <1> 激活 Aware 方法。
  • <2> 后置处理器。
  • <3> 自定义的 init 方法。

1.1、Aware

Aware ,英文翻译是意识到的,感知的。Spring 提供了诸多 Aware 接口,用于辅助 Spring Bean 以编程的方式调用 Spring 容器,通过实现这些接口,可以增强 Spring Bean 的功能。

Spring 提供了如下系列的 Aware 接口:

LoadTimeWeaverAware:加载Spring Bean时织入第三方模块,如AspectJ BeanClassLoaderAware:加载Spring Bean的类加载器 BootstrapContextAware:资源适配器BootstrapContext,如JCA,CCI ResourceLoaderAware:底层访问资源的加载器 BeanFactoryAware:声明BeanFactory PortletConfigAware:PortletConfig PortletContextAware:PortletContext ServletConfigAware:ServletConfig ServletContextAware:ServletContext MessageSourceAware:国际化 ApplicationEventPublisherAware:应用事件 NotificationPublisherAware:JMX通知 BeanNameAware:声明Spring Bean的名字

Aware比较复杂,后面会专门学习一下这块内容,这里就不多说了。

1.2、后置处理器

BeanPostProcessor 在前面介绍 bean 加载的过程曾多次遇到, 它的作用是: 如果我们想要在 Spring 容器完成 Bean 的实例化,配置和其他的初始化后添加一些自己的逻辑处理,那么请使用该接口,这个接口给与了用户充足的权限去更改或者扩展 Spring,是我们对 Spring 进行扩展和增强处理一个必不可少的接口。

applyBeanPostProcessorsBeforeInitialization() 方法,代码如下:

// AbstractAutowireCapableBeanFactory.java

@Override
public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName)
        throws BeansException {
    Object result = existingBean;
    // 遍历 BeanPostProcessor 数组
    for (BeanPostProcessor processor : getBeanPostProcessors()) {
        // 处理
        Object current = processor.postProcessBeforeInitialization(result, beanName);
        // 返回空,则返回 result
        if (current == null) {
            return result;
        }
        // 修改 result
        result = current;
    }
    return result;
}
复制代码

applyBeanPostProcessorsAfterInitialization() 方法,代码如下:

// AbstractAutowireCapableBeanFactory.java

@Override
public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)
        throws BeansException {
    Object result = existingBean;
    // 遍历 BeanPostProcessor
    for (BeanPostProcessor processor : getBeanPostProcessors()) {
        // 处理
        Object current = processor.postProcessAfterInitialization(result, beanName);
        // 返回空,则返回 result
        if (current == null) {
            return result;
        }
        // 修改 result
        result = current;
    }
    return result;
}
复制代码

其逻辑就是通过 getBeanPostProcessors() 方法,获取定义的 BeanPostProcessor ,然后分别调用其 postProcessBeforeInitialization() 和 postProcessAfterInitialization() 方法,进行自定义的业务处理。

1.3、自定义init方法

在xml中有一个< bean >标签的配置, init-method 方法,是可以让我们在Bean初始化的时候,先执行我们自定义的一些逻辑。 其实就是在这里被触发的,代码如下:

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 {
            // <1> 属性初始化的处理
            ((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)) {
            // <2> 激活用户自定义的初始化方法
            invokeCustomInitMethod(beanName, bean, mbd);
        }
    }
}
复制代码

首先,检查是否为 InitializingBean 。如果是的话,需要执行 afterPropertiesSet() 方法,因为我们除了可以使用 init-method 来自定初始化方法外,还可以实现 InitializingBean 接口。接口仅有一个 afterPropertiesSet() 方法。 两者的执行先后顺序是先 <1> 的 #afterPropertiesSet() 方法,后 <2> 的 init-method 对应的方法。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏java大数据

Mybatis中数据库的增删改查和动态Sql

以下例子的配置文件中出现了一些mybatis特有的动态Sql的标签,比如trim,foreach或set标签。动态Sql是为了应对更多的情况。trim标签的功能...

12490
来自专栏程序员的成长之路

30个精简代码的小技巧(第21-30个)

这样在编译运行时就可以把这些内容放入常量池中,避免运行期间计算生成常量的值。另外,将常量的名字进行大写。

8430
来自专栏凯哥Java

springboot系列教程导学篇

Spring Boot 虽然凯哥从2015年年初开始就接触了spring boot。但是在之后的公司中都没有使用到,来来回回学了好几次了。正好,公司现在准备使用...

12700
来自专栏Java知己

Java13 闪亮来袭,你是否还停留在 Java8

近期 Java 界好消息频传。先是 Java 13 发布,接着 Eclipse 也发布了新版本表示支持新版本的 Java 特性。

20160
来自专栏Java技术栈

Spring Boot 中的 Tomcat 是如何启动的?

https://my.oschina.net/luozhou/blog/3088908

9310
来自专栏码匠的流水账

聊聊nacos的NacosDiscoveryAutoConfiguration

本文主要研究一下nacos的NacosDiscoveryAutoConfiguration

6900
来自专栏Java技术栈

推荐 33 个 IDEA 最牛配置,写代码太爽了!

blog.csdn.net/fly910905/article/details/77868300

32860
来自专栏Java技术栈

如何写好 Java 代码!

代码中的"坏味道",如"私欲"如"灰尘",每天都在增加,一日不去清除,便会越累越多。如果用功去清除这些"坏味道",不仅能提高自己的编码水平,也能使代码变得"精白...

11330
来自专栏孟君的编程札记

建造者模式浅析

建造者模式是一种创建型的模式,其意图是将一个复杂对象的构建与它的表示分离,使得同样的构建过程可以创建不同的表示。

9830
来自专栏Java技术栈

Java RPC 分布式框架性能大比拼,Dubbo排老几?

Dubbo 是开源的一个Java高性能优秀的服务框架,使得应用可通过高性能的 RPC 实现服务的输出和输入功能,可以和 Spring框架无缝集成。

6710

扫码关注云+社区

领取腾讯云代金券

年度创作总结 领取年终奖励