初识Spring —— Bean的装配(二)

这篇文章来自我的博客

正文之前

上一篇文章已经介绍了 Bean 的基本概念,所以今天这篇文章是基于上一篇中 Bean 的三种装配方式来进一步探讨,内容中会穿插着基础的依赖注入的概念

主要内容

  1. 自动装配
  2. 使用 XML 装配
  3. 使用 Java 装配

正文

1. 自动装配

  • Bean 的命名

上一篇文章中使用注解定义 Bean 的时候都是在类的定义上加注解,小写的类名作为 Bean 的 ID,但是我们可以自己命名 Bean:

设置组件的值为 Bean 的名字,如果这么做了,在测试中就找不到名为“student”的 Bean 了:

将测试中获取的 Bean 的名字改为“myBean”之后就能通过测试了

  • 组件扫描

上次讲到了组件扫描,设置组件扫描的方式有两种:

  1. XML 文件中配置
  2. Java 配置类中添加注解

我们再还原 student 这个 Bean 的状态,也就是在类的定义上加一个注解 @Component

在配置文件中添加组建扫描:

这个是上次讲过的,就不再多说,然后来看看 Java 配置类的组件扫描:

添加的注解 @Component 带一个参数,指定了扫描的包的位置,就相当于 XML 配置文件中的 base-package 属性

如果不填写参数,就扫描此文件所在的包,在这里,我们扫描名为“bean”的包,点击注解左边的那个按钮就能跳转到 Student 类(bean 包中)

  • 进阶自动装配

此处引用《Spring实战》的一句话:

简单来说,自动装配就是让 Spring 自动满足 Bean 依赖的一种方法,在满足依赖的过程中,会在 Spring 应用上下文中寻找匹配某个 Bean 需求的其他 Bean

我们这里添加一个接口 Book,至于为什么用接口而不用实现类,因为我们后文要对这个接口做多个实现:

然后创建一个实现了 Book 接口的类,并且将其作为一个 Bean:

既然是自动装配,肯定要用到注解 @Autowired,那么,这个注解可以用在什么地方呢?

  • 实例化
  • 构造器(构造器注入)
  • Setter 方法(设值注入)

第一点之前说过,在实例化时候添加这个注解进行自动装配

怎么在构造器上使用呢?

我们修改一下 Student 类:

在构造 Student 的时候,我将一个 Book 类注入,因为我们刚才已经有了 Book 类的实现,所以这里注入的是 englishBook 这个 Bean

需要注意的是:Spring 在创建 student 这个 Bean 的时候,会传入一个 Book 类型的 Bean

有两种情况会导致抛出异常,没有所匹配的 Bean 或有多个所匹配的 Bean

为了验证第一种情况,我将 EnglishBook 的 @Component 注解去掉,这样子就没有 Book 类型的 Bean 了

  • @Autowired 的属性值 required 设置为 false,可避免图中的缺少 Bean 的情况:

运行测试之后,看到两条主要的错误信息:

Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'student' defined in file [C:\Users\94545\Desktop\Developer Folder\Java Test Folder\springtest\out\production\springtest\bean\Student.class]: Unsatisfied dependency expressed through constructor parameter 0; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'bean.Book' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}

Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'bean.Book' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}

首先是名为“student” 的 Bean 的构造器中的依赖关系出错了,第二是没有 Book 类型的 Bean,导致 Student 的 Bean 的构造器中条件不符合

为了验证第二种情况,我将代码还原,并且创建另一个 Book 类型的 Bean:mathBook,在此情况下,测试是不会通过的,因为没有单一符合条件的 Bean

  • 若有多个符合要求的 Bean,就需要一下方案来消除歧义:
  1. 标识首选的一个 Bean:@Primary 注解

测试后,证实了注解有效:

  1. 在需要注入的地方使用 @Qualifier注解:

这个注解对于构造器注入是不可用的,接下来在 Setter 方法上装配的时候再讲解

怎么在 Setter 方法上使用呢?

我们需要修改一下 Student 类,并且暂时隐藏一下 mathBook 这个 Bean:

带有 @Autowired 注解,在实例化时能够自动匹配 Book 类型的 Bean:

关于 Setter 方法和构造器来装配 Bean,就目前的学习情况来说,只有一点区别,如果有多个不同类型的 Bean,使用构造器能够指定装配的顺序

现在我们还原 mathBook 这个 Bean,如果出现上述的两个可能抛出异常的情况,这里有两种解决方式:

  1. @Primary 注解:

和上述类似,不多说

  1. 在需要注入的地方使用限定符,也就是 @Qualifier注解:

然后就能打印出英语书的信息(不截图了)

这个注解有一点灵活的地方就是,能够在 Bean 的定义上设置限定符,也就是说,限定符不一定要是 Bean 的名字,做个测试:

这时候,如果还使用 englishBook 作为限定符,也能够定位到这个 Bean,但是我们用自定义的限定符也可以,修改 Setter 方法上的限定符,进行测试:

证明使用 testQualifier 作为限定符也能得到结果

2. 使用 XML 装配

使用 XML 装配也有分构造器注入还是设值注入(Setter)方法,接下来的前三个使用的是构造器注入

1. 构造器注入
传递 Bean 的引用

