【Spring源码解析】一文读懂Spring注入模型:开发者必备知识

news2024/11/30 0:35:32

文章目录

  • 什么是注入模型
  • 注入模型的种类
  • 案例分析
    • 例子一
      • 创建Bean对象
      • 创建配置类
      • 重写后置处理器
      • 创建测试类
      • 执行测试方法
      • 分析
    • 例子二
      • 改写站点类,去掉@Autowired注解
      • 重写后置处理器
      • 执行测试方法
      • 分析
      • 思考

✨这里是第七人格的博客✨小七,欢迎您的到来~✨

🍅系列专栏:【Spring源码解析】🍅

✈️本篇内容: 浅谈Spring注入模型✈️

🍱本篇收录完整代码地址:https://gitee.com/diqirenge/spring-book/tree/master/injection-model🍱

什么是注入模型

首先我们要明确,注入模型和注入Bean的方式不能混为一谈。从Spring的官网我们可以知道,Spring注入bean的方式有两种,一种是通过构造方法进行注入,另外一种是通过setter方法进行注入。说简单一点就是注入Bean的方式是一种注入bean的策略,而注入模型是Bean的一种属性(其实是BeanDefinition的属性),他会影响bean的一些行为。

注入模型的种类

Spring的注入模型一共有四种,作为静态常量定义在AbstractBeanDefinition类当中

/**
 * Constant that indicates no external autowiring at all.
 * 手动注入,这也是默认的注入方式
 *
 * @see #setAutowireMode
 */
public static final int AUTOWIRE_NO = AutowireCapableBeanFactory.AUTOWIRE_NO;

/**
 * Constant that indicates autowiring bean properties by name.
 * 按名字自动注入
 *
 * @see #setAutowireMode
 */
public static final int AUTOWIRE_BY_NAME = AutowireCapableBeanFactory.AUTOWIRE_BY_NAME;

/**
 * Constant that indicates autowiring bean properties by type.
 * 按类型自动注入
 *
 * @see #setAutowireMode
 */
public static final int AUTOWIRE_BY_TYPE = AutowireCapableBeanFactory.AUTOWIRE_BY_TYPE;

/**
 * Constant that indicates autowiring a constructor.
 * 按构造方法自动注入
 *
 * @see #setAutowireMode
 */
public static final int AUTOWIRE_CONSTRUCTOR = AutowireCapableBeanFactory.AUTOWIRE_CONSTRUCTOR;

案例分析

想要获取一个Spring管理的对象,我们不少人会采用在属性上加@Autowired或者@Resource来实现,接下来,我们就先写一个使用@Autowired的最常见的例子。

例子一

创建Bean对象

地址

@Component
@Slf4j
public class Address {
 
    public void info(){
        log.info("小七个人博客地址:www.52javaee.com");
    }
}

站点

@Component
@Slf4j
public class Website {
    @Autowired
    private Address address;
 
    public Website(){
        log.info("执行了默认的无参的构造方法");
    }
 
    public Website(Address address){
        log.info("执行了有参的构造方法{}", address);
        this.address = address;
    }
 
    public void setAddress(Address address){
        log.info("执行了set方法{}", address);
        this.address = address;
    }
 
    public void showAddress(){
        this.address.info();
    }
}

创建配置类

@ComponentScan("org.example.model.case1")
public class MyBeanConfig {
}

重写后置处理器

@Slf4j
public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        AbstractBeanDefinition beanDefinition = (AbstractBeanDefinition) beanFactory.getBeanDefinition("website");
        log.info("Spring注入模型[mode]:{}",beanDefinition.getAutowireMode());
    }
}

为什么要重写后置处理器呢?因为我们可以通过这个方式,获取website这个beanDefinition在Spring容器中对应的autowireMode的值,并且可以修改这个autowireMode的值,来观察下注入模型的改变,对bean的注入方式有什么影响。

创建测试类

public class Main {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
        // 向上下文注册MyBeanConfig类,并且注册MyBeanFactoryPostProcessor类
        context.register(MyBeanConfig.class);
        context.register(MyBeanFactoryPostProcessor.class);
        // 刷新上下文
        context.refresh();
        // 获取Website实例
        Website website = context.getBean(Website.class);
        // 打印Website实例的地址
        website.showAddress();
    }
}

执行测试方法

请添加图片描述

分析

通过执行结果我们可以知道,在属性上面加@Autowired的方式,使用的是Spring默认的注入模型——手动注入。

例子二

改写站点类,去掉@Autowired注解

@Component
@Slf4j
public class Website {
    private Address address;
 
    public Website(){
        log.info("执行了默认的无参的构造方法");
    }
 
    public Website(Address address){
        log.info("执行了有参的构造方法{}", address);
        this.address = address;
    }
 
    public void setAddress(Address address){
        log.info("执行了set方法{}", address);
        this.address = address;
    }
 
