Spring系列4:依赖注入的2种方式

定义2个简单的bean类,BeanOne 和 BeanTwo,前者依赖后者。

package com.crab.spring.ioc.demo02;

public class BeanTwo {
}
package com.crab.spring.ioc.demo02;

/**
 * @author zfd
 * @version v1.0
 * @date 2022/1/12 16:59
 */
public class BeanOne {
    private int age;
    private String name;
    private BeanTwo beanTwo;

    /**
     * 构造函数,用于依赖注入,定义3个依赖
     * @param age
     * @param name
     * @param beanTwo
     */
    public BeanOne(int age, String name, BeanTwo beanTwo) {
        this.age = age;
        this.name = name;
        this.beanTwo = beanTwo;
    }

    @Override
    public String toString() {
        return "BeanOne{" +
                "age=" + age +
                ", name='" + name + '\'' +
                ", beanTwo=" + beanTwo +
                '}';
    }
}

通过xml配置文件实现bean定义和依赖注入


来个测试类验证下注入

package com.crab.spring.ioc.demo02;
/**
 * @author zfd
 * @version v1.0
 * @date 2022/1/12 17:09
 */
public class demo02Test {

    @Test
    public void test_construct() {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("demo02/spring1.xml");
        BeanOne bean1 = context.getBean("bean1", BeanOne.class);
        System.out.println(bean1);
        context.close();
    }

输出如下

BeanOne{age=20, name='xxx', beanTwo=com.crab.spring.ioc.demo02.BeanTwo@5204062d}

对照配置文件BeanOne的3个依赖都通过构造器的方式进行注入了,符合预期,很简单。

标签 constructor-arg支持的元素列表如下。

元素名 作用 name 参数名 index 参数索引,0开始 type 参数类型 value 值 ref bean引用

例如案例中的配置


注意: 在 没有引起歧义的情况下,上面的部分元素并不是都必须配置的。如指定了 index时可以定位参数位置,那么 name是可以不配置的,又如通过 ref引用依赖bean, type可以省略


当在 <constructor></constructor>使用 name元素指定构造函数中的参数名时,尤其要方法参数名编译后是否保留的情况。举个例子

// 编译前的方法参数
public BeanOne(int age, String name, BeanTwo beanTwo)
// 编译后的方法参数
public BeanOne(int var1, String var2, BeanTwo var3)

编译后方法参数被编译成 var1这种形式会导致仅指定 name的构造注入失效。

如何解决?提供2种方法

基于 Setter 的 DI 是通过容器在调用无参数构造函数或无参数静态工厂方法来实例化 bean 后调用 bean 上的 setter 方法来完成的。

来一个简单类 BeanThree依赖 BeanOneBeanTwo并提供了 Setter方法来设置。

package com.crab.spring.ioc.demo02;

/**
 * @author zfd
 * @version v1.0
 * @date 2022/1/13 8:18
 */
public class BeanThree {
    private BeanTwo beanTwo;
    private BeanOne beanOne;

    public void setBeanTwo(BeanTwo beanTwo) {
        this.beanTwo = beanTwo;
    }

    public void setBeanOne(BeanOne beanOne) {
        this.beanOne = beanOne;
    }
}

对应的配置文件可以通过标签 property中的 refname来设置属性引用或是属性值


运行测试类和结果,可见依赖注入成功

public class demo02Test {

