Spring(二)装配Spring Bean

  控制反转的概念:控制反转是一种通过描述(在Java中或者是XML或者注解)并通过第三方去产生或获取特定对象的方式。

  在Spring中实现控制反转的是IoC容器,其实现方法是依赖注入(Dependency Injection, DI)。

  在Spring中,对象无需自己查找或者创建与其所关联的其他对象。相反,容器负责把需要相互协作的对象引用赋予各个对象。

  创建应用对象之间协作关系的行为通常称为装配(wiring),这也是依赖注入(DI)的本质。

  依赖注入的3种方式:

  • 构造器注入:构造器注入依赖于构造方法实现,而构造方法可以是有参数或者无参数的。在大部分情况下,都是通过类的构造方法来创建类对象,Spring也可以采用反射的方式,通过使用构造方法来完成注入,这就是构造器注入的原理。使用<constructor-arg index="0" value="参数值"/>来对构造器中第一个参数赋值,其他同理。
  • setter注入:是Spring中最主流的注入方式,它利用Java Bean规范所定义的setter方法来完成注入,灵活且可读性高。它消除了使用构造器注入时出现多个参数的可能性,首先可以把构造方法声明为无参数的,然后使用setter注入为其设置对应的值,其实也是通过Java反射技术实现的。
  • 接口注入:有些时候资源并非来自于自身系统,而是来自于外界,比如数据库链接资源完全可以在Tomcat下配置,然后通过JNDI的形式去获取它,这样数据库连接资源是属于开发工程外的资源,这个时候可以采取接口注入的形式类获取它。

  

  一、Spring配置的可选方案。

  Spring容器负责创建应用程序中的bean并通过DI来协调这些对象之间的关系,当描述bean如何进行装配时,Spring具有非常大的灵活性,它提供了三种主要的装配机制:

  • 在XML中进行显式配置
  • 在Java中进行显式配置
  • 隐式Bean的发现机制和自动装配

  原则上,有三条准则:

  • 尽可能地使用自动装配的机制,显式配置越少越好。
  • 当你必须要显式配置bean的时候(有些源码不是由你来维护的,而当你需要为这些代码配置bean的时候),推荐使用类型安全并且比XML更加强大的JavaConfig。
  • 只有当你想要使用便利的XML命名空间,并且在JavaConfig中没有同样的实现时,才应该使用XML。

  

  二、自动化装配bean

  在便利性方面,最强大的还是Spring的自动化配置。

  Spring从两个角度来实现自动化装配:

  • 组件扫描(component scanning):Spring会自动发现应用上下文中所创建的bean。
  • 自动装配(autowiring):Spring自动满足bean之间的依赖。

  组件扫描和自动装配组合在一起就能发挥出强大的威力,它们能够将你的显示配置降低到最少。

  利用带有注释的代码来解释这种装配方式:

  • 第一种方式是通过Java代码定义了Spring的装配规则:

  代码结构为:

  

  示例程序为:

  CompactDisc接口:

 1 package autoConfig1;
 2 /**
 3  * 如果你不将CD插入(注入)到CD播放器中,那么CD播放器其实没有太大用处的。
 4  * 所以可以这样说,CD播放器依赖于CD才能完成它的使命。
 5  * CompactDisc接口定义了CD播放器对一盘CD所能进行的操作。
 6  * 它将CD播放器的任意实现与CD本身的耦合降低到了最小的程度。
 7  */
 8 public interface CompactDisc {
 9     
10     void play();
11 }

  MediaPlayer接口:

1 package autoConfig1;
2 /**
3  * MediaPlayer接口作为CD播放器的接口。
4  */
5 public interface MediaPlayer {
6 
7     void play();
8 
9 }

  CDPlayerConfig类用于开启Spring的组件扫描:

 1 package autoConfig1;
 2 import org.springframework.context.annotation.ComponentScan;
 3 import org.springframework.context.annotation.Configuration;
 4 /**
 5  * @Configuration用于定义配置类,可替换XML文件。
 6  * @ComponentScan注解能够在Spring中启用组件扫描:
 7  * 1.如果没有其他配置的话,@ComponentScan默认会扫描与配置类相同的包。
 8  * 2.因此Spring将会扫描autoConfig1包以及这个包下的所有子包,寻找带有@Component注解的类。
 9  * 3.由于SgtPeppers类带有@Component注解,所以被发现了,并且会在Spring中自动为其创建一个bean。
10  */
11 @Configuration
12 @ComponentScan
13 // 类CDPlayerConfig通过Java代码定义了Spring的装配规则,并没有显式地声明任何bean。
14 public class CDPlayerConfig {
15     
16 }

  实现了CompactDisc接口的组件类SgtPeppers类:

 1 package autoConfig1;
 2 import org.springframework.stereotype.Component;
 3 /**
 4  * 《Sgt. Pepper's Lonely Hearts Club Band》 是英国摇滚乐队The Beatles发行的第8张录音室专辑。
 5  * 在SgtPeppers类上使用了@Component注解。
 6  * 组件扫描默认是不启用的,还需要命令Spring去寻找带有@Component注解的类,并为其创建bean。
 7  */
 8 @Component // 这个注解表明该类会作为组件类,并告知Spring要为这个类创建bean。
 9 public class SgtPeppers implements CompactDisc {
10 
11     private String title = "Sgt. Pepper's Lonely Hearts Club Band";
12     private String artist = "The Beatles";
13 
14     public void play() {
15         System.out.println("Playing " + title + " by " + artist);
16     }
17 
18 }

  实现了MediaPlayer接口,并且自动装配CompactDisc bean,同时本身也是一个组件类的CDPlayer类:

 1 package autoConfig1;
 2 
 3 import org.springframework.beans.factory.annotation.Autowired;
 4 import org.springframework.stereotype.Component;
 5 
 6 /**
 7  * 声明CDPlayer类作为组件类,并且添加注解实现自动装配。
 8  * 自动装配就是让Spring自动满足bean依赖的一种方法。
 9  * 在满足依赖的过程中,会在Spring应用上下文中寻找匹配某个bean需求的其他bean。
10  * 如果没有匹配的bean,那么在应用上下文创建的时候,Spring会抛出一个异常。
11  * 为了避免异常的出现,可以使用@Autowired(required=false),让没有匹配的bean处于为匹配状态。
12  * 但是,这种情况如果没有进行null检查的话,这个处于为装配状态的属性有可能会出现空指针异常。
13  */
14 @Component // 这个注解表明该类会作为组件类,并告知Spring要为这个类创建bean。
15 public class CDPlayer implements MediaPlayer {
16     private CompactDisc cd;
17     // 1.在CDPlayer类的构造器上添加@Autowired注解。
18     // 2.这表明当Spring创建CDPlayer bean的时候,会通过这个构造器来进行实例化,
19     // 3.并且会传入一个可设置给CompactDisc类型的bean。
20     @Autowired 
21     public CDPlayer(CompactDisc cd) {
22         this.cd = cd;
23     }
24 
25     public void play() {
26         cd.play();
27     }
28 
29 }

  测试类CDPlayerTest,包括两部分的测试:

 1 package autoConfig1;
 2 import static org.junit.Assert.*;
 3 import org.junit.Rule;
 4 import org.junit.Test;
 5 import org.junit.contrib.java.lang.system.SystemOutRule;
 6 import org.junit.runner.RunWith;
 7 import org.springframework.beans.factory.annotation.Autowired;
 8 import org.springframework.test.context.ContextConfiguration;
 9 import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
