Spring|Spring原理篇(7)--Spring最经常使用的一个功能 依赖注入, 该功能源码是一定需要知道的;这是我们日常开发中的核心; 了解其源码;这一篇就够了;
@TOC# Spring系列
记录在程序走的每一步___auth:huf
回复: 【Spring|Spring原理篇(7)--Spring最经常使用的一个功能 依赖注入, 该功能源码是一定需要知道的;这是我们日常开发中的核心; 了解其源码;这一篇就够了;】在文章的前面会 回复一些同学的疑问 注意 在使用InstantiationAwareBeanPostProcessor 的时候 如果return 返回值 不写;
此时正在创建对象过程当中;
这时候 会导致 该对象里面的@Autowired 注入失败;
对象不完整;
这时候请注意 InstantiationAwareBeanPostProcessor 的写法;
因为此时你 正在介入Bean的 实例化;
Spring 依赖注入 此处作为一个Spring的一个新的篇章;
Bean的生命周期已经在上面的篇幅已经陈述完了 ;
现在进入新的篇章 Spring 依赖注入;
首先来一个 二连问:
第一问: 依赖注入有几种方式? 在面试中;
该问题不会经常被问到;
但是如果问这个问题的人 可以看出 该面试官 对Spring 肯定是有一定理解的;
你怎么答这个问题才能体现自己对Spring的理解?
Spring中 分两种 依赖注入的方式:
1 手动注入 为什么会谈先手动注入;
在很久很久以前;
我们再Spring中写注入的时候;
我们是再XML文件里面写注入的;
我当时在一家跟政府合作的企业中进行开发;
那里的配置文件 体量非常非常大 一个文件少说有几千行配置在里面;
里面用的方式 就是XML 注入方式;
手动注入;
当时也遇到了人生中的第一位导师~而且是女导师 偏了… 继续~
1:通过set的方式注入;
2:通过构造方法方式注入;
所以手动注入的方式 再分两种 : 一: 通过set方式注入; 二:通过构造方式注入;
2 自动注入 1: XML的 autowire 自动注入;
在XML的bean标签中 有一个属性; 是autowire 里面有几个参数 :
1). byType 2). byName 3). constructor 4). default 5). no
这么写,表示Spring会自动的给userService中所有的属性自动赋值(不需要这个属性上有 @Autowired注解,但需要这个属性有对应的set方法)
以上这些方式 感兴趣的朋友可以一个一个去尝试; 该篇文章 比较长; 这里就不做过多的演示;
源码: 在创建Bean的过程当中 在填充属性的时候.Spring会去解析当前这个类的所有方法; 在解析的过程当中 Spring会得到 PropertyDescriptor 叫做属性描述器的类 这个类的作用 就是描述 某个属性的 get 与 set 方法; PropertyDescriptor 是java.bean 的一个类; 有喜欢的小伙伴 可以自己去查看以下; 这个类挺方便的; 以下就是我自己个人 使用这个类的一个简单实例:
通过属性名字 以及 类class 创建PropertyDescriptor
PropertyDescriptor pd = new PropertyDescriptor(declaredField.getName(), clazz);
通过这个类可以得到Method方法;
这是读方法 也就是get
Method method = pd.getReadMethod();
Object invoke = method.invoke(obj);
String v = execute((String) invoke, maskStr, staNumber, endNumber);
这是写方法 也就是set;
pd.getWriteMethod().invoke(obj, v);
重点 2:@Autowired注解的自动注入; 我们重点关注的Autowired
Essentially, the @Autowired annotation provides the same
capabilities as described in Autowiring Collaborators but with more
fine‐grained control and wider applicability
先看这一段 本质上 @Autowired 提供了相同的功能; 但是拥有更细粒度的控 制和更广泛的适用性;
@Autowired 作用更细粒度; XML中的autowire控制的是整个bean的所有属性,而@Autowired注解是直接写在某个属性、某个 set方法、某个构造方法上的; @Autowired 是Xml当中 ByType 与 ByName的结合
先根据属性类型去找Bean,如果找到多个再根据属性名确定一个
@Autowired
private StudentMapper studentMapper;
先根据方法参数类型去找Bean,如果找到多个再根据参数名确定一个
@Autowired
public void setStudentMapper (StudentMapper studentMapper){}
先根据方法参数类型去找Bean,如果找到多个再根据参数名确定一个
@Autowired
public StudentServiceImpl(StudentMapper studentMapper) {}
- 属性注入
- set方法注入
- 构造方法注入
第二问 @Autowired 是怎么注入进来的? 我们在第一问讲了 是根据类型先找 然后再根据名字选出; 那么 是怎么找的? 我们带着疑问 一起来一探究竟

