您的应用程序在运行之前必须工作的环境数量通常取决于几件事,包括您组织的业务流程,应用程序的规模以及它的“重要性”(即,如果您正在编写税收表)系统为您的国家/地区提供税收服务,那么测试过程可能比为本地商店编写电子商务应用程序时更为严格。 为使您有所了解,以下是想到的所有不同环境的快速列表(可能不完整):

  1. 本地开发者机器
  2. 开发测试机
  3. 测试团队功能测试机
  4. 集成测试机
  5. 克隆环境(实时副本)
  6. 生活

这不是一个新问题,通常可以通过为每个环境创建一组Spring XML和属性文件来解决。 XML文件通常包含一个导入其他环境特定文件的主文件。 然后在编译时将它们耦合在一起以创建不同的WAR或EAR文件。 这种方法已经使用了多年,但确实存在一些问题:

  1. 这是非标准的。 每个组织通常都有自己的解决方法,没有两种方法可以完全相同/
  2. 很难实现,为错误留出了很大的空间。
  3. 必须为每个环境创建一个不同的WAR / EAR文件并将其部署在每个环境上,这需要花费时间和精力,而花更多的时间编写代码可能更好。

PropertyPlaceholderConfigurer类和相关的$ {}标记将它们注入到Spring XML文件中。

<bean id='propertyConfigurer' class='org.springframework.beans.factory.config.PropertyPlaceholderConfigurer'>
        <property name='locations'>
            <list>
                <value>db.properties</value>
            </list>
        </property>
    </bean>

其次,有特定于环境的bean类,例如数据源,它们通常根据您连接数据库的方式而有所不同。

例如,在开发中,您可能具有:

<bean id='dataSource' class='org.springframework.jdbc.datasource.DriverManagerDataSource'>
        <property name='driverClassName'>
            <value>${database.driver}</value>
        </property>
        <property name='url'>
            <value>${database.uri}</value>
        </property>
        <property name='username'>
            <value>${database.user}</value>
        </property>
        <property name='password'>
            <value>${database.password}</value>
        </property>
    </bean>

…无论是测试还是现场直播,您都可以简单地写:

<jee:jndi-lookup id='dataSource' jndi-name='jdbc/LiveDataSource'/>

PropertyPlaceholderConfigurer初始化简单的Bean属性; 但是,您可能希望使用Spring配置文件将特定于环境的PropertyPlaceholderConfigurer注入到Spring上下文中。

话虽如此,我将在示例代码中打破这一约定,因为我想用最简单的代码来演示Spring配置文件的功能。

Spring配置文件和XML配置
在XML配置方面,Spring 3.1在spring-beans模式的bean元素中引入了新的profile属性:

<beans profile='dev'>

配置文件属性充当开关。

为了进一步解释所有这些,我将使用一个简单的想法,即您的应用程序需要加载一个人员类,并且该人员类包含不同的属性,具体取决于您的程序在其上运行的环境。

Person类非常简单,看起来像这样:

public class Person {



  private final String firstName;

  private final String lastName;

  private final int age;



  public Person(String firstName, String lastName, int age) {

    this.firstName = firstName;

    this.lastName = lastName;

    this.age = age;

  }



  public String getFirstName() {

    return firstName;

  }



  public String getLastName() {

    return lastName;

  }



  public int getAge() {

    return age;

  }

}

…并在以下XML配置文件中定义:

<?xml version='1.0' encoding='UTF-8'?>
<beans xmlns='http://www.springframework.org/schema/beans'
 xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'
 xsi:schemaLocation='http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd'
  profile='test1'>

 <bean id='employee' class='profiles.Person'>
  <constructor-arg value='John' />
  <constructor-arg value='Smith' />
  <constructor-arg value='89' />
 </bean>
</beans>

…和

<?xml version='1.0' encoding='UTF-8'?>
<beans xmlns='http://www.springframework.org/schema/beans'
 xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'
 xsi:schemaLocation='http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd'
  profile='test2'>

 <bean id='employee' class='profiles.Person'>
  <constructor-arg value='Fred' />
  <constructor-arg value='ButterWorth' />
  <constructor-arg value='23' />
 </bean>
</beans>

test-1-profile.xml和test-2-profile.xml

不幸的是,仅仅定义个人资料还不够,您必须告诉Spring您正在加载哪个个人资料。 这意味着遵循旧的“标准”代码现在将失败:

