博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Spring 源码分析之 bean 依赖注入原理(注入属性)
阅读量:5913 次
发布时间:2019-06-19

本文共 17142 字,大约阅读时间需要 57 分钟。

     最近在研究Spring bean 生命周期相关知识点以及源码,所以打算写一篇 Spring bean生命周期相关的文章,但是整理过程中发现涉及的点太多而且又很复杂,很难在一篇文章中把Spring bean 的生命周期讲清楚,所以最后决定分解成几个模块来写,最后在写一篇文章把各个内容串联起来,这样可以讲的更细更深入不会犹豫篇幅而讲的太笼统。bean 生命周期所涉及的主要流程如下图所示。

     在上篇文章写了有bean实例创建相关的内容,感兴趣的朋友可以去看看 这篇文章。
     本文想写bean 生命周期的第二阶段 bean 的依赖注入(注入属性)部分按下面几个步骤来讲解。

  • Spring容器与依赖注入
  • 什么时候会触发依赖注入?
  • 关于依赖注入与属性注入的几点说明
  • 解析Spring 依赖注入源码部分
  • 总结

一. Spring容器与依赖注入

    Spring最有名的高级特性非ioc莫属了,虽然ioc 不是本次要讨论的重点,但还是有必要说一下。对于Spring的ioc我不想过多教科书式的解释这名次,我也相信每个使用Spring的程序员都有自己的理解,只是有时很难把自己的理解清楚的解释给别人而已。下面我说说我自己的理解,有说错的地方欢迎大家指正。Spring ioc 至少要具备一下两点功能:

准备Bean 整个生命周期需要的数据

这一步是Spring 容器启动的时候会 定位我们的配置文件,加载文件,并解析成Bean的定义文件BeanDefinition来为下一步作准备,这个BeanDefinition会贯穿Spring 启动初始化的整个流程,非常重要,因为他是数据基础。

管理Bean的整个生命周期

  • 需要具备创建一个Bean的功能
  • 需要具备根据Bean与Bean之间的关系依赖注入功能(本次要讲的内容)
  • 需要能够执行初始化方法以及销毁方法

有了以上几个功能之后Spring ioc 就能够控制bean的流程了,这不控制反转了么。而我们只需用注解或者配置文件配置bean的特性以及依赖关系即可。下面说一下有关ApplicationContext 和 BeanDefinition:

1. 核心容器ApplicationContext

    上述这些功能都可以由Spring容器(比如 ApplicationContext)来实现,Spring启动时会把所有需要的bean扫描并注册到容器里,在这个过程当中Spring会根据我们定义的bean之间的依赖关系来进行注入,依赖关系的维护方式有两种即XML配置文件或者注解,Spring启动时会把这些依赖关系转化成Spring能够识别的数据结构BeanDefinition,并根据它来进行bean的初始化,依赖注入等操作。下面看看一个简单的Spring容器如下图:

Spring 依赖注入的实现是由像ApplicationContext这种容器来实现的,右边的Map里存储这bean之间的依赖关系的定义BeanDefinition,比如OrderController依赖OrderService这种,具体定义下面介绍。

结论:BeanDefinition提供了原材料数据基础,而ApplicationContext 提供了流程的设计与实现的算法

2. Bean依赖关系的定义

    我们需要为Spring容器提供所有bean的定义以及bean之间的依赖关系,从而进行bean的依赖注入通常有两种方式,XML配置或者注解,不管是那种最终都会解析成BeanDefinition。

通过XML配置Bean依赖关系

复制代码
public class OrderController {    private OrderService orderService;    public OrderService getOrderService() {        return orderService;    }    public void setOrderService(OrderService orderService) {        this.orderService = orderService;    }}复制代码

这种注入方式叫做set 方法注入,只需xml配置 加上对引用的bean的get set方法即可

通过注解定义置Bean依赖关系

复制代码
@Controllerpublic class OrderController {        @Autowired    private OrderService orderService;    public OrderController() {    }}复制代码
@Servicepublic class OrderService {    @Autowired    private OrderDao orderDao;    public OrderService() {    }}复制代码
@Repositorypublic class OrderDao {    public OrderDao() {    }}复制代码

Spring 启动时会把我们的定义信息转化成Spring能看懂的BeanDefinition,然后就可以由容器来创建bean以及依赖注入了,具体依赖注入的时候对于配置文件和注解的处理手段还不同,这个一会儿在解释。