10 
11 // 声明测试套件运行器,为了让测试在Spring容器环境下执行,以便在测试开始的时候自动创建Spring的上下文。
12 @RunWith(SpringJUnit4ClassRunner.class)   
13 // 1.告诉Spring要在CDPlayerConfig中加载配置,因为CDPlayerConfig类中包含了@ComponentScan,启动了Spring的组件扫描。
14 // 2.由于Spring启动了组件扫描,因此可以扫描出所有带有@Component注解的类,即SgtPeppers类和CDPlayer类,并且在Spring中为其创建一个bean。
15 @ContextConfiguration(classes = CDPlayerConfig.class)
16 public class CDPlayerTest {
17     
18     @Rule // 这个注解是为了在执行case的时候加入测试者特有的操作,而不影响原有的case代码:减小了特有操作和case原逻辑的耦合。
19     public final SystemOutRule systemOutRule = new SystemOutRule().enableLog();
20     
21     // 将MediaPlayer bean注入到测试代码之中。
22     @Autowired
23     private MediaPlayer player;
24 
25     // 将CompactDisc bean注入到测试代码之中。
26     @Autowired
27     private CompactDisc cd;
28 
29     // 简单的测试断言cd属性不为null。
30     // 如果它不为null,就意味着Spring能够发现CompactDisc类,自动在Spring上下文中将其创建为bean并将其注入到测试代码之中。
31     @Test
32     public void cdShouldNotBeNull() {
33         assertNotNull(cd);
34     }
35     
36     // 简单的测试断言player属性不为null。
37     // 如果它不为null,就意味着Spring能够发现CompactDisc类,自动在Spring上下文中将其创建为bean并将其注入到测试代码之中。
38     @Test
39     public void playerShouldNotBeNull() {
40         assertNotNull(player);
41     }
42 
43     // systemOutRule规则可以基于控制台的输出编写断言,这里断言play()方法的输出被发送到了控制台。
44     @Test
45     public void play() {
46         player.play();
47         assertEquals("Playing Sgt. Pepper's Lonely Hearts Club Band by The Beatles
", systemOutRule.getLog());
48     }
49 
50 }
  • 第二种方式是通过XML配置文件定义了Spring的装配规则:

  代码结构为:

  

  XML配置文件,用于开启Spring的组件扫描:

 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <beans     xmlns="http://www.springframework.org/schema/beans"
 3         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"     
 4         xmlns:context="http://www.springframework.org/schema/context"
 5         xmlns:c="http://www.springframework.org/schema/c" 
 6         xmlns:p="http://www.springframework.org/schema/p"
 7         xsi:schemaLocation="http://www.springframework.org/schema/beans 
 8                             http://www.springframework.org/schema/beans/spring-beans.xsd
 9                             http://www.springframework.org/schema/context 
10                             http://www.springframework.org/schema/context/spring-context.xsd">
11 
12     <!-- 使用XML来启用组件扫描 -->
13     <context:component-scan base-package="autoConfig2" />
14 
15 </beans>

  用于测试XML自动装配的测试类,通过定义配置文件的路径加载配置信息,同样包括两部分的测试。

 1 package autoConfig2;
 2 
 3 import static org.junit.Assert.*;
 4 
 5 import org.junit.Rule;
 6 import org.junit.Test;
 7 import org.junit.contrib.java.lang.system.SystemOutRule;
 8 import org.junit.runner.RunWith;
 9 import org.springframework.beans.factory.annotation.Autowired;
10 import org.springframework.test.context.ContextConfiguration;
11 import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
12 
13 @RunWith(SpringJUnit4ClassRunner.class)
14 // 设置配置文件xml文件的路径,Spring回去这个路径下面去寻找配置文件中的相关配置。
15 @ContextConfiguration(locations = "classpath:autoConfig2/autoConfig2.xml")
16 public class CDPlayerXMLConfigTest {
17 
18     @Rule
19     public final SystemOutRule log = new SystemOutRule().enableLog();
20 
21     @Autowired
22     private MediaPlayer player;
23 
24     @Autowired
25     private CompactDisc cd;
26 
27     @Test
28     public void cdShouldNotBeNull() {
29         assertNotNull(cd);
30     }
31 
32     @Test
33     public void play() {
34         player.play();
35         assertEquals("Playing Sgt. Pepper's Lonely Hearts Club Band by The Beatles
", log.getLog());
36     }
37 
38 }

  

  ·三、通过Java代码装配bean

  如果你想要将第三方库中的组件装配到你的应用中,在这种情况下,是没有办法在它的类上添加@Component和@Autowired注解的,因此就不能使用自动化装配的方案了。在这种情况下,必须要采用显式装配的方式。

  在进行显式配置的时候,有两种方案可以选择:

  • Java
  • XML

  在进行显式装配的时候,JavaConfig是更好的方案,因为它更为强大,类型安全并且对重构友好。因为它就是Java代码,就像应用程序中其他Java代码一样。

  尽管它与其他的组件一样都使用相同的语言进行表述,但是JavaConfig是配置代码,这意味着它不应该包含任何业务逻辑,JavaConfig也不应该侵入到业务逻辑代码之中。

  尽管不是必须的,但通常会将JavaConfig放到单独的包中,使他与其他的应用程序逻辑分离开,这样对于它的意图就不会产生困惑了。

  代码结构为:

  

  CompactDisc接口,和之前一样没有变化。

 1 package javaConfig;
 2 /**
 3  * 如果你不将CD插入(注入)到CD播放器中,那么CD播放器其实没有太大用处的。
 4  * 所以可以这样说,CD播放器依赖于CD才能完成它的使命。
 5  * CompactDisc接口定义了CD播放器对一盘CD所能进行的操作。
 6  * 它将CD播放器的任意实现与CD本身的耦合降低到了最小的程度。
 7  */
 8 public interface CompactDisc {
 9     
10     void play();
11 }

  MediaPlayer接口,和之前一样也没有变化。

1 package javaConfig;
2 /**
3  * MediaPlayer接口作为CD播放器的接口。
4  */
5 public interface MediaPlayer {
6 
7     void play();
8 
9 }

  SgtPeppers类,和之前的不一样,少了@Component注解:

 1 package javaConfig;
 2 
 3 public class SgtPeppers implements CompactDisc {
 4 
 5     private String title = "Sgt. Pepper's Lonely Hearts Club Band";
 6     private String artist = "The Beatles";
 7 
 8     public void play() {
 9         System.out.println("Playing " + title + " by " + artist);
10     }
11 
12 }

  CDPlayer类,和之前不一样,同样少了@Component注解:

 1 package javaConfig;
 2 
 3 import org.springframework.beans.factory.annotation.Autowired;
 4 
 5 public class CDPlayer implements MediaPlayer {
 6     
 7     private CompactDisc cd;
 8 
 9     @Autowired
10     public CDPlayer(CompactDisc cd) {
11         this.cd = cd;
12     }
13 
14     public void play() {
15         cd.play();
16     }
17 
18 }

  在这之前的例子中,都是通过@Component装配Bean,但是@Component只能注解在类上,不能注解到方法上。对于Java而言,大部分的开发都需要引入第三方的包(jar文件),而且往往并没有这些包的源码,这时候将无法为这些包的类加入@Component注解,让它们变为开发环境的Bean。但可以使用新类扩展(extends)其包的类,然后在新类上使用@Component注解,这样显得不伦不类。

  为了解决这个问题,Spring的@Bean注解可以在方法上使用,并且将方法返回的对象作为Spring的Bean。

  最主要的类,CDPlayerConfig配置类:

 1 package javaConfig;
 2 
 3 import org.springframework.context.annotation.Bean;
 4 import org.springframework.context.annotation.Configuration;
 5 /**
 6  * 创建JavaConfig类的关键在于为其添加@Configuration注解
 7  * 在没有@ComponentScan注解的情况下,即不开启组件扫描时,会出现BeanCreationException异常。
 8  * 要在JavaConfig中声明bean,需要编写一个方法,这个方法会创建所需类型的实例,然后给这个方法添加@Bean注解。
 9  * 构造器和Setter方法,是支持@Bean方法的两个简单的例子,可以采用任何必要的Java功能来产生bean实例。
10  */
11 @Configuration // 这个注解表明这个类是一个配置类,该类包含在Spring应用上下文中如何创建bean的细节。
12 public class CDPlayerConfig {
13     
14     //第一种情况是:CompactDisc bean是非常简单的,它自身没有其他的依赖。
15     //@Bean注解会告诉Spring的是compactDisc方法将会返回一个SgtPeppers对象,该对象要注册为Spring应用上下文中的bean。
16     @Bean
17     public CompactDisc sgtPeppers() {
18         return new SgtPeppers();
19     }
20 
21     // 第二种情况是:CDPlayer bean依赖于CompactDisc bean
22     // @Bean注解会告诉Spring的是cdPlayer方法将会返回一个CDPlayer对象,该对象要注册为Spring应用上下文中的bean。
23     // 1.当Spring调用cdPlayer方法创建CDPlayer bean的时候,它会自动装配一个CompactDisc bean到配置方法中。
24     // 2.然后,方法体就可以按照合适的方式来使用它。
25     // 3.cdPlayer方法也能够将CompactDisc注入到CDPlayer的构造器中,而且不用明确引用CompactDisc的@Bean方法。
26     // 4.不管CompactDisc bean是通过什么方式创建出来的,Spring都会将其传入到配置方法中,并用来创建CDPlayer bean。
27     @Bean
28     public CDPlayer cdPlayer(CompactDisc compactDisc) {
29         return new CDPlayer(compactDisc);
30     }
31 
32 }

  测试类CDPlayerTest类:

 1 package javaConfig;
 2 
 3 import static org.junit.Assert.*;
 4 import org.junit.Rule;
 5 import org.junit.Test;
 6 import org.junit.contrib.java.lang.system.SystemOutRule;
 7 import org.junit.runner.RunWith;
 8 import org.springframework.beans.factory.annotation.Autowired;
 9 import org.springframework.test.context.ContextConfiguration;
10 import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
11 
12 @RunWith(SpringJUnit4ClassRunner.class)
13 @ContextConfiguration(classes = CDPlayerConfig.class)
14 public class CDPlayerTest {
15 
16     @Rule
17     public final SystemOutRule log = new SystemOutRule().enableLog();
18 
19     @Autowired
20     private MediaPlayer player;
21 
22     @Test
23     public void play() {
24         player.play();
25         assertEquals("Playing Sgt. Pepper's Lonely Hearts Club Band by The Beatles
", log.getLog());
26     }
27 
28 }

  另外,由于@Bean不能在类上使用,只能使用在方法上,因此要想在注解中实现自定义的初始化方法和销毁方法,也可以通过@Bean的配置项来实现,@Bean的配置项包含4个配置:

  • name:是一个字符串数组,允许配置多个BeanName
  • autowire:标识是否是一个引用的Bean对象,默认值是Autowire.NO
  • initMethod:自定义初始化方法
  • destroyMethod:自定义销毁方法

  例如:

@Bean(name="juiceMaker2", initMethod="init", destroyMethod="myDestroy")
public JuiceMaker2 initJuiceMaker2(){
    JuiceMaker2 juiceMaker2 = new JuiceMaker2();
    juiceMaker2.setBeverageShop("贡茶")
    Source source = new Source();
    source.setFruit("橙子");
    source.setSize("大杯");
    source.setSugar("少糖");
    juiceMaker2.setSource(source);
    return juiceMaker2;
}

  四、通过XML装配bean

  在Spring刚刚出现的时候,XML是描述配置的主要方式。但是,Spring现在有了强大的自动化配置和基于Java的配置,XML不应该再是第一选择了。

  在基于XML的Spring配置中声明一个bean,要使用 <bean></bean>标签,相当于JavaConfig中的@Bean注解。

  在没有明确给定ID的情况下,需要通过class属性指定“包名+类名”来指定bean类,创建的bean将会根据全限定类名类命名:

<bean class="soundsystem.BlankDisc" />

  尽管自动化的bean命名方式非常方便,但如果稍后引用的话,自动产生的名字就没有多大的用处了,因此,通常更好的方法是借助id属性,为每个bean设置一个你自己选择的名字:

<bean id="compactDisc" class="soundsystem.BlankDisc" />

  同样 x1,在JavaConfig中,也可以给bean命名:

    @Bean(name="lonelyHeartsClubBand")
    public CompactDisc sgtPeppers() {
        return new SgtPeppers();
    }

  同样 x2,在自动装配中,也可以给bean命名:

@Component("lonelyHeartClubBand")
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);
    }

}

  那么再来分析一下XML方式配置bean的一些特征:

