1.13。环境抽象 【spring 核心技术 翻译】

1.13。环境抽象

环境接口是集成在容器中的抽象,它为应用程序环境的两个关键方面建模: profilesproperties

profiles文件是一个命名的bean定义逻辑组,只有在给定的概要文件处于活动状态时才向容器注册。bean可以被分配给一个配置文件,无论这个配置文件是用XML定义的还是用注释定义的。与概要文件相关的Environment对象的作用是确定哪些概要文件(如果有的话)当前处于活动状态,以及默认情况下哪些概要文件(如果有的话)应该处于活动状态。

properties在几乎所有的应用程序中都扮演着重要的角色,并且可以来自各种来源:属性文件、JVM系统属性、系统环境变量、JNDI、servlet上下文参数、特别属性对象、映射对象等等。与属性相关的环境对象的作用是为用户提供一个方便的服务接口,用于配置属性源和从属性源解析属性。

1.13.1。Bean定义概要文件 profiles

Bean定义profiles文件在核心容器中提供了一种机制,允许在不同环境中注册不同的Bean。“environment”这个词对不同的用户可能意味着不同的东西,这个特性可以帮助很多用例,包括:

  • 在开发中使用内存中的数据源,而在QA或生产中从JNDI查找相同的数据源。
  • 仅在将应用程序部署到性能环境中时才注册监视基础设施。
  • 为客户A和客户B部署注册定制的bean实现。

考虑需要数据源的实际应用程序中的第一个用例。在测试环境中,配置可能类似如下:

@Bean
public DataSource dataSource() {
    return new EmbeddedDatabaseBuilder()
        .setType(EmbeddedDatabaseType.HSQL)
        .addScript("my-schema.sql")
        .addScript("my-test-data.sql")
        .build();
}

现在考虑如何将该应用程序部署到QA或生产环境中,假设应用程序的数据源已注册到生产应用程序服务器的JNDI目录中。我们的数据源bean现在看起来如下所示:

@Bean(destroyMethod="")
public DataSource dataSource() throws Exception {
    Context ctx = new InitialContext();
    return (DataSource) ctx.lookup("java:comp/env/jdbc/datasource");
}

问题是如何根据当前环境在使用这两种变体之间切换。随着时间的推移,Spring用户已经设计了许多方法来实现这一点,通常依赖于系统环境变量和包含${占位符}标记的XML<import/>语句的组合,这些标记根据环境变量的值解析为正确的配置文件路径。Bean定义概要文件是为这个问题提供解决方案的核心容器特性。

如果我们泛化前面环境特定bean定义示例中所示的用例,我们最终需要在某些上下文中注册某些bean定义,而不是在其他上下文中。您可以说,您希望在情形a中注册某个bean定义的概要文件,在情形b中注册一个不同的概要文件。我们首先更新配置以反映这一需求。

使用@Profile

当一个或多个指定的概要文件处于活动状态时,@Profile注释可以指示组件是否有资格注册。使用我们前面的例子,我们可以重写数据源配置如下:

@Configuration
@Profile("development")
public class StandaloneDataConfig {

    @Bean
    public DataSource dataSource() {
        return new EmbeddedDatabaseBuilder()
            .setType(EmbeddedDatabaseType.HSQL)
            .addScript("classpath:com/bank/config/sql/schema.sql")
            .addScript("classpath:com/bank/config/sql/test-data.sql")
            .build();
    }
}

@ bean方法,如前所述,您通常选择使用程序化的JNDI查找,通过使用Spring的JndiTemplate / JndiLocatorDelegate助手或直接使用JNDI InitialContext JndiObjectFactoryBean变体,但不是早些时候显示这将迫使你声明返回类型作为FactoryBean类型。

@Configuration
public class AppConfig {

    @Bean("dataSource")
    @Profile("development") 
    public DataSource standaloneDataSource() {
        return new EmbeddedDatabaseBuilder()
            .setType(EmbeddedDatabaseType.HSQL)
            .addScript("classpath:com/bank/config/sql/schema.sql")
            .addScript("classpath:com/bank/config/sql/test-data.sql")
            .build();
    }

    @Bean("dataSource")
    @Profile("production") 
    public DataSource jndiDataSource() throws Exception {
        Context ctx = new InitialContext();
        return (DataSource) ctx.lookup("java:comp/env/jdbc/datasource");
    }
}

standaloneDataSource方法只在开发概要文件中可用。
jndiDataSource方法仅在生产配置文件中可用。

