Spring 配置方案
在Spring中,对象无需自己查找或创建与其所关联的其它对象。相反,容器负责把所需要相互协作的对象引用赋予各个对象。(控制反转IOC)
创建应用对象之间关系的行为成为装配(wiring),也就是依赖注入(DI)的本质。
Spring 的三种主要的装配机制:
- XML显式配置
- Java显式配置
- 隐式bean发现机制和自动装配
一般来说,尽可能用自动装配,少用显式配置。
如果需要显式配置,可以用JavaConfig配置。
如果想用XML的命名空间,才用XML。
自动化装配bean
Spring 如何实现自动装配:
-
组件扫描(component scan):Spring自动发现应用上下文创建的bean。
-
自动装配(autowring):Spring 自动满足bean之间的依赖。
创建可被发现的bean
@Component 注解表明该类作为组件类,并告知Spring要为该类创建bean。
@ComponentScan 注解启用组件扫描。
- 默认扫描与配置类相同的包。
用XML实现组件扫描,用Spring context 命名空间的<context:component-scan>
元素:
<context:component-scan base-package="package-name" />
创建Spring的应用上下文,加载有 @ComponentScan 的注解的config 类,即可在上下文中包含相应的bean。
如下demo所示:
// src/test/java/soundsystem/CDPlayerTest.java
package soundsystem;
import static org.junit.Assert.*;
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;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes=CDPlayerConfig.class)
public class CDPlayerTest {
@Rule
public final StandardOutputStreamLog log = new StandardOutputStreamLog();
@Autowired
private MediaPlayer player;
@Autowired
private CompactDisc cd;
@Test
public void cdShouldNotBeNull() {
assertNotNull(cd);
}
@Test
public void play() {
player.play();
assertEquals(
"Playing Sgt. Pepper's Lonely Hearts Club Band by The Beatles\n",
log.getLog());
}
}
// src/main/java/soundsystem/CDPlayerConfig.java
package soundsystem;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
// 这里的Configuration 不必要
// @Configuration
@ComponentScan
public class CDPlayerConfig {
}
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);
}
}
package soundsystem;
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();
}
}
bean 命名
Spring给所有的bean一个默认的ID,就是类名首字母小写。
也可以设置ID ,@Component("设置的名字")
。
另外,可以用 Java依赖注入规范(Java Dependency Injection) 中的 @Named
替换 @Component
。
bean ID有啥用?哦,可以设置引用。比如 xml中的 ref=”bean id”
设置要扫描的包
@ComponentScan
默认扫描当前配置类所在的,但是也可以指定扫描的包:
// 扫描指定的包
@ComponentScan("package-name")
// 也可以这么写。
@ComponentScan(basePackages="package-name")
// 扫描多个包
@ComponentScan(basePackages={"package-name", "package-name"})
上述扫描设置的包是以String类型表示的。这种是 类型不安全的(not type-safe) ???
所以还有另外一种方法:指定包中所含包含的某个类或接口。
// basePackageClasses 属性所设置的数组中包含的类所在的包会作为组件扫描的基础包
@ComponentScan(basePackageClasses = {Test.class})
自动装配
通过 @Autowired 注解,Spring会在应用上下文中寻找匹配某个bean需求的其它bean。
@Autowired 注解可以用在 任何 方法或者属性上。
Spring在初始化bean之后,就会去尽可能满足bean的依赖。这里 依赖是通过@Autowired注声明。如下代码所示。
@Component
public class CDPlayer implements MediaPlayer {
private CompactDisc cd;
@Autowired
public CDPlayer(CompactDisc cd) {
this.cd = cd;
}
public void play() {
cd.play();
}
}
需要注意:如果没有匹配的bean,会报错。所以可以设置如下:
// 即设置不一定非要找到,但这个时候,如果没找到,代码中的bean就是NULL了,所以需要做下检查。
@Autowired(required=false)
// 不能这样写。。
// @Autowired(false)
另外,@Autowired
注解可以用 java依赖注入规范中的 @Inject
注解替换。
JavaConfig 装配
一般来说,自动装配最方便,但是如果是想加载第三方库,没法在它的类上添加@Component
和@Autowired
注解,这时还是要用 Java 或者XML 来显式装配。
创建配置类
添加 @Configuration
注解
@Configuration
public class CDPlayerConfig {
}
声明bean
@Bean
public CompactDisc compactDisc() {
return new SgtPeppers();
}
@Bean
注解告知Spring 返回一个对象,该对象注册为Spring应用上下文的bean。
也可以指定bean-id:@Bean(name="aaa")
实现注入
@Configuration
public class CDPlayerConfig {
@Bean
public CompactDisc compactDisc() {
return new SgtPeppers();
}
@Bean
public CDPlayer cdPlayer() {
return new CDPlayer(compactDisc());
}
}
这里通过调用 compactDisc 来传入bean。
因为 compactDisc 方法加了 @Bean
注解,Spring会拦截所有对它的调用,并确保直接返回该方法创建的bean,而不是每次都对其进行实际的调用。
默认情况下,Spring 中的bean都是单例的。如下代码,两个cdPlayer bean得到的是相同的bean实例。
@Bean
public CompactDisc compactDisc() {
return new SgtPeppers();
}
@Bean
public CDPlayer cdPlayer() {
return new CDPlayer(compactDisc());
}
@Bean
public CDPlayer anotherCDPlayer() {
return new CDPlayer(compactDisc());
}
// 但是这样会报错?
// expected single matching bean but found 2: cdPlayer,anotherCDPlayer
其他注入方式,传入构造器参数。
@Bean
public CDPlayer cdPlayer(CompactDisc compactDisc) {
return new CDPlayer(compactDisc);
}
Setter
@Bean
public CDPlayer cdPlayer(CompactDisc compactDisc) {
CDPlayer cdPlayer = new CDPlayer(compactDisc);
cdPlayer.setCompactDisc(compactDisc);
return cdPlayer;
}
总之,加了 @Bean
注解后,方法体力可以用任何java功能来实现bean。