Spring之注入模型

news2024/12/27 11:11:50

前言

之前我写过一篇关于BeanDefinition的文章,讲述了各个属性的作用,其中有一个属性我没有提到,因为这个属性比较重要,所以这里单独开一篇文章来说明

上一篇博文链接Spring之BeanDefinitionicon-default.png?t=N7T8https://blog.csdn.net/qq_38257958/article/details/134823169?spm=1001.2014.3001.5501

再探AbstractBeanDefinition源码

通过源码我们得出结论,注入类型有四种

  • AUTOWIRE_NO (0)
  • AUTOWIRE_BY_NAME (1)
  • AUTOWIRE_BY_TYPE (2)
  • AUTOWIRE_CONSTRUCTOR (3)

PS : 实际有五种,AUTOWIRE_AUTODETECT已过期,我们暂不讨论

默认情况下的注入模型

代码准备

创建配置类AppConfig

package com.test.model.config;


import org.springframework.context.annotation.ComponentScan;

@ComponentScan("com.test.model")
public class AppConfig {

}

创建一个普通bean

package com.test.model.component;

import org.springframework.stereotype.Component;

@Component
public class MixComponent {

}

创建一个BeanFactoryPostProcessor(后面简称bfpp) 

package com.test.model.bfpp;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.stereotype.Component;

@Component
public class AutowireBeanFactoryPostProcessor implements BeanDefinitionRegistryPostProcessor {


    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
        AbstractBeanDefinition beanDefinition = (AbstractBeanDefinition) registry.getBeanDefinition("mixComponent");
        int autowireMode = beanDefinition.getAutowireMode();
        System.out.println(autowireMode);
    }

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {

    }
}

创建启动类

package com.test.model;

import com.test.model.config.AppConfig;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class Main {

    public static void main(String[] args) {

        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
    }
}

运行main方法,查看运行结果 

 结论 : 默认的注入模型是0 (AUTOWIRE_NO)

默认情况下的依赖注入

创建三个普通bean

@Component
public class ComponentA {
}

@Component
public class ComponentB {
}

@Component
public class ComponentC {
}

依赖注入

package com.test.model.component;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component
public class MixComponent {

    @Autowired
    private ComponentA componentA;

    private ComponentB componentB;

    private ComponentC componentC;

    @Autowired
    public void setComponentB(ComponentB componentB) {
        this.componentB = componentB;
    }

    public MixComponent() {
    }

    @Autowired
    public MixComponent(ComponentC componentC) {
        this.componentC = componentC;
    }
}

默认情况下的依赖注入的三种方式

  • 属性注入
  • setter注入
  • 构造器注入

运行main方法,查看运行结果

 byName情况下的依赖注入

通过bfpp修改注入模型

package com.test.model.bfpp;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.stereotype.Component;

@Component
public class AutowireBeanFactoryPostProcessor implements BeanDefinitionRegistryPostProcessor {


    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
        AbstractBeanDefinition beanDefinition = (AbstractBeanDefinition) registry.getBeanDefinition("mixComponent");
        // 将注入模型改为byName
        beanDefinition.setAutowireMode(AutowireCapableBeanFactory.AUTOWIRE_BY_NAME);
    }

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {

    }
}

改造MixComponent 

package com.test.model.component;

import org.springframework.stereotype.Component;

@Component
public class MixComponent {
    
    // 没有Autowired注解
    private ComponentA componentA;

    // 没有Autowired注解
    private ComponentB componentB;

    // 没有Autowired注解
    private ComponentC componentC;

    public void setComponentA(ComponentA componentA) {
        this.componentA = componentA;
    }

    public void setComponentB(ComponentB componentB) {
        this.componentB = componentB;
    }

    public void setComponentC(ComponentC componentC) {
        this.componentC = componentC;
    }
}

 运行main方法,查看运行结果

源码解析

AbstractAutowireCapableBeanFactory#populateBean

AbstractAutowireCapableBeanFactory#autowireByName

AbstractAutowireCapableBeanFactory#unsatisfiedNonSimpleProperties

 unsatisfiedNonSimpleProperties方法大概分为两个步骤

  1. 利用Spring的自省机制获取pds
  2. 判断这个pd是否符合条件
    1. 判断pd是否存在writeMethod
    2. 判断pd的propertyType是不是需要忽略的类
    3. pvs是否已存在相同name的值
    4. propertyType是不是普通类

