第二章 bean装配
Spring配置的可选方案
1.在XML中进行显式配置。
2.在Java中进行显式配置。
3.隐式的bean发现机制和自动装配。
自动化装配bean
Spring从两个角度来实现自动化装配:
组件扫描(component scanning) : Spring会自动发现应用上下文中所创建的bean。
自动装配(autowiring) : Spring自动满足bean之间的依赖。
创建可被发现的bean
package soundsystem; public interface CompactDisc { public void play(); }
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"; @Override public void play() { System.out .println("Playing " + title + "by" + artist); } }
@ComponentScan注解启用了组件扫描
package soundsystem; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; @Configuration @ComponentScan public class CDPlayerConfig { }
通过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.xsd"> <context:component-scan base-package="soundsystem" /> </beans>
运行:
package soundsystem; import 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=CDPlayerConfig.class) public class CDtest { @Autowired private CompactDisc cd; @Test public void cdShouldNotNull() { Assert.assertNotNull(cd); cd.play(); } }
为组件扫描的bean命名
Spring应用上下文中所有的bean都会给定一个ID。 在前面的例子中, 尽管我们没有明确地为SgtPeppersbean设置ID, 但Spring会根据类名为
其指定一个ID。 具体来讲, 这个bean所给定的ID为sgtPeppers, 也就是将类名的第一个字母变为小写。
如果想为这个bean设置不同的ID, 你所要做的就是将期望的ID作为值传递给@Component注解。 比如说, 如果想将这个bean标识
为lonelyHeartsClub, 那么你需要将SgtPeppers类的@Component注解配置为如下所示:
还有另外一种为bean命名的方式, 这种方式不使用@Component注解, 而是使用Java依赖注入规范(Java Dependency Injection) 中所提供
的@Named注解来为bean设置ID:
package soundsystem; import org.springframework.stereotype.Component; //import javax.inject.Named; @Component("lonelyHeartsClub") //@Named("lonelyHearsClub") public class SgtPeppers implements CompactDisc
设置扫描的基础包
package soundsystem; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; @Configuration @ComponentScan("soundsystem") @ComponentScan(basePackages = "soundsystem") @ComponentScan(basePackages = {"soundsystem","video"}) @ComponentScan(basePackageClasses = {CDPlayer.class,SgtPeppers.class}) public class CDPlayerConfig { }
通过为bean添加注解实现自动装配
通过自动装配, 将一个CompactDisc注入到CDPlayer之中
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; } @Override public void play() { cd.play(); } }
@Autowired注解不仅能够用在构造器上, 还能用在属性的Setter方法上。 比如说, 如果CDPlayer有一个setCompactDisc()方法, 那么可
以采用如下的注解形式进行自动装配:
@Autowired public void setCompactDisc(CompactDisc cd) { this.cd = cd; }
@Autowired注解可以用在类的任何方法上。 假设CDPlayer类有一个insertDisc()方法, 那
么@Autowired能够像在setCompactDisc()上那样, 发挥完全相同的作用:
@Autowired public void insertDisc(CompactDisc cd) { this.cd = cd; }
如果没有匹配的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。
通过JAVA代码装配bean
创建配置类
package soundsystem; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class CDPlayerConfig { /*@Bean public CompactDisc sgtPeppers() { return new SgtPeppers(); }*/ @Bean("lonelyHeartsClub") public CompactDisc sgtPeppers() { return new SgtPeppers(); } }
看起来, CompactDisc是通过调用sgtPeppers()得到的, 但情况并非完全如此。 因为sgtPeppers()方法上添加了@Bean注解, Spring
将会拦截所有对它的调用, 并确保直接返回该方法所创建的bean, 而不是每次都对其进行实际的调用。
比如说, 假设你引入了一个其他的CDPlayerbean, 它和之前的那个bean完全一样
@Bean public CDPlayer cdPlayer() { return new CDPlayer(sgtPeppers()); } @Bean public CDPlayer anotherCDPlayer() { return new CDPlayer(sgtPeppers()); }
在这里,cdPlayer()方法请求一个CompactDisc作为参数。 当Spring调用cdPlayer()创建CDPlayerbean的时候, 它会自动装配一
个CompactDisc到配置方法之中。 然后, 方法体就可以按照合适的方式来使用它。 借助这种技术, cdPlayer()方法也能够
将CompactDisc注入到CDPlayer的构造器中, 而且不用明确引用CompactDisc的@Bean方法。
@Bean public CDPlayer cdPlayer(CompactDisc compactDisc) { return new CDPlayer(compactDisc); }
通过XML装配bean
创建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" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> </beans>
声明一个简单的<bean>
<bean class="soundsystem.SgtPeppers"/>
因为没有明确给定ID, 所以这个bean将会根据全限定类名来进行命名。 在本例中, bean的ID将会是“soundsystem.SgtPeppers#0”。 其
中, “#0”是一个计数的形式, 用来区分相同类型的其他bean。 如果你声明了另外一个SgtPeppers, 并且没有明确进行标识, 那么它自动得到
的ID将会是“soundsystem.SgtPeppers#1”。
借助id属性, 为每个bean设置一个你自己选择的名字:
<bean id="compactDisc" class="soundsystem.SgtPeppers"/>
借助构造器注入初始化bean
<bean class="soundsystem.CDPlayer"> <constructor-arg ref="CompactDisc"/> </bean>
设置属性
<bean class="soundsystem.CDPlayer"> <property name="compactDisc" ref="compactDisc"/> </bean>
在JavaConfig中引用XML配置
@Import(CDConfig.class)
@ImportResource
package soundsystem; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class CDConfig { @Bean public CompactDisc sgtPeppers() { return new SgtPeppers(); } }
package soundsystem; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; import org.springframework.context.annotation.ImportResource; @Configuration @Import(CDConfig.class) @ImportResource("classpath:bean.xml") public class CDPlayerConfig { @Bean public CDPlayer cdPlayer(CompactDisc compactDisc) { return new CDPlayer(compactDisc); } }
在XML配置中引用JavaConfig
<bean class="soundsystem.CDConfig"/>
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:c="http://www.springframework.org/schema/c" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean class="soundsystem.CDConfig"/> <bean id="cdPlayer" class="soundsystem.CDPlayer"> <property name="compactDisc" ref="compactDisc"/> </bean> </beans>
<import resource="cdplayer-config.xml"/>
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:c="http://www.springframework.org/schema/c" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean class="soundsystem.CDConfig"/> <import resource="cdplayer-config.xml"/> </beans>