    @Test
    public void test_construct() {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("demo02/spring1.xml");
        BeanOne bean1 = context.getBean("bean1", BeanOne.class);
        System.out.println(bean1);

        System.out.println("演示Setter注入");
        BeanThree beanThree = context.getBean(BeanThree.class);
        System.out.println(beanThree);
        context.close();
    }
}
BeanOne{age=20, name='xxx', beanTwo=com.crab.spring.ioc.demo02.BeanTwo@68ceda24}
演示Setter注入
BeanThree{name='xxxx', beanTwo=com.crab.spring.ioc.demo02.BeanTwo@68ceda24, beanOne=BeanOne{age=20, name='xxx', beanTwo=com.crab.spring.ioc.demo02.BeanTwo@68ceda24}}
  • ApplicationContext是用描述所有bean的配置元数据创建和初始化的。配置元数据可以通过XML、Java代码或注释指定。
  • 对于每个bean,它的依赖项都以属性、构造函数参数或静态工厂方法的参数的形式表示(如果使用静态工厂方法而不是普通构造函数的话)。当bean实际创建时,这些依赖项被提供给bean。
  • 每个属性或构造函数参数都是要设置的值的实际定义,或者是对容器中另一个bean的引用。
  • 每个具有值的属性或构造函数参数都将从其指定的格式转换为该属性或构造函数参数的实际类型。默认情况下,Spring可以将字符串格式提供的值转换为所有内置类型,如int、long、string、boolean等。

什么是循环依赖,如何处理?
如果主要使用构造函数注入,则可能会创建一个不可解析的循环依赖场景。
例如:类A需要类B的一个实例通过构造函数注入,类B需要类A的一个实例通过构造函数注入。如果将类A和类B的bean配置为相互注入,Spring IoC容器会在运行时检测此循环引用,并抛出 BeanCurrentlyInCreationException
一个可能的解决方案是编辑一些由setter而不是构造函数配置的类的源代码。或者,避免构造函数注入,只使用setter注入。换句话说,可以使用setter注入配置循环依赖项。
与典型情况(没有循环依赖关系)不同,bean A 和 bean B 之间的循环依赖关系强制其中一个 bean 在完全初始化之前注入另一个 bean(典型的先有鸡还是先有蛋的场景)。

选择用构造器注入还是Setter注入?

Spring官方的推荐,构造函数用于强制依赖项,将 setter 方法或配置方法用于可选依赖项。请注意,在 setter 方法上使用 @Required 注释可用于使属性成为必需的依赖项;然而,带有参数的编程验证的构造函数注入是更可取的。

Spring 团队通常提倡构造函数注入,因为它允许您将应用程序组件实现为不可变对象,并确保所需的依赖项不为空。此外,构造函数注入的组件总是以完全初始化的状态返回给客户端(调用)代码。作为旁注,大量的构造函数参数是一种不好的代码气味,这意味着该类可能有太多的职责,应该重构以更好地解决适当的关注点分离。 Setter 注入应该主要只用于可以在类中分配合理默认值的可选依赖项。否则,必须在代码使用依赖项的任何地方执行非空检查。 setter 注入的一个好处是 setter 方法使该类的对象可以在以后重新配置或重新注入。

有时,在处理没有源代码的第三方类时,如果第三方类没有公开任何 setter 方法,那么构造函数注入可能是 DI 的唯一可用形式。

本文演示2种依赖注入的方式:构造函数注入和Setter方法注入,并对比如何选择这2种方式。下一篇继续深入依赖注入。

知识分享,转载请注明出处。学无先后,达者为先!

Original: https://www.cnblogs.com/kongbubihai/p/15824985.html
Author: kongxubihai
Title: Spring系列4:依赖注入的2种方式

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/572068/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • 用反向代理nginx proxy_pass配置解决ie8 ajax请求被拦截问题 ie8用nginx代理实现跨域请求访问 nginx405正向代理request_uri

    最近调PC版网站ie8的兼容性,发现所有ajax请求还没到后端服务器就直接ajax error了 ie8发不出ajax请求,断点调试发现ajax全进入了error,提示&#8221…

    Java 2023年5月30日
    074
  • Android-Java-接口Interface

    接口Interface 与 抽象类不同: 抽象类关注的是事物本质,例如:水果Fruit 属于抽象的,说去买水果 是模糊的概念 是抽象的概念 不具体,到底买什么水果不知道,而水果包含…