二. 什么时候会触发依赖注入?

  • Spring 容器启动初始化的时候(所有单例非懒加载的bean)
  • 懒加载(lazy-init)的bean 第一次进行getBean的时候

1.Spring 容器启动初始化的时候

ApplicationContext context = new ClassPathXmlApplicationContext("spring-beans.xml");复制代码
public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent)throws BeansException {    super(parent);    setConfigLocations(configLocations);    if (refresh) {        // 容器初始化入口        refresh();    }}复制代码
public void refresh() throws BeansException, IllegalStateException {    synchronized (this.startupShutdownMonitor) {        prepareRefresh();        // Prepare the bean factory for use in this context.        prepareBeanFactory(beanFactory);        // Allows post-processing of the bean factory in context subclasses.        postProcessBeanFactory(beanFactory);        // Invoke factory processors registered as beans in the context.        invokeBeanFactoryPostProcessors(beanFactory);        // Register bean processors that intercept bean creation.        registerBeanPostProcessors(beanFactory);        // Instantiate all remaining (non-lazy-init) singletons.        // 初始化所有非 懒加载的bean!!!!        finishBeanFactoryInitialization(beanFactory);        // Last step: publish corresponding event.        finishRefresh();    }  }复制代码

finishBeanFactoryInitialization(beanFactory);// 初始化所有非 懒加载的bean!!!

protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {   	// Stop using the temporary ClassLoader for type matching.   	beanFactory.setTempClassLoader(null);   	// 此处省略多行与本次无关代码   	// Instantiate all remaining (non-lazy-init) singletons.   	beanFactory.preInstantiateSingletons();   }复制代码
public void preInstantiateSingletons() throws BeansException {   // 所有beanDefinition集合   List
beanNames = new ArrayList
(this.beanDefinitionNames); // 触发所有非懒加载单例bean的初始化 for (String beanName : beanNames) { RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName); // 判断是否是懒加载单例bean,如果是单例的并且不是懒加载的则在Spring 容器 if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) { // 判断是否是FactoryBean if (isFactoryBean(beanName)) { // 对FactoryBean的处理 }else { // 如果是普通bean则进行初始化依赖注入,此 getBean(beanName)接下来触发的逻辑跟 // context.getBean("beanName") 所触发的逻辑是一样的 getBean(beanName); } } }}复制代码
@Overridepublic Object getBean(String name) throws BeansException {       return doGetBean(name, null, null, false);}复制代码

2.懒加载(lazy-init)的bean 第一次进行getBean

懒加载的bean 第一次进行getBean的操作调用的也是同一个方法

@Overridepublic Object getBean(String name) throws BeansException {       return doGetBean(name, null, null, false);}复制代码

doCreateBean是依赖注入的入口,也是我们本次要谈的核心函数。该方法具体实现在AbstractAutowireCapableBeanFactory类,感兴趣的朋友可以进去看看调用链。下面才刚刚开始进入依赖注入源码阶段。

三. 关于依赖注入与属性注入的几点说明

依赖注入其实是属性注入的一种特殊类型,他的特殊之处在于他要注入的是Bean,同样由Spring管理的Bean,而不是其他的参数,如String,List,Set,Array这种。

普通的属性的值用
value (类型包括 String list set map ...)

Bean类型的属性的引用 ref,这种注入属于依赖注入

四. 解析Spring 依赖注入源码部分

  • 依赖注入实现的入口
  • 注解形式注入的源码
  • xml 配置形式注入的源码

1. 依赖注入实现的入口

protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args) {	//第一步 创建bean实例 还未进行属性填充和各种特性的初始化	BeanWrapper instanceWrapper = null;	if (instanceWrapper == null) {		instanceWrapper = createBeanInstance(beanName, mbd, args);	}	final Object bean = (instanceWrapper != null ? instanceWrapper.getWrappedInstance() : null);	Class
beanType = (instanceWrapper != null ? instanceWrapper.getWrappedClass() : null); Object exposedObject = bean; try { // 第二步 进行依赖注入(注入属性) populateBean(beanName, mbd, instanceWrapper); if (exposedObject != null) { // 第三步 执行bean的初始化方法 exposedObject = initializeBean(beanName, exposedObject, mbd); } }catch (Throwable ex) { // 抛相应的异常 } return exposedObject;}复制代码

我们这里需要关注的是第二步关于依赖注入这一块,下面这行代码

populateBean(beanName, mbd, instanceWrapper);复制代码
protected void populateBean(String beanName, RootBeanDefinition mbd, BeanWrapper bw) {        // 所有的属性	PropertyValues pvs = mbd.getPropertyValues();	// 这里是处理自动装配类型的, autowire=byName 或者byType。如果不配置不走这个分支,xml或注解都可配 	if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_NAME ||			mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_TYPE) {		pvs = newPvs;	}	// 后处理器是否已经准备好(后处理器会处理已@Autowired 形式来注入的bean, 有一个  	// 子类AutowiredAnnotationBeanPostProcessor来处理@Autowired注入的bean)	boolean hasInstAwareBpps = hasInstantiationAwareBeanPostProcessors();	// 是否需要依赖检查	boolean needsDepCheck = (mbd.getDependencyCheck() != RootBeanDefinition.DEPENDENCY_CHECK_NONE);	if (hasInstAwareBpps || needsDepCheck) {		PropertyDescriptor[] filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);		if (hasInstAwareBpps) {			for (BeanPostProcessor bp : getBeanPostProcessors()) {				if (bp instanceof InstantiationAwareBeanPostProcessor) {					InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor)bp;					// 这里会处理对注解形式的注入 重点!!!!					pvs = ibp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);					if (pvs == null) {						return;					}				}			}		}	}    // 注入参数的方法(注解的Bean的依赖注入除外)	applyPropertyValues(beanName, mbd, bw, pvs);}复制代码

2. 注解形式注入的源码

// 后处理器是否已经准备好(后处理器会处理已@Autowired 形式来注入的bean, 有一个  // 子类AutowiredAnnotationBeanPostProcessor来处理@Autowired注入的bean)boolean hasInstAwareBpps = hasInstantiationAwareBeanPostProcessors();// 是否需要依赖检查boolean needsDepCheck = (mbd.getDependencyCheck() != RootBeanDefinition.DEPENDENCY_CHECK_NONE);if (hasInstAwareBpps || needsDepCheck) {	PropertyDescriptor[] filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);	if (hasInstAwareBpps) {		for (BeanPostProcessor bp : getBeanPostProcessors()) {			if (bp instanceof InstantiationAwareBeanPostProcessor) {				InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor)bp;				// 这里会处理对注解形式的注入,比如 @Autowired注解 由类AutowiredAnnotationBeanPostProcessor来处理				pvs = ibp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);				if (pvs == null) {					return;				}			}		}	}}复制代码
pvs = ibp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);复制代码

@Overridepublic PropertyValues postProcessPropertyValues(PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeansException {        // 这里定义了把谁注入到哪里	InjectionMetadata metadata = findAutowiringMetadata(beanName, bean.getClass(), pvs);	try {  	   // 进行注入	   metadata.inject(bean, beanName, pvs);	}catch (Throwable ex) {		throw new BeanCreationException(beanName, "Injection of autowired dependencies failed", ex);	}	return pvs;}复制代码

InjectionMetadata在这个类里头封装了依赖的bean与被依赖的bean的信息,比如orderCcontroller 依赖orderService,需要把orderService 注入到orderController。下面贴一下我debug的图片

injectedElements 就是所有需要被注入的bean

protected void inject(Object target, String requestingBeanName, PropertyValues pvs) throws Throwable {	if (this.isField) {		Field field = (Field) this.member;		ReflectionUtils.makeAccessible(field);		field.set(target, getResourceToInject(target, requestingBeanName));	}else {		if (checkPropertySkipping(pvs)) {		   return;		}		try {		   Method method = (Method) this.member;		   ReflectionUtils.makeAccessible(method);		   method.invoke(target, getResourceToInject(target, requestingBeanName));		}catch (InvocationTargetException ex) {		  throw ex.getTargetException();		}	}}复制代码

3. xml 配置形式注入的源码

applyPropertyValues(beanName, mbd, bw, pvs);复制代码

这个步骤主要做的就是把属性转换成相对应的类的属性类型,并最后注入到依赖的bean里头,由一下步骤组成:

  • 判断是否已转换
  • 进行转换
  • 注入到bean
protected void applyPropertyValues(String beanName, BeanDefinition mbd, BeanWrapper bw, PropertyValues pvs) {        MutablePropertyValues mpvs = null;        List
original; if (pvs instanceof MutablePropertyValues) { mpvs = (MutablePropertyValues) pvs; // 判断是否已转换,已经转换了则return if (mpvs.isConverted()) { // Shortcut: use the pre-converted values as-is. bw.setPropertyValues(mpvs); return; } original = mpvs.getPropertyValueList(); } TypeConverter converter = getCustomTypeConverter(); BeanDefinitionValueResolver valueResolver = new BeanDefinitionValueResolver(this, beanName, mbd, converter); // Create a deep copy, resolving any references for values. List
deepCopy = new ArrayList
(original.size()); boolean resolveNecessary = false; for (PropertyValue pv : original) { if (pv.isConverted()) { deepCopy.add(pv); } else { // 属性名 如(name,orderService) String propertyName = pv.getName(); // 未转换前的值,稍后贴出debug时的图 Object originalValue = pv.getValue(); // 转换后的值,进行转换处理(重要!!!!) Object resolvedValue = valueResolver.resolveValueIfNecessary(pv, originalValue); Object convertedValue = resolvedValue; // if (resolvedValue == originalValue) { if (convertible) { pv.setConvertedValue(convertedValue); } deepCopy.add(pv); } else if (convertible && originalValue instanceof TypedStringValue && !((TypedStringValue) originalValue).isDynamic() && !(convertedValue instanceof Collection || ObjectUtils.isArray(convertedValue))) { pv.setConvertedValue(convertedValue); deepCopy.add(pv); } else { resolveNecessary = true; deepCopy.add(new PropertyValue(pv, convertedValue)); } } } // 转换完成 if (mpvs != null && !resolveNecessary) { mpvs.setConverted(); } // 这里就是进行属性注入的地方,跟上面的inject方法类似 try { bw.setPropertyValues(new MutablePropertyValues(deepCopy)); } catch (BeansException ex) { throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Error setting property values", ex); } }复制代码

下面介绍上述步骤中的两个核心的流程:

  • 进行转换操作,生成最终需要注入的类型的对象
  • 进行注入操作

1.进行转换操作,生成最终需要注入的类型的对象

这个方法会返回一个我们最终要注入的一个属性对应类的一个对象

Object resolvedValue = valueResolver.resolveValueIfNecessary(pv, originalValue);复制代码

根据参数类型做具体的转换处理,参数类型包括 1.Bean 2.Array 3.List 4.Set 5.Map 6.String 等等。

public Object resolveValueIfNecessary(Object argName, Object value) {     // Bean 类型    if (value instanceof RuntimeBeanReference) {        RuntimeBeanReference ref = (RuntimeBeanReference) value;        return resolveReference(argName, ref);    }    else if (value instanceof ManagedArray) {        // 处理数组        return resolveManagedArray(argName, (List
) value, elementType); } else if (value instanceof ManagedList) { // 处理list return resolveManagedList(argName, (List
) value); } else if (value instanceof ManagedSet) { // 处理set return resolveManagedSet(argName, (Set
) value); } else if (value instanceof ManagedMap) { // 处理map return resolveManagedMap(argName, (Map
) value); } else if (value instanceof TypedStringValue) { // 处理字符串 } else { return evaluate(value); }}复制代码
private Object resolveReference(Object argName, RuntimeBeanReference ref) {    try {	String refName = ref.getBeanName();	refName = String.valueOf(doEvaluate(refName));	if (ref.isToParent()) {		if (this.beanFactory.getParentBeanFactory() == null) {			throw new BeanCreationException("");		}		return this.beanFactory.getParentBeanFactory().getBean(refName);	}else {		Object bean = this.beanFactory.getBean(refName);		this.beanFactory.registerDependentBean(refName, this.beanName);		return bean;	}    }catch (BeansException ex) {throw new BeanCreationException(this.beanDefinition.getResourceDescription(), this.beanName,				"Cannot resolve reference to bean '" + ref.getBeanName() + "' while setting " + argName,ex);    }}复制代码
Object bean = this.beanFactory.getBean(refName);复制代码
this.beanFactory.getParentBeanFactory().getBean(refName);复制代码

从sprig ioc 容器的双亲中获取bean(被依赖的bean),假如orderCcontroller依赖orderService,则从容器中获取orderService。这里有个关键点,也就是这个获取bean的过程也是一个依赖注入的过程,换句话说依赖注入是个递归的过程!!!!!!知道被依赖的bean不依赖任何bean。

  • orderCcontroller 依赖 orderService 的操作会触发 orderService 依赖 orderDao的操作

2.进行注入操作

这一步是通过Java的反射机制根据set 方法把属性注入到bean里。

bw.setPropertyValues(new MutablePropertyValues(deepCopy));复制代码
protected void setPropertyValue(AbstractNestablePropertyAccessor.PropertyTokenHolder tokens, PropertyValue pv) throws BeansException {    String propertyName = tokens.canonicalName;    String actualName = tokens.actualName;    if (tokens.keys != null) {     // 处理集合类型    }    else {        // 对非集合类型的处理        AbstractNestablePropertyAccessor.PropertyHandler ph = getLocalPropertyHandler(actualName);        Object oldValue = null;        try {            Object originalValue = pv.getValue();            Object valueToApply = originalValue;            if (!Boolean.FALSE.equals(pv.conversionNecessary)) {                if (pv.isConverted()) {                    valueToApply = pv.getConvertedValue();                }else {                    if (isExtractOldValueForEditor() && ph.isReadable()) {                        try {                            oldValue = ph.getValue();                        }catch (Exception ex) {}                    }                    valueToApply = convertForProperty(propertyName, oldValue, originalValue, ph.toTypeDescriptor());                }                pv.getOriginalPropertyValue().conversionNecessary = (valueToApply != originalValue);            }             // 通过反射注入            ph.setValue(object, valueToApply);            }        }    }复制代码
public void setValue(final Object object, Object valueToApply) throws Exception {    final Method writeMethod = this.pd.getWriteMethod();    if (!Modifier.isPublic(writeMethod.getDeclaringClass().getModifiers()) && !writeMethod.isAccessible()) {        if (System.getSecurityManager() != null) {            AccessController.doPrivileged(new PrivilegedAction() {                @Override                public Object run() {                    writeMethod.setAccessible(true);                    return null;                }            });        } else {            writeMethod.setAccessible(true);        }    }    final Object value = valueToApply;    if (System.getSecurityManager() != null) {    } else {        // 通过反射 用set 方法注入属性        writeMethod.invoke(getWrappedInstance(), value);    }}复制代码

总结

本文主要写了一下几点:

1.依赖注入这个步骤是整个Spring ioc 的一部分以及一个功能

2.依赖注入是属性注入的一种,区别在于这个属性是由Spring管理的bean
3.触发依赖注入的时机
4.两种依赖注入方式 配置文件以及注解
5.源码部分

欢迎大家提出改进点,小弟入行不久功力浅。

参考:

《Spring 技术内幕》 《Spring 源码深度剖析》

其他文章

转载于:https://juejin.im/post/5ca6fd485188250d9e1bda0c

你可能感兴趣的文章
linux测试某进程占用oi、cpu、内存的使用情况
查看>>
jQery简单Tab选项卡效果
查看>>
Java Object类及其equals方法
查看>>
[摘录]为你的感情开个账户
查看>>
CHIL-ORACLE-修改密码
查看>>
java在线聊天项目 客户端登陆窗口LoginDialog的注册用户功能 修改注册逻辑 增空用户名密码的反馈 增加showMessageDialog()提示框...
查看>>
删除缓存
查看>>
在Unity(C#)下实现Lazy Theta*寻路
查看>>
数据获取以及处理系统 --- 功能规格说明书
查看>>
深入理解linux系统下proc文件系统内容
查看>>
axios 或 ajax 请求文件
查看>>
Mysql 备份与恢复
查看>>
nohup和&后台运行,进程查看及终止
查看>>
Apache2.2和Apache2.4中httpd.conf配置文件 权限的异同
查看>>
设计模式-简单工厂、工厂方法模式、抽象工厂模式详解
查看>>
利用Python进行文章特征提取(二)
查看>>
前端开发中同步和异步的区别
查看>>
HDU3627 set+map
查看>>
firefox 被劫持hao123 主页
查看>>
网络爬虫练习
查看>>