Spring从两个角度来实现自动化装配:

  • 组件扫描(component scanning):Spring会自动发现应用上下文中需要创建的bean。
  • 自动装配(autowiring):Spring会自动满足bean之间的依赖。

为了更形象的解释组件扫描与自动装配,我们举一个音响系统的例子,主要包含以下内容:

  • CD接口
  • CD接口的一个实现类
  • CD播放器

关于CD和CD播放器关系的解释:

如果你不将CD插入(注入)到CD播放器中,那么CD播放器其实是没有太大用处的。所以,可以这样说,

CD播放器依赖于CD才能完成它的使命。

1. 创建可被发现的bean

先创建CD接口CompactDisc:

package chapter02;

public interface CompactDisc {
void play();
}

然后创建CD接口的一个实现类SgtPeppers:

package chapter02;

import org.springframework.stereotype.Component;

@Component
public class SgtPeppers implements CompactDisc {
@Override
public void play() {
String title = "Sgt.Pepper's Lonely Hearts Club Band";
String artists = "The Beatles";
System.out.println("Playing " + title + " By " + artists);
}
}

SgtPeppers类与以往类的区别在于使用了@Component注解。

这个注解表明该类会作为组件类,并告知Spring要为这个类创建bean。

那么如何让Spring发现它并创建bean呢?

这时就需要用到组件扫描,不过,在Spring中,组件扫描默认是不启用的

因此我们需要显式配置一下Spring,从而命令它去寻找带有@Component注解的类,并为其创建bean。

创建CDPlayerConfig类:

package chapter02;

import org.springframework.context.annotation.ComponentScan;

@ComponentScan
public class CDPlayerConfig {
}

这个类与以往类的区别是使用了@ComponentScan注解,这个注解能够让Spring启用组件扫描。

@ComponentScan默认会扫描与配置类相同的包以及这个包下的所有子包,查找带有@Component注解的类。

2. 验证组件扫描

为了验证创建的bean能否被Spring发现,我们创建一个简单的JUnit测试,完成此测试需要导入以下两个jar包:

  • hamcrest-core-2.1.jar
  • junit-4.12.jar

导入jar包的方式如下:

导入完成后的项目结构图如下所示:

package chapter02;

import static org.junit.Assert.*;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; @RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = CDPlayerConfig.class)
public class CDPlayerTest { @Autowired
private CompactDisc compactDisc; @Test
public void cdShouldNotBeNull() {
assertNotNull(compactDisc);
compactDisc.play();
}
}

代码简单讲解:

@RunWith(SpringJUnit4ClassRunner.class),会在测试开始的时候自动创建Spring的应用上下文。

@ContextConfiguration(classes = CDPlayerConfig.class)会告诉Spring需要在CDPlayerConfig中加载配置。

字段compactDisc上的@Autowired注解,会将SgtPeppers bean注入到字段compactDisc上,因为它是接口CompactDisc的实现类并且添加了@Component注解。

运行测试方法cdShouldNotBeNull,会发现测试通过,compactDisc不为null:

3. 设置bean ID

Spring应用上下文中所有的bean都会给定一个ID,默认情况下,Spring会将类名的第一个字母变为小写,作为该bean的ID

如上面代码中SgtPeppers bean的ID为sgtPeppers。

有以下两种方式来设置bean ID:

3.1 使用@Component设置bean ID

@Component("lonelyHeartsClub")
public class SgtPeppers implements CompactDisc {
......
}

3.2 使用@Named设置bean ID

@Named注解不是Spring框架的注解,而是Java 依赖注入规范(Java Dependency Injection)中的注解,因此需要导入jar包:javax.inject-1.jar,导入jar包的方式可以参考 Spring入门(一):创建Spring项目

import javax.inject.Named;

@Named("lonelyHeartsClub")
public class SgtPeppers implements CompactDisc {
......
}

在Spring项目中建议使用@Component注解。

4. 设置组件扫描的基础包

按照默认规则 ,@ComponentScan注解会以配置类所在的包作为基础包(base package)来扫描组件。

但有时候,我们会将配置类放在单独的包中,使其与其他的应用代码区分开来。

这种场景下,默认的基础包就满足不了需求。

@ComponentScan注解支持传入指定的基础包,有以下几种场景:

4.1 指定要扫描的基础包(单个)

@ComponentScan("chapter02")
public class CDPlayerConfig {
}

或者:

@ComponentScan(basePackages = "chapter02")
public class CDPlayerConfig {
}

4.2 指定要扫描的基础包(多个)

@ComponentScan(basePackages = {"chapter01", "chapter02"})
public class CDPlayerConfig {
}

4.3 指定要扫描的基础包(类型安全)

@ComponentScan(basePackageClasses = {CDPlayer.class})
public class CDPlayerConfig {
}

basePackageClasses也支持指定多个类,指定类所在的包将会作为组件扫描的基础包。

