Spring源码深度解析-初始化bean

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步的执行顺序决定了流程图的执行步骤,这次知道流程图是咋来的了吧。下面根据实际代码和相关源码具体分析如下:

  1. invokeAwareMethods方法,对应流程图的(1)(2)(3)步,如果bean实现了BeanNameAware、BeanClassLoaderAware、 BeanFactoryAware的接口,则调用bean实现的相对应的set方法。代码实例如下:

  2. applyBeanPostProcessorsBeforeInitialization方法,对应流程图的(4)步,将调用spring容器里的所有的实现了BeanPostProcessor接口的bean的postProcessBeforeInitialization()方法。

  3. invokeInitMethods方法,如果bean实现了InitializingBean接口,则先调用bean实现的afterPropertiesSet()方法,对应(5)步;否则直接执行init-method属性指定的方法,对应第(6)步。

  4. 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、 总结

  1. 本文主要分析了实现了BeanFactory的接口,未实现ApplicationContext接口的简单容器的bean 初始化步骤,源码的执行顺序决定了下面的初始化bean初始化步骤,概括起来如下图:
    在这里插入图片描述
  2. 针对Aware系列接口,如BeanNameAware、ApplicationContextAware、BeanFactoryAware的作用,我们的bean通过实现此类接口,通过上面分析的源码,Spring在初始化bean的时候来调用set方法进行赋值。
  3. BeanPostProcessor的字面意思是bean的后置处理器,所以这个接口的作用于所有bean的初始化流程,可以用于对每一个bean在初始化的时候做统一的处理。
  4. 最后,spring将先调用InitializingBean的afterPropertiesSet()方法,后调用用户自定义的初始化方法init-method属性指定的方法。

写在最后:写到这里,spring为什么要有这样的一个初始化流程?为了我们能够便利的介入到bean的初始化流程,spring给出了这样的一套初始化流程。总之,这样的一套流程,我们就当是王八的屁股-规定(龟腚),记住就行了(谁让咱们用spring的框架,用就要接受人家的设计流程!)。我相信已经能够完全理解Spring的BeanFactory是如何去初始化咱们的bean了,美中不足的是没有分析ApplicationContext的初始化流程,既然开了这个坑,以后分析。如果有啥不明白的,欢迎点赞留言,咱们可以探讨一番。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值