Spring从两个角度来实现自动化装配:
(1)组件扫描(component scanning):Spring会自动发现应用上下文中所创建的 bean。
(2)自动装配(autowiring):Spring自动满足 bean 之间的依赖。
1、创建可被发现的Bean
(1)创建一个接口CompactDisc:
package soundsystem;
public interface CompactDisc{
void play();
}
(2)创建带有@Component注解的CompactDisc接口的一个实现类:
package soundsystem;
import org.springframework.stereotype.Component;
@Component
public class SgtPeppers implements CompactDisc{
private String title = "Sgt, Pepper's Lonely Hearts Club Band";
private String artist = "The Beatles";
public void play(){
System.out.println("Playing " + title + " By " + artist);
}
}
注:
@Component注解表明,该类会作为组件类,并告知Spring要为这个类创建bean。不过,组件的扫描默认是不启动的,需要显式地对Spring进行配置,从而命令它去寻找带有@Component注解的类,并为其创建bean。
(3)创建配置类:
package soundsystem;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@Configuration
@ComponentScan
public class CDPlayerConfig{
...
}
注:
@ComponentScan注解能够在Spring当中启动组件扫描,它会默认扫描与配置类相同的包以及这个包下的所有子包,查找带有@Component注解的类。如此一来,就能发现CompactDisc,并且会在Spring中自动为其创建一个bean。
(4)当然我们也可以使用XML配置的方式来启动组件扫描:
<?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="soundsystem"/>
</beans>
(5)Junit测试:
package soundsystem;
import org.junit.Test;
import static org.junit.Assert.*;
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 cd;
@Test
public void cdShuldNotBeNull(){
assertNotNull(cd);
}
}
注:
a、SpringJUnit4ClassRunner用于在测试开始的时候自动创建Spring的应用上下文。
b、@ContextConfiguration注解会告诉它需要在CDPlayerConfig中加载配置。由于CDPlayerConfig类当中包含了@ComponentScan注解、SgtPeppers当中包含了@Component注解,因此最终的应用上下文就会包含CompactDisc的bean。
c、@Autowired注解用于将CompactDisc的bean注入到测试代码当中。
2、为组件扫描的bean命名
(1)Spring上下文中所有的bean都会给定一个ID,若直接使用 @Component 注解来声明一个 bean,其bean 的名字默认为类名首字母小写。例如,如上 SgtPeppers 类的默认 bean 名称为 sgtPeppers。当然我们可以在 @Component 注解中说明此 bean 的名称。
@Component("lonelyHeartClub")
public class SgtPeppers implements CompactDisc{
...
}
也可以使用另外一种为bean命名的方式:
@Named("lonelyHeartClub")
public class SgtPeppers implements CompactDisc{
...
}
注:
@Named是Java依赖注入规范(Java Dependency Injection)中所提供的为bean设置ID的注解。Spring支持将@Named作为@Component注解的替代方案,两者之间有一些细微的差异,但在大多数场景下,它们是可以相互替换的。然由于@Named其本身并无法表明它是干什么的,因此人们常常会选择使用@Component注解来命名。
3、设置组件扫描的基础包
在之前的案列中,我们没有为 @ComponentScan注解设置任何属性,这意味着,按照默认规则,它会以配置类所在包作为基础包( base package )来扫描组件。但是有个原因会促使我们要明确的设置基础包,那就是我们想要将配置类放在单独的包中,使其与其他的应用代码区分开来。
(1)为了指定不同的包,只需要在 @ComponentScan的value属性中指明包的名称:
@Configuration
@ComponentScan("soundsystem")
public class CDPlayerConfig{
...
}
(2)如果你想更加明确的表名你所设置的是基础包,那么你可以通过basePackages属性进行配置:
@Configuration
@ComponentScan(basePackages="soundsystem")
public class CDPlayerConfig{
...
}
(3)如果你想扫描多个包,只需要将basePackages属性的值设置为要扫描包的一个数组即可:
@Configuration
@ComponentScan(basePackages={"soundsystem","video"})
public class CDPlayerConfig{
...
}
(4)在上面的例子当中,basePackages属性的值是一个String类型的数组,这样配置没有问题,但却是类型不安全的,如果要重构代码的话,这些包的名字可能会被修改,从而导致包扫描出现错误,除了将包设置为简单的String类型之外,@ComponentScan 还提供了另一种方法,那就是将其指定为要扫描包中所含的类或接口。
@Configuration
@ComponentScan(basePackageClasses={CDPlayer.class, DVDPlayer.class})
public class CDPlayerConfig{
...
}
注:
你可以在包中设置一个专门用来进行包扫描的空标记接口,这样,可以避免对任何实际应用的代码进行重构后,包扫描出现错误。
4、通过为bean添加注解实现自动装配
在你的应用程序当中,如果所有的对象都是独立的,彼此之间没有任何的依赖,就像ShtPeppers的bean一样,那么你所需要的可能就是组件的扫描而已。但是很多对象会依赖其他的对象才能完成任务。如此一来,我们就需要有一种方法将组件扫描得到的bean和它们的依赖装配在一起。这就涉及到Spring的自动装配。自动装配就是让Spring自动满足bean依赖的一种方法,在满足依赖的过程中,会在Spring应用上下文中寻找匹配某个bean需求的其他bean,这里借助@Autowired注解来声明自动装配。
(1)以下示例的构造器上添加了@Autowired注解,这表明当Spring创建CDPlayer bean的时候,会通过这个构造器进行实例化并传入一个CompactDisc类型的 bean。@Autowired 注解可以用在类的任何方法上。
@Component
public class CDPlayer implements MediaPlayer{
private CompactDisc cd;
@Autowired
public CDPlayer(CompacrDisc cd){
this.cd = cd;
}
public void play(){
cd.play();
}
}
(2)不管是构造器、Setter 方法还是其他方法,Spring 都会尝试满足方法参数上所声明的依赖,假如有且仅有一个bean依赖需求的话,那么这个bean就会被装填进来。如果没有匹配的bean,那么在应用上下文创建的时候,Spring会抛出一个异常。为了避免异常的出现,你可以将@Autowired的required属性设置为false:
@Autowired(required=false)
public CDPlayer(CompactDisc cd){
this.cd = cd;
}
注:
但是,把required属性设置为false时,需要谨慎对待。如果你的代码中没有进行null检查的话,这个处于未装配状态的属性可能会出现NullPointerException。
(3)如果有多个bean都能满足依赖关系的话,Spring也将会抛出一个异常,表明没有明确指定要选择那个bean进行装配。