对于@Bean方法上的@Profile,可能会应用一种特殊的场景:在重载了相同Java方法名的@Bean方法的情况下(类似于构造函数重载),需要在所有重载的方法上一致地声明一个@Profile条件。如果条件不一致,则只有重载方法中第一个声明的条件才会起作用。因此,@Profile不能用于选择具有特定参数签名的重载方法。同一个bean的所有工厂方法之间的解析遵循c中的Spring构造函数解析算法.

如果您希望定义具有不同配置文件条件的可选bean,请使用不同的Java方法名称,通过使用@Bean name属性指向相同的bean名称,如前面的示例所示。如果参数签名都是相同的(例如,所有的变量都不带参数工厂方法),这是唯一的方式来表示这种安排在第一时间有效的Java类(因为只能有一个方法的名称和参数签名)。

XML Bean定义配置文件

XML对应元素是<beans>元素的profile属性。我们前面的示例配置可以在两个XML文件中重写,如下:

<beans profile="development"
    xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:jdbc="http://www.springframework.org/schema/jdbc"
    xsi:schemaLocation="...">

    <jdbc:embedded-database id="dataSource">
        <jdbc:script location="classpath:com/bank/config/sql/schema.sql"/>
        <jdbc:script location="classpath:com/bank/config/sql/test-data.sql"/>
    </jdbc:embedded-database>
</beans>
<beans profile="production"
    xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:jee="http://www.springframework.org/schema/jee"
    xsi:schemaLocation="...">

    <jee:jndi-lookup id="dataSource" jndi-name="java:comp/env/jdbc/datasource"/>
</beans>

也可以避免在同一个文件中分割和嵌套<beans/>元素,如下面的例子所示:

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:jdbc="http://www.springframework.org/schema/jdbc"
    xmlns:jee="http://www.springframework.org/schema/jee"
    xsi:schemaLocation="...">

    <!-- other bean definitions -->

    <beans profile="development">
        <jdbc:embedded-database id="dataSource">
            <jdbc:script location="classpath:com/bank/config/sql/schema.sql"/>
            <jdbc:script location="classpath:com/bank/config/sql/test-data.sql"/>
        </jdbc:embedded-database>
    </beans>

    <beans profile="production">
        <jee:jndi-lookup id="dataSource" jndi-name="java:comp/env/jdbc/datasource"/>
    </beans>
</beans>

spring-bean.xsd 被限制为只允许将这样的元素作为文件中的最后一个元素。这将有助于提供灵活性,而不会导致XML文件的混乱。

XML对等物不支持前面描述的概要文件表达式。但是,可以通过使用!操作符。也可以通过嵌套配置文件来应用逻辑“和”,如下面的例子所示:

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:jdbc="http://www.springframework.org/schema/jdbc"
    xmlns:jee="http://www.springframework.org/schema/jee"
    xsi:schemaLocation="...">

    <!-- other bean definitions -->

    <beans profile="production">
        <beans profile="us-east">
            <jee:jndi-lookup id="dataSource" jndi-name="java:comp/env/jdbc/datasource"/>
        </beans>
    </beans>
</beans>

在前面的示例中,如果生产配置文件和us-east配置文件都是活动的,则将公开数据源bean。

激活一个概要文件

现在我们已经更新了配置,我们仍然需要指示Spring哪个配置文件是活动的。如果我们现在启动示例应用程序,我们将看到抛出NoSuchBeanDefinitionException异常,因为容器无法找到名为dataSource的Spring bean。

激活一个概要文件可以通过多种方式完成,但是最直接的方式是根据通过ApplicationContext提供的Environment API编程地激活它。下面的例子展示了如何做到这一点:

AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.getEnvironment().setActiveProfiles("development");
ctx.register(SomeConfig.class, StandaloneDataConfig.class, JndiDataConfig.class);
ctx.refresh();

此外,您还可以通过spring.profiles.active 属性声明性地激活配置文件。可以通过系统环境变量、JVM系统属性、web中的servlet上下文参数来指定。xml,或者甚至作为JNDI中的一个条目(请参阅PropertySource抽象)。在集成测试中,可以使用spring-test模块中的@ActiveProfiles注释来声明活动概要文件(请参阅环境概要文件的上下文配置)。

请注意,配置文件不是一个“非此即彼”的命题。您可以同时激活多个配置文件。通过编程,您可以向setActiveProfiles()方法提供多个概要文件名称,该方法接受字String…可变参数。下面的示例激活多个配置文件:

ctx.getEnvironment().setActiveProfiles("profile1", "profile2");

声明spring.profiles.active可以接受以逗号分隔的配置文件名称列表,如下例所示:

 -Dspring.profiles.active="profile1,profile2"