我来解释一下pd需要满足的四个条件

条件1
package com.test.model.component;

import org.springframework.stereotype.Component;

@Component
public class Demo1 {

    public Object getAbc() {
        return null;
    }

    public void setAbc(Object abc) {

    }

    public Object getXyz() {
        return null;
    }

    public void setXyz(Object xyz) {

    }
}

不管我们是不是真实存在abc、xyz这样的属性,只要类中存在setXxx或者getXxx这样的方法,我们就认为类中存在一个名称为xxx的属性

PS : 我测试了一下,setXxx方法只能存在一个参数

条件2

比如bean实现了xxxAware接口,其相应的实现方法会被过滤掉

相关源码

AbstractApplicationContext#prepareBeanFactory

PS : 可能还有其他地方也添加了相关接口,debug的时候不止这几个

条件3
package com.test.model.bfpp;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.stereotype.Component;

@Component
public class Demo1FactoryPostProcessor implements BeanDefinitionRegistryPostProcessor {

    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
        AbstractBeanDefinition b2 = (AbstractBeanDefinition) registry.getBeanDefinition("demo1");

        b2.getPropertyValues().add("abc", new Object());
    }

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {

    }
}

我们可以手动给指定bean的属性设置值,那样就不会在Spring容器中查找符合条件的bean了。

条件4

Spring定义的普通类

byType情况下的依赖注入

byType和byName类似 主要就是根据参数类型从Spring中查找符合条件的bean,主要关注unsatisfiedNonSimpleProperties方法

测试发现一个有意思的情况,下方代码在byName的情况下会错,在byType的情况下会正确注入

package com.test.model.component;

import org.springframework.stereotype.Component;

@Component
public class Demo2 {

    private ComponentC componentC;

    public void setComponentA(ComponentC componentC) {
        this.componentC = componentC;
    }
}

构造器注入

相关源码AbstractAutowireCapableBeanFactory#createBeanInstance

构造器注入并非只有注入模型是AUTOWIRE_CONSTRUCTOR才会执行构造器注入的逻辑,另外三个条件如果满足其一也会执行构造器注入,这里我用代码演示其他三种情况

具体细节可以看我之前写的博文Spring之推断构造方法icon-default.png?t=N7T8https://blog.csdn.net/qq_38257958/article/details/134957631?spm=1001.2014.3001.5501

ctors != null的几种情况
package com.test.model.component;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component
public class Demo3 {

    /**
     * case1:有且仅有一个有参构造方法
     */
    private ComponentA componentA;

    public Demo3(ComponentA componentA) {
        this.componentA = componentA;
    }

    /**
     * case2:有且仅有一个@Autowired(required = true)标注的构造方法
     */
    private ComponentB componentB;

    private ComponentC componentC;

    @Autowired(required = true)
    public Demo3(ComponentB componentB) {
        this.componentB = componentB;
    }

    public Demo3(ComponentC componentC) {
        this.componentC = componentC;
    }

    /**
     * case3:有多个@Autowired(required = false)标注的构造方法
     */
    @Autowired(required = false)
    public Demo3(ComponentB componentB) {
        this.componentB = componentB;
    }

    @Autowired(required = false)
    public Demo3(ComponentC componentC) {
        this.componentC = componentC;
    }
}
mbd.hasConstructorArgumentValues()
package com.test.model.component;

import org.springframework.stereotype.Component;

@Component
public class Demo4 {

    private ComponentB componentB;

    private ComponentC componentC;


    public Demo4(ComponentB componentB) {
        this.componentB = componentB;
    }

    public Demo4(ComponentC componentC) {
        this.componentC = componentC;
    }
}
package com.test.model.bfpp;

import com.test.model.component.ComponentB;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
import org.springframework.stereotype.Component;

@Component
public class Demo4FactoryPostProcessor implements BeanDefinitionRegistryPostProcessor {

    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
        AbstractBeanDefinition beanDefinition = (AbstractBeanDefinition) registry.getBeanDefinition("demo4");
        // 指定构造器Demo4(ComponentB componentB)
        beanDefinition.getConstructorArgumentValues().addIndexedArgumentValue(0,new ComponentB());
    }

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {

    }
}
 !ObjectUtils.isEmpty(args)
package com.test.model.component;

import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;

@Component
@Lazy
public class Demo5 {