    Java 2023年5月29日
    069
  • Spring boot 注意问题 !!

    1,lombok在pom文件中需要引入正确版本,否则会导致maven的complier无法正常导入!!, 当前正确配置: java;gutter:true;org.projectl…

    Java 2023年5月30日
    068
  • @TransactionalEventListener 之Spring的事务监听器

    业务场景: 有时候需要一个动作之后触发另外一个动作,类似消息的机制。但使用kafka等又太重。 这时候可以使用Spring的事件来处理。比如我这有一个需要触发一个异步任务的业务场景…

    Java 2023年5月29日
    064
  • Spring Boot Cache使用与整合

    Spring 提供了对缓存功能的抽象:即允许绑定不同的缓存解决方案(如Caffeine、Ehcache等),但本身不直接提供缓存功能的实现。它支持注解方式使用缓存,非常方便。 Sp…

    Java 2023年5月30日
    075
  • ReadWriteLock读写锁

    ReadWriteLock读写锁 概念: 读写锁:存在着两个锁,一个 &#x8BFB;&#x9501;&#xFF08;&#x5171;&#x…

    Java 2023年6月5日
    072
  • Java基础 三目运算符 用if-else对其进行解释

    JDK :OpenJDK-11 OS :CentOS 7.6.1810 IDE :Eclipse 2019‑03 typesetting :Markdown code packag…

    Java 2023年5月29日
    0129
  • SpringBoot与Thymeleaf入门级操作

    使用Thymeleaf 三大理由: 简洁漂亮 容易理解 完美支持HTML5 使用浏览器直接打开页面 不新增标签 只需增强属性 学习目标 快速掌握Thymeleaf的基本使用:五大基…

    Java 2023年6月16日
    089
  • day03_1_idea教程

    idea使用教程 一、idea相关概念介绍 1.1 IDE概念介绍 集成开发环境(IDE,Integrated Development Environment)是用于提供程序开发环…

    Java 2023年6月8日
    055
  • 【转】【数学】判断一个点是否在一个多边形里面

    “判断一个点是否在一个多边形里”,一开始以为是个挺难的问题,但Google了一下之后发现其实蛮简单,所用到的算法叫做”Ray-casting A…

    Java 2023年5月29日
    091
  • [Java编程思想] 第六章 访问权限控制

    6.1 包:库单元 包内含有一组类,它们在单一的名字空间之下被组织在了一起。 当编写一个Java源代码文件时,此文件通常被称为编译单元。每个编译单元都必须有一个后缀名.java,而…

    Java 2023年6月5日
    063
  • 【设计模式】Java设计模式-享元模式

    Java设计模式 – 享元模式 😄 不断学习才是王道🔥 继续踏上学习之路,学之分享笔记👊 总有一天我也能像各位大佬一样🏆原创作品,更多关注我CSDN: 一个有梦有戏的人…

    Java 2023年6月16日
    095
  • SPI使用

    Dubbo 的可扩展性是基于 SPI 去实现的,而且Dubbo所有的组件都是通过 SPI 机制加载。 SPI 全称为 (Service Provider Interface) ,是…

    Java 2023年6月16日
    050
  • ActiveMq 之JMS 看这一篇就够了

    什么是JMS MQ 全称:Java MessageService 中文:Java 消息服务。 JMS 是 Java 的一套 API 标准,最初的目的是为了使应用程序能够访问现有的 …

    Java 2023年6月7日
    059
  • RabbitMQ——消息确认机制(手动/自动)

    非常好理解的推文 报错: Only one ConfirmCallback is supported by each RabbitTemplate 解决方法: Original: …

    Java 2023年5月30日
    077
  • 继承扩展DataGrid的editors

    @author YHC 一些常见的editors 添加到datagrid以便用户编辑数据,所有的editor都定义在$.fn.datagrid.defaults.editors对象…

    Java 2023年5月29日
    058
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球