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

点击上方蓝字专一我吧!程序员



上篇文章咱们已经学习了1.4小结中关于依赖注入跟方法注入的内容。这篇文章咱们继续学习这结中的其余内容,顺便解决下咱们上篇文章留下来的一个问题-----注入模型web

前言:

在看下面的内容以前,咱们先要对自动注入及精确注入有一个大概的了解,所谓精确注入就是指,咱们经过构造函数或者setter方法指定了咱们对象之间的依赖,也就是咱们上篇文章中讲到的依赖注入,而后Spring根据咱们指定的依赖关系,精确的给咱们完成了注入。那么自动注入是什么?咱们看下面一段代码: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",那么这个属性是什么意思呢?为何加这个属性就能帮咱们完成注入呢?不要急,咱们带着问题继续往下看。app

自动注入:

这部份内容主要涉及官网中的1.4.5小结。编辑器

咱们先看官网上怎么说的:函数


自动注入的优势:

大概翻译以下:工具

Spring能够自动注入互相协做的bean之间的依赖。自动注入有如下两个好处:学习

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

注入模型:

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


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

  • no

这是目前Spring默认的注入模型,也能够说默认状况下Spring是关闭自动注入,必需要咱们经过setter方法或者构造函数完成依赖注入,而且Spring也不推荐修改默认配置。咱们使用IDEA时也能够看到

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

从上面来讲,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">

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

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

在这里插入图片描述
  • 从关注的点上来看, 自动注入是针对的整个对象,或者一整批对象。好比咱们若是将 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注入。也能够画图以下:
在这里插入图片描述

补充(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的生命周期回调又是什么?这些在官网中都能找到答案。

给本身加油,也给全部看到这篇文章的同窗加油~!!

您点的每一个赞,我都认真当成了喜欢




本文分享自微信公众号 - 程序员DMZ(programerDmz)。
若有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一块儿分享。

相关文章
相关标签/搜索