    private ComponentB componentB;

    private ComponentC componentC;


    public Demo5(ComponentB componentB) {
        this.componentB = componentB;
    }

    public Demo5(ComponentC componentC) {
        this.componentC = componentC;
    }
}

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

旅游景区公共广播 园区广播 公路服务区广播

旅游景区公共广播 园区广播 公路服务区广播 旅游景区公共广播 旅游景区公共广播(又称背景音乐)简称BGM,它的主要作用是掩盖噪声并创造一种轻松和谐的气氛,是一种创造轻松愉快环境气氛的音乐。掩盖环境噪声,创造与旅游景区相适应的气氛&#…

48. 【Linux教程】yum 软件包管理

本小节介绍如何在 Linux 系统中使用 yum 命令软件管理。 1.yum 简介 yum 是 Red Hat 软件包管理器,它能够查询有关可用软件包的信息,从存储库获取软件包,安装和卸载软件包,以及将整个系统更新到最新的可用版本。yum 在更新&#…

(2022级)成都工业学院Java程序设计(JAVA)实验一:编写一个简单的Java程序

写在前面 1、基于2022级软件工程/计算机科学与技术实验指导书 2、代码仅提供参考 3、如果代码不满足你的要求,请寻求其他的途径 运行环境 window11家庭版 IntelliJ IDEA 2023.2.2 jdk17.0.6 实验要求 1、 控制台菜单。要求如下: 1)…

【算法面试题】-07

小明找位置 题目描述 小朋友出操&#xff0c;按学号从小到大排成一列;小明来迟了&#xff0c;请你给小明出个主意&#xff0c;让他尽快找到他应该排的位置。 算法复杂度要求不高于nLog(n);学号为整数类型&#xff0c;队列规模<10000; 输入描述 1、第一行:输入已排成队列的…

金融知识分享系列之:财不入急门——迫切盈利的欲望是痛苦的根源

金融知识分享系列之&#xff1a;财不入急门——迫切盈利的欲望是痛苦的根源 一、错误观点二、正确观点 一、错误观点 迫切盈利&#xff1a; 总是怕错过机会&#xff0c;着急入场自己认为很好的机会&#xff0c;就想重仓押注&#xff0c;挽回损失想学习一套规则&#xff0c;立…

PCB差分通孔的数值建模方法

目录 0 引言 1 基于CST的3D通孔模型 2 通孔模型的近似等效计算 3 利用ADS进行电路仿真分析 4 总结 0 引言 当数据速率超过10Gbps时&#xff0c;PCB上的通孔所带来的寄生参数会成为影响数据误码率的关键因素之一&#xff0c;虽然通过三维电磁场求解器提取过孔的行为模型&…

京津冀协同发展:北京·光子1号金融算力中心——智能科技新高地

京津冀协同发展是党中央在新的历史条件下提出的一项重大国家战略&#xff0c;对于全面推进“五位一体”总体布局&#xff0c;以中国式现代化全面推进强国建设、民族复兴伟业&#xff0c;具有重大现实意义和深远历史意义。随着京津冀协同发展战略的深入推进&#xff0c;区域一体…

高项-项目整合管理

今天找到一个讲的还不错的视频&#xff0c;放上来存一下&#xff1a;【第4版】第8章-项目整合管理(8.1)_哔哩哔哩_bilibili 项目整合管理的目标 资源分配平衡竞争性需求研究各种备选方法裁剪过程以实现项目目标管理各个项目管理知识域之间的依赖关系 项目整合管理的过程 制…

系统设计学习(二)用户认证场景

一、常用鉴权方式 HTTP Basic Authentication (HTTP基本认证) session-cookie 1&#xff0c;服务器在接受客户端首次访问时在服务器端创建session&#xff0c;然后保存session(我们可以将session保存在内存中&#xff0c;也可以保存在redis中&#xff0c;推荐使用后者)&…

【Docker】 ubuntu18.04编译时内存不足需要使用临时交换分区解决“c++: internal compiler error“错误

【Docker】 ubuntu18.04编译时内存不足需要使用临时交换分区解决"c: internal compiler error"错误 问题描述 安装独立功能包时编译不成功&#xff0c;出现 “c: internal compiler error: Killed(program cciplus)” 错误。 解决方案 出现这个问题的原因大概率是…

[LeetCode][LCR174] 寻找二叉搜索树中的目标节点

