Spring官网阅读(三)自动注入

简介: Spring官网阅读(三)自动注入

前言:


在看下面的内容之前,我们先要对自动注入及精确注入有一个大概的了解,所谓精确注入就是指,我们通过构造函数或者setter方法指定了我们对象之间的依赖,也就是我们上篇文章中讲到的依赖注入,然后Spring根据我们指定的依赖关系,精确的给我们完成了注入。那么自动注入是什么?我们看下面一段代码:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/dbeans/spring-beans.xsd"
>
  <bean id="auto" class="com.dmz.official.service.AutoService" autowire="byType"/>
  <bean id="dmzService" class="com.dmz.official.service.DmzService"/>
</beans>
public class AutoService {
  DmzService service;
  public void setService(DmzService dmzService){
    System.out.println("注入dmzService"+dmzService);
    service = dmzService;
  }
}
public class DmzService {
}
public class Main03 {
  public static void main(String[] args) {
    ClassPathXmlApplicationContext cc =
        new ClassPathXmlApplicationContext("application.xml");
    System.out.println(cc.getBean("auto"));
  }
}

在上面的例子中我们可以看到

1.我们没有采用注解@Autowired进行注入

2.XML中没有指定属性标签<property>

3.没有使用构造函数

但是,打印结果如下:

注入dmzServicecom.dmz.official.service.DmzService@73a8dfcc  // 这里完成了注入
com.dmz.official.service.AutoService@1963006a

可能细心的同学已经发现了,在AutoService的标签中我们新增了一个属性autowire="byType",那么这个属性是什么意思呢?为什么加这个属性就能帮我们完成注入呢?不要急,我们带着问题继续往下看。


自动注入:


这部分内容主要涉及官网中的1.4.5小结。


我们先看官网上怎么说的:

微信图片_20221112152042.jpg

自动注入的优点:


大概翻译如下:


Spring可以自动注入互相协作的bean之间的依赖。自动注入有以下两个好处:


  • 自动注入能显著的减少我们指定属性或构造参数的必要。这个不难理解,我们在上篇文章中讲过了,依赖注入的两种方式,setter方法跟构造函数,见上篇文章依赖注入。在前言中的例子我们也能发现,我们并不需要指定属性或构造参数
  • 自动装配可以随着对象的演化更新配置。例如,如果需要向类添加依赖项,则可以自动满足该依赖项,而不需要修改配置。因此,自动装配在开发过程中特别有用,但是当我们的代码库变的稳定时,自动装配也不会影响我们将装配方式切换到精确注入(这个词是我根据官网阅读加自己理解翻译过来的,也就是官网中的(explicit wiring)


注入模型:


接下来,官网给我们介绍了自动注入的四种模型,如图:

微信图片_20221112152209.jpg


我们一一进行解析并测试:


  • no

这是目前Spring默认的注入模型,也可以说默认情况下Spring是关闭自动注入,必须要我们通过setter方法或者构造函数完成依赖注入,并且Spring也不推荐修改默认配置。我们使用IDEA时也可以看到


[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DZC5fi2F-1576598384639)(image/2019120204.jpg)]

用红线框出来的部分建议我们使用精确的方式注入依赖。


从上面来说,Spring自动注入这种方式在我们实际开发中基本上用不到,但是为了更好的理解跟学习Spring源码,我们也是需要好好学习这部分知识的。


  • byName

这种方式,我们为了让Spring完成自动注入需要提供两个条件

1.提供setter方法

2.如果需要注入的属性为xxx,那么setter方法命名必须是setXxx,也就是说,命名必须规范

在找不到对应名称的bean的情况下,Spring也不会报错,只是不会给我们完成注入。


测试代码:

//记得需要将配置信息修改为:<bean id="auto" class="com.dmz.official.service.AutoService"        autowire="byName"/>
public class AutoService {
  DmzService dmzService;
  /**
   *  setXXX,Spring会根据XXX到容器中找对应名称的bean,找到了就完成注入
   */
  public void setDmzService(DmzService dmzService){
    System.out.println("注入dmzService"+dmzService);
    service = dmzService;
  }
}

另外我在测试的时候发现,这种情况下,如果我们提供的参数不规范也不会完成注入的,如下:

public class AutoService {
  DmzService dmzService;
    // indexService也被Spring所管理
  IndexService indexService;
  /**
   * setXXX,Spring会根据XXX到容器中找对应名称的bean,找到了就完成注入
   */
  public void setDmzService(DmzService dmzService,IndexService indexService) {
    System.out.println("注入dmzService" + dmzService);
    this.dmzService = dmzService;
  }
}

本以为这种情况Spring会注入dmzService,indexService为null,实际测试过程中发现这个set方法根本不会被调用,说明Spring在选择方法时,还对参数进行了校验,byName这种注入模型下,参数只能是我们待注入的类型且只能有一个


  • byType

测试代码跟之前唯一不同的就是修改配置autowire="byType",这里我们测试以下三种异常情况


1.找不到合适类型的bean,发现不报异常,同时不进行注入

2.找到了多个合适类型的bean,Spring会直接报错Caused by: org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'com.dmz.official.service.DmzService' available: expected single matching bean but found 2: dmzService,dmzService2

3.set方法中有两个参数,切两个参数都能找到唯一一个类型符合的bean,不报异常,也不进行注入

另外需要说明的是,我在测试的过程,将set方法仅仅命名为set,像这样public void set(DmzService dmzService),这种情况下Spring也不会进行注入


我们可以发现,对于这两种注入模型都是依赖setter方法完成注入的,并且对setter方法命名有一定要求(只要我们平常遵从代码书写规范,一般也不会踩到这些坑)。第一,不能有多个参数;第二,不能仅仅命名为set


  • constructor

当我们使用这种注入模型时,Spring会根据构造函数查找有没有对应参数名称的bean,有的话完成注入(跟前文的byName差不多),如果根据名称没找到,那么它会再根据类型进行查找,如果根据类型还是没找到,就会报错。


自动注入的缺陷:


这里不得不说一句,Spring官网在这一章节有三分之二的内容是在说自定注入的缺陷以及如何将一个类从自动注入中排除,结合默认情况下自动注入是关闭的(默认注入模型为no),可以说明,在实际使用情况中,Spring是非常不推荐我们开启自动注入这种模型的。从官网中我们总结自动注入有以下几个缺陷:


  • 精确注入会覆盖自动注入。并且我们不能注入基本数据类型,字符串,Class类型(这些数据的数组也不行)。而且这是Spring故意这样设计的
  • 自动注入不如精确注入准确。而且我们在使用自动注入时,对象之间的依赖关系不明确
  • 对于一些为Spring容器生成文档的工具,无法获取依赖关系
  • 容器中的多个bean定义可能会与自动注入的setter方法或构造函数参数指定的类型匹配。对于数组、集合或映射实例,这可能不会产生什么问题。但是,对于期望单个值的依赖项,我们无法随意确定到底有谁进行注入。如果没有唯一的bean定义可用,则会抛出异常


如何将Bean从自动注入中排除?


这里主要用到autowire-candidate这个属性,我们要将其设置为false,这里需要注意以下几点:


1.这个设置只对类型注入生效。这也很好理解,例如我们告诉Spring要自动注入一个indexService,同时我们又在indexService的配置中将其从自动注入中排除,这就是自相矛盾的。所以在byName的注入模型下,Spring直接忽略了autowire-candidate这个属性

2.autowire-candidate=false这个属性代表的是,这个bean不作为候选bean注入到别的bean中,而不是说这个bean不能接受别的bean的注入。例如在我们上面的例子中我们对AutoService进行了如下配置:

  <bean id="auto" class="com.dmz.official.service.AutoService" autowire="byType" autowire-candidate="false"/>

代表的是这个bean不会被注入到别的bean中,但是dmzService任何会被注入到AutoService中

另外需要说明的是,对于自动注入,一般我们直接在顶级的标签中进行全局设置,如下:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"
    <!--在这里进行配置-->
 default-autowire="byName">

自动注入跟精确注入的比较总结:


连同上篇文章依赖注入,我画了下面一个图:

微信图片_20221112153408.jpg

  • 从关注的点上来看,自动注入是针对的整个对象,或者一整批对象。比如我们如果将autoService这个bean的注入模型设置为byName,Spring会为我们去寻找所有符合要求的名字(通过set方法)bean并注入到autoService中。而精确注入这种方式,是我们针对对象中的某个属性,比如我们在autoService中的dmzService这个属性字段上添加了@AutoWired注解,代表我们要精确的注入dmzService这个属性。而方法注入主要是基于方法对对象进行注入。
  • 我们通常所说***byName***,byType***跟我们在前文提到的注入模型中的byName,byType是完全不一样的。通常我们说的***byName,***byType***是Spring寻找bean的手段。比如,当我们注入模型为constructor时,Spring会先通过名称找对符合要求的bean,这种通过名称寻找对应的bean的方式我们可以称为byName。我们可以将一次注入分为两个阶段,首先是寻找符合要求的bean,其次再是将符合要求的bean注入。也可以画图如下:

微信图片_20221112153454.jpg


补充(1.4小结的剩余部分)


这部分比较简单,也是1.4小节中剩余的两个小知识,在这篇文章我们也一并学习了~


depends-on:


我们首先要知道,默认情况下,Spring在实例化容器中的对象时是按名称进行自然排序进行实例化的。比如我们现在有A,B,C三个对象,那么Spring在实例化时会按照A,B,C这样的顺序进行实例化。但是在某些情况下我们可能需要让B在A之前完成实例化,这个时候我们就需要使用depends-on这个属性了。我们可以通过形如下面的配置完成:

<bean id="a" class="xx.xx.A" depends-on="b"/>
<bean id="b" class="xx.xx.B" />

或者:

@Component
@DependsOn("b")
public class A {
}

lazy:


默认情况下,Spring会在容器启动阶段完成所有bean的实例化,以及一系列的生命周期回调。某些情况下,我们

可能需要让某一个bean延迟实例化。这种情况下,我们需要用到lazy属性,有以下两种方式:

1.XML中bean标签的lazy-init属性

<bean id="lazy" class="com.something.ExpensiveToCreateBean" lazy-init="true"/>

1.@Lazy注解

@Component
// 懒加载
@Lazy
public class A {
}

到此为止,官网中1.4小节中的内容我们就全学习完啦!最核心的部分应该就是上文中的这个图了。我们主要总结了Spring让对象产生依赖的方式,同时对各个方式进行了对比。通过这部分的学习,我觉得大家应该对Spring的依赖相关知识会更加系统,这样我们之后学习源码时碰到疑惑也会少很多。


下面我们还要继续学习Spring的官网,比如前面文章提到的Beandefinition到底是什么东西?Spring中的Bean的生命周期回调又是什么?这些在官网中都能找到答案。