@Test(expected = NoSuchBeanDefinitionException.class)

  public void testProfileNotActive() {



    // Ensure that properties from other tests aren't set

    System.setProperty('spring.profiles.active', '');



    ApplicationContext ctx = new ClassPathXmlApplicationContext('test-1-profile.xml');



    Person person = ctx.getBean(Person.class);

    String firstName = person.getFirstName();

    System.out.println(firstName);

  }

幸运的是,有几种选择配置文件的方法,在我看来,最有用的方法是使用“ spring.profiles.active”系统属性。 例如,以下测试现在将通过:

System.setProperty('spring.profiles.active', 'test1');

    ApplicationContext ctx = new ClassPathXmlApplicationContext('test-1-profile.xml');



    Person person = ctx.getBean(Person.class);

    String firstName = person.getFirstName();

    assertEquals('John', firstName);

显然,您不希望像我上面那样对事情进行硬编码,最佳实践通常意味着将系统属性定义与应用程序分开。 这使您可以选择使用简单的命令行参数,例如:

-Dspring.profiles.active='test1'

…或通过添加

# Setting a property value
spring.profiles.active=test1

catalina.properties

bean元素配置文件属性创建Spring XML配置文件,并通过将spring.profiles.active系统属性设置为配置文件的名称来打开要使用的配置文件

获得一些额外的灵活性

但是,这还不是故事的结局,因为Spring的Guy已添加了许多以编程方式加载和启用配置文件的方法-如果您愿意的话。

@Test

  public void testProfileActive() {



    ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext(

        'test-1-profile.xml');

    ConfigurableEnvironment env = ctx.getEnvironment();

    env.setActiveProfiles('test1');

    ctx.refresh();



    Person person = ctx.getBean('employee', Person.class);

    String firstName = person.getFirstName();

    assertEquals('John', firstName);

  }

ConfigurableEnvironment类来激活“ test1”配置文件。

@Test

  public void testProfileActiveUsingGenericXmlApplicationContextMultipleFilesSelectTest1() {



    GenericXmlApplicationContext ctx = new GenericXmlApplicationContext();

    ConfigurableEnvironment env = ctx.getEnvironment();

    env.setActiveProfiles('test1');

    ctx.load('*-profile.xml');

    ctx.refresh();



    Person person = ctx.getBean('employee', Person.class);

    String firstName = person.getFirstName();

    assertEquals('John', firstName);

  }

GenericApplicationContext类而不是ClassPathXmlApplicationContext和FileSystemXmlApplicationContext,因为这样可以提供更大的灵活性。 例如,在上面的代码中,我已经使用GenericApplicationContext的load(...)方法使用通配符来加载许多配置文件:

ctx.load('*-profile.xml');

test-1-profile.xml和test-2-profile.xml

test1和test2配置文件:

@Test

  public void testMultipleProfilesActive() {



    GenericXmlApplicationContext ctx = new GenericXmlApplicationContext();

    ConfigurableEnvironment env = ctx.getEnvironment();

    env.setActiveProfiles('test1', 'test2');

    ctx.load('*-profile.xml');

    ctx.refresh();



    Person person = ctx.getBean('employee', Person.class);

    String firstName = person.getFirstName();

    assertEquals('Fred', firstName);

  }

当心,在本示例中,我有两个标识为“ employee”的bean,并且无法分辨哪个是有效的,并且应该优先。 从我的测试中,我猜测读取的第二个覆盖或掩盖了对第一个的访问。 没关系,因为您不应该拥有多个具有相同名称的bean –激活多个配置文件时需要提防这一点。

最后,可以做的更好的简化之一是使用嵌套的<beans />元素。

<?xml version='1.0' encoding='UTF-8'?>
<beans xmlns='http://www.springframework.org/schema/beans'
 xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' xmlns:context='http://www.springframework.org/schema/context'
 xsi:schemaLocation='http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
  http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd'>

 <beans profile='test1'>
  <bean id='employee1' class='profiles.Person'>
   <constructor-arg value='John' />
   <constructor-arg value='Smith' />
   <constructor-arg value='89' />
  </bean>
 </beans>

 <beans profile='test2'>
  <bean id='employee2' class='profiles.Person'>
   <constructor-arg value='Bert' />
   <constructor-arg value='William' />
   <constructor-arg value='32' />
  </bean>
 </beans>

</beans>

这消除了通配符和加载多个文件的所有繁琐处理,尽管是以最小的灵活性为代价的。

@Profile注释结合使用的@Configuration注释,总结了我对Spring概要文件的了解,等等。