文章图片
以上 就是简简单单的一个注入; 在我们Spring进行生命周期的时候 会通过RootBeanDefinition 以及 非常多的BeanPostProcessor 实例化 以及 初始化这个类; 实例化就是创建这个Bean 在创建完成之后 经过初始化; 完成整个Bean的 构建 最后保存在单例池里 这里 就是Bean创建完成之后的代码 也就是 属性注入;
以下源码中穿插自己对源码的见解; 源码是Spring 5.3.5 版本的;
现在我们以Debug的方式 进行依赖注入代码追踪;
前面的省… 在 AbstractBeanFactory 在这个类中: doGetBean方法中 我们可以体会到整个Bean的创建流程; 上一章节我们在 该源码中说道 有兴趣的 调到上面一个章节 直接搜索 doCreateBean 里面写到;
protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
throws BeanCreationException {开始实例化Bean
BeanWrapper instanceWrapper = null;
判断Bean是不是单例的 如果是
if (mbd.isSingleton()) {先从Factory 删除Bean;
这里 就是个ConcurrentMap 也就是删除正在创建的Bean的包装对象;
instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
}
删除了之后 是不是null;
然后开始createBeanInstance
if (instanceWrapper == null) {开始进入createBeanInstance
instanceWrapper = createBeanInstance(beanName, mbd, args);
}
以下源码暂时不解析;
在之后的篇章会详细说的;
目前就到这里可以了
-----------------------------------------------------------------------------
这里接上之前的源码把该方法源码全部展示完:获取 实例化完成的Bean这时候 Bean里面的属性是空的
Object bean = instanceWrapper.getWrappedInstance();
获取了 正在创建的Bean class文件;
Class> beanType = instanceWrapper.getWrappedClass();
这里不用多说也应该可以看明白;
if (beanType != NullBean.class) {mbd.resolvedTargetType = beanType;
}
这里就是执行我们后置MergedBeanDefinitionPostProcessors 后置处理器的调度方法;
synchronized (mbd.postProcessingLock) {if (!mbd.postProcessed) {try {applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
}
catch (Throwable ex) {throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"Post-processing of merged bean definition failed", ex);
}
mbd.postProcessed = true;
}
}缓存单例,以便能够解析循环引用
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {if (logger.isTraceEnabled()) {logger.trace("Eagerly caching bean '" + beanName +
"' to allow for resolving potential circular references");
}
该方法 是解决循环依赖;
循环依赖将放在下个篇章去讲;
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}这时候 开始了Bean的属性注入;
Object exposedObject = bean;
try {这个方法 是主要方法;
populateBean(beanName, mbd, instanceWrapper);
exposedObject = initializeBean(beanName, exposedObject, mbd);
}
以下源码全部省去;
进入populateBean里面去看看;
}
进入 populateBean
@SuppressWarnings("deprecation")
protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {按照惯例 先隐藏前面代码;
方便阅读;
前面有说道 会先去拿它的PropertyDescriptor 这个东西是什么作用的 自己去看上面的我写的案例;
PropertyDescriptor[] filteredPds = null;
if (hasInstAwareBpps) {if (pvs == null) {pvs = mbd.getPropertyValues();
}
循环当前Bean的 BeanPostProcessor BP
在5.3.5 版本有6个 其他版本可能个数会不一样
主要的一个BP 是 : AutowiredAnnotationBeanPostProcessor
for (InstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().instantiationAware) {PropertyValues pvsToUse = bp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName);
if (pvsToUse == null) {if (filteredPds == null) {filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
}
pvsToUse = bp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);
if (pvsToUse == null) {return;
}
}
pvs = pvsToUse;
}
}
隐藏后面代码
这个for循环这个List给你们打印出来
在通过了这个bp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName);
会往BeanWrapper.getWrappedInstance() 这里 保存的值 的内存引用 就是单例池里面的相对应需要注入的哪个Bean. 所以 这里将他注入; 那么外面哪个Bean里面就有值了;

