目录
1、Spring初始化bean
在我们的项目中会使用@Controller、@Service等注解方式或标签配置文件的形式向Spring容器注入各种bean,其本质是Spring框架扫描我们添加了@Controller、@Service等注解的类或解析标签的class属性利用java反射机制创建bean对象的过程。本篇主要从Spring源码级别,讨论Spring在创建bean对象后,Spring是如何调用各种初始化方法来初始化这个bean对象的。
2、BeanFactory的初始化Bean
2.1 BeanFactory接口和ApplicationContext接口
BeanFactory是Spring框架中最基本的一个接口,注意体会这个最字和接口。即这个接口是定义了一个bean容器的最基本的功能方法,比如获取bean实例getBean()、获取bean实例的类型getType(),那么它的具体实现类有DefaultListableBeanFactory。
ApplicationContext是Spring框架中功能更加全面的,也是在我们的项目中经常能看到它的身影的一个接口。它将BeanFactory接口作为一个内部的私有属性,是Spring更高一级的容器,它在BeanFactory的基础上提供了更多的功能,比如国际化、消息发送、AOP等,那么它的实现类有ClassPathXmlApplicationContext、FileSystemXmlApplicationContext、
AnnotationConfigApplicationContext。对于bean的初始化Application使用的也是BeanFactory相关的功能,即用的是同一块代码逻辑。
假如把我们的bean比作是一滴一滴的水,一个bean就是一滴水,那么BeanFactory就好比是一个能够盛水的水壶,能做到的只是简单的盛水,而ApplicationContext就是一个电水壶不仅能盛水,还能加热,水开了还能提示。比如我们的DefaultListableBeanFactory就是一个普通的玻璃水壶,而ClassPathXmlApplicationContext就是一个电水壶。
2.2 BeanFactory的初始化Bean
那么,BeanFactory的实现类AbstractAutowireCapableBeanFactory是如何初始化bean的呢?具体的初始化步骤如下图。
Spring容器就是按照这个步骤,一步一步的顺序进行bean的初始化代码执行,最终初始化完成交给给我们使用的。网上很多的博客都是每一个步骤简单解释了一下,然后就不了了之,只能让我去死记硬背这个流程,现在咱们看看spring源码是如何实现这个流程。如果让咱们自己写代码实现这个流程呢?
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#initializeBean(String, Object, RootBeanDefinition)
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 {
//1、对实现了BeanNameAware、BeanClassLoaderAware、
// BeanFactoryAware的bean进行处理
invokeAwareMethods(beanName, bean);
}
Object wrappedBean = bean;
if (mbd == null || !mbd.isSynthetic()) {
//2、调用 bean后置处理器 的初始化前置方法
wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
}
try {
//3、先调用InitializingBean的afterPropertiesSet()方法
//后调用用户自定义的初始化方法init-method属性指定的方法
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()) {
//4、调用 bean后置处理器 的初始化前置方法
wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
}
//最后返回包装后的bean
return wrappedBean;
}
以上就是spring的BeanFactory初始化bean的源码,源码中的bean就是我们项目中的Controller、Service、DAO这些bean。总共分为4步,源码中这4步的执行顺序决定了流程图的执行步骤,这次知道流程图是咋来的了吧。下面根据实际代码和相关源码具体分析如下:
-
invokeAwareMethods方法,对应流程图的(1)(2)(3)步,如果bean实现了BeanNameAware、BeanClassLoaderAware、 BeanFactoryAware的接口,则调用bean实现的相对应的set方法。代码实例如下:
-
applyBeanPostProcessorsBeforeInitialization方法,对应流程图的(4)步,将调用spring容器里的所有的实现了BeanPostProcessor接口的bean的postProcessBeforeInitialization()方法。
-
invokeInitMethods方法,如果bean实现了InitializingBean接口,则先调用bean实现的afterPropertiesSet()方法,对应(5)步;否则直接执行init-method属性指定的方法,对应第(6)步。
-
applyBeanPostProcessorsAfterInitialization方法,对应流程图的第(7)步,将调用spring容器里的所有的实现了BeanPostProcessor接口的bean的
postProcessAfterInitialization()方法。
下面我依次分析这四个方法,并且将点出来其中的与spring相关的知识点。
2.3 Spring的Aware接口
Aware中文意思:知道、意识到。那么xxxAware接口就是知道XXX,比如BeanNameAware,如果我们的bean实现了BeanNameAware接口,就是让我们的bean通过重写的setBeanName方法知道beanName是什么,即Spring将beanName为参数调用bean重写的setBeanName方法,这样我们的bean便知道了beanName。同理我们项目中常用的ApplicationContextAware、BeanFactoryAware都有对应的set方法,让我们的bean知道applicationContext、beanFactory。有了这样的理解,咱们再看看invokeAwareMethods方法的源码。
spring源码位置:org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#invokeAwareMethods
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);
}
}
}
此段代码,只是依次判断bean是否实现了BeanNameAware、BeanClassLoaderAware、BeanFactoryAware接口并调用对应的set方法。下面是一个常见的Controller实现了这三个接口的demo。
package com.wuqinghai.springcloud.controller;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanClassLoaderAware;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.web.bind.annotation.RestController;
/**
* @ClassName UserController
* @Description TODO
* @Author wuqinghai01
* @Date 2020/7/8 20:53
* Version 1.0
*/
@RestController
@Slf4j
public class UserController implements BeanNameAware, BeanClassLoaderAware, BeanFactoryAware {
/**
* 必须实现BeanNameAware接口的setBeanName方法,
* spring在初始化UserController的bean的时候,spring将调用这个方法
* 注意:spring在初始化UserController的bean就是指,spring创建UserController的对象,但spring并不是是使用new UserController()。
* spring会利用反射先判断UserController有没有实现BeanNameAware接口,如果没有实现则进行下一步,
* 如果实现了则,spring调用UserController的setBeanName方法,源码位置:org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#invokeAwareMethods(java.lang.String, java.lang.Object)。
* @param name
*/
@Override
public void setBeanName(String name) {
log.info("UserController的beanName:{}",name);
}
/**
* 必须实现BeanClassLoaderAware接口的setBeanClassLoader方法,原理同BeanNameAware接口
* @param classLoader
*/
@Override
public void setBeanClassLoader(ClassLoader classLoader) {
log.info("UserController的类加载器classLoader:{}", classLoader);
}
/**
* 必须实现BeanFactoryAware接口的setBeanFactory方法,原理同BeanNameAware接口
* @param beanFactory
*/
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
log.info("UserController获取到的spring容器:{}", beanFactory);
}
}
2.4 BeanPostProcessor所有bean的后置处理器
BeanPostProcessor是一个接口,翻译成中文就是Bean后置处理器,什么作用呢?见名知意,用于***扩展bean创建后的处理逻辑***。这个接口只有两个方法分别是:
spring 源码
package org.springframework.beans.factory.config;
import org.springframework.beans.BeansException;
import org.springframework.lang.Nullable;
public interface BeanPostProcessor {
@Nullable
default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
@Nullable
default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
}
如果我们的bean AAA实现了BeanPostProcessor接口,那么AAA的
postProcessBeforeInitialization见名知意,方法会在每一个bean对象的初始化方法调用之前回调;postProcessAfterInitialization同样见名知意,方法会在每个bean对象的初始化方法调用之后被回调。Spring的命名是多么的优雅!!!重点注意下每一个bean对象。
那么有咱们分析的AbstractAutowireCapableBeanFactory#initializeBean方法的源码决定了这两个方法的调用顺序,而applyBeanPostProcessorsBeforeInitialization方法就是真正的调用AAA实现的postProcessBeforeInitialization方法
spring源码位置:org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#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;
}
如下面的例子,LogBeanPostProcessor在spring容器初始化bean之前将被事先准备好,然后spring容器在初始化每一个bean的时候,spring都会调用LogBeanPostProcessor的前置方法postProcessBeforeInitialization()。即初始化UserController、UserService、UserDao等等都会执行一次,有多少个bean执行多少次。
package com.wuqinghai.springcloud.config;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.stereotype.Component;
/**
* @ClassName LogBeanPostProcessor
* @Description TODO
* @Author wuqinghai01
* @Date 2020/7/9 11:31
* Version 1.0
*/
@Component
@Slf4j
public class LogBeanPostProcessor implements BeanPostProcessor {
/**
* 每个bean在初始化时,都需要执行的统一前置方法逻辑
* @param bean
* @param beanName
* @return
* @throws BeansException
*/
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
log.info("bean:{},开始初始化",beanName);
return bean;
}
/**
* 每个bean在初始化时,都需要执行的统一后置方法逻辑
* @param bean
* @param beanName
* @return
* @throws BeansException
*/
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
log.info("bean:{},结束初始化",beanName);
return bean;
}
}
2.4 InitializingBean接口和init-method属性
直接上源码,看看如果我们的bean实现了InitializingBean接口,或者定义了init-method属性,Spring会如何处理。
org.springframework.beans.factory.InitializingBean
public interface InitializingBean {
void afterPropertiesSet() throws Exception;
}
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#invokeInitMethods
protected void invokeInitMethods(String beanName, final Object bean, @Nullable RootBeanDefinition mbd)
throws Throwable {
//1、判断bean是否实现了InitializingBean接口
boolean isInitializingBean = (bean instanceof InitializingBean);
if (isInitializingBean && (mbd == null || !mbd.isExternallyManagedInitMethod("afterPropertiesSet"))) {
if (logger.isDebugEnabled()) {
logger.debug("Invoking afterPropertiesSet() on bean with name '" + beanName + "'");
}
if (System.getSecurityManager() != null) {
try {
AccessController.doPrivileged((PrivilegedExceptionAction<Object>) () -> {
((InitializingBean) bean).afterPropertiesSet();
return null;
}, getAccessControlContext());
}
catch (PrivilegedActionException pae) {
throw pae.getException();
}
}
else {
//执行afterPropertiesSet方法
((InitializingBean) 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)) {
// 执行用户通过init-method方法指定的自定义初始化方法
invokeCustomInitMethod(beanName, bean, mbd);
}
}
}
如果我们的bean实现了InitializingBean接口,那么将先执行afterPropertiesSet方法,这个方法没有任何入参,只是单纯的执行初始化逻辑。之后会判断我们的bean有没有指定init-method属性,如果制定了将通过invokeCustomInitMethod方法来执行自定义的方法。
invokeCustomInitMethod方法的逻辑就是利用java反射机制来执行init-method指定的方法,这里偷个懒不做详细分析,Spring源码博大进深,请原谅一个辛苦码字的小伙子。如下代码是我们的日常用法
package com.wuqinghai.springcloud.controller;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.InitializingBean;
@Slf4j
public class ProductController implements InitializingBean {
@Override
public void afterPropertiesSet() throws Exception {
log.info("ProductController的afterPropertiesSet方法被调用执行");
}
/**
* 这个初始化方法可以使用@Bean(initMethod = "initMethod")进行指定
*/
public void initMethod123() {
log.info("ProductController的initMethod方法被调用执行");
}
}
package com.wuqinghai.springcloud.config;
import com.wuqinghai.springcloud.controller.ProductController;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class BeanConfig {
@Bean(initMethod = "initMethod123")
public ProductController getProductController() {
return new ProductController();
}
}
2.5 BeanPostProcessor的bean初始化后置方法
同2.3节相呼应,执行实现了BeanPostProcessor接口的bean的初始化后置方法
postProcessAfterInitialization方法,具体的源码如下
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#applyBeanPostProcessorsAfterInitialization
public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)
throws BeansException {
Object result = existingBean;
for (BeanPostProcessor processor : getBeanPostProcessors()) {
Object current = processor.postProcessAfterInitialization(result, beanName);
if (current == null) {
return result;
}
result = current;
}
return result;
}
具体的用例代码可以参考2.3BeanPostProcessor的bean初始化前置方法。
这里单独指出一点,BeanPostProcessor还具备一个包装、加工bean的功能,为什么这么说呢?BeanPostProcessor的两个方法postProcessBeforeInitialization、
postProcessAfterInitialization入参都是bean和beanName,并且返回值都是必须是bean,那么咱们就可以在BeanPostProcessor的实现类里,通过这两个方法操作bean,对bean进行一定的处理后返回。由源码
wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
看这个变量名的中文含义:被包装的Bean,从这个变量的命名也能看出Spring设计BeanPostProcessor时想提供的包装的含义。
3、 总结
- 本文主要分析了实现了BeanFactory的接口,未实现ApplicationContext接口的简单容器的bean 初始化步骤,源码的执行顺序决定了下面的初始化bean初始化步骤,概括起来如下图:
- 针对Aware系列接口,如BeanNameAware、ApplicationContextAware、BeanFactoryAware的作用,我们的bean通过实现此类接口,通过上面分析的源码,Spring在初始化bean的时候来调用set方法进行赋值。
- BeanPostProcessor的字面意思是bean的后置处理器,所以这个接口的作用于所有bean的初始化流程,可以用于对每一个bean在初始化的时候做统一的处理。
- 最后,spring将先调用InitializingBean的afterPropertiesSet()方法,后调用用户自定义的初始化方法init-method属性指定的方法。
写在最后:写到这里,spring为什么要有这样的一个初始化流程?为了我们能够便利的介入到bean的初始化流程,spring给出了这样的一套初始化流程。总之,这样的一套流程,我们就当是王八的屁股-规定(龟腚),记住就行了(谁让咱们用spring的框架,用就要接受人家的设计流程!)。我相信已经能够完全理解Spring的BeanFactory是如何去初始化咱们的bean了,美中不足的是没有分析ApplicationContext的初始化流程,既然开了这个坑,以后分析。如果有啥不明白的,欢迎点赞留言,咱们可以探讨一番。