配置Bean容器主要有三种配置机制:
1.在XML中进行显示配置。
2.在java中进行显示配置
3.隐式的bean发现机制和自动装配。
1.首先我们来看下Spring的自动化装配Bean的方式。Spring从两个角度实现自动化装配:
1.组件扫描(component scanning):Spring会自动发现应用上下文中所创建的Bean。
2.自动装配(autowiring):Spring自动满足bean之间的依赖。组件扫描和自动装配的组合能使显示配置降低到最少。
下面贴上通过组件扫描的方式进行创建的Bean(java配置)
package soundsystem; public interface CompactDisc { void play(); }
package soundsystem; import org.springframework.stereotype.Component; @Component//声明为组件类 public class SgtPepperss implements CompactDisc { private String title ="zzf."; private String artist ="the Beatles"; @Override public void play() { //return "titile="+title+"by "+artist; System.out.println("titile="+title+"by "+artist); } }
package soundsystem; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; @Configuration @ComponentScan//能够在Spring中启用组件扫描。默认扫描与配置类相同的包,查找带有@Component注解的包 public class CDPlayerConfig { }
package soundsystem; 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 = CDPlayerConfig.class) public class CDPlayerTest { @Autowired private CompactDisc compactDisc; @Test public void cdShouldNotBeNull() { //System.out.println(compactDisc.play()); assertNotNull(compactDisc); compactDisc.play(); } }
最后输出结果如下:
<?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:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd"> <!-- 自动扫描 --> <context:component-scan base-package="soundsystem" /> </beans>然后将classes=XXXX改为xml文件的路径
package soundsystem; 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 = CDPlayerConfig.class) @ContextConfiguration({"classpath:soundsystem/CD.xml"}) public class CDPlayerTest { @Autowired private CompactDisc compactDisc; @Test public void cdShouldNotBeNull() { //System.out.println(compactDisc.play()); assertNotNull(compactDisc); compactDisc.play(); } }
以上程序需要额外导入这两个jar包
此时我的目录结构是
1.为组件扫描的bean命名
Spring应用上下文中所有的bean都会给定一个ID.如在上文定义的SgtPepperss,Spring会自动根据它的类名为其指明一个ID(sgtPepperss)把类的第一个字母变成小写。
如果想将期望的ID值传递给@Component注解,那么可以将注解配置改为@Component("你期望的ID值")。
还有一种bean命名方式不使用@Component注解,使用@Named注解来为bean设置ID如@Named(“你期望的ID值”)。Spring支持将@Named作为@Component注解的替代方案,在大多数场景中它们可以互相替换。
2.设置组件扫描的基础包
只要在@ComponentScan的Value属性中指明包的名称:@ComponentScan(“soundsystem”)如果想更清晰指明所设置的是基础包@ComponentScan(basePackages="soundsystem"),如果想要设置多个基础包只要将属性设置成一个数组即可@ComponentScan(basePackages={"soundsystem","video"})。
除了将包设置成String类型外另一种方法是将其指定为包中所含的类或接口@ComponentScan(basePackageClasses={CDPlayer.class,DVDPlayer.class}),basePackages属性被替换成了basePackageClasses,同时我们会将这些类所在的包作为组件扫描的基础包。
3.通过为bean添加注解实现自动装配
自动装配是Spring自动满足bean依赖的一种方法,在满足依赖的过程中会在Spring应用上下文中寻找匹配某个bean需求的其他bean.为了声明要进行自动装配我们可以用@Autowired注解。
@Autowired注解不仅能够用在构造器上,还能用在属性的Setter方法上。
并且Setter方法没有什么特殊的地方,@Autowired注解可以用在类的任何方法上。如:
如果没有匹配的bean,那么在应用上下文创建的时候回抛出一个异常,如果想避免异常出现可以将@Autowired的required属性设置为false:@Autowired(required=false)
@Autowired是 Spring特有的注解,在java依赖注入规范中@Inject跟@Autowired有细微的差距但是在大多数场景下可以互相替换。
2.通过java代码装配bean
通常将第三方库中的组件装配到应用中,用组件扫描和自动装配实现是行不通的,因此我们需要明确的配置Spring。这种情况下必须采用显示装配的方式,在显示配置中有两种可选方案:Java和XML方式。
通过JavaConfig显示配置Spring时的关键在于为其添加@Configuration注解,@Configuration注解表明这个类是一个配置类,该类应该在Spring应用上下文中如何创建bean的细节。
此时将@ComponentScan注解移除,再运行上面的代码,测试会失败,并且出现BeanCreationException异常。
1.声明简单的Bean
在JavaConfig中声明bean需要编写一个方法,这个方法会创建所需类型的实例,然后给这个方法添加@Bean注解。@Bean注解会告诉Spring这个方法将返回一个对象,该对象要注册为Spring应用上下文中的bean。方法体包含最终产生bean实例的逻辑。(默认情况下bean的ID与带@Bean注解的方法名是一样的。本例中,bean的名字将会是getSgtPepperss。通过name属性可以指定bean的ID如:@Bean(name="指定的Bean的ID"))。
2.借助JavaConfig实现注入
我们声明的CDPlayerBean它依赖于CompactDisc,在JavaConfig中装配bean最简单的方式是引用创建bean的方法,如:
@Configuration //@ComponentScan(basePackages = {"soundsystem"})//能够在Spring中启用组件扫描。默认扫描与配置类相同的包,查找带有@Component注解的包 public class CDPlayerConfig { @Bean public CompactDisc getSgtPepperss() { return new SgtPepperss(); } @Bean public CDPlayer getCDPlayer(){ return new CDPlayer(getSgtPepperss()); } }
CDPlayer类:
public class CDPlayer implements MedisPlayer { private CompactDisc cd; public CDPlayer(CompactDisc cd) { this.cd = cd; } @Override public void play() { cd.play(); } }
cdPlayer()方法体和sgtPepperss()的区别在于这里没有使用默认的构造器构建实例,而是调用了需要传入CompactDisc对象的构造器来创建CDPlayer实例。看起来CompactDisc是通过sgtPepperss()得到的,事实上因为sgtPepperss()方法上添加了@Bean注解,Spring会拦截所有对它的调用,取保不是每次都对其进行实际的调用。
默认情况下Spring的bean都是单例的。如:
这两个CDPlayerbean会得到相同的SgtPepperss实例。
这个CDPlayer()方法请求一个CompactDisc作为参数,当Spring调用cdPlayer()创建CDPlayerbean的时候,它会自动装配一个CompactDisc到配置方法中。然后方法体可以按照合适的方式来使用它。借助这种技术cdPlayer()方法也能将CompactDisc注入到CDPlayer构造器中,并且不用明确引用CompactDisc的@Bean方法。
方式一:
@Configuration //@ComponentScan(basePackages = {"soundsystem"})//能够在Spring中启用组件扫描。默认扫描与配置类相同的包,查找带有@Component注解的包 public class CDPlayerConfig { /** @Bean public CDPlayer getCDPlayer(){ return new CDPlayer(getSgtPepperss()); }**/ @Bean public CompactDisc getSgtPepperss() { return new SgtPepperss(); } @Bean public CDPlayer getCDPlayer(CompactDisc compactDisc){ return new CDPlayer(compactDisc); } }方式二:
@Configuration @ComponentScan(basePackages = {"soundsystem"})//能够在Spring中启用组件扫描。默认扫描与配置类相同的包,查找带有@Component注解的包 public class CDPlayerConfig { /** @Bean public CDPlayer getCDPlayer(){ return new CDPlayer(getSgtPepperss()); }**/ @Bean public CDPlayer getCDPlayer(CompactDisc compactDisc){ return new CDPlayer(compactDisc); } }
同理通过Setter方法注入CompactDisc的代码:
带有@Bean注解的方法可以采用任何必要的java功能来产生Bean实例,构造器和Setter方法只是@Bean方法的两个简单样例。
3.通过XML装配bean
在使用JavaConfig时候要创建带@Configuration注解的类,在XML配置中药创建一个XML文件,并且以<beans>元素为根。
1.声明一个简单的<bean>
在xml的Spring配置文件中声明一个bean我们要使用<bean>元素,<bean>元素类似于JavaConfig中的@Bean注解。声明一个CompactDisc bean:
<bean class="soundsystem.SgtPeppers" />
创建这个bean的类通过class属性来指定,并且使用全限定的类名。因为没有明确的给定id,所以这个bean将会根据全限定类名来命名。本例中bean的id会是“soundsystem.SgtPeppers#0”其中“#0”是一个计数的形式,用来区分相同类型的其他bean。如果声明了另一个Sgtpeppers,并且没有明确的进行表示,那么他的ID将会是“soundsystem.SgtPeppers#1”。
通常通过id属性进行对bean的配置:<bean id="compactDisc" class="soundsystem.SgtPeppers" />。
2.借助构造器注入初始化bean
在Spring XML配置中只有一种声明bean的方式:使用<bean>元素并指定class属性。Spring会从这获取必要的信息来创建bean。在XML中声明DI时时,有两种基本的配置方案:
1.<constructor-arg>元素
2.使用Spring3.0所引入的c-命名空间
此时已经声明了SgtPeppers bean 并且SgtPeppers类实现了CompactDisc接口,所以我们已经有了一个可以注入到CDPlayerbean中的bean。我们所要做的就是在XML中声明CDPlayer并通过ID引用SgtPeppers:
当Spring遇到这个<bean>元素,它会创建一个CDPlayer实例,<constructor-arg>元素会告知Spring要将一个ID为compactDisc的bean引用传递到CDPlayer的构造器中。
使用c-命名空间:
属性名以“c”开头,也就是命名空间的前缀。接下来是要装配的构造器参数名,在此之后是”-ref”,这是一个命名的约定,它会告诉Spring,正在装配的是一个bean的引用,这个bean的名字是compactDisc,而不是字面量“compactDisc”。
将字面量注入到构造器中:
<?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:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd"> <!-- 自动扫描 --> <!--<context:component-scan base-package="soundsystem" />--> <bean id="compactDisc" class="soundsystem.BlankDisc"> <constructor-arg value="aaaaaaaaaaaaaaaaaa"/> <constructor-arg value="bbbbbbbbbbbbb"/> </bean> </beans>
package soundsystem; 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 = CDPlayerConfig.class) @ContextConfiguration({"classpath:soundsystem/CD.xml"}) public class CDPlayerTest { @Autowired private CompactDisc compactDisc; // @Autowired // private MedisPlayer medisPlayer; @Test public void cdShouldNotBeNull() { //System.out.println(compactDisc.play()); assertNotNull(compactDisc); //compactDisc.play(); compactDisc.play(); } }
使用c-命名空间将字面量传入构造器:
1.用构造器参数的名字
2.用参数索引装配
装配集合
package soundsystem; import java.util.List; 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; } @Override public void play() { System.out.println(title+" ------ "+artist); for (String track:tracks) { System.out.println("--"+track); } } }
<?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:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd"> <!-- 自动扫描 --> <!--<context:component-scan base-package="soundsystem" />--> <bean id="compactDisc" class="soundsystem.BlankDisc"> <constructor-arg value="aaaaaaaaaaaaaaaaaa"/> <constructor-arg value="bbbbbbbbbbbbb"/> <constructor-arg > <list> <value>"1"</value> <value>"111"</value> <value>"11111"</value> <value>"1111111"</value> <value>"11111111111"</value> </list> </constructor-arg> </bean> </beans>
<list>是<constructor-arg>的子元素,表明一个包含值的列表会传递到构造器中。其中<value>元素用来指定列表中的每个元素。类似的我们可以通过<ref>元素代替<value>元素实现bean引用列表的装配。
如:
装配<set>元素:
<set>与<list>元素的最主要的不同在于当Spring创建要装配的集合时,如果是set所有的重复的值都会被忽略,且存放顺序没有保障。
3.设置属性
在选择构造器注入和属性注入时有个通用的规则是:对强依赖使用构造器注入,对可选性的依赖使用属性注入。
此时我们将CDPlayer改成这样:
CDPlayer只有默认构造器,他没有任何强依赖。所以可以采用这种方式声明bean:
创建bean的时候不会有问题。但是测试的时候会出现NullPointerException这个异常。解决方式如下:
<property>元素为属性的Setter方法所提供的功能跟<constructor-arg>元素为构造器所提供的功能使一样的。
Spring为<constructor-arg>元素提供了c-命名空间作为替代方案,与之类似spring也为<property>提供了p-命名空间。启用p-命名空间要在xml文件中与其他命名空间一起对其进行声明:
使用p-命名空间装配compactDisc属性:
假设我们将BlankDisc改成:
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; } @Override public void play() { System.out.println(title+" ------ "+artist); for (String track:tracks) { System.out.println("--"+track); } } }
此时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:p="http://www.springframework.org/schema/p" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd"> <!-- 自动扫描 --> <!--<context:component-scan base-package="soundsystem" />--> <!--构造器注入--> <!--<bean id="compactDisc" class="soundsystem.BlankDisc"> <constructor-arg value="aaaaaaaaaaaaaaaaaa"/> <constructor-arg value="bbbbbbbbbbbbb"/> <constructor-arg > <list> <value>"1"</value> <value>"111"</value> <value>"11111"</value> <value>"1111111"</value> <value>"11111111111"</value> </list> </constructor-arg> </bean>--> <!--设值注入--> <bean id="compactDisc" class="soundsystem.BlankDisc"> <property name="title" value="aaaaaaaaaaaaaaaaaa"/> <property name="artist" value="bbbbbbbbbbbbb"/> <property name="tracks"> <list> <value>"1"</value> <value>"111"</value> <value>"11111"</value> <value>"1111111"</value> <value>"11111111111"</value> </list> </property> </bean> </beans>
最后启动Junit
设值注入成功。
但是不能用p-命名空间啊来装配集合,但是可以使用Spring-utili命名空间来简化BlankDiscbean,首先要在xml中声明util命名空间及其模式:
通过<util:list>来创建列表bean:
Spring-util命名空间的中的元素:
4.导入和混合配置
或者再创建一个更高级别的JavaConfig文件在这个类中用@Import将两个配置类组合在一起:
那如何加载一个XML配置文件呢?可以用@ImportResource注解,如:
而在XML中则使用<import>元素来引用配置文件
5.小结
这篇博客讲了Spring装配Bean的三种方式:自动化配置,JavaConfig配置,XML配置。建议尽可能使用自动化配置,在无法进行自动化配置时用JavaConfig配置,XML配置(显示配置),如果对MyBatis的整合。
参考:《Spring实战》