自动化装配Bean

创建应用对象之间协作关系的行为成为装配(wiring),这也是依赖注入的本质。

在Spring中,对象无需自己负责查找或创建与其关联的其他对象。相反,容器负责把需要相互协作的对象引用赋予各个对象。

Spring配置的可选方案有三种:

  1. 隐式的Bean发现机制和自动装配
  2. 在Java中进行显示装配
  3. 在XML中进行显示配置

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

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

从下面具体的例子中,学习Spring是如何实现自动装配的。

创建可被发现的bean

首先定义一个接口:CompactDisc.java

1
2
3
4
5
package com.dings503.spring;

public interface CompactDisc {
void play();
}

然后需要一个接口的实现:SgtPeppers.java

1
2
3
4
5
6
7
8
9
10
11
12
13
package com.dings503.spring;

import org.springframework.stereotype.Component;

@Component
public class SgtPeppers implements CompactDisc {
private String title = "sgtPeppers.title";
private String artist = "The Beatles";

public void play() {
System.out.println("Playing " + title + "by " + artist);
}
}

在SgtPeppers类上使用了@Component注解,这个注解表明该类会作为组件类,并告知Spring要为这个类创建bean。
不过,组件扫描默认是不启用的,还需要显示配置一下Spring,从而命令它去寻找带有@Component注解的类,并为其创建bean。
配置类如下:CDPlayerConfig.Java

1
2
3
4
5
6
7
8
9
10
package com.dings503.spring;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

@Configuration
@ComponentScan
public class CDPlayerConfig {

}

类CDPlayerConfig通过Java代码定义了Spring的装配规则。它使用了@ComponentScan注解,这个注解能够在Spring中启用组件扫描。
如果没有其他配置的话,@ComponentScan会默认扫描与配置类相同的包。Spring会扫描这个包以及这个包下的所有子包,查找带有@Component注解的类,并为其创建一个bean。

当然,也可以通过XML来启用组件扫描,使用Spring context命名空间的元素,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.2.xsd">

<context:component-scan base-package="com.dingsheng.spring" />

</beans>

为组件扫描的bean命名

Spring应用上下文中所有的bean都会给定一个ID,默认是根据类名指定的,将类名的第一个字母变为小写。
自定义为bean命名代码如下:

1
2
3
4
@Component("sgtPeppers-name")
public class SgtPeppers implements CompactDisc {
...
}

或者:

1
2
3
4
5
import javax.inject.Name;
@Named("sgtPeppers-name")
public class SgtPeppers implements CompactDisc {
...
}

在大多数情况下,这两种命名方式是可以相互替换的。

设置组件扫描的基础包

@ComponentScan注解按照默认规则会以配置类所在的包作为基础包(base package)来扫描组件。如果想要扫描不同包内的组件就需要对该注解进行设置。
具体的,可以在@ComponentScan的value属性中指明包的名字:

1
2
3
@Congiguration
@ComponentScan("com.dings503.spring")
public class CDPlayerConfig{}

如果想要更清晰地表明所设置的是基础包,还可以通过basePackages属性来进行配置:

1
2
3
@Congiguration
@ComponentScan(basePackage="com.dings503.spring")
public class CDPlayerConfig{}

如果要设置多个基础包时,可以将basepackages属性设置为数组:

1
2
3
@Configuration
@ComponentScan(basePackage={"com.dings503.spring","com.dings503.test"})
public class CDPlayerConfig{}

除了这种方法外,@Component还提供了另外一种方法,那就是将其指定为包中所包含的类或接口:

1
2
3
@Configuration
@ComponentScan(basePackageClasses={CDPlayer.class,DVDPlayer.class})
public class CDPlayerConfig{}

这里basePackages属性被替换成了basePackageClasses属性,basePackageClasses属性中的类所在的包将作为组件扫描的基础包。

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

简单来说,自动装配就是让Spring自动满足bean依赖的一种方法,在满足依赖的过程中,会在Spring应用上下文中寻找匹配某个bean需求的其他bean。为了声明要进行自动装配,可以借助Spring的@Autowired注解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package com.dingsheng.spring;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component
public class CDPlayer implements MediaPlayer {
private CompactDisc cd;

@Autowired
public CDPlayer(CompactDisc cd) {
this.cd = cd;
}

public void play() {
cd.play();
}
}

@Autowired注解可以用在类的任何方法上,可以是setter方法,也可以是其他方法。
如果没有匹配的bean,那么在应用上下文创建的时候,Spring会抛出一个异常,为避免异常的出现,可以将@Autowired的required属性设置为false:

1
2
3
4
@Autowired(required=false)
public CDPlayer(CompactDisc cd) {
this.cd = cd;
}

将required属性设置为false时,Spring会尝试执行自动装配,但是如果没有匹配的bean的话,Spring会让这个bean处于未匹配的状态。

如果不想使用Spring特有的@Autowired注解也可以使用Java依赖注入规范中的@Inject注解:

1
2
3
4
5
6
7
8
9
10
11
12
13
package com.dingsheng.spring;

impore javax.inject.Inject;
import javax.inject.Named;

@Named
public class CDPlayer {

@inject
public CDPlayer(CompactDisc cd) {
this.cd = cd;
}
}

在大多数情况下这两种注解可以相互替换。