建议使用这种类型安全方式来指定扫描的基础包。

5. 通过为bean添加注解实现自动装配

自动装配是让Spring自动满足bean依赖的一种方法,在满足依赖的过程中,会在Spring应用上下文中寻找匹配某个bean需要的其他bean。

实现自动装配,需要使用Spring的@Autowired注解。

@Autowired一般情况下,有以下3种使用方式:

5.1 使用在构造器上

package chapter02;

public interface MediaPlayer {
void play();
}
package chapter02;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component; @Component
public class CDPlayer implements MediaPlayer { private CompactDisc compactDisc; @Autowired
public CDPlayer(CompactDisc compactDisc) {
this.compactDisc = compactDisc;
} @Override
public void play() {
compactDisc.play();
}
}

5.2 使用在属性的Setter方法上

package chapter02;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component; @Component
public class CDPlayer implements MediaPlayer { private CompactDisc compactDisc; @Autowired
public void setCompactDisc(CompactDisc compactDisc) {
this.compactDisc = compactDisc;
} @Override
public void play() {
compactDisc.play();
}
}

5.3 使用在类的任何方法上

package chapter02;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component; @Component
public class CDPlayer implements MediaPlayer { private CompactDisc compactDisc; @Autowired
public void insertDisc(CompactDisc compactDisc) {
this.compactDisc = compactDisc;
} @Override
public void play() {
compactDisc.play();
}
}

不管是构造器、Setter方法还是其他的方法,Spring都会尝试满足方法参数上所声明的依赖。

假如有且只有一个bean匹配依赖需求的话,那么这个bean将会被装配进来。

如果没有匹配的bean,那么在应用上下文创建的时候,Spring会抛出一个异常。

可以通过设置require属性为false避免该异常出现:

@Autowired(required = false)
public CDPlayer(CompactDisc compactDisc) {
this.compactDisc = compactDisc;
}

不过建议谨慎使用,避免未找到bean进行匹配,而且代码没有进行null检查而出现NullPointerException。

如果有多个bean都能满足依赖关系的话,Spring将会抛出一个异常,表明没有明确指定要选择哪个bean进行自动装配。

@Autowired注解也可以替换成@Inject注解(来源于Java依赖注入规范),同样可以实现自动装配:

package chapter02;

import org.springframework.stereotype.Component;
import javax.inject.Inject; @Component
public class CDPlayer implements MediaPlayer { private CompactDisc compactDisc; @Inject
public CDPlayer(CompactDisc compactDisc) {
this.compactDisc = compactDisc;
} @Override
public void play() {
compactDisc.play();
}
}

在Spring项目中建议使用@Autowired注解

6. 验证自动装配

修改CDPlayerTest类代码测试自动装配。

package chapter02;

import org.junit.Rule;
import org.junit.Test;
import org.junit.contrib.java.lang.system.StandardOutputStreamLog;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull; @RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = CDPlayerConfig.class)
public class CDPlayerTest { @Rule
public final StandardOutputStreamLog log = new StandardOutputStreamLog(); @Autowired
private MediaPlayer mediaPlayer; @Autowired
private CompactDisc compactDisc; @Test
public void cdShouldNotBeNull() {
assertNotNull(compactDisc);
compactDisc.play();
} @Test
public void play() {
mediaPlayer.play();
assertEquals("Playing Sgt.Pepper's Lonely Hearts Club Band By The Beatles\r\n", log.getLog());
}
}

因为代码中使用了StandardOutputStreamLog类,因此需要导入jar包:system-rules-1.16.0.jar,导入jar包的方式可以参考 Spring入门(一):创建Spring项目

运行测试方法play(),输出内容和预期一致,说明字段mediaPlayer已经被MediaPlayer的实现类CDPlayer bean装配,测试通过,如下所示:

7. 源码及参考

源码地址:https://github.com/zwwhnly/spring-action.git,欢迎下载。

Craig Walls 《Spring实战(第4版)》

原创不易,如果觉得文章能学到东西的话,欢迎点个赞、评个论、关个注,这是我坚持写作的最大动力。

如果有兴趣,欢迎添加我的微信:zwwhnly,等你来聊技术、职场、工作等话题(PS:我是一名奋斗在上海的程序员)。

