SpringInAction笔记(二)—— 装配Bean
(1)在xml中进行显示装配
(2)在Java中进行显示装配
(3)隐式的bean发现机制和自动装配
作者建议:尽可能地使用自动配置的机制。显示配置越少越好。
一、自动化装配
Spring从两个角度实现自动化装配:
(1)组件扫描:Spring会自动发现应用上下文中所创建的bean
(2)自动扫描:spring自动满足bean之间的依赖
1.1 组件扫描实例
1) CD接口CompactDisc.java,定义了CD播放器对一盘CD所能进行的操作
package com.springinaction.ch02.autoconfig;
/**
* CD接口
* @author lenovo
*
*/
public interface CompactDisc {
void play();
}
2)CompactDisc接口实现SgtPeppers.java
package com.springinaction.ch02.autoconfig;
import org.springframework.stereotype.Component;
//@Component注解表明该类会作为组件类,并告知Spring要为这个类创建bean
@Component
public class SgtPeppers implements CompactDisc {
private String title = "Sgt. Pepper's Lonely Hearts Club Band";
private String artist = "The Beatles";
@Override
public void play() {
// TODO Auto-generated method stub
System.out.println("Playing " + title + " by " + artist);
}
}
注意SgtPeppers 使用@Component注解,这个注解表明该类会作为组件类,并告知Spring要为这个类创建bean。
不过,组件扫描默认是不启用的。还需显示配置Spring,从而命令它去寻找带有@Component注解的类,并为其创建bean。
3)CDPlayerConfig.java,定义装配规则,声明JAVA配置类,启动自动扫描注解
package com.springinaction.ch02.autoconfig;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@Configuration //声明为配置类
@ComponentScan(basePackages = "com.springinaction")
public class CDPlayerConfig {
}
@ComponentScan默认会扫描与配置类相同的包,及这个包下的所有子包,查找带有@Component注解的类。但因为示例工程下还有其他的包和配置会干扰,所以这里设置了基础包来指定扫描。
或使用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-3.1.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="com.springinaction"></context:component-scan>
</beans>
4)测试类CDPlayerTest.java,验证CompactDisc是不是真的创建出来
package com.springinaction.ch02.autoconfig.Test;
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;
import com.springinaction.ch02.autoconfig.CDPlayerConfig;
import com.springinaction.ch02.autoconfig.CompactDisc;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = CDPlayerConfig.class)
public class CDPlayerTest {
@Autowired
private CompactDisc cd;
@Test
public void cdShouldNotBeNull() {
assertNotNull(cd);
}
}
CDPlayerTest使用了Spring的SpringJUnit4ClassRunner,以便在测试开始的时候自动创建Spring的应用上下文。注解 @ContextConfiguration会告诉它需要在CDPlayerConfig中加载配置。因为CDPlayerConfig类中包含了@ComponentScan,因此最终的应用上下文中应该包含CompactDiscbean。
测试结果为通过。
1.1.1 为组件扫描的bean命名
Spring应用上下文中所有的bean都会给定一个ID。在前面的例子中, 尽管我们没有明确地为SgtPeppersbean设置ID,但Spring会根据类 名为其指定一个ID。具体来讲,这个bean所给定的ID 为sgtPeppers,也就是将类名的第一个字母变为小写。 为bean指定命名时,可在@Component后的括号内配置字符串来命名,或使用@Named注解
//@Component
//@Component("lonelyHeartsClub")或
@Named("lonelyHeartsClub")
public class SgtPeppers implements CompactDisc {
.......
}
Spring支持将@Named作为@Component注解的替代方案。两者之间有一些细微的差异,但是在大多数场景中,它们是可以互相替换(@Named是Java依赖注入规范(Java Dependency Injection)中所提供注解)。
为了指定不同的基础包, 所需要做的就是在@ComponentScan的value属性中指明包的名称;
@Configuration
@ComponentScan("com.springinaction")
public class CDPlayerConfig { }
或者通过basePackages属性进行配置可以设置多个基础包,只需要将basePackages属性设置为要扫描包的一个数组即可。
@Configuration
@ComponentScan(basePackages={"com.springinaction", "com.sencond"})
public class CDPlayerConfig { }
也可以除了将包设置为简单的String类型之外,@ComponentScan还提供了另外一种方法,那就是将其指定为包中所包含的类或接口 ,这样最好。basePackages属性被替换成了basePackageClasses。同时,我们不是再使用String类型的名称来指定包,为basePackageClasses属性所设置的数组中包含了类。这些类所在的包将会作为组件扫描的基础包。
@ComponentScan(basePackageClasses={CompactDisc.class})
public class CDPlayerConfig {
}
1.2 通过为bean添加注解实现自动装配
自动装配就是让Spring自动满足bean依赖的一种方法,在满足依赖的过程中,会在Spring应用上下文寻找匹配某个bean需求的其他bean。为了声明要进行自动装配,可以借助Spring的@Autowired注解。
MediaPlayer接口:
package com.springinaction.ch02.autoconfig;
public interface MediaPlayer {
void play();
}
MediaPlayer实现类:
package com.springinaction.ch02.autoconfig;
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();
}
}
CDPlayer构造器上添加了@Autowired注解,这表明当Spring创建CDPlayerbean的时候,会通过这个构造器来进行实例化并且会传入一个可设置给CompactDisc类型的bean。 也可以在属性的Setter方法上和用在类的任何方法之上。
不管时构造器、Setter方法还是其他的方法,Spring都会尝试满足方法参数上所声明的依赖。如果有且只有一个bean匹配依赖需求的话,那么这个bean就会被装配进来。
如果没有匹配的bean,那么在应用上下文创建的时候,Spring会抛出一个异常。为了避免异常的出现,可以将@Autowired的required属性设置为false:
@Autowired(required=false)
public CDPlayer(CompactDisc cd) {
this.cd = cd;
}
将required属性设置为false时,Spring会尝试执行自动装配,但是如果没有匹配的bean的话,Spring将会让这个bean处于未装配的状 态。
把required属性设置为false时,你需要谨慎对待。如果在你的代码中没有进行null检查的话,这个处于未装配状态的属性 有可能会出现NullPointerException。
如果有多个bean都能满足依赖关系的话,Spring将会抛出一个异常, 表明没有明确指定要选择哪个bean进行自动装配。
@Autowired是Spring特有的注解。也可以考虑将其替换为@Inject,Spring同时支持@Inject和 @Autowired。尽管@Inject和@Autowired之间有着一些细微的 差别,但是在大多数场景下,它们都是可以互相替换的。
1.3 验证自动装配
需使用System Rules库,这里使用的是system-rules-1.17.0.jar, Junit版本是4.1x,低版本的JUnit跟rules库部匹配,可能会报错:
package com.springinaction.ch02.autoconfig.Test;
import static org.junit.Assert.*;
import org.junit.Rule;
import org.junit.Test;
import org.junit.contrib.java.lang.system.SystemOutRule;
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 com.springinaction.ch02.autoconfig.CDPlayerConfig;
import com.springinaction.ch02.autoconfig.CompactDisc;
import com.springinaction.ch02.autoconfig.MediaPlayer;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes=CDPlayerConfig.class)
public class CDPlayerTest {
@Rule
public final SystemOutRule log = new SystemOutRule().enableLog();
@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\r\n",
log.getLog());
}
}
JUnit4中包含两个注解@Rule和@ClassRule用于修饰Field或返回Rule的 Method,Rule是一组实现了TestRule接口的共享类,提供了验证、监视TestCase和外部资源管理等能力。JUnit提供了以下几个Rule实现,必要时也可以自己实现Rule。Verifier: 验证测试执行结果的正确性。
ErrorCollector: 收集测试方法中出现的错误信息,测试不会中断,如果有错误发生测试结束后会标记失败。
ExpectedException: 提供灵活的异常验证功能。
Timeout: 用于测试超时的Rule。
ExternalResource: 外部资源管理。
TemporaryFolder: 在JUnit的测试执行前后,创建和删除新的临时目录。
TestWatcher: 监视测试方法生命周期的各个阶段。
TestName: 在测试方法执行过程中提供获取测试名字的能力。
简单的说就是提供了测试用例执行过程中一些通用功能的共享的能力,使我们不必重复编写一些功能类似的代码。JUnit用于标注Rule的注解包括@Rule和@ClassRule,区别在于作用域不同@Rule的作用域是测试方法,@ClassRule则是测试Class。下面简单介绍下上述Rule的使用。
测试结果为通过。
二、通过Java装配bean
尽管很多场景下通过组件扫描和自动装配实现Spring的自动化配置是更为推荐的方式,但有时候自动化配置的方案行不通,因此需要明确配置Spring。比如说,你想要将第三方库中的组件装配搭配你的应用中,这种情况下,是没有办法在它的类上添加@Component和@Autowired注解的,指南采用显示装配的方式。显示装配有两种方案:Java和XML。在进行显示配置时,JavaConfig时更好的方案,因为它更为强大、类型安全并且对重构友好。因为它就是Java代码,就像应用程序中的其他Java代码一样。
JavaConfig与其他的Java代码有所区别,在概念上,它与应用程序中的业务逻辑和领域代码是不同的。尽管它与其他的组件一样 都使用相同的语言进行表述,但JavaConfig是配置代码。这意味着它不应该包含任何业务逻辑,JavaConfig也不应该侵入到业务逻辑代码之中。尽管不是必须的,但通常会将JavaConfig放到单独的包中,使它与其他的应用程序逻辑分离开来,这样对于它的意图就不会产生困惑了。
2.1 声明简单的bean
SgtPeppers类中移除@Component注释:
package com.springinaction.ch02.javaconfig;
public class SgtPeppers implements CompactDisc {
private String title = "Sgt. Pepper's Lonely Hearts Club Band";
private String artist = "The Beatles";
@Override
public void play() {
// TODO Auto-generated method stub
System.out.println("Playing " + title + " by " + artist);
}
}
要在JavaConfig中声明bean(没有其他依赖),我们需要编写一个方法,这个方法会创建所需类型的实例,然后给这个方法添加@Bean注解,@Bean注解会告诉Spring这个方法将会返回一个对象,该对象要注册为Spring应用上下文中的bean。方法体中包含了最终产生bean实例的逻辑。默认情况下,bean的ID与带有@Bean注解的方法名是一样的。在本例中,bean的名字将会是sgtPeppers。如果你想为其设置成一个不同的名字的话,那么可以重命名该方法,也可以通过name属性指定。
@Bean(name="lonelyheartsClubBand")
public CompactDisc sgtPeppers(){
return new SgtPeppers();
}
2.2 借助Javaconfig实现bean之间的依赖注入
前面声明CompactDisc没有其他依赖,非常简单。但现在要声明CDPlayer bean,它依赖于CompactDisc。
package com.springinaction.ch02.javaconfig;
import org.springframework.beans.factory.annotation.Autowired;
public class CDPlayer implements MediaPlayer {
private CompactDisc cd;
@Autowired
public CDPlayer(CompactDisc cd) {
this.cd = cd;
}
public void play() {
cd.play();
}
}
在JavaConfig中装配bean的最简单方式就是引用创建bean的方法。例如,下面就是一种声明CDPlayer的可行方案:
@Bean public CDPlayer cdPlay(){ return new CDPlayer(sgtPeppers()); }cdPlayer()的方法体与sgtPeppers()稍微有些区别。在这里并没有使用默认的构造器构建实例,而是调用了需要传入CompactDisc对象的构造器来创建CDPlayer实例。看起来,CompactDisc是通过调用sgtPeppers()得到的,但情况并非完全如此。因为sgtPeppers()方法上添加了@Bean注解,Spring将会拦截所有对它的调用,并确保直接返回该方法所创建的bean,而不是每次都对其进行实际的调用 。
默认情况下,Spring中的bean都是单例的,我们并没有必要为第二个CDPlayer bean创建完全相同的SgtPeppers实例。所以,Spring会拦截对sgtPeppers()的调用并确保返回的是Spring所创建的bean,也就是Spring本身在调用sgtPeppers()时所创建的CompactDiscbean。因此,两个CDPlayer bean会得到相同的SgtPeppers实例。
还可以通过构造器来引用其他bean:
@Bean public CDPlayer cdPlayer(CompactDisc compactDisc) { return new CDPlayer(compactDisc); }cdPlayer()方法请求一个CompactDisc作为参数。当Spring调用cdPlayer()创建CDPlayerbean的时候,它会自动装配一个CompactDisc到配置方法之中。然后,方法体就可以按照合适的方式来使用它。借助这种技术,cdPlayer()方法也能够将CompactDisc注入到CDPlayer的构造器中,而且不用明确引用CompactDisc的@Bean方法。
通过这种方式引用其他的bean通常是最佳的选择,因为它不会要求将CompactDisc声明到同一个配置类之中。在这里甚至没有要求CompactDisc必须要在JavaConfig中声明,实际上它可以通过组件扫描功能自动发现或者通过XML来进行配置。你可以将配置分散到多个配置类、XML文件以及自动扫描和装配bean之中,只要功能完整健全即可。不管CompactDisc是采用什么方式创建出来的,Spring都会将其传入到配置方法中,并用来创建CDPlayer bean。
所以,CDPlayerConfig.java:
package com.springinaction.ch02.javaconfig; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration //声明为配置类 public class CDPlayerConfig { @Bean public CompactDisc sgtPeppers() { return new SgtPeppers(); } @Bean public CDPlayer cdPlayer(CompactDisc compactDisc) { return new CDPlayer(compactDisc); } }通过CDPlayerTest 验证是通过的。
三、通过XML装备bean
3.1 创建XML配置规范
在XML配置中,要创建一个XML文件,并且要以<beans>元素为根;在使用XML时,需要在配置文件的顶部声明多个XML模式(XSD)文件,这些文件定义了配置Spring的XML元素。
3.2 声明一个简单的<bean>
package com.springinaction.ch02.xmlconfig; 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); } }
按照如下方式声明CompactDisc bean:
<bean class="com.springinaction.ch02.xmlconfig.SgtPeppers" />bean的类通过class属性来指定的,并且要使用全限定的类名。因为没有明确给定ID,所以这个bean将会根据全限定类名来进行命名。在本例中,bean的ID将会是“com.springinaction.ch02.xmlconfig.SgtPeppers#0”。其中,“#0”是一个计数的形式,用来区分相同类型的其他bean。如果你声明了另外一个SgtPeppers,并且没有明确进行标识,那么它自动得到的ID将会是“com.springinaction.ch02.xmlconfig.SgtPeppers#1”。
可借助id属性来设置bean的名字:
<bean id="compactDisc" class="com.springinaction.ch02.xmlconfig.SgtPeppers" />
3.3 借助构造器注入初始化bean
package com.springinaction.ch02.xmlconfig; import org.springframework.beans.factory.annotation.Autowired; public class CDPlayer implements MediaPlayer { private CompactDisc cd; public CDPlayer(CompactDisc cd) { this.cd = cd; } public void play() { cd.play(); } }
在Spring XML配置中,只有一种声明bean的方式:使用<bean> 元素并指定class属性。Spring会从这里获取必要的信息来创建bean。但是,在XML中声明DI时,会有多种可选的配置方案和风格。具体到构造器注入,有两种基本的配置方案可供选择:
1)、<constructor-arg>元素
2)、使用Spring 3.0所引入的c-命名空间
3.3.1 <constructor-arg>构造器注入bean引用
<bean id="cdPlayer" class="com.springinaction.ch02.xmlconfig.CDPlayer"> <constructor-arg ref="compactDisc"/>
然后,分别将两个bean配置到默认的XML文件applicationContext.xml中:
<bean id="compactDisc" class="com.springinaction.ch02.xmlconfig.SgtPeppers"/> <bean id="cdPlayer" class="com.springinaction.ch02.xmlconfig.CDPlayer"> <constructor-arg ref="compactDisc"/> </bean>
当Spring遇到<bean>元素时,会创建一个CDPlayer实例。<constructor-arg>元素会告知Spring要将一个ID为compactDisc的bean引用传递到CDPlayer的构造器中。
作为替代方案,也可以使用Spring的c-命名空间。c-命名空间是在Spring 3.0开始引入的,它是在XML中更为简洁地描述构造器参数的方式。要使用它的话,必须在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:c="http://www.springframework.org/schema/c" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd "> ... </beans>
模式声明之后,就可以用它来配置构造器参数了,如下所示:
<bean id="cdPlayer" class="com.springinaction.ch02.xmlconfig.CDPlayer" c:cd-ref="compactDisc" />属性名以“c:”开头,也就是命名空间的前缀。接下来就是要装配的构造器参数名,在此之后是“-ref”,这是一个命名的约定,它会告诉Spring,正在装配的是一个bean的引用,这个bean的名字是compactDisc,而不是字面量“compactDisc”。
<bean id="cdPlayer" class="com.springinaction.ch02.xmlconfig.CDPlayer" c:_0-ref="compactDisc" />如果构造器有多个参数的话,这尤其有用。
只有一个构造器参数时,我们还有另外一个方案——根本不用去标示参数:
<bean id="cdPlayer" class="com.springinaction.ch02.xmlconfig.CDPlayer" c:_-ref="compactDisc" />
验证XML方式装配bean(通过构造器传递引用参数):
package com.springinaction.ch02.xmlconfig.test; import static org.junit.Assert.*; import org.junit.Rule; import org.junit.Test; import org.junit.contrib.java.lang.system.SystemOutRule; 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 com.springinaction.ch02.xmlconfig.MediaPlayer; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("/applicationContext.xml") public class ConstructorArgReferenceTest { @Rule public final SystemOutRule log = new SystemOutRule().enableLog(); @Autowired private MediaPlayer player; @Test public void play() { player.play(); assertEquals("Playing Sgt. Pepper's Lonely Hearts Club Band by The Beatles\r\n", log.getLog()); } }
无需像前面的方式那样借助CDPlayerConfig来装配bean,@ContextConfiguration("/applicationContext.xml") 通过指定配置文件路径,让系统在这个XML文件中寻找bean并实例化bean,自动装配。
加载context有三种方式:(1)通过@ContextConfiguration的value属性指定XML路径,刚才就是使用这种方式
(2)在测试类所在的路径下创建xml文件,文件以“测试类名-
context.xml”命名
,接下来的例子中会使用到(3)通过@ContextConfiguration的locations属性指定XML路径,可以指定多个XML文件,比如:
@ContextConfiguration(locations = { "/com/javarticles/spring/context2.xml", "/com/javarticles/spring/context3.xml" })
3.3.2 将字面量注入到构造器中
引入CompaxtDisc的新实现:
package com.springinaction.ch02.xmlconfig; import java.util.List; public class BlankDisc implements CompactDisc { private String title; private String artist; public BlankDisc(String title, String artist) { this.title = title; this.artist = artist; } public void play() { System.out.println("Playing " + title + " by " + artist); } }
验证构造器字面量注入:
package com.springinaction.ch02.xmlconfig.test; import static org.junit.Assert.*; import org.junit.Rule; import org.junit.Test; import org.junit.contrib.java.lang.system.SystemOutRule; 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 com.springinaction.ch02.xmlconfig.MediaPlayer; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration public class ConstructorArgValueTest { @Rule public final SystemOutRule log = new SystemOutRule().enableLog(); @Autowired private MediaPlayer player; @Test public void play() { player.play(); assertEquals("Playing Sgt. Pepper's Lonely Hearts Club Band by The Beatles\r\n", log.getLog()); } }
在ConstructorArgValueTest类所在的目录下创建配置文件ConstructorArgValueTest-context.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:c="http://www.springframework.org/schema/c" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd"> <bean id="blankDisc" class="com.springinaction.ch02.xmlconfig.BlankDisc"> <constructor-arg name="title" value="Sgt. Pepper's Lonely Hearts Club Band" /> <constructor-arg name="artist" value="The Beatles"/> </bean> <bean id="cdPlayer" class="com.springinaction.ch02.xmlconfig.CDPlayer" c:_-ref="blankDisc" /> </beans>
使用<constructor-arg>元素的value属性,表明给定的值要以字面量的形式注入到构造器之中。验证通过。
3.3.3 装配集合:
<constructor-arg>能够实现,c-命名空间却无法做到的,即集合装配到构造器参数中。
package com.springinaction.ch02.xmlconfig.collections; import java.util.List; import com.springinaction.ch02.xmlconfig.CompactDisc; public class BlankDisc implements CompactDisc { private String title; private String artist; //磁道列表 private List<String> tracks; public BlankDisc(String title, String artist, List<String> tracks){ this.title=title; this.artist=artist; this.tracks = tracks; } public void play() { System.out.println("Playing " + title + " by " + artist); for (String track : tracks) { System.out.println("-Track: " + track); } } }
配置文件ConstructorArgCollectionTest-context.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:c="http://www.springframework.org/schema/c" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd"> <bean id="blankDisc" class="com.springinaction.ch02.xmlconfig.collections.BlankDisc"> <constructor-arg name="title" value="Sgt. Pepper's Lonely Hearts Club Band" /> <constructor-arg name="artist" value="The Beatles"/> <constructor-arg> <list> <value>Sgt. Pepper's Lonely Hearts Club Band</value> <value>With a Little Help from My Friends</value> <value>Fixing a Hole</value> </list> </constructor-arg> </bean> <bean id="cdPlayer" class="com.springinaction.ch02.xmlconfig.CDPlayer" c:_-ref="blankDisc" /> </beans><list>元素是<constructor-arg>的子元素,这表明一个包含值的列表将会传递到构造器中。其中,<value>元素用来指定列表中的每个元素。也可以使用<ref>元素替代<value>,实现bean引用。
当构造器参数的类型是java.util.List时,使用<list>元素是合情合理的。也可以按照同样的方式使用<set>元素。
如果是Set的话,所有重复的值都会被忽略掉,存放顺序也不会得以保证。不过无论在哪种情况下,<set>或<list>都可以用来装配List、Set甚至数组。
验证构造器装配集合:
package com.springinaction.ch02.xmlconfig.test; import static org.junit.Assert.*; import org.junit.Rule; import org.junit.Test; import org.junit.contrib.java.lang.system.SystemOutRule; 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 com.springinaction.ch02.xmlconfig.MediaPlayer; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration public class ConstructorArgCollectionTest { @Rule public final SystemOutRule log = new SystemOutRule().enableLog(); @Autowired private MediaPlayer player; @Test public void play() { player.play(); assertEquals( "Playing Sgt. Pepper's Lonely Hearts Club Band by The Beatles\r\n" + "-Track: Sgt. Pepper's Lonely Hearts Club Band\r\n" + "-Track: With a Little Help from My Friends\r\n" + "-Track: Fixing a Hole\r\n", log.getLog()); } }
package com.springinaction.ch02.xmlconfig.properties; import org.springframework.beans.factory.annotation.Autowired; import com.springinaction.ch02.xmlconfig.CompactDisc; import com.springinaction.ch02.xmlconfig.MediaPlayer; public class CDPlayer implements MediaPlayer { private CompactDisc compactDisc; public void setCompactDisc(CompactDisc compactDisc) { this.compactDisc = compactDisc; } public void play() { compactDisc.play(); } }
CDPlayer该选择构造器注入还是属性注入呢?作为一个通用的规则,倾向于对强依赖使用构造器注入,而对可选性的依赖使用属性注入( 可选择性:即便没有将CompactDisc装入进去,CDPlayer依然还能具备一些有限的功能,这样就是选择性)。
a、引用注入使用<property>元素
<bean id="cdPlayer" class="com.springinaction.ch02.xmlconfig.properties.CDPlayer" > <property name="compactDisc" ref="compactDisc"></property> </bean><property>元素为属性的Setter方法所提供的功能。上面是使用的引用传递值,引用了ID为sgtPeppers的bean(通过ref属性),并将其注入到cd属性中(通过setCompactDisc()方法)。
b、引用注入使用p命名空间
<?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:p="http://www.springframework.org/schema/p" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.1.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.1.xsd "> ... </beans>
XML文件中声明了p-命名空间,因此可以使用p-命名空间来装配compactDisc:
<bean id="cdPlayer" class="com.springinaction.ch02.xmlconfig.CDPlayer" p:cd-ref="compactDisc"/>
但p-命名空间不能用来装配集合。
将字面量注入到属性中
属性也可以注入字面量,这与构造器参数非常类似。重新写一个BlankDisc
package com.springinaction.ch02.xmlconfig; import java.util.List; public class BlankDisc implements CompactDisc { private String title; private String artist; //磁道列表 private List<String> tracks; public void setTitle(String title) { this.title = title; } public void setArtist(String artist) { this.artist = artist; } public void setTracks(List<String> tracks) { this.tracks = tracks; } public void play() { System.out.println("Playing " + title + " by " + artist); for(String track : tracks) { System.out.println("-Track: " + track); } } }配置如下:
<bean id="compactDisc" class="com.springinaction.ch02.xmlconfig.properties.BlankDisc"> <property name="title" value="Sgt. Pepper's Lonely Hearts Club Band"/> <property name="artist" value="The Beatles"/> <property name="tracks"> <list> <value>Sgt. Pepper's Lonely Hearts Club Band</value> <value>With a Little Help from My Friends</value> <value>Fixing a Hole</value> </list> </property> </bean>
还可以使用Spring util-命名空间的一些功能来简化BlankDisc bean。
使用util-命名空间之前需声明:
<?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:c="http://www.springframework.org/schema/c" xmlns:util="http://www.springframework.org/schema/util" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.1.xsd"> ... </beans>
<util:list>元素会创建一个列表的bean。可以将磁道列表tracks移除到BlankDisc bean之外,并将其声明到单独的bean之中,如下所示:
<util:list id="trackList"> <value>Sgt. Pepper's Lonely Hearts Club Band</value> <value>With a Little Help from My Friends</value> <value>Fixing a Hole</value> </util:list> <bean id="compactDisc" class="com.springinaction.ch02.xmlconfig.properties.BlankDisc"> <property name="title" value="Sgt. Pepper's Lonely Hearts Club Band"/> <property name="artist" value="The Beatles"/> <property name="tracks" ref="trackList" /> </bean>
验证属性设置
package com.springinaction.ch02.xmlconfig.test; import static org.junit.Assert.*; import org.junit.Rule; import org.junit.Test; import org.junit.contrib.java.lang.system.SystemOutRule; 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 com.springinaction.ch02.xmlconfig.MediaPlayer; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration public class PropertyValueTest { @Rule public final SystemOutRule log = new SystemOutRule().enableLog(); @Autowired private MediaPlayer player; @Test public void play() { player.play(); assertEquals( "Playing Sgt. Pepper's Lonely Hearts Club Band by The Beatles\r\n" + "-Track: Sgt. Pepper's Lonely Hearts Club Band\r\n" + "-Track: With a Little Help from My Friends\r\n" + "-Track: Fixing a Hole\r\n", log.getLog()); } }
四、导入和混合配置
4.1在JavaConfig中引入xml配置
把一个JavaConfig类拆成多个进行组合。将CompactDisc 从CDPlayerConfig拆分出来,定义到自己的CDConfig类中:
package com.springinaction.ch02.mixedconfig; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class CDConfig { @Bean public CompactDisc compactDisc() { return new SgtPeppers(); } }
现在需要一种方式,将CompactDisc bean 跟CDPlayer bean组合在一起。一种方法就是在CDPlayerConfig中使用@Import注解导入CDConfig:
package com.springinaction.ch02.mixedconfig; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; @Configuration @Import(CDConfig.class) public class CDPlayerConfig { @Bean public CDPlayer cdPlayer(CompactDisc compactDisc) { return new CDPlayer(compactDisc); } }或者采用一个更好的办法,也就是不在CDPlayerConfig中使用@Import,而是创建一个更高级别的SoundSystemConfig,在这个类中使用@Import将两个配置类组合在一起。
package com.springinaction.ch02.mixedconfig; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; @Configuration @Import({CDPlayerConfig.class, CDConfig.class}) public class SoundSystemConfig { }
验证:
package com.springinaction.ch02.mixedconfig.test; import static org.junit.Assert.*; import org.junit.Rule; import org.junit.Test; import org.junit.contrib.java.lang.system.SystemOutRule; 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 com.springinaction.ch02.mixedconfig.CompactDisc; import com.springinaction.ch02.mixedconfig.MediaPlayer; import com.springinaction.ch02.mixedconfig.SoundSystemConfig; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes=SoundSystemConfig.class) public class CDPlayerTest { @Rule public final SystemOutRule log = new SystemOutRule().enableLog(); @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\r\n", log.getLog()); } }
现在假设希望通过XML来配置BlankDisc bean,如下所示:
<?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:c="http://www.springframework.org/schema/c" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd"> <bean id="blankDisc" class="com.springinaction.ch02.mixedconfig.BlankDisc"> <constructor-arg name="title" value="Sgt. Pepper's Lonely Hearts Club Band" /> <constructor-arg name="artist" value="The Beatles"/> <constructor-arg> <list> <value>Sgt. Pepper's Lonely Hearts Club Band</value> <value>With a Little Help from My Friends</value> <value>Fixing a Hole</value> </list> </constructor-arg> </bean> </beans>
该配置文件名为:cd-config.xml,位于BlankDisc所在的目录下。修改SoundSystemConfig,使用@ImportResource注解,让它同时加载XML配置跟Java配置:
package com.springinaction.ch02.mixedconfig; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; import org.springframework.context.annotation.ImportResource; @Configuration @Import(CDPlayerConfig.class) @ImportResource("classpath:com/springinaction/ch02/mixedconfig/cd-config.xml") public class SoundSystemConfig { }
注意:@ImportResource("classpath:com/springinaction/ch02/mixedconfig/cd-config.xml") ,XML文件如果不是在根目录下,请务必加上classpath: 。
验证代码:
package com.springinaction.ch02.mixedconfig.test; import static org.junit.Assert.assertEquals; import org.junit.Rule; import org.junit.Test; import org.junit.contrib.java.lang.system.SystemOutRule; 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 com.springinaction.ch02.mixedconfig.MediaPlayer; import com.springinaction.ch02.mixedconfig.SoundSystemConfig; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes=SoundSystemConfig.class) public class ImportXmlConfigTest { @Rule public final SystemOutRule log = new SystemOutRule().enableLog(); @Autowired private MediaPlayer player; @Test public void play() { player.play(); assertEquals( "Playing Sgt. Pepper's Lonely Hearts Club Band by The Beatles\r\n" + "-Track: Sgt. Pepper's Lonely Hearts Club Band\r\n" + "-Track: With a Little Help from My Friends\r\n" + "-Track: Fixing a Hole\r\n", log.getLog()); } }
4.2在中xml引入JavaConfig配置
前面已经将BlankDisc bean拆分到自己的配置文件中,该文件名为cd-config.xml。CDPlayer bean也通过XML配置,并且在XML配置文件中使用<import>元素来引用BlankDisc bean的配置文件:
<?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:c="http://www.springframework.org/schema/c" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd"> <import resource="classpath:com/springinaction/ch02/mixedconfig/cd-config.xml"/> <bean id="cdPlayer" class="com.springinaction.ch02.mixedconfig.CDPlayer" c:cd-ref="blankDisc"></bean> </beans>
文件名为cdplayer-config.xml,在CDPlayer所在的目录下。
现在假设不再将Compact bean配置在XML文件之中,而是将其配置到JavaConfig中:
package com.springinaction.ch02.mixedconfig; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class CDConfig { @Bean public CompactDisc compactDisc() { return new SgtPeppers(); }
cdplayer-config.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:c="http://www.springframework.org/schema/c" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd"> <bean class="com.springinaction.ch02.mixedconfig.CDConfig"></bean> <bean id="cdPlayer" class="com.springinaction.ch02.mixedconfig.CDPlayer" c:cd-ref="compactDisc"></bean> </beans>
验证:
package com.springinaction.ch02.mixedconfig.test; import static org.junit.Assert.assertEquals; import org.junit.Rule; import org.junit.Test; import org.junit.contrib.java.lang.system.SystemOutRule; 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 com.springinaction.ch02.mixedconfig.MediaPlayer; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("classpath:com/springinaction/ch02/mixedconfig/cdplayer-config.xml") public class ImportJavaConfigTest { @Rule public final SystemOutRule log = new SystemOutRule().enableLog(); @Autowired private MediaPlayer player; @Test public void play() { player.play(); assertEquals( "Playing Sgt. Pepper's Lonely Hearts Club Band by The Beatles\r\n", log.getLog()); } }
也可以创建一个更高层次的配置文件,这个配置文件不声明任何的bean,只是负责将两个或更多的配置组合起来。先将CDConfig bean从之前的cdplayer-config.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:c="http://www.springframework.org/schema/c" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd"> <bean id="cdPlayer" class="com.springinaction.ch02.mixedconfig.CDPlayer" c:cd-ref="compactDisc"></bean> </beans>
创建soundsystem-config.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:c="http://www.springframework.org/schema/c" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd"> <bean class="com.springinaction.ch02.mixedconfig.CDConfig"></bean> <import resource="classpath:com/springinaction/ch02/mixedconfig/cdplayer-config.xml"/> </beans>
验证:
package com.springinaction.ch02.mixedconfig.test; import static org.junit.Assert.assertEquals; import org.junit.Rule; import org.junit.Test; import org.junit.contrib.java.lang.system.SystemOutRule; 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 com.springinaction.ch02.mixedconfig.MediaPlayer; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("classpath:com/springinaction/ch02/mixedconfig/soundsystem-config.xml") public class ImportJavaConfigTest { @Rule public final SystemOutRule log = new SystemOutRule().enableLog(); @Autowired private MediaPlayer player; @Test public void play() { player.play(); assertEquals( "Playing Sgt. Pepper's Lonely Hearts Club Band by The Beatles\r\n", log.getLog()); } }
推荐阅读
-
SpringInAction笔记(二)—— 装配Bean
-
Spring定义Bean类(二) Enable*注解装配
-
Spring In Action(二):基于XML配置装配bean
-
第二章 装配Bean(Spring in action,3th)
-
Spring in action 读书笔记(第二章)bean的装配(java类显式配置和spring-test单元测试的使用)
-
Spring学习(二)---在IoC容器中装配Bean 博客分类: Spring 3.x 企业应用开发笔记 springbeanioc框架
-
Spring入门(二):自动化装配bean
-
Spring学习二:通过java配置装配Bean
-
Spring学习二:通过java配置装配Bean
-
Spring IOC学习笔记(二)—— Bean装配方式