• 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

  • 相关阅读:
    TCP/IP和HTTP的举例理解
    c#中栈和堆的理解
    c#设计模式之单例模式
    JSON.stringify实例应用—将对象转换成JSON类型进行AJAX异步传值
    JSON.stringify初识
    c# 过滤字符串中的重复字符
    C#中jQuery Ajax实例(二)
    C#中jQuery Ajax实例(一)
    jQuery动态对表格Table进行添加或删除行以及修改列值操作
    asp.net中控件的Attributes用法
  • 原文地址:https://www.cnblogs.com/BigJunOba/p/9076121.html
Copyright © 2020-2023  润新知