题目 LCR 174. 寻找二叉搜索树中的目标节点 某公司组织架构以二叉搜索树形式记录&#xff0c;节点值为处于该职位的员工编号。请返回第 cnt 大的员工编号。 示例 1: 输入&#xff1a;root [7, 3, 9, 1, 5], cnt 27/ \3 9/ \ 1 5 输出&#xff1a;7示例 2: 输入: ro…

【Redis】-Redis实现高并发下秒杀系统

文章目录 前言 一、场景二、商品超卖的场景三、使用分布式锁解决超卖四、使用Redis事务乐观锁解决超卖 ** 前言 Redis事务   Redis事务是一种将多个命令打包执行的机制&#xff0c;确保这些命令要么全部执行成功&#xff0c;要么全部执行失败。Redis事务通过MULTI、EXEC、D…

机器视觉检测设备的组成要素

机器视觉检测设备是一种先进的自动化检测技术工具&#xff0c;它利用光学、图像处理和计算机硬件及软件技术模拟并扩展人类的视觉功能&#xff0c;以实现对产品或目标物体进行自动化的尺寸测量、缺陷检测、表面质量评估、颜色识别、形状匹配以及位置判断等功能。这种设备通常包…

【Rockchip android7.1 平台rtl8821cs wifi移植调试】

Rockchip 平台rtl8821cs wifi移植调试 问题描述解决方法 郑重声明:本人原创博文&#xff0c;都是实战&#xff0c;均经过实际项目验证出货的 转载请标明出处:攻城狮2015 Platform: Rockchip rk3128 OS:Android 7.1.2 Kernel: 3.10 问题描述 客户需要在现在的板子上调一款RTL882…

【惠友精术】你见过给膝关节“打补丁”吗?单髁置换术,微创保膝真有一套

有什么方法可以让“换膝盖”既没那么痛&#xff0c;苦头又吃得少呢&#xff1f;你别说&#xff0c;还真有&#xff01; 近日&#xff0c;关节外科团队顺利为一例膝关节退变的患者完成了“膝关节单髁置换术”&#xff0c;实施了微小切口下对膝关节病变部位的精准治疗。该手术的成…

SV-7045网络草坪音箱安装说明 景区园区背景音乐广播石头音箱

SV-7045网络草坪音箱安装说明 景区园区背景音乐广播石头音箱 在做室外公共广播/背景音乐系统时&#xff0c;对于草坪音箱的安装方法,大家可能不甚了解&#xff0c;所以将具体安装方法作简要说明。 音箱安装步骤如下&#xff1a; 1.测量草地音箱的底座尺寸大小&#xff0c;最…

07-java基础-锁之AQSReentrantLockBlockingQueueCountDownLatchSemapho

文章目录 0&#xff1a;AQS简介-常见面试题AQS具备特性state表示资源的可用状态AQS定义两种资源共享方式AQS定义两种队列自定义同步器实现时主要实现以下几种方法&#xff1a;同步等待队列条件等待队列 1&#xff1a;AQS应用之ReentrantLockReentrantLock如何实现synchronized不…

【数据结构C++】线性表/顺序表-数组与vector

系列文章目录 第一章 线性表/顺序表-数组与vector 文章目录 系列文章目录前言一、数据结构主要研究的内容&#xff1f;二、数据结构三要素三、数组1.C中数组基本语法1.1 数组的声明、初始化1.2 vector的底层结构 2.数据结构之数组详解2.1 数据结构之数组操作——增、删、改、查…

55寸oled曲面拼接屏的特性

55寸OLED曲面拼接屏具有一系列引人注目的特性&#xff0c;使其在高端展示、商业广告、会议室和舞台演出等场合具有广泛的应用价值。以下是关于55寸OLED曲面拼接屏特性的详细解析&#xff1a; 高亮度与高对比度&#xff1a;OLED技术使得屏幕能够在各种光线条件下呈现出明亮且清晰…

【构建部署_Docker介绍与安装】

构建部署_Docker介绍与安装 构建部署_Docker介绍与安装Docker介绍Docker安装CentOS安装DockerCompose 构建部署_Docker介绍与安装 Docker介绍 Docker 是一个基于go语言开发的开源的应用容器引擎&#xff0c;让开发者可以打包他们的应用以及依赖包到一个可移植的容器中&#x…