spring的自动注入

Spring自动注入

spring的ioc

在刚开始学习spring的时候肯定都知道spring的两个特点:ioc,aop,控制反转和切面编程,这篇就只说说ioc

ioc是什么:在我们原来的代码中,如果A依赖了B,那么我们会自己在A类中来new B,创建B的实例来使用,是程序主动的去创建依赖,但是我们在使用spring的了之后还会在A中主动的去创建B吗?基本不会,因为创建对象的这个操作从原来的我们来控制变成了spring来管理,这个过程就称为控制反转,ioc并不是一种技术,而是一种编程思想,代码的设计思路

那么这样做的好处是什么?

  • 解耦,将对象之间的依赖关系交给spring来处理,避免硬编码导致高度耦合
  • 资源的集中易于管理和配置

而spring实现ioc使用的方法是通过DI(依赖注入),当我们大部分的对象都被spring管理后那么spring也需要将我们A中所依赖的B,C,D…都给填充到A中,这个过程是由spring来管理的,我们只需要按照spring的规则声明或指定,那么spring也会帮我们完成依赖的注入

自动注入

了解完spring的基本思想后,来回想一下当时学习spring的入门,有没有说过spring的一个特点就是可以自动注入?

@Component
public class A   {
    @Autowired
    private B b;
}
@Component
public class B {
}

简单的一段代码,将A,B交给spring管理,在A中属性b上添加一个 @Autowired,当我们从spring的容器中取出A后发现属性b居然有值,这个不就是自动注入吗?

我是认为添加@Autowired注解是不算自动注入的,原因如下

1.名词解释

首先,什么叫自动(给翻译翻译什么叫tm的自动),生活中肯定都见过自动门,通过过自动门,当我们靠近的时候门会自动打开,通过后门自动关闭,自动就是指我们不需要手动的去开门/关门,那么这里的自动注入也是,我们不需要去手动的在需要注入的属性上添加一个@Autowired注解

2.官方文档

https://docs.spring.io/spring-framework/docs/current/reference/html/

在官方文档的注入方式中,能看到

Dependency injection (DI) is a process whereby objects define their dependencies (that is, the other objects with which they work) only through constructor arguments, arguments to a factory method, or properties that are set on the object instance after it is constructed or returned from a factory method. The container then injects those dependencies when it creates the bean. This process is fundamentally the inverse (hence the name, Inversion of Control) of the bean itself controlling the instantiation or location of its dependencies on its own by using direct construction of classes or the Service Locator pattern.

Code is cleaner with the DI principle, and decoupling is more effective when objects are provided with their dependencies. The object does not look up its dependencies and does not know the location or class of the dependencies. As a result, your classes become easier to test, particularly when the dependencies are on interfaces or abstract base classes, which allow for stub or mock implementations to be used in unit tests.

DI exists in two major variants: Constructor-based dependency injection and Setter-based dependency injection.

翻译就是

依赖项注入(DI)是一个过程,对象仅通过构造函数参数、工厂方法的参数或对象实例构造或从工厂方法返回后设置的属性来定义其依赖项(即与它们一起工作的其他对象)。然后,容器在创建bean时注入这些依赖项。这个过程基本上是bean本身的逆过程(因此称为控制反转),通过使用类的直接构造或服务定位器模式来控制其依赖项的实例化或位置。

使用DI原则,代码更干净,当对象具有依赖关系时,解耦更有效。对象不查找其依赖项,也不知道依赖项的位置或类别。因此,您的类变得更容易测试,尤其是当依赖项位于接口或抽象基类上时,这允许在单元测试中使用存根或模拟实现。

DI有两种主要变体:基于构造函数的依赖项注入和基于Setter的依赖项注入。

注意最后一段话:有两种 主要的变体:基于构造和setter方法的依赖注入,也就是说还有其他的注入方式,下面再细说

那么你可能会问?我需要怎么指定或声明让他去使用构造或setter方法进行注入而不是我手动指定@Autowired呢?

自动注入模式

还是spring的官网,网页翻译的

spring的自动注入

可以看到spring的自动装配模式有下面的4种,而默认的是no,也就是不自动注入

那么会发现自动注入以及DI的主要实现这里面并没有所说的 “@Autowired”

那么我们怎么使用自动注入呢?

在xml配置中,只需要将头定义中加上一句

default-autowire=”byType”


而在javaconfig中要麻烦一点,我们需要自定义一个配置类,实现 BeanFactoryPostProcessorBeanDefinitionRegisterPostProcessor来修改beanDefinition的注入模型

@Component
public class BeanFactoryPostProcessor implements BeanFactoryPostProcessor {

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        ScannedGenericBeanDefinition a =(ScannedGenericBeanDefinition) beanFactory.getBeanDefinition("a");
        a.setAutowireMode(2);
    }
}