相关文章
|
4月前
|
Java Spring
在使用Spring的`@Value`注解注入属性值时,有一些特殊字符需要注意
【10月更文挑战第9天】在使用Spring的`@Value`注解注入属性值时,需注意一些特殊字符的正确处理方法,包括空格、引号、反斜杠、新行、制表符、逗号、大括号、$、百分号及其他特殊字符。通过适当包裹或转义,确保这些字符能被正确解析和注入。
288 3
|
4月前
|
Java 测试技术 程序员
为什么Spring不推荐@Autowired用于字段注入?
作为Java程序员,Spring框架在日常开发中使用频繁,其依赖注入机制带来了极大的便利。然而,尽管@Autowired注解简化了依赖注入,Spring官方却不推荐在字段上使用它。本文将探讨字段注入的现状及其存在的问题,如难以进行单元测试、违反单一职责原则及易引发NPE等,并介绍为何Spring推荐构造器注入,包括增强代码可读性和维护性、方便单元测试以及避免NPE等问题。通过示例代码展示如何将字段注入重构为构造器注入,提高代码质量。
137 1
|
2月前
|
Java Spring
一键注入 Spring 成员变量,顺序编程
介绍了一款针对Spring框架开发的插件,旨在解决开发中频繁滚动查找成员变量注入位置的问题。通过一键操作(如Ctrl+1),该插件可自动在类顶部添加`@Autowired`注解及其成员变量声明,同时保持光标位置不变,有效提升开发效率和代码编写流畅度。适用于IntelliJ IDEA 2023及以上版本。
一键注入 Spring 成员变量,顺序编程
|
4月前
|
缓存 Java Spring
源码解读:Spring如何解决构造器注入的循环依赖?
本文详细探讨了Spring框架中的循环依赖问题,包括构造器注入和字段注入两种情况,并重点分析了构造器注入循环依赖的解决方案。文章通过具体示例展示了循环依赖的错误信息及常见场景,提出了三种解决方法:重构代码、使用字段依赖注入以及使用`@Lazy`注解。其中,`@Lazy`注解通过延迟初始化和动态代理机制有效解决了循环依赖问题。作者建议优先使用`@Lazy`注解,并提供了详细的源码解析和调试截图,帮助读者深入理解其实现机制。
123 1
|
6月前
|
缓存 Java 数据库连接
Spring Boot 资源文件属性配置,紧跟技术热点,为你的应用注入灵动活力!
【8月更文挑战第29天】在Spring Boot开发中,资源文件属性配置至关重要,它让开发者能灵活定制应用行为而不改动代码,极大提升了可维护性和扩展性。Spring Boot支持多种配置文件类型,如`application.properties`和`application.yml`,分别位于项目的resources目录下。`.properties`文件采用键值对形式,而`yml`文件则具有更清晰的层次结构,适合复杂配置。此外,Spring Boot还支持占位符引用和其他外部来源的属性值,便于不同环境下覆盖默认配置。通过合理配置,应用能快速适应各种环境与需求变化。
69 0
|
27天前
|
XML Java 应用服务中间件
Spring Boot 两种部署到服务器的方式
本文介绍了Spring Boot项目的两种部署方式:jar包和war包。Jar包方式使用内置Tomcat,只需配置JDK 1.8及以上环境,通过`nohup java -jar`命令后台运行,并开放服务器端口即可访问。War包则需将项目打包后放入外部Tomcat的webapps目录,修改启动类继承`SpringBootServletInitializer`并调整pom.xml中的打包类型为war,最后启动Tomcat访问应用。两者各有优劣,jar包更简单便捷,而war包适合传统部署场景。需要注意的是,war包部署时,内置Tomcat的端口配置不会生效。
208 17
Spring Boot 两种部署到服务器的方式
|
27天前
|
Dart 前端开发 JavaScript
springboot自动配置原理
Spring Boot 自动配置原理:通过 `@EnableAutoConfiguration` 开启自动配置,扫描 `META-INF/spring.factories` 下的配置类,省去手动编写配置文件。使用 `@ConditionalXXX` 注解判断配置类是否生效,导入对应的 starter 后自动配置生效。通过 `@EnableConfigurationProperties` 加载配置属性,默认值与配置文件中的值结合使用。总结来说,Spring Boot 通过这些机制简化了开发配置流程,提升了开发效率。
61 17
springboot自动配置原理
|
1月前
|
XML JavaScript Java
SpringBoot集成Shiro权限+Jwt认证
本文主要描述如何快速基于SpringBoot 2.5.X版本集成Shiro+JWT框架,让大家快速实现无状态登陆和接口权限认证主体框架,具体业务细节未实现,大家按照实际项目补充。
80 11
|
1月前
|
缓存 安全 Java
Spring Boot 3 集成 Spring Security + JWT
本文详细介绍了如何使用Spring Boot 3和Spring Security集成JWT,实现前后端分离的安全认证概述了从入门到引入数据库,再到使用JWT的完整流程。列举了项目中用到的关键依赖,如MyBatis-Plus、Hutool等。简要提及了系统配置表、部门表、字典表等表结构。使用Hutool-jwt工具类进行JWT校验。配置忽略路径、禁用CSRF、添加JWT校验过滤器等。实现登录接口,返回token等信息。
349 12
|
2月前
|
Java 数据库连接 Maven
最新版 | 深入剖析SpringBoot3源码——分析自动装配原理(面试常考)
自动装配是现在面试中常考的一道面试题。本文基于最新的 SpringBoot 3.3.3 版本的源码来分析自动装配的原理,并在文未说明了SpringBoot2和SpringBoot3的自动装配源码中区别,以及面试回答的拿分核心话术。
最新版 | 深入剖析SpringBoot3源码——分析自动装配原理(面试常考)