<bean id="compactDisc" class="soundsystem.BlankDisc" />
  • 在基于JavaConfig的配置中,已经知道了通过@Bean注解,可以不用再创建BlankDisc的实例了。同样,当Spring发现这个<bean>元素时,它将会调用BlankDisc的默认构造器来创建bean
  • 在这个简单的<bean>声明中,将bean的类型以字符串的形式设置在了class属性中,但是,如何保证给class属性的值是真正的类呢?万一对类进行重命名就用不了了,这是XML配置的一个重大的缺点。

  下面通过几个典型的分类举例说明XML配置方式可以实现哪些功能。

  1.使用<constructor-arg>元素实现依赖注入。

  代码结构为:

  

  CompactDisc接口:

1 package xmlConfigTest1;
2 
3 public interface CompactDisc {
4 
5     void play();
6 
7 }

  MediaPlayer接口:

1 package xmlConfigTest1;
2 
3 public interface MediaPlayer {
4 
5     void play();
6 
7 }

  实现了CompactDisc 接口的SgtPeppers类(正常Java代码,没有任何注解):

 1 package xmlConfigTest1;
 2 
 3 public class SgtPeppers implements CompactDisc {
 4 
 5     private String title = "Sgt. Pepper's Lonely Hearts Club Band";
 6     private String artist = "The Beatles";
 7 
 8     public void play() {
 9         System.out.println("Playing " + title + " by " + artist);
10     }
11 
12 }

  实现了MediaPlayer接口的CDPlayer类(正常Java代码,没有任何注解):

 1 package xmlConfigTest1;
 2 
 3 public class CDPlayer implements MediaPlayer {
 4     private CompactDisc cd;
 5 
 6     public CDPlayer(CompactDisc cd) {
 7         this.cd = cd;
 8     }
 9 
10     public void play() {
11         cd.play();
12     }
13 
14 }

  ConstructorArgReferenceTest-context.xml配置文件:

 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <beans xmlns="http://www.springframework.org/schema/beans"
 3        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 4        xsi:schemaLocation="http://www.springframework.org/schema/beans 
 5                               http://www.springframework.org/schema/beans/spring-beans.xsd">
 6 
 7     <!-- 声明SgtPeppers bean,并且SgtPeppers类实现了CompacDisc接口,所以实际上已经有了一个可以注入到CDPlayer bean中的bean -->
 8     <bean id="compactDisc" class="xmlConfigTest1.SgtPeppers" />
 9 
10     <!-- 1.当Spring遇到这个标签时,会创建一个CDPlayer实例。 -->
11     <!-- 2.<constructor-arg>元素会告知Spring要将一个ID为compactDisc的bean引用传递到CDPlayer的构造器中。 -->
12     <bean id="cdPlayer" class="xmlConfigTest1.CDPlayer">
13         <constructor-arg ref="compactDisc" />
14     </bean>
15 
16 </beans>

  测试类ConstructorArgReferenceTest类(这里有一个疑问:测试类中没有指明xml配置文件的路径,那么是不是默认读取对应的“类名-context.xml”配置文件呢?通过通知台可以发现,是的!):

 1 package xmlConfigTest1;
 2 
 3 import static org.junit.Assert.*;
 4 import org.junit.Rule;
 5 import org.junit.Test;
 6 import org.junit.contrib.java.lang.system.SystemOutRule;
 7 import org.junit.runner.RunWith;
 8 import org.springframework.beans.factory.annotation.Autowired;
 9 import org.springframework.test.context.ContextConfiguration;
