Spring IOC - Bean的生命周期之依赖注入

        在Spring启动流程中,创建的factoryBean是DefaultListableBeanFactory,其类图如下所示:

        可以看到其直接父类是AbstractAutoireCapableBeanFactory,他主要负责完成Bean的自动装配和创建工作。 具体来说,AbstractAutowireCapableBeanFactory会完成以下工作:

  1. 根据Bean的定义信息创建Bean实例;
  2. 根据Bean的定义信息完成Bean的依赖注入;
  3. 根据Bean的定义信息完成Bean的初始化工作;
  4. 返回创建好的Bean实例。

        其中,属性注入是AbstractAutowireCapableBeanFactory的核心功能之一。它会根据Bean的定义信息,自动将依赖的Bean注入到当前Bean中。具体来说,它会根据Bean的依赖关系,自动查找并创建依赖的Bean实例,并将其注入到当前Bean中。

         除了属性注入,AbstractAutowireCapableBeanFactory还支持构造函数注入、工厂方法注入等多种注入方式,可以满足不同场景下的需求。

        其核心方法AbstractAutowireCapableBeanFactory#populateBean,下面,我们看一下populateBean方法是怎么进行依赖注入的。它处理Spring的各种依赖注入:包括自动注入(名称注入和类型注入)、注解注入(@Autowired和@Value等)、手动注入等。

  1. 首先,判断是否需要进行属性注入。调用 ibp#postProcessAfterInstantiation

  2. 自动注入:包括名称注入和类型注入。不推荐使用,只支持 XML 配置方式。

  3. 注解注入:处理 @Autowired 和 @Value 等注解,Spring 提供 ibp#postProcessProperties 可以调整 bean 实例。如 AutowiredAnnotationBeanPostProcessor 用于处理 @Autowired 和 @Value 注解。CommonAnnotationBeanPostProcessor 用于处理 @Resource 注解。

  4. 依赖检查:检查要注入的依赖是否已经完整。可以只检查简单类型(Java 原生类型、Enum、Class 等),也可以只检查对象类型。

  5. 手动注入:最基础的注入方式,实际上都委托给了 BeanWrapper 处理。

protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {// 1. 判断是否需要进行属性注入:ibp#postProcessAfterInstantiationif (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {for (BeanPostProcessor bp : getBeanPostProcessors()) {if (bp instanceof InstantiationAwareBeanPostProcessor) {InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;if (!ibp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) {return;}}}}PropertyValues pvs = (mbd.hasPropertyValues() ? mbd.getPropertyValues() : null);// 2. 自动注入:包括名称注入和类型注入int resolvedAutowireMode = mbd.getResolvedAutowireMode();if (resolvedAutowireMode == AUTOWIRE_BY_NAME || resolvedAutowireMode == AUTOWIRE_BY_TYPE) {MutablePropertyValues newPvs = new MutablePropertyValues(pvs);// 2.1 自动根据名称注入if (resolvedAutowireMode == AUTOWIRE_BY_NAME) {autowireByName(beanName, mbd, bw, newPvs);}// 2.2 自动根据类型注入if (resolvedAutowireMode == AUTOWIRE_BY_TYPE) {autowireByType(beanName, mbd, bw, newPvs);}pvs = newPvs;}boolean hasInstAwareBpps = hasInstantiationAwareBeanPostProcessors();boolean needsDepCheck = (mbd.getDependencyCheck() != AbstractBeanDefinition.DEPENDENCY_CHECK_NONE);// 3. 注解注入:后置处理器ibp#postProcessProperties,大名鼎鼎的@Autowired就是在这处理的。PropertyDescriptor[] filteredPds = null;if (hasInstAwareBpps) {if (pvs == null) {pvs = mbd.getPropertyValues();}for (BeanPostProcessor bp : getBeanPostProcessors()) {if (bp instanceof InstantiationAwareBeanPostProcessor) {InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;PropertyValues pvsToUse = ibp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName);if (pvsToUse == null) {if (filteredPds == null) {filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);}pvsToUse = ibp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);if (pvsToUse == null) {return;}}pvs = pvsToUse;}}}// 4. 依赖检查,循环依赖...if (needsDepCheck) {if (filteredPds == null) {filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);}checkDependencies(beanName, mbd, filteredPds, pvs);}// 5. 手动依赖注入if (pvs != null) {applyPropertyValues(beanName, mbd, bw, pvs);}
}

        这里重点讲解AutowiredAnnotationBeanPostProcessor为代表的注解注入方式。其会调用到Spring进行依赖查找的核心API: beanFactory#resolveDependency,其本质是根据类型查找依赖,调用beanFactory#beanNamesForType方法根据类型查找依赖名称。他解决了以下四个场景:

  1. Optional:JDK8 提供了 API。主要是将依赖设置非强制依赖,即 descriptor.required=false。

  2. 延迟依赖注入支持:ObjectFactory、ObjectProvider、javax.inject.Provider

  3. 另一种延迟注入的支持 - @Lazy 属性。

  4. 根据类型查找依赖 - doResolveDependency。