将自动装配部分添加的注解取消掉,把代码还原成初始状态,然后我们开始使用 XML 装配:

暂时不配置 mathBook 这个 Bean,并修改 StudentTest 的上下文配置信息:

最后便是 Bean 的配置文件:

首先创建 englishBook 这个 Bean,在创建 student 这个 Bean,这里有一个元素

<constructor-arg ref=""/>

因为我在 student 的构造器中有一个名叫 englishBook 的Bean 作为参数,所以 Spring 会创建一个 ID 为 englishBook 的Bean,并将其引用传递至 student 的构造器中,ref 属性表明了传递引用的 Bean 的ID

传递字符串

如果我在 student 的构造器中传入的不是某个 Bean 的引用,而是某个字符串呢?

修改 XML 文件中的配置信息:

value 属性表示的是构造器中的字面量的值,可以是字符串、数字或布尔值等

然后测试一下:

证明字面量被成功注入了

传递列表

一个学生不可能只有一本书,所以我们要考虑,如何传入列表呢?

首先我们先修改 Student 类,传入的参数不仅是字符串,还有一个存放书本的列表:

然后在 XML 文件中配置 Bean:

    <bean id="englishBook" class="bean.EnglishBook"/>
    <bean id="mathBook" class="bean.MathBook"/>

最后配置 student 这个 Bean,并且配置构造器参数:

    <bean id="student" class="bean.Student">
        <constructor-arg value="I have some books"/>
        <constructor-arg>
            <list>
                <ref bean="englishBook"/>
                <ref bean="mathBook"/>
            </list>
        </constructor-arg>
    </bean>

第一个参数是字符串,第二个是 Book 类型的 List,列表中存放的是刚才创建的两个 Bean 的引用,然后来进行测试:

如果列表中也存放字符串,而不是引用,在元素<list></list>中使用<value>属性来代替<ref>属性

2. 设值注入

修改 Student 类和 Bean 配置文件:

然后在 Bean 的配置文件中加上这么一句:

    <bean id="student" class="bean.Student">
        <property name="book" ref="englishBook"/>
    </bean>

<property>属性是为 Setter 方法提供服务的,和上述的<constructor-arg>属性所提供的服务是一样的,在这个语句中,我在 book 中注入了 ID 为 englishBook 的 Bean 的引用,然后测试,会输出英语书的信息:

关于字面量和列表的注入,其实和构造器注入是类似的,将<constructor-arg>属性替换为<property>,只不过,设置注入需要设置属性名,也就是将 a Bean 的引用(或字面量或集合)注入到 b 属性中,就不再详述

3. 使用 Java 装配

除了使用 XML,还可使用 Java 代码来装配 Bean 并且达到依赖注入的目的

所以我们需要先修改测试中的上下文配置信息:

@ContextConfiguration(classes = StudentConfig.class)

再将 Student 类改回构造器注入的类型:

使用 Java 进行 Bean 的装配在上一篇文章中已经提到,这里就说一说依赖注入的实现:

在 Java 配置类中定义两个 Bean:

在 student 中使用构造器注入了 englishBook 这个 Bean,运行测试,将会得到英语书的输出:

总结

其实,这一篇文章把 Bean 的装配和依赖注入混合起来将了,可能有些人会觉得乱,但是三种方式其实从思路来说是类似的,通过认真地阅读这两篇关于 Bean 的介绍,就能对 Spring 的 DI 以及 IoC 有一个基本了解

DI(Dependency Injection)称为依赖注入,通俗的说,是在运行时,动态地满足某个对象对其他对象的需求,用上面的例子来说,student 在运行时需要 Book 类型的 Bean 作为依赖,我就给它注入一个 Bean,叫做 englishBook,这就能够叫做依赖注入

IoC(Inversion of Control)称为控制反转,在上面做的所有测试中,我都没有手动创建某个对象的实例,比如说注入一个列表,我甚至都没有创建一个列表的实例,那么为什么还会通过测试呢?因为 Spring 帮我在程序运行时创建了各个 Bean 的实例

IoC 能够由 DI 来实现,也就是说,程序中的各个组件之间的依赖关系都是由 Spring 来管理的,这就称作控制反转,这是 Spring 的核心

接下来还陆续会有文章来讲述这一概念

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,362评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,330评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,247评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,560评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,580评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,569评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,929评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,587评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,840评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,596评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,678评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,366评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,945评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,929评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,165评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,271评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,403评论 2 342

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,580评论 18 139
  • 1.1 spring IoC容器和beans的简介 Spring 框架的最核心基础的功能是IoC(控制反转)容器,...
    simoscode阅读 6,699评论 2 22
  • Spring Boot 参考指南 介绍 转载自:https://www.gitbook.com/book/qbgb...
    毛宇鹏阅读 46,724评论 6 342
  • 本章内容: 声明Bean 构造器注入和Setter方法注入 装配Bean 控制bean的创建和销毁 任何一个成功的...
    谢随安阅读 1,627评论 0 9
  • 装配 Bean 的概述 前面已经介绍了 Spring IoC 的理念和设计,这一篇文章将介绍的是如何将自己开发的 ...
    我没有三颗心脏阅读 5,211评论 4 27