这个2是啥意思呢,在接口 AutowireCapableBeanFactory中,spring定义了每个注入模型的值

public interface AutowireCapableBeanFactory extends BeanFactory {
    int AUTOWIRE_NO = 0;

    int AUTOWIRE_BY_NAME = 1;

    int AUTOWIRE_BY_TYPE = 2;

    int AUTOWIRE_CONSTRUCTOR = 3;
    @Deprecated
    int AUTOWIRE_AUTODETECT = 4;
    ....

}

那么当我们设置了byName或byType后,只需要提供一个setter方法即可,那么只要这个属性名称的bean存在于spring容器中就会被注入

public interface Test {
}
@Component
public class TestA implements Test {
}
@Component
public class A {

    @Autowired
    private Test testA;

    public void setTestA(Test testA) {
        System.out.println("走set方法啦");
        this.testA = testA;
    }

    public A() {
        System.out.println("走无参构造方法啦");
    }

    public A(Test testA) {
        System.out.println("走有参构造方法啦");
        this.testA = testA;
    }

    @Override
    public String toString() {
        return "A{" +
                "testA=" + testA +
                '}';
    }
}

结果:

走无参构造方法啦
走set方法啦
A{testA=com.jame.pojo.test.TestA@721e0f4f}

当我设置为byType后结果一样,就不再粘贴代码了

而当我设置为根据构造注入后,将注入的模型改成3

@Component
public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        ScannedGenericBeanDefinition a =(ScannedGenericBeanDefinition) beanFactory.getBeanDefinition("a");
        a.setAutowireMode(3);
    }
}

结果

走有参构造方法啦
A{testA=com.jame.pojo.test.TestA@28864e92}

那么现在你会明白了所谓的@Autowired并不是自动注入,只要指定了这个bean的注入模型为byType/byName/构造后才是自动注入

@Autowired

@Autowired是使用setter注入是构造注入呢?是使用byName还是byType呢?

回答第一个问题:@Autowired是使用setter注入是构造注入呢?

将我的配置类 BeanFactoryPostProcessor上的@Component注解去掉,然后在Test testA添加@Autowired

@Component
public class A {

    @Autowired
    private Test testA;

    //下面的代码和上面粘贴出的的代码一样

}

结果

走无参构造方法啦
A{testA=com.jame.pojo.test.TestA@546a03af}

??什么情况,setter和构造都没走,因为@Autowired底层使用的反射filed.set()来填充的属性

DI有两种主要变体:基于构造函数的依赖项注入和基于Setter的依赖项注入

主要的是使用构造和setter,而其他的说的就是这种@Autowired的注入方式

第二个问题:是使用byName还是byType呢?

答案是两个都是,或者两个都不是,来看例子

public interface Test {
}
@Component
public class TestA implements Test {
}
@Component
public class A {
    @Autowired
    private Test test;//注意这里的属性为test

    ....省略
}

这样写然后从spring容器中获取肯定能获取到结果,A中的testA属性肯定有值的,就不演示了

但是,我给Test接口添加一个实现类TestB

@Component
public class TestB implements Test{

}

继续执行代码,发现报错了

Error creating bean with name 'a': Unsatisfied dependency expressed through field 'test'; nested exception is org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'com.jame.pojo.test.Test' available: expected single matching bean but found 2: testA,testB
创建名称为"a"的 bean 时出错:通过字段"test"表示的依赖关系不满足;嵌套异常是 org.springframework.beans.factory.NoUniqueBeanDefinitionException:没有"com.jame.pojo.test.Test"类型的合格 bean 可用:预期单个匹配 bean,但找到 2:testA,testB

到这里是不是就可以说明@Autowired是byType呢?,那怎么证明它也是byName呢?

修改A中的Test test属性为testA时

@Component
public class A {

    @Autowired
    private Test testA;//注意这里的属性为testA

    .....省略
}

继续执行代码发现又可以了

走无参构造方法啦
A{testA=com.jame.pojo.test.TestA@28864e92}

我们把属性改为Test testB后继续测试

走无参构造方法啦
A{testB=com.jame.pojo.test.TestB@28864e92}

发现也是可以的,那么结论就是:@Autowired既是byType也是byName

当Spring容器中只有一个匹配的类时,会根据Type直接注入,而存在多个匹配的时候(接收的属性定义为接口,有多个实现类),会直接抛出异常

这时候我们可以使用@Qualifier(“testA”)来指定具体哪一个类来注入

或者修改属性的名字为需要注入类的名称(首字母小写)

测试了一下@Qualifier()是高于byName的,也就是说即使存在两个匹配的类,即使属性名叫testB,我只要使用@Qualifier(“testA”)来指定bean,那么注入的就是testA