@Override
public Object resolveDependency(DependencyDescriptor descriptor, String requestingBeanName, Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException {// ParameterNameDiscovery用于解析方法参数名称descriptor.initParameterNameDiscovery(getParameterNameDiscoverer());// 1. Optional<T>if (Optional.class == descriptor.getDependencyType()) {return createOptionalDependency(descriptor, requestingBeanName);// 2. ObjectFactory<T>、ObjectProvider<T>} else if (ObjectFactory.class == descriptor.getDependencyType() ||ObjectProvider.class == descriptor.getDependencyType()) {return new DependencyObjectProvider(descriptor, requestingBeanName);// 3. javax.inject.Provider<T>} else if (javaxInjectProviderClass == descriptor.getDependencyType()) {return new Jsr330Factory().createDependencyProvider(descriptor, requestingBeanName);} else {// 4. @LazyObject result = getAutowireCandidateResolver().getLazyResolutionProxyIfNecessary(descriptor, requestingBeanName);// 5. 正常情况if (result == null) {result = doResolveDependency(descriptor, requestingBeanName, autowiredBeanNames, typeConverter);}return result;}
}

        最底层都会调用beanFactory#doResolveDependency方法,其封装了依赖查找的各种情况。

  1. 快速查找: @Autowired 注解处理场景。AutowiredAnnotationBeanPostProcessor 处理 @Autowired 注解时,如果注入的对象只有一个,会将该 bean 对应的名称缓存起来,下次直接通过名称查找会快很多。

  2. 注入指定值:@Value 注解处理场景。QualifierAnnotationAutowireCandidateResolver 处理 @Value 注解时,会读取 @Value 对应的值进行注入。如果是 String 要经过三个过程:①占位符处理 -> ②EL 表达式解析 -> ③类型转换,这也是一般的处理过程,BeanDefinitionValueResolver 处理 String 对象也是这个过程。

  3. 集合依赖查询:直接全部委托给 resolveMultipleBeans 方法。

  4. 单个依赖查询:先调用 findAutowireCandidates 查找所有可用的依赖,如果有多个依赖,则根据规则匹配: @Primary -> @Priority -> ③方法名称或字段名称。

public Object doResolveDependency(DependencyDescriptor descriptor, String beanName, Set<String> autowiredBeanNames, TypeConverter typeConverter) throws BeansException {InjectionPoint previousInjectionPoint = ConstructorResolver.setCurrentInjectionPoint(descriptor);try {// 1. 快速查找,根据名称查找。AutowiredAnnotationBeanPostProcessor用到Object shortcut = descriptor.resolveShortcut(this);if (shortcut != null) {return shortcut;}// 2. 注入指定值,QualifierAnnotationAutowireCandidateResolver解析@Value会用到Class<?> type = descriptor.getDependencyType();Object value = getAutowireCandidateResolver().getSuggestedValue(descriptor);if (value != null) {if (value instanceof String) {// 2.1 占位符解析String strVal = resolveEmbeddedValue((String) value);BeanDefinition bd = (beanName != null && containsBean(beanName) ?getMergedBeanDefinition(beanName) : null);// 2.2 Spring EL 表达式value = evaluateBeanDefinitionString(strVal, bd);}TypeConverter converter = (typeConverter != null ? typeConverter : getTypeConverter());try {// 2.3 类型转换return converter.convertIfNecessary(value, type, descriptor.getTypeDescriptor());} catch (UnsupportedOperationException ex) {return (descriptor.getField() != null ?converter.convertIfNecessary(value, type, descriptor.getField()) :converter.convertIfNecessary(value, type, descriptor.getMethodParameter()));}}// 3. 集合依赖,如 Array、List、Set、Map。内部查找依赖也是使用findAutowireCandidatesObject multipleBeans = resolveMultipleBeans(descriptor, beanName, autowiredBeanNames, typeConverter);if (multipleBeans != null) {return multipleBeans;}// 4. 单个依赖查询Map<String, Object> matchingBeans = findAutowireCandidates(beanName, type, descriptor);// 4.1 没有查找到依赖,判断descriptor.requireif (matchingBeans.isEmpty()) {if (isRequired(descriptor)) {raiseNoMatchingBeanFound(type, descriptor.getResolvableType(), descriptor);}return null;}String autowiredBeanName;Object instanceCandidate;// 4.2 有多个,如何过滤if (matchingBeans.size() > 1) {// 4.2.1 @Primary -> @Priority -> 方法名称或字段名称匹配 autowiredBeanName = determineAutowireCandidate(matchingBeans, descriptor);// 4.2.2 根据是否必须,抛出异常。注意这里如果是集合处理,则返回nullif (autowiredBeanName == null) {if (isRequired(descriptor) || !indicatesMultipleBeans(type)) {return descriptor.resolveNotUnique(descriptor.getResolvableType(), matchingBeans);} else {return null;}}instanceCandidate = matchingBeans.get(autowiredBeanName);} else {// We have exactly one match.Map.Entry<String, Object> entry = matchingBeans.entrySet().iterator().next();autowiredBeanName = entry.getKey();instanceCandidate = entry.getValue();}// 4.3 到了这,说明有且仅有命中一个if (autowiredBeanNames != null) {autowiredBeanNames.add(autowiredBeanName);}// 4.4 实际上调用 getBean(autowiredBeanName, type)。但什么情况下会出现这种场景?if (instanceCandidate instanceof Class) {instanceCandidate = descriptor.resolveCandidate(autowiredBeanName, type, this);}Object result = instanceCandidate;if (result instanceof NullBean) {if (isRequired(descriptor)) {raiseNoMatchingBeanFound(type, descriptor.getResolvableType(), descriptor);}result = null;}if (!ClassUtils.isAssignableValue(type, result)) {throw new BeanNotOfRequiredTypeException(autowiredBeanName, type, instanceCandidate.getClass());}return result;} finally {ConstructorResolver.setCurrentInjectionPoint(previousInjectionPoint);}
}

        这里重点看下看下第四种单个依赖的查询,集合依赖与其异曲同工:

  1. 查找容器中所有可用依赖:findAutowireCandidates 方法根据类型查找依赖。

  2. 如何有多个依赖怎么处理?其实 Spring 有一套通用的流程,先按 @Primary 查找,再按 @Priority,最后按方法名称或字段名称查找,直到只有一个 bean 为止。相关的匹配规则见 determineAutowireCandidate 方法。

  3. 此时只有一个依赖,从容器获取真实的 bean。descriptor.resolveCandidate 方法根据名称 autowiredBeanName 实例化对象。

        从 findAutowireCandidates 方法,我们可以看到 Spring IoC 依赖注入的来源:

  1. 先查找 Spring IoC 内部依赖 resolvableDependencies。在 AbstractApplicationContext#prepareBeanFactory 方法中默认设置了如下内部依赖:BeanFactory、ResourceLoader、ApplicationEventPublisher、ApplicationContext。

  2. 在父子容器进行类型查找:查找类型匹配的 beanNames,beanFactory#beanNamesForType 方法根据类型查找是,先匹配单例实例类型(包括 Spring 托管 Bean),再匹配 BeanDefinition 的类型。从这一步,我们可以看到 Spring 依赖注入的另外两个来源:一是 Spring 托管的外部 Bean,二是 Spring BeanDefinition。

protected Map<String, Object> findAutowireCandidates(@Nullable String beanName, Class<?> requiredType, DependencyDescriptor descriptor) {Map<String, Object> result = new LinkedHashMap<>(candidateNames.length);// 1. Spring IoC 内部依赖 resolvableDependenciesfor (Map.Entry<Class<?>, Object> classObjectEntry : this.resolvableDependencies.entrySet()) {Class<?> autowiringType = classObjectEntry.getKey();if (autowiringType.isAssignableFrom(requiredType)) {Object autowiringValue = classObjectEntry.getValue();autowiringValue = AutowireUtils.resolveAutowiringValue(autowiringValue, requiredType);if (requiredType.isInstance(autowiringValue)) {result.put(ObjectUtils.identityToString(autowiringValue), autowiringValue);break;}}}// 2. 类型查找:本质上递归调用beanFactory#beanNamesForType。先匹配实例类型,再匹配bd。String[] candidateNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(this, requiredType, true, descriptor.isEager());for (String candidate : candidateNames) {// 2.1 isSelfReference说明beanName和candidate本质是同一个对象//     isAutowireCandidate进一步匹配bd.autowireCandidate、泛型、@@Qualifier等进行过滤if (!isSelfReference(beanName, candidate) && isAutowireCandidate(candidate, descriptor)) {// 2.2 添加到候选对象中addCandidateEntry(result, candidate, descriptor, requiredType);}}// 3. 补偿机制:如果依赖查找无法匹配,怎么办?包含泛型补偿和自身引用补偿两种。if (result.isEmpty()) {boolean multiple = indicatesMultipleBeans(requiredType);// 3.1 fallbackDescriptor: 泛型补偿,实际上是允许注入对象类型的泛型存在无法解析的情况DependencyDescriptor fallbackDescriptor = descriptor.forFallbackMatch();// 3.2 补偿1:不允许自称依赖,但如果是集合依赖,需要过滤非@Qualifier对象。什么场景?for (String candidate : candidateNames) {if (!isSelfReference(beanName, candidate) && isAutowireCandidate(candidate, fallbackDescriptor) &&(!multiple || getAutowireCandidateResolver().hasQualifier(descriptor))) {addCandidateEntry(result, candidate, descriptor, requiredType);}}// 3.3 补偿2:允许自称依赖,但如果是集合依赖,注入的集合依赖中需要过滤自己if (result.isEmpty() && !multiple) {for (String candidate : candidateNames) {if (isSelfReference(beanName, candidate) &&(!(descriptor instanceof MultiElementDescriptor) || !beanName.equals(candidate)) &&isAutowireCandidate(candidate, fallbackDescriptor)) {addCandidateEntry(result, candidate, descriptor, requiredType);}}}}return result;
}

        findAutowireCandidates 大致可以分为三步:先查找内部依赖,再根据类型查找,最后没有可注入的依赖则进行补偿。

  1. 查找内部依赖:Spring IoC 容器本身相关依赖,这部分内容是用户而言是透明的,也不用感知。resolvableDependencies 集合中注册如 BeanFactory、ApplicationContext 、ResourceLoader、ApplicationEventPublisher 等。

  2. 根据类型查找:包括 ①外部托管 Bean ②注册 BeanDefinition。类型查找调用 beanFactory#beanNamesForType 方法。

    1. 自身引用:isSelfReference 方法判断 beanName 和 candidate 是否是同一个对象,包括两种情况:一是名称完全相同,二是 candidate 对应的工厂对象创建了 beanName。

    2. 是否可以注入:底层实际调用 resolver.isAutowireCandidate 方法进行过滤,包含三重规则:①bd.autowireCandidate=true -> ②泛型匹配 -> ③@Qualifier。下面会详细介绍这个方法。

  3. 补偿机制:如果依赖查找无法匹配,怎么办?Spring 提供了两种补偿机制:一是泛型补偿,允许注入对象对象的泛型无法解析,二是自身引用补偿,对这两种机制使用如下:

    1. 先使用泛型补偿,不允许自身引用:即 fallbackDescriptor。此时如果是集合依赖,对象必须是 @Qualifier 类型。

    2. 允许泛型补偿和自身引用补偿:但如果是集合依赖,必须过滤自己本身,即 beanName.equals(candidate) 必须剔除。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/194755.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

设计模式-行为型模式-策略模式

一、什么是策略模式 策略模式是一种行为设计模式&#xff0c;它允许在运行时选择算法或行为&#xff0c;并将其封装成独立的对象&#xff0c;使得这些算法或行为可以相互替换&#xff0c;而不影响使用它们的客户端。&#xff08;ChatGPT生成&#xff09; 主要组成部分&#xff…

argocd

部署argocd https://github.com/argoproj/argo-cd/releases kubectl create namespace argocd kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/v2.9.1/manifests/install.yaml官网 https://argo-cd.readthedocs.io/en/stable/ kubectl crea…

程序员开发者神器:10个.Net开源项目

今天一起盘点下&#xff0c;8月份推荐的10个.Net开源项目&#xff08;点击标题查看详情&#xff09;。 1、基于C#开发的适合Windows开源文件管理器 该项目是一个基于C#开发、开源的文件管理器&#xff0c;适用于Windows&#xff0c;界面UI美观、方便轻松浏览文件。此外&#…

leetcode刷题日记:190. Reverse Bits(颠倒二进制位)和191. Number of 1 Bits( 位1的个数)

190. Reverse Bits&#xff08;颠倒二进制位&#xff09; 题目要求我们将一个数的二进制位进行颠倒&#xff0c;画出图示如下(以8位二进制为例)&#xff1a; 显然对于这种问题我们需要用到位操作&#xff0c;我们需要将原数的每一位取出来然后颠倒之后放进另一个数。 我们需要…

「Verilog学习笔记」根据状态转移图实现时序电路

专栏前言 本专栏的内容主要是记录本人学习Verilog过程中的一些知识点&#xff0c;刷题网站用的是牛客网 这是一个典型的米利型状态机。三段式即可解决。 米利型状态机&#xff1a;即输出不仅和当前状态有关&#xff0c;也和输入有关。 其中ST0&#xff0c;ST1&#xff0c;ST3的…

UnitTest框架

目标&#xff1a; 1.掌握UnitTest框架的基本使用方法 2.掌握断言的使用方法 3.掌握如何实现参数化 4.掌握测试报告的生成 1.定义 &#xff08;1&#xff09;框架(framework)&#xff1a;为解决一类事情的功能集合。&#xff08;需要按照框架的规定(套路) 去书写代码&…

计算机毕业设计选题推荐-点餐微信小程序/安卓APP-项目实战

✨作者主页&#xff1a;IT毕设梦工厂✨ 个人简介&#xff1a;曾从事计算机专业培训教学&#xff0c;擅长Java、Python、微信小程序、Golang、安卓Android等项目实战。接项目定制开发、代码讲解、答辩教学、文档编写、降重等。 ☑文末获取源码☑ 精彩专栏推荐⬇⬇⬇ Java项目 Py…

变量命名的规则与规范

变量命名的规则与规范 变量命名的规则不能使用关键字字母须区分大小写由字母、数字、_、$组成&#xff0c;且不能以数字开头 变量命名的规范起名须有一定的意义遵守小驼峰命名法 变量命名的规则 不能使用关键字 在JavaScript中声明变量不能使用JavaScript的常用关键字&#x…

【python】--python基础学习

目录 一、基础语法二、基础数据类型1、变量赋值2、数值型3、字符串型4、列表List5、元组Tuple6、字典dictionary7、数据类型转换 三、python运算符四、条件控制与循环五、常用函数1、字符串函数2、format函数 一、基础语法 标识符是允许作为变量&#xff08;函数、类等&#x…

变频器干扰PLC,我们是这么解决的……

PLC是变频器的上位机&#xff0c;但是&#xff0c;在很多工程现场中&#xff0c;经常也会出现这样的问题&#xff0c;就是变频器开始运行后&#xff0c;PLC就开始罢工了&#xff0c;有的时候死机&#xff0c;有的时候指令传达不畅&#xff0c;有的时候会出现通讯时断时续等等&a…

FL Studio2024免费编曲音乐制作软件

用FL Studio编曲&#xff0c;让音乐成为你的翅膀&#xff0c;飞翔在无尽的创作海洋中吧&#xff01; FL Studio作为一款功能强大且备受赞誉的音乐制作软件&#xff0c;为你提供了一个独特的创作平台。通过FL Studio&#xff0c;你可以自由地创作、编曲&#xff0c;制作属于自己…

七、文件包含漏洞

一、文件包含漏洞 解释&#xff1a;文件包含漏洞是一种注入型漏洞&#xff0c;其本质就是输入一段用户能够控制的脚本或者代码&#xff0c;并让服务端执行&#xff1b;其还能够使得服务器上的源代码被读取&#xff0c;在PHP里面我们把可重复使用的函数写入到单个文件中&#x…