缺省的预置文件

默认配置文件表示默认启用的配置文件。考虑下面的例子:

@Configuration
@Profile("default")
public class DefaultDataConfig {

    @Bean
    public DataSource dataSource() {
        return new EmbeddedDatabaseBuilder()
            .setType(EmbeddedDatabaseType.HSQL)
            .addScript("classpath:com/bank/config/sql/schema.sql")
            .build();
    }
}

如果没有配置文件是活动的,则创建数据源。您可以将此看作为一个或多个bean提供默认定义的一种方法。如果启用了任何配置文件,则不应用默认配置文件。

您可以通过在环境中使用setDefaultProfiles()或声明性地使用spring.profile.default属性来更改默认配置文件的名称。

1.13.2。PropertySource抽象

Spring的环境抽象提供了在属性源的可配置层次结构上的搜索操作。考虑下面的清单:

ApplicationContext ctx = new GenericApplicationContext();
Environment env = ctx.getEnvironment();
boolean containsMyProperty = env.containsProperty("my-property");
System.out.println("Does my environment contain the 'my-property' property? " + containsMyProperty);

在前面的代码片段中,我们看到了一种高级方法,可以询问Spring是否为当前环境定义了my-property属性。要回答这个问题,环境对象对一组PropertySource对象执行搜索。PropertySource是对任何键值对源的简单抽象,Spring的StandardEnvironment配置了两个PropertySource对象—一个表示JVM系统属性集(system . getproperties()),另一个表示系统环境变量集(system .getenv())。

这些默认属性源是为StandardEnvironment提供的,用于独立应用程序。StandardServletEnvironment使用其他默认属性源进行填充,包括servlet配置和servlet上下文参数。它可以选择启用JndiPropertySource。详细信息请参见javadoc。
具体地说,当您使用StandardEnvironment时,如果运行时存在一个my-property系统属性或my-property环境变量,那么对env.containsProperty(“my-property”)的调用将返回true。

所执行的搜索是分层的。默认情况下,系统属性优先于环境变量。因此,如果在调用env.getProperty(“my-property”)期间,恰好在两个位置设置了my-property属性,则系统属性值“胜出”并被返回。注意,属性值没有被合并,而是被前面的条目完全覆盖。

对于普通 StandardServletEnvironment,完整的层次结构如下所示,最高优先级的条目位于顶部:

  1. ServletConfig参数(如果适用——例如,在DispatcherServlet上下文的情况下)
  2. ServletContext参数(web.xml上下文参数项)
  3. JNDI环境变量(java:comp/env/ entries)
  4. JVM系统属性(-D命令行参数)
  5. JVM系统环境(操作系统环境变量)

最重要的是,整个机制是可配置的。也许您有一个想要集成到这个搜索中的自定义属性源。为此,实现并实例化您自己的PropertySource并将其添加到当前环境的PropertySources集合中。下面的例子展示了如何做到这一点:

ConfigurableApplicationContext ctx = new GenericApplicationContext();
MutablePropertySources sources = ctx.getEnvironment().getPropertySources();
sources.addFirst(new MyPropertySource());

在前面的代码中,在搜索中以最高的优先级添加了MyPropertySource。如果它包含my-property属性,则检测并返回该属性,从而有利于任何其他PropertySource中的任何my-property属性。MutablePropertySources API公开了许多允许精确操作属性源集的方法。

1.13.3。使用@PropertySource

@PropertySource注释为向Spring的环境添加PropertySource提供了一种方便的声明性机制。
给定一个名为app.properties的文件,其中包含键值对testbean.name=myTestBean,下面的@Configuration类使用@PropertySource,调用testBean.getName()会返回myTestBean:

@Configuration
@PropertySource("classpath:/com/myco/app.properties")
public class AppConfig {

    @Autowired
    Environment env;

    @Bean
    public TestBean testBean() {
        TestBean testBean = new TestBean();
        testBean.setName(env.getProperty("testbean.name"));
        return testBean;
    }
}

1.13.4。语句中的占位符解析

过去,元素中的占位符的值只能根据JVM系统属性或环境变量解析。现在情况已经不一样了。因为环境抽象集成在整个容器中,所以很容易通过它来解析占位符。这意味着您可以以任何您喜欢的方式配置解析过程。您可以更改搜索系统属性和环境变量的优先级,或者完全删除它们。您还可以在适当的情况下添加您自己的属性源。

具体地说,无论客户属性定义在哪里,只要它在环境中可用,以下语句都有效:

<beans>
    <import resource="com/bank/service/${customer}-config.xml"/>
</beans>
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值