芜湖没了,拜拜

Original: https://www.cnblogs.com/sunankang/p/16226549.html
Author: Jame!
Title: spring的自动注入

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

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

(0)

大家都在看

  • [Mysql]null与真值

    SQL的逻辑运算使用的是三值逻辑,逻辑表达式的计算结果有三种可能, true, false, unknown 比较运算的表达式含 NULL时会产生 unknown结果,例如 SEL…

    数据库 2023年6月16日
    081
  • tomcat线程池

    tomcat线程池和普通的线程池设计上有所区别,下面主要来看看它是如何设计的 tomcat中线程池的创建 <span>org.apache.tomcat.util.ne…

    数据库 2023年6月16日
    087
  • Python open函数详解

    演示环境,操作系统:Win10 21H2(64bit);Python解释器:3.8.10。 open是Python的一个内置函数,一般用于本地文件的读写操作。用法如下。 1 my_…

    数据库 2023年6月11日
    0112
  • CMU 15-445 Project 0 实现字典树

    原文链接:https://juejin.cn/post/7139572163371073543 项目准备 代码、手册 本文对应 2022 年的课程,Project 0 已经更新为实…

    数据库 2023年6月14日
    097
  • MurmurHash

    高运算性能,低碰撞率的hash算法 redis已经使用了。spring导入redis有这个类,可以体验一下package redis.clients.util; 引用redis后直…

    数据库 2023年6月9日
    090
  • login方法访问不到解决过程

    背景:由于项目登录模块之前使用传统的字符验证码,干扰又太严重,经常会有输入十次以上才能蒙对的情况。于是提出让改为滑动验证码(斗鱼,B站等等)。如图所示: 原有的: 要改的: 这个实…

    数据库 2023年6月11日
    0134
  • Windows界面个人常用快捷键

    分享一下个人常用快捷键。 说明:字母排序规则遵循字母表(a->z) 快捷键 介绍 windows+d 由当前应用直接返回桌面,再按一次回到应用 windows+e 打开文件资…

    数据库 2023年6月14日
    0101
  • mysql常用操作汇总

    工作中经常用会遇到这种情况,可以访问mysql所在的服务器,但是服务器端口不对外暴露(通常因为安全原因)。这时,操作数据库只能通过命令行和 mysql client窗口来实现。我对…

    数据库 2023年5月24日
    076
  • MySQL主从备库过滤参数分析和测试

    测试环境: GTID的主从复制,主库(9900)——》备库(9909),存在测试库表: 9900_db1库:t1、t2、t3、t4、t5表 9900_db2库:t6、t7、t8、t…

    数据库 2023年5月24日
    063
  • MySQL实战45讲 19

    19 | 为什么我只查一行的语句,也执行这么慢? 有些情况下,”查一行”,也会执行得特别慢。 需要说明的是,如果 MySQL 数据库本身就有很大的压力,导致…

    数据库 2023年6月14日
    070
  • Spring 学习笔记

    Spring 框架是由于软件开发的复杂性而创建的。Spring 使用的是基本的 JavaBean 来完成以前只可能由 EJB 完成的事情。 Spring 一. Spring Fra…

    数据库 2023年6月11日
    080
  • PHP最全编码规约

    1.1 标签 (1)【强制】PHP 程序可以使用或来界定 PHP 代码,在 HTML 页面中嵌入纯变量时,可以使用这样的形式,不可使用其他的标签变种。 正例: (2)【强制】纯 P…

    数据库 2023年6月14日
    0113
  • postman操作数据库

    一、安装node.js 二、配置环境变量 三、安装xmysql,连接数据库 四、postman操作数据库 功能说明 node.js:是一个javascript运行环境,优点:单线程…

    数据库 2023年6月14日
    0109
  • 设计模式之(1)——简单工厂模式

    创建型模式:主要用于对象的创建; 结构型模式:用于处理类或者对象的组合; 行为型模式:用于描述类或对象怎样交互和分配职责; ————————————————————————————…

    数据库 2023年6月14日
    096
  • Python第二十二天 stat模块 os.chmod方法 os.stat方法 pwd grp模块 os.access()方法

    Python第二十二天 stat模块 os.chmod方法 os.stat方法 pwd grp模块 os.access()方法 stat模块描述了os.stat(filename)…

    数据库 2023年6月9日
    069
  • 算法-贪心思想

    算法-贪心思想 庭前看玉树,肠断忆连枝 一、剪绳子 1、题目描述 把一根绳子剪成多段,并且使得每段的长度乘积最大。 2、解题思路 尽可能得多剪长度为 3 的绳子,并且不允许有长度为…

    数据库 2023年6月14日
    0145
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球