在Spring中,对象无需自己查找或创建与其所关联的其他对象。相反,容器负责把需要相互协作的对象引用赋予各个对象。
创建应用对象之间协作关系的行为通常称为装配(wiring),这也是
依赖注入(DI)的本质。
在Spring中装配bean有多种方式。我们先花一点时间来介绍一下配置Spring容器最常见的三种方法。
-在XML中进行显式配置。
-在Java中进行显式配置。
-隐式的bean发现机制和自动装配。
我的建议是尽可能地使用自动配置的机制。显式配置越少越好。当你必须要显式配置bean的时候(比如,有些源码不是由你来维护的,而当你需要为这些代码配置bean的时候),我推荐使用类型安全并且比XML更加强大的JavaConfig。最后,只有当你想要使用便利的XML命名空间,并且在JavaConfig中没有同样的实现时,才应该使用XML。
自动化装配 bean
Spring从两个角度来实现自动化装配:
-组件扫描(component scanning):Spring会自动发现应用上下文中所创建的bean。
-自动装配(autowiring):Spring自动满足bean之间的依赖。
我们用一个 CD 播放器来阐述如何装配 bean:
先建立 CD 的概念。
package soundsystem;
public interface CompactDisc{
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";
public void play(){
System.out.println("Playing" + title + "by" + artist);
}
}
SgtPeppers类上使用了@Component注解。这个简单的注解表明该类会作为组件类,并告知Spring要为这个类创建bean。没有必要显式配置SgtPeppersbean,因为这个类使用了@Component注解,所以Spring会为你把事情处理妥当。
不过,组件扫描默认是不启用的。我们还需要显式配置一下Spring,从而命令它去寻找带有@Component注解的类,并为其创建bean。
package soundsystem;
import org.springframework.context.annotation.componentScan;
import org.springframework.context.annotation.Configuration;
@Configuration
@ComponentScan
public class CDPlayerConfig{
}
它使用了@ComponentScan注解,这个注解能够在Spring中启
用组件扫描。如果没有其他配置的话,@ComponentScan默认会扫描与配置类相同的包。因为CDPlayerConfig类位于soundsystem包中,因此Spring将会扫描这个包以及这个包下的所有子包,查找带有@Component注解的类。这样的话,就能发现CompactDisc,并且会在Spring中自动为其创建一个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"
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://wwww.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="soundsystem"/>
</beans>
通过 Java 代码装配 bean
尽管在很多场景下通过组件扫描和自动装配实现Spring的自动化配置是更为推荐的方式,但有时候自动化配置的方案行不通,因此需要明确配置Spring。
在这种情况下,你必须要采用显式装配的方式。在进行显式配置的时候,有两种可选方案:Java和XML。
创建配置类
package soundsystem;
import org.springframework.context.annotation.Configuration;
@Configuration
public class CDPlayerConfig{
}
创建JavaConfig类的关键在于为其添加@Configuration注解,@Configuration注解表明这个类是一个配置类,该类应该包含在Spring应用上下文中如何创建bean的细节。
生命简单的 bean
@Bean
public CompactDisc sgtPeppers(){
return new SgtPeppers();
}
@Bean注解会告诉Spring这个方法将会返回一个对象,该对象要注册为Spring应用上下文中的bean。方法体中包含了最终产生bean实例的逻辑。
可以为 bean 指定一个名字:
@Bean(name="lonelyHeartsClubBand")
public CompactDisc sgtPeppers(){
return new SgtPeppers();
}
在JavaConfig中装配bean的最简单方式就是引用创建bean的方法。例如,下面就是一种声明CDPlayer的可行方案:
@Bean
public CDPlayer cdPlayer(){
return new CDPlayer(sgtPeppers());
}
@Bean
public CDPlayer anotherCDPlayer(){
return new CDPlayer(sgtPeppers());
}
这里为什么创建了两个 bean 是为了说明一个事情,Spring将会拦截所有对 sgtPeppers 的调用,并确保直接返回该方法所创建的bean,而不是每次都对其进行实际的调用。Spring中的bean都是单例的,没有必要创建完全相同的第二个实例。
另一种装配方式:
@Bean
public CDPlayer cdPlayer(CompactDisc compactDisc){
return new CDPlayer(compactDisc);
}
在这里,cdPlayer()方法请求一个CompactDisc作为参数。当Spring调用cdPlayer()创建CDPlayerbean的时候,它会自动装配一个CompactDisc到配置方法之中。然后,方法体就可以按照合适的方式来使用它。借助这种技术,cdPlayer()方法也能够将CompactDisc注入到CDPlayer的构造器中,而且不用明确引用CompactDisc的@Bean方法。
Setter 方法注入:
@Bean
public CDPlayer cdPlayer(CompactDisc compactDisc){
CDPlayer cdplayer = new CDPlayer(compactDisc);
cdPlayer.setCompactDisc(compactDisc);
return cdPlayer;
}
通过 XML 装配
最为简单的Spring 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
http://www.springframework.org/schema/context">
<!-- configuration details go here -->
</beans>
声明一个简单的<bean>:
<bean class="soundsystem.SgtPeppers"/>
在本例中,bean的ID将会是“soundsystem.SgtPeppers#0”。其中,“#0”是一个计数的形式,用来区分相同类型的其他bean。如果你声明了另外一个SgtPeppers,并且没有明确进行标识,那么它自动得到的ID将会是“soundsystem.SgtPeppers#1”。
借助构造器注入初始化 bean
在XML中声明DI时,会有多种可选的配置方案和风格。具体到构造器注入,有两种基本的配置方案可供选择:
-<constructor-arg>元素
<bean id="cdPlayer"
class="soundsystem.CDPlayer">
<constructor-arg ref="compactDisc" />
</bean>
-使用Spring 3.0所引入的c-命名空间
<?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 id="compactDisc" class="soundsystem.BlankDisc">
c:cd-ref="compactDisc"/>
</bean>
</beans>
上面这种直接引用了参数的名称,可以用下面方法来替代:(因为不支持数字 所以参数标号前面加下划线)
<bean id="cdPlayer" class="soundsystem.CDPlayer"
c:_0-ref="compactDisc"/>
或者不用标志参数
<bean id="cdPlayer" class="soundsystem.CDPlayer"
c:_-ref="compactDisc"/>
将字面量值装配到构造器之中:
<bean id="compactDisc" class="soundsystem.BlankDisc">
<constructor-arg value="Sgt. Pepper's Lonely Hearts Club Band" />
<constructor-arg value="The Beatles" />
</bean>
用 c 命名空间:
<bean id="compactDisc" class="soundsystem.BlankDisc">
<c:title="Sgt. Pepper's Lonely Hearts Club Band" />
<c:artist="The Beatles" />
</bean>
也可以去掉 “-ref” 后缀
<bean id="compactDisc" class="soundsystem.BlankDisc">
<c:_0="Sgt. Pepper's Lonely Hearts Club Band" />
<c:_1="The Beatles" />
</bean>
装配集合
<?xml version="1.0" encoding="UTF-8" ?>
<bean id="compactDisc" class="soundsystem.BlankDisc">
<constructor-arg value="Sgt.Pepper's Lonely Hearts Club Band"/>
<constructor-arg 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>Lucky in the Sky with Diamonds</value>
<value>Getting Better</value>
<value>Fixing a Hole</value>
......
</list>
</constructor-arg>
</bean>
也可以装配 set 集合,就将 list 换成 set 就行了。
设置属性
下面我们看一下如何用 xml 实现属性注入,假设属性注入的 CDPlayer 如下所示:
public class CDPlayer implements MediaPlayer {
private CompactDisc compactDisc;
@Autowired
public void setCompactDisc(CompactDisc compactDisc){
this.compactDisc = compactDisc;
}
public void play(){
compactDisc.play();
}
}
一般我们对强依赖使用构造器注入,而对可选性的依赖使用属性注入。
<bean id="cdPlayer" class="soundsystem.CDPlayer">
<property name="compactDisc" ref="compactDisc"/>
</bean>
也可以使用 p 命名空间装配
<bean id="cdPlayer" class="soundsystem.CDPlayer" p:compactDisc-ref="compactDisc"/>
首先,属性的名字使用了“p:”前缀,表明我们所设置的是一个属性。接下来就是要注入的属性名。最后,属性的名称以“-ref”结尾,这会提示Spring要进行装配的是引用,而不是字面量。
将字面量注入到属性中
<bean id="compactDisc"
class="soundsystem.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>Lucy in the Sky with Diamonds</value>
<value>Getting Better</value>
<value>Fixing a Hole</value>
<value>She's Leaving Home</value>
<value>Being for the Benefit of Mr. Kite!</value>
<value>Within You Without You</value>
<value>When I'm Sixty-Four</value>
<value>Lovely Rita</value>
<value>Good Morning Good Morning</value>
<value>Sgt. Pepper's Lonely Hearts Club Band (Reprise)</value>
<value>A Day in the Life</value>
</list>
</property>
</bean>
跟之前 c 命名空间一样:
<bean id="compactDisc"
class="soundsystem.properties.BlankDisc"
p:title="Sgt. Pepper's Lonely Hearts Club Band"
p:artist="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>Lucy in the Sky with Diamonds</value>
<value>Getting Better</value>
<value>Fixing a Hole</value>
<value>She's Leaving Home</value>
<value>Being for the Benefit of Mr. Kite!</value>
<value>Within You Without You</value>
<value>When I'm Sixty-Four</value>
<value>Lovely Rita</value>
<value>Good Morning Good Morning</value>
<value>Sgt. Pepper's Lonely Hearts Club Band (Reprise)</value>
<value>A Day in the Life</value>
</list>
</property>
</bean>
注意不能用 p 命名空间装配集合。
参考文献:spring in action