前言
上一篇我们简单的介绍了
DI
和AOP
,并在其中以简单的小例子对其进行讲解,在例子中,我们使用了Spring
三种主要装配机制的一种:在XML中进行显示的配置
,内容见:Spring实战(一)-DI和AOP入门 而在这里,我们将对Spring的三种装配方式进行详细的介绍
Spring容器负责创建应用程序中的bean并通过DI来协调这些对象的关系,但作为开发人员,我们需要根据自己的需要来告诉Spring要创建哪些bean并选择其装配方式,它提供了以下三种装配方式:
- 在XML中进行显示配置
- 在Java中进行显示配置
- 隐式的bean发现机制和自动装配
以下对自动化配置进行介绍
自动化装配Bean
自动装配能够将显示配置降低到最少,Spring从以下两个角度来实现自动化装配:
组件扫描(component scanning)
:Spring会自动发现应用上下文中所创建的Bean自动装配(autowiring)
:Spring自动满足Bean之间的依赖
为了更好的理解组件扫描和自动装配的概念,我们创建几个Bean,它们代表一个印象系统中的组件。类CD
为唱片类,Spring会发现它并将其创建为一个bean
。类CDPlayer
,让Spring发现它,并将CD
这个创建好的bean
注入进来。
代码如下:
// 定义CD接口
package music;
public interface CD{
//唱片具有播放的功能
void play();
}
// 定义CD接口的实现类
package music;
@Component
public class CnCD implements CD{
private String title = "中国唱片";
public void play(){
System.out.println("Playing " + title);
}
}
可以看出,我们在CnCD
类中添加了@Component
注解,这个简单的注解表明该类会作为组件类,并告知Spring要为这个类创建bean。
但组件扫描默认是不启用的,还需要显示配置一下Spring,使它去寻找带有@Component
注解的类,并为其创建bean,如下代码,在CDPlayer
中开启组件扫描:
package music;
import org.springframework.context.annotation.componentScan;
import org.springframework.context.annotation.Con;
@Configuration
@ComponentScan
public class CDPlayer{}
类CDPlayer通过Java代码定义了Spring 的装配规则,在这个类中,并未显示的声明任何bean,只不过它使用了@ComponentScan注解,它能够启用组件扫描。
若你想使用XML来启动组件扫描,那么可以在spring.xml中添加如下代码:
<context:component-scan base-package="music" />
接下来我们做一个简单的JUnit测试,它会创建Spring上下文,并判断CD是不是真的被创建出来了,代码如下:
package music;
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=CDPlayer.class)
public class CDPlayerTest{
@Autowired
private CD cd;
@Test
public void cdShouldNotBeNull(){
assertNotNull(cd);
}
}
其中的@RunWith(SpringJUnit4ClassRunner.class)
使其自动创建Spring的应用上下文,注解@ContextConfiguration
会告诉它要在CDPlayer中加载配置,而在CDPlayer中包含了@Component
,因此最终的应用上下文中应该包含CD这个Bean;
在上一篇文章中,我们获取Spring应用上下文后,会根据spring.xml中配置的bean的id来获取对应的bean,但在上述的自动配置中,我们并未配置这个id的值,那么,它又是怎么样给这个bean命名的呢?
- 未设置id时,Spring会根据类名给其指定一个id值。具体在这个例子中,id的值为cnCD,将类型的第一个字母变为小写作为bean的id值。
- 若想为这个bean设置不同的ID,可在@Component注解中传入期望的id值,如下:
@Component("diffCD")
public class CnCD implements CD{...}
另外,还有一种为bean命名的方式,Spring中支持@Named作为@Component注解的方案,二者有些细微的差别,但在大多数场景下,它们可以相互替换:
package music;
import javax.inject.Named;
@Named("diffCD")
public class CnCD implements CD{...}
到目前为止,在@ComponentScan注解中为设置任何属性,按照默认的规则,它会以配置类所在的包为基础包(base package)来扫描组件。可通过如下几种方式配置:
@Configuration
@Component("music")
public class CDPlayer(){}
--------------------------------------------------
@Configuration
@Component(basePackages="music")
public class CDPlayer(){}
--------------------------------------------------
// 可配置扫描多个包
@Configuration
@ComponentScan(basePackages={"music", "video"})
public class CDPlayer(){}
--------------------------------------------------
// 可配置扫描多个包,参数以String类型是类型不安全(not type-safe)的,因此可以如下配置
@Configuration
@ComponentScan(basePackages={CDPlayer.class, DVDPlayer.class})
public class CDPlayer(){}
通过为bean添加注解(@Autowired)实现自动装配
1.构造器注入
package music;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class CDPlayer implements MediaPlayer{
private CD cd;
@Autowired
public CDPlayer(CD cd){
this.cd = cd;
}
public void play(){
cd.play();
}
}
2.setter方法注入
@Autowired
public void setCD(CD cd){
this.cd = cd;
}
实际上,@Autowired注解可以作用在类的任何方法上,只要添加了该注解,Spring都会尝试满足方法参数上所声明的依赖。如果有且只有一个bean匹配依赖,那么这个bean就会被装配进来。如果没有匹配的bean,那么在应用上下文创建的时候,Spring会抛出一个异常,为了避免这个异常出现,可将@Autowired的required属性设置为false:
@Autowired(required=false)
public CDPlayer(CD cd){
this.cd = cd;
}
注:此设置需谨慎,required属性设置为false时,Spring尝试自动装配若没有匹配的bean,则会让这个bean处于未装配的状态。但若你在代码中没有进行null检查的话,这个处于未装配状态的属性就有可能会出现NullPointerException