    public void showAddress(){
        this.address.info();
    }
}

重写后置处理器

@Slf4j
public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        AbstractBeanDefinition beanDefinition = (AbstractBeanDefinition) beanFactory.getBeanDefinition("website");
//        // case2-1:默认不使用自动注入
//        beanDefinition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_NO);

//        // case2-2:使用自动注入(按名称/按类型)
//        beanDefinition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_NAME);
//        beanDefinition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);

//        // case2-3:使用自动注入(按构造方法)
//        beanDefinition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_CONSTRUCTOR);
        log.info("Spring注入模型[mode]:{}",beanDefinition.getAutowireMode());
    }
}

暂时什么都不改

执行测试方法

请添加图片描述

分析

观察以上结果,我们可以大胆的得出2个结论

  1. Spring默认的注入模型是0,符合我们在AbstractBeanDefinition看到的结果

  2. 注入模型是0,意味着执行默认的构造方法,并且不会执行set方法,所以我们的address对象是空的,因此

    this.address.info();

这行代码就抛出了空指针异常。

针对第一个结论,我们改变一下注入模型,再执行测试方法看看结果

1.1 在后置处理器添加以下代码:

 beanDefinition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_NAME);

执行结果正常了,并且调用了我们的set方法

在这里插入图片描述

1.2 再次修改注入模型为AUTOWIRE_BY_TYPE

beanDefinition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);

执行结果如下,也调用了set方法

在这里插入图片描述

1.3 再次修改注入模型为AUTOWIRE_CONSTRUCTOR

beanDefinition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_CONSTRUCTOR);

执行结果如下

在这里插入图片描述

不再调用set方法,而且跳过了无参构造方法,执行了有参构造方法,说明在注入模型为3时,Spring自动帮我们选择了正确的构造方法来注入(也就是说Spring会对使用哪个构造方法来注入进行推断)。

思考

回到第二个结论,如果我们不改变注入模型,直接去掉默认的构造方法,那么是什么结果呢?

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

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

相关文章

华为云云耀云服务器L实例评测|怎么搭建企业综合Web平台

前言 记得2019年,公司搞混合云的时候,测试过多家公有云,其中就有华为云。因公司也在深圳,项目也比较急,我司业务上云经验又不足,华为官方获悉情况后,第二天就派了4人小团队到我司来交流&#x…

金蝶云星空与金蝶云星空对接集成发货通知单查询打通发货通知单新增

金蝶云星空与金蝶云星空对接集成发货通知单查询打通发货通知单新增 数据源平台:金蝶云星空 金蝶K/3Cloud结合当今先进管理理论和数十万家国内客户最佳应用实践,面向事业部制、多地点、多工厂等运营协同与管控型企业及集团公司,提供一个通用的ERP服务平台…

加载动态库失败(loadLibrary返回为空 GetLastError126)解决办法 dll有依赖的dll缺失

问题:加载动态库失败(loadLibrary返回为空) 排除:64位也对。平台相同。 错误:至少找不到一个必需的隐式或转发依赖项。这个不影响。 SmartPay_PGL.dll下的四个dll,则是他所依赖的四个dll。因为我这里有缺失…

破天荒呀!小杜微信有名额了

写在前面 小杜粉,众所周知前面加小杜微信为好友的基本都是由名额限制的。一般都是付费进入社群且进行备注,小杜才会长期保留微信好友。主要由于,添加的人数太多了,微信账号人数名额有限。因此,小杜过一段时间&#xf…

在线会计软件推荐:高效实用的选择解析

如果您始终在密切关注Zoho,您一定知道,我们的软件在一个接一个的增加,为的是构建出一套可以全面在线协作、提升业务生产力的应用系统,我们始终致力于为各类企业构建完整的业务应用,以便他们在Zoho上运行整个业务系统。…

操作系统OS的发展历史和分类