10 import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
11 
12 @RunWith(SpringJUnit4ClassRunner.class)
13 @ContextConfiguration
14 public class ConstructorArgReferenceTest {
15 
16   @Rule
17   public final SystemOutRule log = new SystemOutRule().enableLog();
18 
19   @Autowired
20   private MediaPlayer player;
21 
22   @Test
23   public void play() {
24     player.play();
25     assertEquals("Playing Sgt. Pepper's Lonely Hearts Club Band by The Beatles
", log.getLog());
26   }
27 
28 }

  2.使用Spring的c-命名空间实现依赖注入。

  代码结构为(其中4个基础类不变):

  

  CNamespaceReferenceTest-context.xml配置文件类:

 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <beans xmlns="http://www.springframework.org/schema/beans"
 3        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
 4        xmlns:c="http://www.springframework.org/schema/c"
 5        xsi:schemaLocation="http://www.springframework.org/schema/beans 
 6                            http://www.springframework.org/schema/beans/spring-beans.xsd">
 7 
 8     <bean id="compactDisc" class="xmlConfigTest2.SgtPeppers" />
 9 
10     <!-- 1.使用c-命名空间来声明构造器参数 -->
11     <!-- 2.使用c-命名空间属性要比使用<constructor-arg>元素简练得多-->
12     <!-- 3.这里要注意的是“c:cd-ref”中cd是CDPlayer类的构造器中指明的CompactDisc类型的字段。 -->
13     <!-- 4.可以将参数名称替换为索引,即“c:_0-ref”表示的是第一个构造器参数 -->
14     <!-- 5.在只有一个构造器参数的情况下,根本不用去标示参数。 -->
15     <bean id="cdPlayer" class="xmlConfigTest2.CDPlayer" c:cd-ref="compactDisc" />
16 
17 </beans>

  测试类CNamespaceReferenceTest类(也没有写xml文件路径,但是可以自动发现):

 1 package xmlConfigTest2;
 2 
 3 import static org.junit.Assert.*;
 4 
 5 import org.junit.Rule;
 6 import org.junit.Test;
 7 import org.junit.contrib.java.lang.system.SystemOutRule;
 8 import org.junit.runner.RunWith;
 9 import org.springframework.beans.factory.annotation.Autowired;
10 import org.springframework.test.context.ContextConfiguration;
11 import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
12 
13 
14 @RunWith(SpringJUnit4ClassRunner.class)
15 @ContextConfiguration
16 public class CNamespaceReferenceTest {
17 
18     @Rule
19     public final SystemOutRule log = new SystemOutRule().enableLog();
20 
21     @Autowired
22     private MediaPlayer player;
23 
24     @Test
25     public void play() {
26         player.play();
27         assertEquals("Playing Sgt. Pepper's Lonely Hearts Club Band by The Beatles
", log.getLog());
28     }
29 
30 }

  目前所做的DI通常指的都是类型的装配-也就是将对象的引用装配到依赖于它们的其他对象之中--而有时候,需要做的只是用一个字面量值来配置对象。

  因此,需要增加一个实现了CompactDisc接口的新的唱片类,即BlankDisc类,这个类像空磁盘一样,可以设置成任意想要的艺术家和唱片名:

 1 public class BlankDisc implements CompactDisc {
 2 
 3     private String title;
 4     private String artist;
 5 
 6     public BlankDisc(String title, String artist) {
 7         this.title = title;
 8         this.artist = artist;
 9     }
10 
11     public void play() {
12         System.out.println("Playing " + title + " by " + artist);
13     }
14 
15 }

  接下来,需要做的就是,如何设置titleartist这两个属性,即将给定的值以字面量的形式注入到构造器之中。

  3.使用<constructor-arg>元素进行构造器参数的注入

  代码结构为:

  

  ConstructorArgValueTest-context.xml配置xml文件:

 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <beans xmlns="http://www.springframework.org/schema/beans"
 3        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 4        xsi:schemaLocation="http://www.springframework.org/schema/beans 
 5                            http://www.springframework.org/schema/beans/spring-beans.xsd">
 6 
 7     <!-- 使用value属性,通过该属性表明给定的值要以字面量的形式注入到构造器之中。-->
 8     <bean id="compactDisc" class="xmlConfigTest3.BlankDisc">
 9         <constructor-arg value="You Don't Love Me, LaDao" />
10         <constructor-arg value="Jay Chou" />
11     </bean>
12 
13     <bean id="cdPlayer" class="xmlConfigTest3.CDPlayer">
14         <constructor-arg ref="compactDisc" />
15     </bean>
16 
17 </beans>

  测试类ConstructorArgValueTest:

 1 package xmlConfigTest3;
 2 
 3 import static org.junit.Assert.*;
 4 
 5 import org.junit.Rule;
 6 import org.junit.Test;
 7 import org.junit.contrib.java.lang.system.SystemOutRule;
 8 import org.junit.runner.RunWith;
 9 import org.springframework.beans.factory.annotation.Autowired;
10 import org.springframework.test.context.ContextConfiguration;
11 import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
12 
13 @RunWith(SpringJUnit4ClassRunner.class)
14 @ContextConfiguration
15 public class ConstructorArgValueTest {
16 
17   @Rule
18   public final SystemOutRule log = new SystemOutRule().enableLog();
19 
20   @Autowired
21   private MediaPlayer player;
22 
23   @Test
24   public void play() {
25     player.play();
26     assertEquals("Playing You Don't Love Me, LaDao by Jay Chou
", log.getLog());
27   }
28 
29 }

  4.使用c-命名空间进行构造器参数的注入

  代码结构为:

  

  配置xml文件CNamespaceValueTest-context.xml:

 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <beans xmlns="http://www.springframework.org/schema/beans"
 3        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
 4        xmlns:c="http://www.springframework.org/schema/c"
 5        xsi:schemaLocation="http://www.springframework.org/schema/beans 
 6                            http://www.springframework.org/schema/beans/spring-beans.xsd">
 7 
 8     <bean id="compactDisc" class="xmlConfigTest4.BlankDisc"
 9           c:_0="You Don't Love Me, LaDao" 
10           c:_1="Jay Chou" />
11 
12     <bean id="cdPlayer" class="xmlConfigTest4.CDPlayer" c:_-ref="compactDisc" />
13 
14 </beans>

  配置xml文件CNamespaceValueTest-context.xml中还可以换一种方案来写:

1     <bean id="compactDisc" class="xmlConfigTest4.BlankDisc"
2           c:_title="You Don't Love Me, LaDao" 
3           c:_artist="Jay Chou" />

  在装配bean引用和字面量值方面,<constructor-arg>元素和c-命名空间的功能是相同的。但是,有一种情况是<constructor-arg>元素能够实现,而c-命名空间却无法做到的,那就是将结合装配到构造器参数中。

  5.使用<constructor-arg>将集合装配到构造器参数中

  修改BlankDisc类为ListDisc类,增加CD中包含的所有歌曲列表,播放的时候,将每首歌都播放出来:

 1 package xmlConfigTest5;
 2 
 3 import java.util.List;
 4 public class ListDisc implements CompactDisc {
 5 
 6     private String title;
 7     private String artist;
 8     private List<String> tracks;
 9 
10     public ListDisc(String title, String artist, List<String> tracks) {
11         this.title = title;
12         this.artist = artist;
13         this.tracks = tracks;
14     }
15 
16     public void play() {
17         System.out.println("Playing " + title + " by " + artist);
18         for (String track : tracks) {
19             System.out.println("-Track: " + track);
20         }
21     }
22 
23 }

  代码结构为:

  

  配置xml文件ConstructorArgCollectionTest-context.xml(使用<list>元素表明一个包含值的列表将会传递到构造器中,<value>用来指定列表中的每个元素。也可以按照同样的方式使用<set>元素,但是要把ListDisc中引用import java.util.Set,使用Set的时候,所有重复的值都会被忽略掉,存放顺序也不会得以保证。):

 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <beans xmlns="http://www.springframework.org/schema/beans"
 3        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 4        xsi:schemaLocation="http://www.springframework.org/schema/beans 
 5                            http://www.springframework.org/schema/beans/spring-beans.xsd">
 6 
 7     <bean id="compactDisc" class="xmlConfigTest5.ListDisc">
 8         <constructor-arg value="Sgt. Pepper's Lonely Hearts Club Band" />
 9         <constructor-arg value="The Beatles" />
10         <constructor-arg>
11             <list>
12                 <value>Sgt. Pepper's Lonely Hearts Club Band</value>
13                 <value>With a Little Help from My Friends</value>
14                 <value>Lucy in the Sky with Diamonds</value>
15                 <value>Getting Better</value>
16                 <value>Fixing a Hole</value>
17                 <value>She's Leaving Home</value>
18                 <value>Being for the Benefit of Mr. Kite!</value>
19                 <value>Within You Without You</value>
20                 <value>When I'm Sixty-Four</value>
21                 <value>Lovely Rita</value>
22                 <value>Good Morning Good Morning</value>
23                 <value>Sgt. Pepper's Lonely Hearts Club Band (Reprise)</value>
24                 <value>A Day in the Life</value>
25             </list>
26         </constructor-arg>
27     </bean>
28 
29     <bean id="cdPlayer" class="xmlConfigTest5.CDPlayer">
30         <constructor-arg ref="compactDisc" />
31     </bean>
32 
33 </beans>

  测试类ConstructorArgCollectionTest

 1 package xmlConfigTest5;
 2 
 3 import static org.junit.Assert.*;
 4 
 5 import org.junit.Rule;
 6 import org.junit.Test;
 7 import org.junit.contrib.java.lang.system.SystemOutRule;
 8 import org.junit.runner.RunWith;
 9 import org.springframework.beans.factory.annotation.Autowired;
10 import org.springframework.test.context.ContextConfiguration;
11 import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
12 
13 @RunWith(SpringJUnit4ClassRunner.class)
14 @ContextConfiguration
15 public class ConstructorArgCollectionTest {
16 
17   @Rule
18   public final SystemOutRule log = new SystemOutRule().enableLog();
19 
20   @Autowired
21   private MediaPlayer player;
22 
23   @Test
24   public void play() {
25     player.play();
26     assertEquals(
27         "Playing Sgt. Pepper's Lonely Hearts Club Band by The Beatles
" +
28         "-Track: Sgt. Pepper's Lonely Hearts Club Band
" +
29         "-Track: With a Little Help from My Friends
" +
30         "-Track: Lucy in the Sky with Diamonds
" +
31         "-Track: Getting Better
" +
32         "-Track: Fixing a Hole
" +
33         "-Track: She's Leaving Home
" +
34         "-Track: Being for the Benefit of Mr. Kite!
" +
35         "-Track: Within You Without You
" +
36         "-Track: When I'm Sixty-Four
" +
37         "-Track: Lovely Rita
" +
38         "-Track: Good Morning Good Morning
" +
39         "-Track: Sgt. Pepper's Lonely Hearts Club Band (Reprise)
" +
40         "-Track: A Day in the Life
",
41         log.getLog());
42   }
43 
44 }

  到目前为止,CDPlayer和BlankDisc(ListDisc)类完全是通过构造器注入的,没有使用属性的Setter方法。继续研究如何使用Spring XML实现属性注入。

  首先有一个问题,那就是该选择构造器还是属性注入呢?通用的规则是,对强依赖使用构造器注入,而对可选性的依赖使用属性注入。

  修改CDPlayer类,为CompactDisc属性值增加Setter类并去掉CDPlayer类的构造器,现在CDPlayer没有任何的构造器(除了隐含的默认构造器),同时也没有任何的强依赖:

 1 package xmlConfigTest6;
 2 
 3 public class CDPlayer implements MediaPlayer {
 4   private CompactDisc compactDisc;
 5 
 6   
 7   public void setCompactDisc(CompactDisc compactDisc) {
 8     this.compactDisc = compactDisc;
 9   }
10 
11   public void play() {
12     compactDisc.play();
13   }
14 
15 }

  修改BlankDisc类成reallyBlankDisc类,reallyBlankDisc完全通过属性注入进行配置,而不是构造器注入:

 1 package xmlConfigTest6;
 2 
 3 import java.util.List;
 4 
 5 public class reallyBlankDisc implements CompactDisc {
 6 
 7     private String title;
 8     private String artist;
 9     private List<String> tracks;
10 
11     public void setTitle(String title) {
12         this.title = title;
13     }
14 
15     public void setArtist(String artist) {
16         this.artist = artist;
17     }
18 
19     public void setTracks(List<String> tracks) {
20         this.tracks = tracks;
21     }
22 
23     public void play() {
24         System.out.println("Playing " + title + " by " + artist);
25         for (String track : tracks) {
26             System.out.println("-Track: " + track);
27         }
28     }
29 
30 }

  6.使用<property>元素装配bean引用与装配字面量(唯一的区别是是否带有“-ref”后缀,如果没有“-ref”后缀的话,所装配的就是字面量)

  代码结构为:

  

  PropertyRefTest-context.xml配置xml文件:

 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <beans xmlns="http://www.springframework.org/schema/beans"
 3        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
 4        xmlns:p="http://www.springframework.org/schema/p"
 5        xsi:schemaLocation="http://www.springframework.org/schema/beans 
 6                            http://www.springframework.org/schema/beans/spring-beans.xsd">
 7 
 8     <!-- 通过 <property> 元素的value属性来设置title、artist和tracks属性-->
 9     <bean id="compactDisc" class="xmlConfigTest6.reallyBlankDisc">
10         <property name="title" value="Sgt. Pepper's Lonely Hearts Club Band" />
11         <property name="artist" value="The Beatles" />
12         <property name="tracks">
13             <list>
14                 <value>Sgt. Pepper's Lonely Hearts Club Band</value>
15                 <value>With a Little Help from My Friends</value>
16                 <value>Lucy in the Sky with Diamonds</value>
17                 <value>Getting Better</value>
18                 <value>Fixing a Hole</value>
19                 <value>She's Leaving Home</value>
20                 <value>Being for the Benefit of Mr. Kite!</value>
21                 <value>Within You Without You</value>
22                 <value>When I'm Sixty-Four</value>
23                 <value>Lovely Rita</value>
24                 <value>Good Morning Good Morning</value>
25                 <value>Sgt. Pepper's Lonely Hearts Club Band (Reprise)</value>
26                 <value>A Day in the Life</value>
27             </list>
28         </property>
29     </bean>
30 
31     <!-- 1.<property>元素为属性的Setter方法所提供的功能与<contructor-arg>元素为构造器所提供的功能是一样的  -->
32     <!-- 2.通过ref属性引用了ID为compactDisc的bean,并将其通过setCompactDisc()方法注入到compactDisc属性中 -->
33     <bean id="cdPlayer" class="xmlConfigTest6.CDPlayer">
34         <property name="compactDisc" ref="compactDisc" />
35     </bean>
36     
37 
38 </beans>

  测试类PropertyRefAndValueTest(和之前的测试类并没有什么变化)

 1 package xmlConfigTest6;
 2 
 3 import static org.junit.Assert.*;
 4 
 5 import org.junit.Rule;
 6 import org.junit.Test;
 7 import org.junit.contrib.java.lang.system.SystemOutRule;
 8 import org.junit.runner.RunWith;
 9 import org.springframework.beans.factory.annotation.Autowired;
10 import org.springframework.test.context.ContextConfiguration;
11 import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
12 
13 @RunWith(SpringJUnit4ClassRunner.class)
14 @ContextConfiguration
15 public class PropertyRefAndValueTest {
16 
17   @Rule
18   public final SystemOutRule log = new SystemOutRule().enableLog();
19 
20   @Autowired
21   private MediaPlayer player;
22 
23   @Test
24   public void play() {
25     player.play();
26     assertEquals(
27         "Playing Sgt. Pepper's Lonely Hearts Club Band by The Beatles
" +
28         "-Track: Sgt. Pepper's Lonely Hearts Club Band
" +
29         "-Track: With a Little Help from My Friends
" +
30         "-Track: Lucy in the Sky with Diamonds
" +
31         "-Track: Getting Better
" +
32         "-Track: Fixing a Hole
" +
33         "-Track: She's Leaving Home
" +
34         "-Track: Being for the Benefit of Mr. Kite!
" +
35         "-Track: Within You Without You
" +
36         "-Track: When I'm Sixty-Four
" +
37         "-Track: Lovely Rita
" +
38         "-Track: Good Morning Good Morning
" +
39         "-Track: Sgt. Pepper's Lonely Hearts Club Band (Reprise)
" +
40         "-Track: A Day in the Life
",
41         log.getLog());
42   }
43 
44 
45 }

  7.使用p-命名空间装配bean引用与装配字面量

  代码结构为:

  

  配置xml文件PNamespaceRefAndValueTest-context.xml:

 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <beans xmlns="http://www.springframework.org/schema/beans"
 3        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
 4        xmlns:p="http://www.springframework.org/schema/p"
 5        xsi:schemaLocation="http://www.springframework.org/schema/beans 
 6                            http://www.springframework.org/schema/beans/spring-beans.xsd">
 7 
 8     <!-- 使用p-命令空间来设置属性值,与c-命名空间一样, 不能使用p-命名空间来装配集合 -->
 9     <bean id="compactDisc" class="xmlConfigTest7.reallyBlankDisc"
10           p:title="Sgt. Pepper's Lonely Hearts Club Band" 
11           p:artist="The Beatles">
12         <property name="tracks">
13             <list>
14                 <value>Sgt. Pepper's Lonely Hearts Club Band</value>
15                 <value>With a Little Help from My Friends</value>
16                 <value>Lucy in the Sky with Diamonds</value>
17                 <value>Getting Better</value>
18                 <value>Fixing a Hole</value>
19                 <value>She's Leaving Home</value>
20                 <value>Being for the Benefit of Mr. Kite!</value>
21                 <value>Within You Without You</value>
22                 <value>When I'm Sixty-Four</value>
23                 <value>Lovely Rita</value>
24                 <value>Good Morning Good Morning</value>
25                 <value>Sgt. Pepper's Lonely Hearts Club Band (Reprise)</value>
26                 <value>A Day in the Life</value>
27             </list>
28         </property>
29     </bean>
30 
31     <!-- 1.使用p-命名空间装配compactDisc属性-->
32     <!-- 2.通常的格式是p:属性名-ref="所注入bean的ID"-->
33     <bean id="cdPlayer" class="xmlConfigTest7.CDPlayer"
34         p:compactDisc-ref="compactDisc" />
35 
36 </beans>

  8.虽然不能使用p-命名空间来装配集合,但是可以使用Spring util-命名空间来简化reallyBlankDisc bean

  代码结构为:

  

  配置xml文件PNamespaceWithUtilNamespaceTest-context.xml:

 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <beans xmlns="http://www.springframework.org/schema/beans"
 3     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
 4     xmlns:util="http://www.springframework.org/schema/util"
 5     xsi:schemaLocation="http://www.springframework.org/schema/beans 
 6                            http://www.springframework.org/schema/beans/spring-beans.xsd
 7                            http://www.springframework.org/schema/util 
 8                            http://www.springframework.org/schema/util/spring-util.xsd">
 9 
10     <!-- 3.这样就能向使用其他的bean那样,将磁道列表bean注入到reallyBlankDisc bean中的tracks属性中 -->
11     <bean id="compactDisc" class="xmlConfigTest8.reallyBlankDisc"
12           p:title="Sgt. Pepper's Lonely Hearts Club Band" 
13           p:artist="The Beatles"
14           p:tracks-ref="trackList" />
15 
16     <!-- 1.util-命名空间的<util:list>元素,会创建一个列表的bean -->
17     <!-- 2.借助<util:list>元素,可以将磁道列表转移到reallyBlankDisc bean之外,并将其声明到单独的bean之中 -->
18     <util:list id="trackList">
19         <value>Sgt. Pepper's Lonely Hearts Club Band</value>
20         <value>With a Little Help from My Friends</value>
21         <value>Lucy in the Sky with Diamonds</value>
22         <value>Getting Better</value>
23         <value>Fixing a Hole</value>
24         <value>She's Leaving Home</value>
25         <value>Being for the Benefit of Mr. Kite!</value>
26         <value>Within You Without You</value>
27         <value>When I'm Sixty-Four</value>
28         <value>Lovely Rita</value>
29         <value>Good Morning Good Morning</value>
30         <value>Sgt. Pepper's Lonely Hearts Club Band (Reprise)</value>
31         <value>A Day in the Life</value>
32     </util:list>
33 
34     <bean id="cdPlayer" class="xmlConfigTest8.CDPlayer"
35         p:compactDisc-ref="compactDisc" />
36 
37 </beans>

  

  五、导入和混合配置

  混合配置的原理就是,Spring在自动装配时,并不在意要装配的bean来自哪里。自动装配的时候会考虑到Spring容器中所有的bean,不管它是在JavaConfig或XML中声明的还是通过组件扫描获取到的。

  没有任何变化的几个类:

  CompactDisc接口:

1 package mixedConfig1;
2 
3 public interface CompactDisc {
4 
5     void play();
6 
7 }

  MediaPlayer接口:

1 package mixedConfig1;
2 
3 public interface MediaPlayer {
4 
5     void play();
6 
7 }

  实现了MediaPlayer接口的CDPlayer类:

 1 package mixedConfig1;
 2 
 3 public class CDPlayer implements MediaPlayer {
 4     private CompactDisc cd;
 5 
 6     public CDPlayer(CompactDisc cd) {
 7         this.cd = cd;
 8     }
 9 
10     public void play() {
11         cd.play();
12     }
13 
14 }

  实现了CompactDisc接口的第一个唱片类SgtPeppers 类:

 1 package mixedConfig1;
 2 
 3 public class SgtPeppers implements CompactDisc {
 4 
 5     private String title = "Sgt. Pepper's Lonely Hearts Club Band";
 6     private String artist = "The Beatles";
 7 
 8     public void play() {
 9         System.out.println("Playing " + title + " by " + artist);
10     }
11 
12 }

  实现了CompactDisc接口,使用构造器来进行属性注入,并且拥有歌曲磁道列表的第二个唱片类ListBlankDisc类:

 1 package mixedConfig2;
 2 
 3 import java.util.List;
 4 
 5 public class ListBlankDisc implements CompactDisc {
 6 
 7     private String title;
 8     private String artist;
 9     private List<String> tracks;
10 
11     public ListBlankDisc(String title, String artist, List<String> tracks) {
12         this.title = title;
13         this.artist = artist;
14         this.tracks = tracks;
15     }
16 
17     public void play() {
18         System.out.println("Playing " + title + " by " + artist);
19         for (String track : tracks) {
20             System.out.println("-Track: " + track);
21         }
22     }
23 
24 }

  接着,梳理一下混合配置常见的几种情况:

  1.使用@Import注解,将其中一个JavaConfig导入到另一个JavaConfig当中的第一种方法

  代码结构为:

  

  在之前的CDPlayConfig配置类中已经定义了两个bean,就姑且认为它很复杂,所以将其中的SgtPeppers bean分开独立到它自己的配置类CDConfig类中:

 1 package mixedConfig1;
 2 
 3 import org.springframework.context.annotation.Bean;
 4 import org.springframework.context.annotation.Configuration;
 5 
 6 @Configuration
 7 public class CDConfig {
 8     @Bean
 9     public CompactDisc compactDisc() {
10         return new SgtPeppers();
11     }
12 }

  由于已经移除了CompactDisc()方法,因此需要有一种方法将这两个类组合到一起,因此就在CDPlayerConfig类中使用@Import(CDConfig.class)来导入CDConfig配置类:

 1 package mixedConfig1;
 2 
 3 import org.springframework.context.annotation.Bean;
 4 import org.springframework.context.annotation.Configuration;
 5 import org.springframework.context.annotation.Import;
 6 
 7 @Configuration
 8 @Import(CDConfig.class)
 9 public class CDPlayerConfig {
10 
11     @Bean
12     public CDPlayer cdPlayer(CompactDisc compactDisc) {
13         return new CDPlayer(compactDisc);
14     }
15 
16 }

  测试类JavaImportJavaConfigTest 中,需要通过@ContextConfiguration(classes = CDPlayerConfig.class)来指明被导入CDConfig的CDPlayerConfig配置类:

 1 package mixedConfig1;
 2 
 3 import static org.junit.Assert.assertEquals;
 4 
 5 import org.junit.Rule;
 6 import org.junit.Test;
 7 import org.junit.contrib.java.lang.system.SystemOutRule;
 8 import org.junit.runner.RunWith;
 9 import org.springframework.beans.factory.annotation.Autowired;
10 import org.springframework.test.context.ContextConfiguration;
11 import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
12 
13 @RunWith(SpringJUnit4ClassRunner.class)
14 @ContextConfiguration(classes = CDPlayerConfig.class)
15 public class JavaImportJavaConfigTest {
16 
17     @Rule
18     public final SystemOutRule log = new SystemOutRule().enableLog();
19 
20     @Autowired
21     private MediaPlayer player;
22 
23     @Test
24     public void play() {
25         player.play();
26         assertEquals("Playing Sgt. Pepper's Lonely Hearts Club Band by The Beatles
", log.getLog());
27     }
28 
29 }

  2.不在其中一个配置类当中使用@Import注解,创建一个更高级别的配置类SoundSystemConfig类,在这个类中使用@Import将两个配置类组合在一起

  代码结构为:

  

  CDConfig配置类:

 1 package mixedConfig2;
 2 
 3 import org.springframework.context.annotation.Bean;
 4 import org.springframework.context.annotation.Configuration;
 5 
 6 @Configuration
 7 public class CDConfig {
 8     
 9     @Bean
10     public CompactDisc compactDisc() {
11         return new SgtPeppers();
12     }
13 }

  没有加@Import注解的CDPlayerConfig配置类:

 1 package mixedConfig2;
 2 
 3 import org.springframework.context.annotation.Bean;
 4 import org.springframework.context.annotation.Configuration;
 5 
 6 @Configuration
 7 public class CDPlayerConfig {
 8 
 9     @Bean
10     public CDPlayer cdPlayer(CompactDisc compactDisc) {
11         return new CDPlayer(compactDisc);
12     }
13 
14 }

  将两个配置类组合在一起的更高级别的配置类SoundSystemConfig类:

 1 package mixedConfig2;
 2 
 3 import org.springframework.context.annotation.Configuration;
 4 import org.springframework.context.annotation.Import;
 5 
 6 @Configuration
 7 @Import({CDPlayerConfig.class, CDConfig.class})
 8 public class SoundSystemConfig {
 9 
10 }

  测试类JavaImportJavaConfigTest类,通过@ContextConfiguration(classes = SoundSystemConfig.class)将读取最高级别的配置类SoundSystemConfig类:

 1 package mixedConfig2;
 2 
 3 import static org.junit.Assert.assertEquals;
 4 
 5 import org.junit.Rule;
 6 import org.junit.Test;
 7 import org.junit.contrib.java.lang.system.SystemOutRule;
 8 import org.junit.runner.RunWith;
 9 import org.springframework.beans.factory.annotation.Autowired;
10 import org.springframework.test.context.ContextConfiguration;
11 import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
12 
13 @RunWith(SpringJUnit4ClassRunner.class)
14 @ContextConfiguration(classes = SoundSystemConfig.class)
15 public class JavaImportJavaConfigTest {
16 
17     @Rule
18     public final SystemOutRule log = new SystemOutRule().enableLog();
19 
20     @Autowired
21     private MediaPlayer player;
22 
23     @Test
24     public void play() {
25         player.play();
26         assertEquals("Playing Sgt. Pepper's Lonely Hearts Club Band by The Beatles
", log.getLog());
27     }
28 
29 }

  3.使用@ImportResource注解,将配置在XML中的ListBlankDisc bean注入到配置在JavaConfig中的CDPlayer bean中

  代码结构为:

  

  配置类CDPlayerConfig类:

 1 package mixedConfig3;
 2 
 3 import org.springframework.context.annotation.Bean;
 4 import org.springframework.context.annotation.Configuration;
 5 
 6 @Configuration
 7 public class CDPlayerConfig {
 8 
 9     @Bean
10     public CDPlayer cdPlayer(CompactDisc compactDisc) {
11         return new CDPlayer(compactDisc);
12     }
13 
14 }

   配置xml文件cd-config.xml:

 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <beans xmlns="http://www.springframework.org/schema/beans"
 3     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:c="http://www.springframework.org/schema/c"
 4     xsi:schemaLocation="http://www.springframework.org/schema/beans 
 5                            http://www.springframework.org/schema/beans/spring-beans.xsd">
 6 
 7     <bean id="compactDisc" class="mixedConfig3.ListBlankDisc"
 8         c:_0="Sgt. Pepper's Lonely Hearts Club Band" c:_1="The Beatles">
 9         <constructor-arg>
10             <list>
11                 <value>Sgt. Pepper's Lonely Hearts Club Band</value>
12                 <value>With a Little Help from My Friends</value>
13                 <value>Lucy in the Sky with Diamonds</value>
14                 <value>Getting Better</value>
15                 <value>Fixing a Hole</value>
16             </list>
17         </constructor-arg>
18     </bean>
19 
20 </beans>

  最高级别的配置类SoundSystemConfig类:

 1 package mixedConfig3;
 2 
 3 import org.springframework.context.annotation.Configuration;
 4 import org.springframework.context.annotation.Import;
 5 import org.springframework.context.annotation.ImportResource;
 6 
 7 @Configuration
 8 @Import(CDPlayerConfig.class)
 9 @ImportResource("classpath:/mixedConfig3/cd-config.xml")
10 public class SoundSystemConfig {
11 
12 }

  测试类JavaImportXmlConfigTest类,指明SoundSystemConfig为读取的配置类(这里遇到了一个问题,就是如果直接copy xml文档不注意去掉空格的话,可能会报错,所以要先对xml文件format一下):

 1 package mixedConfig3;
 2 
 3 import static org.junit.Assert.assertEquals;
 4 
 5 import org.junit.Rule;
 6 import org.junit.Test;
 7 import org.junit.contrib.java.lang.system.SystemOutRule;
 8 import org.junit.runner.RunWith;
 9 import org.springframework.beans.factory.annotation.Autowired;
10 import org.springframework.test.context.ContextConfiguration;
11 import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
12 
13 @RunWith(SpringJUnit4ClassRunner.class)
14 @ContextConfiguration(classes = SoundSystemConfig.class)
15 public class JavaImportXmlConfigTest {
16 
17     @Rule
18     public final SystemOutRule log = new SystemOutRule().enableLog();
19 
20     @Autowired
21     private MediaPlayer player;
22 
23     @Test
24     public void play() {
25         player.play();
26         assertEquals("Playing Sgt. Pepper's Lonely Hearts Club Band by The Beatles
"
27                    + "-Track: Sgt. Pepper's Lonely Hearts Club Band
" 
28                    + "-Track: With a Little Help from My Friends
"
29                    + "-Track: Lucy in the Sky with Diamonds
" + "-Track: Getting Better
"
30                    + "-Track: Fixing a Hole
", log.getLog());
31     }
32 
33 }

  4.使用<import>元素在XML配置文件中进行配置拆分,在其中一个XML中引用另一个XML

  代码结构为:

  

  配置xml文件cd-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.xsd">

    <bean id="compactDisc" class="mixedConfig4.SgtPeppers" />

</beans>

  使用<import>元素导入其中一个配置文件到配置xml文件cdplayer-config.xml中:

 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <beans xmlns="http://www.springframework.org/schema/beans"
 3        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
 4        xmlns:c="http://www.springframework.org/schema/c"
 5        xsi:schemaLocation="http://www.springframework.org/schema/beans 
 6                            http://www.springframework.org/schema/beans/spring-beans.xsd">
 7 
 8     <import resource="cd-config.xml" />
 9     <bean id="cdPlayer" class="mixedConfig4.CDPlayer" c:cd-ref="compactDisc" />
10 
11 </beans>

  测试类XMLImportXMLConfigTest类,给定最高级别的配置文件路径“/mixedConfig4/cdplayer-config.xml”

 1 package mixedConfig4;
 2 
 3 import static org.junit.Assert.assertEquals;
 4 
 5 import org.junit.Rule;
 6 import org.junit.Test;
 7 import org.junit.contrib.java.lang.system.SystemOutRule;
 8 import org.junit.runner.RunWith;
 9 import org.springframework.beans.factory.annotation.Autowired;
10 import org.springframework.test.context.ContextConfiguration;
11 import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
12 
13 @RunWith(SpringJUnit4ClassRunner.class)
14 @ContextConfiguration("classpath:/mixedConfig4/cdplayer-config.xml")
15 public class XMLImportXMLConfigTest {
16 
17     @Rule
18     public final SystemOutRule log = new SystemOutRule().enableLog();
19 
20     @Autowired
21     private MediaPlayer player;
22 
23     @Test
24     public void play() {
25         player.play();
26         assertEquals("Playing Sgt. Pepper's Lonely Hearts Club Band by The Beatles
", log.getLog());
27     }
28 
29 }

  5.<bean>元素能够用来将JavaConfig配置导入到XML配置中

  代码结构为:

  

  使用JavaConfig配置的配置类CDConfig类:

 1 package mixedConfig5;
 2 
 3 import org.springframework.context.annotation.Bean;
 4 import org.springframework.context.annotation.Configuration;
 5 
 6 @Configuration
 7 public class CDConfig {
 8     
 9     @Bean
10     public CompactDisc compactDisc() {
11         return new SgtPeppers();
12     }
13 }

  使用<bean>元素将CDConfig配置类导入到XML中:

 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <beans xmlns="http://www.springframework.org/schema/beans"
 3     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:c="http://www.springframework.org/schema/c"
 4     xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
 5 
 6     <bean class="mixedConfig5.CDConfig" />
 7 
 8     <bean id="cdPlayer" class="mixedConfig5.CDPlayer" c:cd-ref="compactDisc" />
 9 
10 </beans>

  测试类XMLImportJavaConfigTest类,指明xml配置文件路径为/mixedConfig5/cdplayer-config.xml:

 1 package mixedConfig5;
 2 
 3 import static org.junit.Assert.assertEquals;
 4 
 5 import org.junit.Rule;
 6 import org.junit.Test;
 7 import org.junit.contrib.java.lang.system.SystemOutRule;
 8 import org.junit.runner.RunWith;
 9 import org.springframework.beans.factory.annotation.Autowired;
10 import org.springframework.test.context.ContextConfiguration;
11 import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
12 
13 @RunWith(SpringJUnit4ClassRunner.class)
14 @ContextConfiguration("classpath:/mixedConfig5/cdplayer-config.xml")
15 public class XMLImportJavaConfigTest {
16 
17     @Rule
18     public final SystemOutRule log = new SystemOutRule().enableLog();
19 
20     @Autowired
21     private MediaPlayer player;
22 
23     @Test
24     public void play() {
25         player.play();
26         assertEquals("Playing Sgt. Pepper's Lonely Hearts Club Band by The Beatles
", log.getLog());
27     }
28 
29 }

  6.比较合理的做法是,创建一个更高层次的配置文件,这个文件不包含任何的bean,只是负责将两个或者更多的配置组合起来

  代码结构为:

  

  配置类CDConfig类:

 1 package mixedConfig6;
 2 
 3 import org.springframework.context.annotation.Bean;
 4 import org.springframework.context.annotation.Configuration;
 5 
 6 @Configuration
 7 public class CDConfig {
 8     
 9     @Bean
10     public CompactDisc compactDisc() {
11         return new SgtPeppers();
12     }
13 }

  配置xml文件cdplayer-config.xml:

 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <beans xmlns="http://www.springframework.org/schema/beans"
 3        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
 4        xmlns:c="http://www.springframework.org/schema/c"
 5        xsi:schemaLocation="http://www.springframework.org/schema/beans 
 6                            http://www.springframework.org/schema/beans/spring-beans.xsd">
 7 
 8     <bean id="cdPlayer" class="mixedConfig6.CDPlayer" c:cd-ref="compactDisc" />
 9 
10 </beans>

  最高级别配置文件SoundSystemConfig.xml:

 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <beans xmlns="http://www.springframework.org/schema/beans"
 3     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:c="http://www.springframework.org/schema/c"
 4     xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
 5 
 6     <bean class="mixedConfig6.CDConfig" />
 7 
 8     <import resource="cdplayer-config.xml" />
 9 
10 </beans>

  指明最高级别配置文件路径的测试类SoundSystemConfigTest类:

 1 package mixedConfig6;
 2 
 3 import static org.junit.Assert.assertEquals;
 4 
 5 import org.junit.Rule;
 6 import org.junit.Test;
 7 import org.junit.contrib.java.lang.system.SystemOutRule;
 8 import org.junit.runner.RunWith;
 9 import org.springframework.beans.factory.annotation.Autowired;
10 import org.springframework.test.context.ContextConfiguration;
11 import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
12 
13 @RunWith(SpringJUnit4ClassRunner.class)
14 @ContextConfiguration("classpath:/mixedConfig6/SoundSystemConfig.xml")
15 public class SoundSystemConfigTest {
16 
17     @Rule
18     public final SystemOutRule log = new SystemOutRule().enableLog();
19 
20     @Autowired
21     private MediaPlayer player;
22 
23     @Test
24     public void play() {
25         player.play();
26         assertEquals("Playing Sgt. Pepper's Lonely Hearts Club Band by The Beatles
", log.getLog());
27     }
28 
29 }

  

  六、其他问题

  1.自动装配--@Autowired

  接口类:

package com.ssm.chapter10.annotation.service;

public interface RoleService2 {
    public void printRoleInfo();
}

  实现类:

package com.ssm.chapter10.annotation.service.impl;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import com.ssm.chapter10.annotation.pojo.Role;
import com.ssm.chapter10.annotation.service.RoleService2;

@Component("RoleService2")
public class RoleServiceImpl2 implements RoleService2 {

    @Autowired
    private Role role = null;

    public Role getRole() {
        return role;
    }

//    @Autowired
    public void setRole(Role role) {
        this.role = role;
    }

    @Override
    public void printRoleInfo() {
        System.out.println("id =" + role.getId());
        System.out.println("roleName =" + role.getRoleName());
        System.out.println("note =" + role.getNote());
    }
}

  (1)第一种方式:在字段上注入。

    这里的@Autowired表示在Spring IoC定位所有的Bean后,这个字段需要按类型注入,这样IoC容器就会寻找资源,然后将其注入。

    @Autowired
    private Role role = null;

  (2)@Autowired除了可以配置在属性之外,还允许方法配置,常见的Bean的setter方法也可以使用它来完成注入。

    @Autowired
    public void setRole(Role role) {
        this.role = role;
    }

  2.自动装配的歧义性(@Primary和@Qualifier)

  自动装配在有些时候并不能使用,原因在于按类型的注入方式。按照Spring的建议,在大部分情况下会使用接口编程,但是定义一个接口,并不一定只有一个与之对应的实现类。也就是说,一个接口可以有多个实现类,例如:

  有一个接口:RoleService

package com.ssm.chapter10.annotation.service;

import com.ssm.chapter10.annotation.pojo.Role;

public interface RoleService {
    public void printRoleInfo(Role role);
}
接口

  和两个实现类:RoleServiceImpl和RoleServiceImpl3

package com.ssm.chapter10.annotation.service.impl;

import org.springframework.stereotype.Component;

import com.ssm.chapter10.annotation.pojo.Role;
import com.ssm.chapter10.annotation.service.RoleService;

@Component
public class RoleServiceImpl implements RoleService {
    @Override
    public void printRoleInfo(Role role) {
        System.out.println("id =" + role.getId());
        System.out.println("roleName =" + role.getRoleName());
        System.out.println("note =" + role.getNote());
    }
}
实现类1
package com.ssm.chapter10.annotation.service.impl;

import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Component;

import com.ssm.chapter10.annotation.pojo.Role;
import com.ssm.chapter10.annotation.service.RoleService;

@Component("roleService3")
public class RoleServiceImpl3 implements RoleService {
    
    @Override
    public void printRoleInfo(Role role) {
        System.out.print("{id =" + role.getId());
        System.out.print(", roleName =" + role.getRoleName());
        System.out.println(", note =" + role.getNote() + "}");
    }
}
实现类2

  有一个RoleController类,它有一个字段是RoleService接口类型,由于RoleService有两个实现类,因此Spring IoC容器无法判断要把哪个对象注入进来,于是就会抛出异常,这样@Autowired就会注入失败。产生这样的状况是因为它采用的是按类型来注入对象,而在Java中接口可以有多个实现类,同样的抽象类也可以有多个实例化的类,这样就会造成通过类型(by type)获取Bean的不唯一,从而导致Spirng IoC容器类似于按类型的方法无法获得唯一的实例化类。

package com.ssm.chapter10.annotation.controller;

@Component
public class RoleController {
    
    @Autowired
    private RoleService roleService = null;
    
    public void printRole(Role role) {
        roleService.printRoleInfo(role);
    }
}

  (1)使用@Primary解决

  注解@Primary代表首要的,当Spring IoC通过一个接口或者抽象类注入对象时,@Primary注解的Bean会被优先注入。

package com.ssm.chapter10.annotation.service.impl;

@Component("roleService3")
@Primary
public class RoleServiceImpl3 implements RoleService {
    
    @Override
    public void printRoleInfo(Role role) {
        System.out.print("{id =" + role.getId());
        System.out.print(", roleName =" + role.getRoleName());
        System.out.println(", note =" + role.getNote() + "}");
    }
}

  (2)使用@Qualifier解决

  除了按类型查找Bean,Spring IoC容器的最底层接口BeanFactory也定义了按名称查找的方法,如果采用名称查找而不是按类型查找的方法,就可以消除歧义性了。

  首先把RoleServiceImpl3定义成别名@Component("roleService3")

  然后在装配时就可以使用@Qualifier("roleService3")来注入这个指定的类了。

package com.ssm.chapter10.annotation.controller;

@Component
public class RoleController {
    
    @Autowired
    @Qualifier("roleService3")
    private RoleService roleService = null;
    
    public void printRole(Role role) {
        roleService.printRoleInfo(role);
    }
}

  3.装载带有参数的构造方法类

  对于一些带有参数的构造方法,也允许我们通过注解进行注入。

  例如,可以在构造方法中使用@Autowired和@Qualifier注解对参数进行注入。

package com.ssm.chapter10.annotation.controller;

@Component
public class RoleController2 {
    
    private RoleService roleService = null;
    
    public RoleController2(@Autowired @Qualifier("roleService3") RoleService roleService) {
        this.roleService = roleService;
    }

    public RoleService getRoleService() {
        return roleService;
    }

    public void setRoleService( RoleService roleService) {
        this.roleService = roleService;
    }
    
    public void printRole(Role role) {
        roleService.printRoleInfo(role);
    }
}

  4.使用Profile

  为了在不同的环境下装载不同的Bean,Spring提供了Profile进行支持。

  应用场景:开发人员使用开发数据库,而测试人员使用测试数据库。

  (1)定义Profile有两种方式,使用Java代码中的@Profile注解或者是XML中的profile元素

  使用@Profile注解:

package com.spring.profile

@Component
public class ProdileDataSOurce {

    @Bean(name="devDataSource")
    @Profile("dev")
    public DataSource getDevDataSource(){
        ...
        return dataSource;    
    }

    @Bean(name="testDataSource")
    @Profile("test")
    public DataSource getDevDataSource(){
        ...
        return dataSource;    
    }
}

  使用XML中的profile元素:

<beans profile = "test">
    <bean .../>
</beans>

<beans profile = "dev">
    <bean .../>
</beans>

  (2)激活Profile的方式

  • 在使用SpringMVC的情况下可以配置Web上下文参数,或者DispatchServlet参数
  • 作为JNDI条目
  • 配置环境变量
  • 配置JVM启动参数
  • 在集成测试环境中使用@ActiveProfiles

  例如:

package com.spring.test

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes=ProfileConfig.class)
@ActiveProfiles("dev")
public class ProfileTest {
    @Autowired
    private DataSource dataSource;
    @Test
    public void test() {
        System.out.println(dataSource.getClass().getName());   
    }
}

  5.加载属性(properties)文件

  使用属性文件可以有效地减少硬编码,很多时候修改环境只需要修改配置文件就可以了,这样能够有效地提高运维人员的操作便利性。

  给定属性文件database-config.properties

jdbc.database.driver=com.mysql.jdbc.Driver
jdbc.database.url=jdbc:mysql://localhost:3306/chapter10
jdbc.database.username=root
jdbc.database.password=123456

  (1)使用注解方式加载属性文件

    Spring提供了@PropertySource来加载属性文件,有一些配置项:

  • name:字符串,配置这次属性配置的名称
  • value:字符串数组,可以配置多个属性文件
  • ignoreResourcesNotFound:boolean值,默认为false,表示如果找不到对应的属性文件是否进行忽略处理,false表示如果找不到就抛出异常
  • encoding:编码,默认为“”

    定义Java配置类:ApplicationConfig.java

@Configuration
@PropertySource(value={"classpath:database-config.properties"}, ignoreResourceNotFound=true) public class ApplicationConfig {
}

    在Spirng中使用属性文件中的内容:

    private static void test9() {
        ApplicationContext context = new AnnotationConfigApplicationContext(ApplicationConfig.class);
            String url = context.getEnvironment().getProperty("jdbc.database.url");
            System.out.println(url);
    }

  如果仅仅是这样,在Spring中没有解析属性占位符的能力,Spring推荐使用一个属性文件解析类进行处理,它就是PropertySourcesPlaceholderConfigurer,使用它就意味着允许Spring解析对应的属性文件,通过占位符去引用对应的配置。

    加载数据库属性文件,定义了一个PropertySourcesPlaceholderConfigurer类的Bean,作用是为了让Spring能够解析属性占位符。

@Configuration
@ComponentScan
@PropertySource(value={"classpath:database-config.properties"}, ignoreResourceNotFound=true) public class ApplicationConfig { @Bean public PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() { return new PropertySourcesPlaceholderConfigurer(); }

    通过占位符引用加载进来的属性:

package com.ssm.chapter10.annotation.config;

@Component
public class DataSourceBean {

    @Value("${jdbc.database.driver}")
    private String driver = null;
    
    @Value("${jdbc.database.url}")
    private String url = null;
    
    @Value("${jdbc.database.username}")
    private String username = null;
    
    @Value("${jdbc.database.password}")
    private String password = null;
    
    /**getter and setter**/

    @Bean(name = "dataSource1")
    public DataSource getDataSource() {
        Properties props = new Properties();
        props.setProperty("driver", driver);
        props.setProperty("url", url);
        props.setProperty("username", username);
        props.setProperty("password", password);
        DataSource dataSource = null;
        try {
            dataSource = BasicDataSourceFactory.createDataSource(props);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return dataSource;
    }
}

  (2)使用XML方式加载属性文件

  通过<context:property-placeholder>元素也可以加载一个属性文件或者是多个属性文件。

    <context:component-scan base-package="com.ssm.chapter10.annotation" />
    <!-- 
    <context:property-placeholder
        ignore-resource-not-found="false" location="classpath:database-config.properties" />
      -->
     <!--字符串数组,可配置多个属性文件 --> 
    <bean
        class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
        
        <property name="locations">
            <array>
                <value>classpath:database-config.properties</value>
                <value>classpath:log4j.properties</value>
            </array>
        </property>
        <property name="ignoreResourceNotFound" value="false" />
    </bean>
</beans>

  6.条件化装配Bean

  在某些条件下不需要去装配Bean,比如当属性文件中没有属性配置时,就不要去创建数据源,这时候,需要通过条件化去判断。

  Spring提供了注解@Conditional可以配置一个或多个类

  首先定义一个实现了Condition接口的类,需要实现matches方法,首先获取运行上下文环境,然后判断在环境中属性文件是否配置了数据库的相关参数,如果参数全部配置了就返回true。

package com.ssm.chapter10.annotation.condition;
public class DataSourceCondition implements Condition {
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        //获取上下文环境
        Environment env = context.getEnvironment();
        //判断是否存在关于数据源的基础配置
        return env.containsProperty("jdbc.database.driver") 
                && env.containsProperty("jdbc.database.url")
                && env.containsProperty("jdbc.database.username")
                && env.containsProperty("jdbc.database.password");
    }
}

  然后通过@Conditional({DataSourceCondition.class})去配置,如果所有参数在配置文件中都已经配置了,则返回为true,那么Spring会去创建对应的Bean,否则是不会创建的。

    @Bean(name = "dataSource")
    @Conditional({DataSourceCondition.class})
    public DataSource getDataSource(
            @Value("${jdbc.database.driver}") String driver,
            @Value("${jdbc.database.url}") String url,
            @Value("${jdbc.database.username}") String username, 
            @Value("${jdbc.database.password}") String password) {
        Properties props = new Properties();
        props.setProperty("driver", driver);
        props.setProperty("url", url);
        props.setProperty("username", username);
        props.setProperty("password", password);
        DataSource dataSource = null;
        try {
            dataSource = BasicDataSourceFactory.createDataSource(props);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return dataSource;
    }

  

  7.Bean的作用域

  在默认的情况下,Spring IoC容器只会对一个Bean创建一个实例,而不是多个。

  Spring提供了4种作用域,它会根据情况来决定是否生成新的对象:

  • 单例(singleton):默认的选项,在整个应用中,Spring只为其生成一个Bean的实例。
  • 原型(prototype):当每次注入,或者通过Spring IoC容器获取Bean时,Spring都会为它创建一个新的实例。
  • 会话(session):在Web应用中使用,就是在会话过程中Spring只创建一个实例
  • 请求(request):在Web应用中使用,就是在一次请求中Spring会创建一个实例,但是不同的请求会创建不同的实例。

  可以通过@Scope声明作用域为原型,这样两次分别从Spirng IoC容器中就会获得不同的对象,

@Component
//@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class RoleDataSourceServiceImpl implements RoleDataSourceService {
    ...
}

  代码已上传至GitHub:https://github.com/BigJunOba/SpringDI

原文地址:https://www.cnblogs.com/BigJunOba/p/9076121.html