文章图片
AutowiredAnnotationBeanPostProcessor postProcessProperties
@Override
public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {顾名思义;
应该可以知道这个方法里面是在做些什么事情;
InjectionMetadata metadata = https://www.it610.com/article/findAutowiringMetadata(beanName, bean.getClass(), pvs);
try {依赖注入核心方法;
metadata.inject(bean, beanName, pvs);
}
catch (BeanCreationException ex) {throw ex;
}
catch (Throwable ex) {throw new BeanCreationException(beanName,"Injection of autowired dependencies failed", ex);
}
return pvs;
}
inject
public void inject(Object target, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {这个源码熟吧? 里面的InjectedElement 放着就是 你依赖注入的每一个属性;
Collection checkedElements = this.checkedElements;
Collection elementsToIterate =
(checkedElements != null ? checkedElements : this.injectedElements);
if (!elementsToIterate.isEmpty()) {for (InjectedElement element : elementsToIterate) {element.inject(target, beanName, pvs);
}
}
}
inject2 进入这个inject的时候要注意; 是这个;

文章图片
@Override
protected void inject(Object bean, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {filed就是我们要注入的属性;
Field field = (Field) this.member;
Object value;
检查缓存
if (this.cached) {try {value = https://www.it610.com/article/resolvedCachedArgument(beanName, this.cachedFieldValue);
}
catch (NoSuchBeanDefinitionException ex) {value = resolveFieldValue(field, bean, beanName);
}
}
else {如果没有缓存的情况下 走resolveFieldValue
value = resolveFieldValue(field, bean, beanName);
}
if (value != null) {ReflectionUtils.makeAccessible(field);
field.set(bean, value);
}
}
resolveFieldValue 最后在上面那个方法中的最后 注入这个属性;
@Nullable
private Object resolveFieldValue(Field field, Object bean, @Nullable String beanName) {传入之后 先看你的required 是否为false;
在Autowired 里面的那个required 表示是否一定要注入;
默认为true
DependencyDescriptor desc = new DependencyDescriptor(field, this.required);
desc.setContainingClass(bean.getClass());
Set autowiredBeanNames = new LinkedHashSet<>(1);
Assert.state(beanFactory != null, "No BeanFactory available");
TypeConverter typeConverter = beanFactory.getTypeConverter();
Object value;
try {通过这个步骤找到了 value
value = https://www.it610.com/article/beanFactory.resolveDependency(desc, beanName, autowiredBeanNames, typeConverter);
}
------省掉部分代码
返回这个value;
return value;
}
}
总结 寻找注入点: 在创建一个Bean的过程中,Spring会利用AutowiredAnnotationBeanPostProcessor的 **postProcessMergedBeanDefinition()**找出注入点并缓存,找注入点的流程为:
- 遍历当前类的所有的属性字段Field
- 查看字段上是否存在@Autowired、@Value、@Inject中的其中任意一个,存在则认为该字段 是一个注入点
- 如果字段是static的,则不进行注入
- 获取@Autowired中的required属性的值
- 将字段信息构造成一个AutowiredFieldElement对象,作为一个注入点对象添加到 currElements集合中。
- 遍历当前类的所有方法Method
- 判断当前Method是否是桥接方法,如果是找到原方法
- 查看方法上是否存在@Autowired、@Value、@Inject中的其中任意一个,存在则认为该方法 是一个注入点
- 如果方法是static的,则不进行注入
- 获取@Autowired中的required属性的值
- 将方法信息构造成一个AutowiredMethodElement对象,作为一个注入点对象添加到 currElements集合中。
- 遍历完当前类的字段和方法后,将遍历父类的,直到没有父类
- 最后将currElements集合封装成一个InjectionMetadata对象,作为当前Bean对于的注入点集合 对象,并缓存。
在属性中 我们的Bean是原型Bean 也就是多例的 那么 他属性如果用了static会怎么样? 这个问题如果想明白了 就知道为什么不支持了.
在static修饰的方法中 其字节码文件会生成两个同样的方法 其中一个方法带有 synthetic bridge 并且都是存在@Autowired注解的 所以在Spring中需要处理这种情况,当遍历到桥接方法时,得找到原方法
注入点进行注入 Spring在AutowiredAnnotationBeanPostProcessor的**postProcessProperties()**方法中,会遍 历所找到的注入点依次进行注入。
字段注入
- 遍历所有的AutowiredFieldElement对象
- 将对应的字段封装为DependencyDescriptor对象
- 调用BeanFactory的resolveDependency()方法,传入DependencyDescriptor对象,进行依 赖查找,找到当前字段所匹配的Bean对象。
- 将DependencyDescriptor对象和所找到的结果对象beanName封装成一个 ShortcutDependencyDescriptor对象作为缓存,比如如果当前Bean是原型Bean,那么下次 再来创建该Bean时,就可以直接拿缓存的结果对象beanName去BeanFactory中去那bean对象 了,不用再次进行查找了
- 利用反射将结果对象赋值给字段。
- 遍历所有的AutowiredMethodElement对象
- 遍历将对应的方法的参数,将每个参数封装成MethodParameter对象
- 将MethodParameter对象封装为DependencyDescriptor对象
- 调用BeanFactory的resolveDependency()方法,传入DependencyDescriptor对象,进行依 赖查找,找到当前方法参数所匹配的Bean对象。
- 将DependencyDescriptor对象和所找到的结果对象beanName封装成一个 ShortcutDependencyDescriptor对象作为缓存,比如如果当前Bean是原型Bean,那么下次 再来创建该Bean时,就可以直接拿缓存的结果对象beanName去BeanFactory中去那bean对象 了,不用再次进行查找了
- 利用反射将找到的所有结果对象传给当前方法,并执行。
SEE YOU
推荐阅读
- 2018年11月19日|2018年11月19日 星期一 亲子日记第144篇
- 做一件事情的基本原理是什么()
- Activiti(一)SpringBoot2集成Activiti6
- 《魔法科高中的劣等生》第26卷(Invasion篇)发售
- 拍照一年啦,如果你想了解我,那就请先看看这篇文章
- 亲子日记第186篇,2018、7、26、星期四、晴
- SpringBoot调用公共模块的自定义注解失效的解决
- 解决SpringBoot引用别的模块无法注入的问题
- 漫画初学者如何学习漫画背景的透视画法(这篇教程请收藏好了!)
- 两短篇