1.手工操作阶段 使用纸带机,输入输出速度慢。但计算机速度快,导致计算机资源大部分时间处于闲置状态。主要缺点:用户独占全机、人机速度矛盾导致资源利用率极低。 2.批处理阶段 1.单道批处理系统 引入脱机输入/输出技术(用外围机磁带完成…

Polygon ID:不仅仅是生物识别的人格证明

1. 引言 2023年7月V神在博客 What do I think about biometric proof of personhood? 中指出: 其中的risks包括:不可避免的隐私泄露、人们匿名浏览互联网的能力进一步削弱、威权政府的胁迫,以及在去中心化的同时可能不安全。 Polygon ID致…

【vue】vue 中插槽的三种类型:

文章目录 一、匿名插槽&#xff1a;二、具名插槽&#xff1a;三、作用域插槽 一、匿名插槽&#xff1a;<slot></slot> 1.没有为插槽指定名称 2.通过slot标签可以添加匿名插槽 3.在使用组件的时候&#xff0c;组件中的内容会填充到所有匿名插槽的位置&#xff0c;所…

知识库网站如何搭建?需要注意这五个要点!

正因为知识库提供结构化知识库来记载信息和知识&#xff0c;便于团队沉淀经验、共享资源&#xff0c;形成完整的知识体系并持续进化​&#xff0c;使得它成为当前企业发展新宠。 构建自己/团队的知识库是一个良好的习惯&#xff0c;可以提高工作和学习效率&#xff0c;以下是一…

SpringMVC之JSON数据返回与异常处理机制

目录 一.SpringMVC的JSON数据返回 1.导入Maven依赖 2.配置spring-mvc.xml 3.ResponseBody注解的使用 3.1案例演示 1.List集合转JSON 2.Map集合转JSON 3.返回指定格式String 4. ResponseBody用法 5.Jackson 5.1介绍 5.2常用注解 二.异常处理机制 1.为什么要全局异常处…

$ref赋值之后,子组件不渲染(刷新后,$ref父组件传值,子组件不更新数据问题)

在父组件中&#xff0c;点击搜索&#xff0c; 通过this.$refs传值给子组件 this.$refs.GoodsClassNav.paramsAll.keyword key; 子组件结果中不显示&#xff0c; 但是打印this.$refs.GoodsClassNav.paramsAll.keyword&#xff0c;可以打印到最新的值&#xff0c;点击子组件中…

SpringCloud在idea中一键启动项目

1、如下图文件中加上&#xff1a; <component name"RunDashboard"><option name"configurationTypes"><set><option value"SpringBootApplicationConfigurationType" /></set></option></component>…

CS5817规格书|CS5817芯片参数|多功能便携式显示器方案芯片规格

CS5817支持最高4K 60Hz是集睿致远&#xff08;ASL&#xff09; 新推出的多功能显示控制器芯片&#xff0c;CS5817产品可应用于便携显示器、电竞显示器、桌面显示器、一体式台式机和嵌入式显示系统。 Type-C/DP/HDMI2.0输入转LVDS/eDP/VBO 芯片, 高度集成了多种输入输出接口, 并…

QtCharts详细介绍及其使用

QtCharts模块 QtCharts是Qt框架中的一个模块&#xff0c;用于创建各种图表和数据可视化。它提供了一组功能强大且易于使用的类&#xff0c;使开发人员能够轻松地在应用程序中添加各种交互式图表。 QtCharts模块支持多种常见类型的图表&#xff0c;包括折线图、柱状图、饼图、…

浅谈C++|STL之set篇

一.set 1.1set基本概念 特点&#xff1a; 所有元素在插入时&#xff0c;会自动排序&#xff0c;并且不能插入重复元素。 本质&#xff1a; set/multiset属于关联式容器&#xff0c;底层是红黑树。 set/multiset区别 1.set不允许容器中有重复的元素 2.multiset允许容器中有重复…

FFmpeg入门及编译

文章目录 前言一、FFmpeg 简介二、基本组成1、封装模块 - AVFormat2、编解码模块 - AVCodec3、滤镜模块 - AVFilter4、视频图像转换计算模块 - swscale5、音频转换计算模块 - swresample6、AVUtil - 核心工具库7、AVDevice - 硬件采集&#xff0c;加速&#xff0c;显示 三、命令…

教学必备工具

大家好&#xff0c;今天要给大家介绍一个超级方便的工具——易查分&#xff01;利用易查分&#xff0c;我们可以轻松制作一个便捷高效的作业查询系统&#xff0c;让作业查询变得简单又高效。下面就让我来为大家详细介绍一下易查分的使用教程吧&#xff01; 是不是想有个自己的分…

【Java】-【使用jxl操作excel】

文章目录 下载jxl包并引用基本使用多sheet页使用并与MySQL/Oracle数据库连接 报错excel文件读写报错&#xff1a;jxl.read.biff.BiffException: Unable to recognize OLE stream原因&#xff1a;文件版本不兼容&#xff0c;jxl只支持excecl03版解决办法 下载jxl包并引用 jxl.j…

【1++的C++进阶】之C++11(二)

&#x1f44d;作者主页&#xff1a;进击的1 &#x1f929; 专栏链接&#xff1a;【1的C进阶】 文章目录 一&#xff0c;类的新变化二&#xff0c;可变参数模板三&#xff0c;lambda表达式 一&#xff0c;类的新变化 在C03之前&#xff0c;我们的默认成员函数有6个&#xff0c;…

(2596. 检查骑士巡视方案leetcode,经典深搜)-------------------Java实现

&#xff08;2596. 检查骑士巡视方案leetcode,经典深搜&#xff09;-------------------Java实现 题目表述 骑士在一张 n x n 的棋盘上巡视。在 有效 的巡视方案中&#xff0c;骑士会从棋盘的 左上角 出发&#xff0c;并且访问棋盘上的每个格子 恰好一次 。 给你一个 n x n …