Spring入门(二):自动化装配bean的更多相关文章

  1. Spring学习系列(二) 自动化装配Bean

    一.Spring装配-自动化装配 @Component和@ComponentScan 通过spring注解(@Component)来表明该类会作为组件类,并告知Spring要为这类创建bean,不过组 ...

  2. Spring学习笔记(二)之装配Bean

    一,介绍Bean的装配机制 在Spring中,容器负责对象的创建并通过DI来协调对象之间的关系.但是我们要告诉Spring创建哪些Bean并且如何将其装配在一起.,装配wiring就是DI依赖注入的本 ...

  3. Spring(二)装配Spring Bean

    控制反转的概念:控制反转是一种通过描述(在Java中或者是XML或者注解)并通过第三方去产生或获取特定对象的方式. 在Spring中实现控制反转的是IoC容器,其实现方法是依赖注入(Dependenc ...

  4. Spring 之自动化装配 bean 尝试

    [Spring之自动化装配bean尝试] 1.添加dependencies如下所示(不是每一个都用得到 <dependencies> <dependency> <grou ...

  5. Spring实战2:装配bean—依赖注入的本质

    主要内容 Spring的配置方法概览 自动装配bean 基于Java配置文件装配bean 控制bean的创建和销毁 任何一个成功的应用都是由多个为了实现某个业务目标而相互协作的组件构成的,这些组件必须 ...

  6. Spring 通过Java代码装配bean

    1. 背景 书接上文Spring自动化装配bean 尽管在很多场景下通过组件扫描和自动装配实现Spring的自动化扫描配置是更为推荐的方式,但在有些情况下自动化扫描的方案行不通,如想要将第三方库中的组 ...

  7. 第2章—装配Bean—自动化装配Bean

    自动化装配Bean 2.1.Spring配置可选方案 ​ 装配是依赖注入DI的本质,Spring提供了以下三种注入的装配机制: 在XMl中进行显式配置 在java中进行显式配置 隐式的Bean发现机制 ...

  8. Spring实战3:装配bean的进阶知识

    主要内容: Environments and profiles Conditional bean declaration 处理自动装配的歧义 bean的作用域 The Spring Expressio ...

  9. Spring框架(3)---IOC装配Bean(注解方式)

    IOC装配Bean(注解方式) 上面一遍文章讲了通过xml来装配Bean,那么这篇来讲注解方式来讲装配Bean对象 注解方式需要在原先的基础上重新配置环境: (1)Component标签举例 1:导入 ...

随机推荐

  1. config.go

    package blog4go import ( "encoding/xml" "errors" "io/ioutil" "os& ...

  2. C++中的内联函数和C中的宏定义的区别

    在C++中内联函数: 内联函数即是在函数的声明和和定义前面加上“inline”关键字,内联函数和常规函数一样,都是按照值来传递参数的,如果参数为表达式,如4.5+7.5,则函数将传递表达式的值(这里为 ...

  3. bzoj 3505 [Cqoi2014]数三角形 组合

    ans=所有的三点排列-共行的-共列的-斜着一条线的 斜着的枚举每个点和原点的gcd,反过来也可以,还能左右,上下挪 #include<cstdio> #include<cstrin ...

  4. nginx与Elasticsearch结合使用

    Elasticsearch是一种先进的,高性能的,可扩展的开源搜索引擎,提供全文搜索和实时分析的结构化和非结构化的数据. 它的特定是可以通过HTTP使用 RESTful API,很容易的融入现有的we ...

  5. Java开源生鲜电商平台-团购模块设计与架构(源码可下载)

    Java开源生鲜电商平台-团购模块设计与架构(源码可下载) 说明:任何一个电商系统中,对于促销这块是必不可少的,毕竟这块是最吸引用户的,用户也是最爱的模块之一,理由很简单,便宜. 我的经验是无论是大的 ...

  6. WebView,我已经长大了,知道自己区分是否安全了!

    一.前言 如果你在用 Android 原生系统(Google Play 服务),在使用 WebView 加载某些网页时,一定遇到过以下的安全警告红屏. 这是 WebView 的安全浏览保护策略,在 A ...

  7. [数据库锁机制] 深入理解乐观锁、悲观锁以及CAS乐观锁的实现机制原理分析

    前言: 在并发访问情况下,可能会出现脏读.不可重复读和幻读等读现象,为了应对这些问题,主流数据库都提供了锁机制,并引入了事务隔离级别的概念.数据库管理系统(DBMS)中的并发控制的任务是确保在多个事务 ...

  8. ES 18 - (底层原理) Elasticsearch写入索引数据的过程 以及优化写入过程

    目录 1 Lucene操作document的流程 1.1 添加document的流程 1.2 删除document的流程 2 优化写入流程 - 实现近实时搜索 2.1 流程的改进思路 2.2 设置re ...

  9. RabbitMQ死信队列另类用法之复合死信

    前言 在业务开发过程中,我们常常需要做一些定时任务,这些任务一般用来做监控或者清理任务,比如在订单的业务场景中,用户在创建订单后一段时间内,没有完成支付,系统将自动取消该订单,并将库存返回到商品中,又 ...

  10. Data Lake Analytics的Geospatial分析函数

    0. 简介 为满足部分客户在云上做Geometry数据的分析需求,阿里云Data Lake Analytics(以下简称:DLA)支持多种格式的地理空间数据处理函数,符合Open Geospatial ...