高级装配 —— Spring profile

来源:互联网 发布:linux oracle 是否安装 编辑:程序博客网 时间:2024/06/07 11:54

Q:什么是 profile?
A:可以理解为我们在Spring容器中所定义的Bean的逻辑组名称。只有当这些 Profile 被激活的时候,才会将 Profile 中所对应的 Bean 注册到 Spring 容器中。

Q:为什么要使用 profile bean?
A: 因为在不同的环境中某个 bean 会有所不同。我们就必须得有一种方法来配置 bean,使其在每种环境下都会选择最为合适的配置。
其中一种方法:在单独的配置类(或 XML 文件)中配置每个 bean,然后在构建阶段(可能会使用 Maven 的 profiles)确定要将哪一个配置编译到可部署的应用中。然而,这种方法的问题在于要为每种环境重新构建应用。比如:发开阶段 -> QA 阶段时,需要重新构建。QA 阶段 -> 开发阶段,需要重新构建。
而 Spring 所提供的解决方案(profile bean)不需要重新构建。他不是在构建阶段做出决策,而是等到运行时再来确定要将哪一个配置编译到可部署的应用中。这样的好处就是:同一个部署单元(可能会是 WAR 文件)能够适用于所有的环境,没有必要进行重新构建

如:在开发中,通常会出现在开发的时候使用一个开发数据库,测试的时候使用一个测试的数据库,而实际部署的时候需要一个数据库。环境的更改,每次都需要修改配置文件。但是使用 profile 就不需要了。

在 Spring 3.1 中,Spring 引入了 bean profile 的功能。

Q:怎么使用 profile?
A:首先要将所有不同的 bean 定义整理到一个或多个 profile 之中,再将应用部署到每个环境时,要确保对应的 profile 处于激活的状态。

Q:怎么配置 profile bean?

在 JavaConfig 中配置 profile

方式一:单个创建

package com.myapp;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.context.annotation.Profile;import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType;import javax.activation.DataSource;/** * 在 Java 配置中,可以使用 @Profile 注解指定某个 bean 属于哪一个 profile。 * 在配置类中,嵌入式数据库的 DataSource 可能会配置成如下所示。 * * 注意:@Profile 注解应用在类级别上,它会告诉 Spring 这个配置类中的 bean 只有在 dev profile 激活时才会创建。 *      如果 dev profile 没有激活的话,那么带有 @Bean 注解的方法都会被忽略掉。 */@Configuration@Profile("dev")public class DevelopmentProfileConfig {    @Bean    public DataSource dataSource(){        return (DataSource) new EmbeddedDatabaseBuilder()                .setType(EmbeddedDatabaseType.H2)                .addScript("classpath:schema.sql")                .addScript("classpath:test-data.sql")                .build();    }}

可能还需要一个适用于生产环境的配置:

package com.myapp;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.context.annotation.Profile;import org.springframework.jndi.JndiObjectFactoryBean;import javax.activation.DataSource;/** * 只有 prod profile 激活时,才会创建对应的 bean。 */@Configuration@Profile("prod")public class ProductionProfileConfig {    @Bean    public DataSource dataSource(){        JndiObjectFactoryBean jndiObjectFactoryBean = new JndiObjectFactoryBean();        jndiObjectFactoryBean.setJndiName("jdbc/myDS");        jndiObjectFactoryBean.setResourceRef(true);        jndiObjectFactoryBean.setProxyInterface(javax.sql.DataSource.class);        return (DataSource) jndiObjectFactoryBean.getObject();    }}

在 Spring 3.1 中,只能在类级别上使用 @Profile 注解。不过,从 Spring 3.2 开始,你也可以在方法级别上使用 @Profile 注解,与 @Bean 注解一同使用。这样的话,就能将这两个 bean 的声明放到同一个配置类之中

方式二:放在同一个配置类之中

package com.myapp;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.context.annotation.Profile;import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseBuilder;import org.springframework.jdbc.datasource.embedded.EmbeddedDatabaseType;import org.springframework.jndi.JndiObjectFactoryBean;import javax.activation.DataSource;/** * @Profile 注解基于激活的 profile 实现 bean 的装配 * * 注意:尽管每个 DataSource bean 都被声明在一个 profile 中,并且只有当规定的 profile 激活时, * 相应的 bean 才会被创建,但是可能会有其他的 bean 并没有声明在一个给定的 profile 范围内。 * 没有指定 profile 的 bean 始终都会被创建,与激活哪个 profile 没有关系。 */@Configurationpublic class DataSourceConfig {    @Bean    @Profile("dev")  //为 dev profile 装配的 bean    public DataSource embeddedDataSource(){        return (DataSource) new EmbeddedDatabaseBuilder()                .setType(EmbeddedDatabaseType.H2)                .addScript("classpath:schema.sql")                .addScript("classpath:test-data.sql")                .build();    }    @Bean    @Profile("prod") //为 prod profile 装配的 bean    public DataSource jndiDataSource(){        JndiObjectFactoryBean jndiObjectFactoryBean = new JndiObjectFactoryBean();        jndiObjectFactoryBean.setJndiName("jdbc/myDS");        jndiObjectFactoryBean.setResourceRef(true);        jndiObjectFactoryBean.setProxyInterface(javax.sql.DataSource.class);        return (DataSource) jndiObjectFactoryBean.getObject();    }}

在 XML 中配置 profile

方式一:单个配置文件

<?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:jdbc="http://www.springframework.org/schema/jdbc"       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc.xsd"       profile="dev">    <!-- 通过<beans>元素的 profile 属性,在 XML 配置中配置 profile bean -->    <!--        定义了适用于开发阶段的嵌入式数据库 DataSource bean,        也可以将 profile 设置成 prod,创建适用于生产环境的从 JNDI 获取的 DataSource bean     -->    <jdbc:embedded-database id="dataSource">        <jdbc:script location="classpath:schema.sql"/>        <jdbc:script location="classpath:test-data.sql"/>    </jdbc:embedded-database></beans>

方式二:重复使用元素来指定多个 profile

<?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:jdbc="http://www.springframework.org/schema/jdbc"       xmlns:jee="http://www.springframework.org/schema/jee"       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc.xsd">    <!--        这里的两个 bean ,类型都是 javax.sql.DataSource,并且ID 都是 dataSource        但是运行时,只会创建一个 bean,这取决于处于激活状态的是哪个 profile     -->    <!-- dev profile 的 bean -->    <beans profile="dev">        <jdbc:embedded-database id="dataSource">            <jdbc:script location="classpath:schema.sql"/>            <jdbc:script location="classpath:test-data.sql"/>        </jdbc:embedded-database>    </beans>    <!-- prod profile 的 bean -->    <beans profile="prod">        <jee:jndi-lookup jndi-name="jdbc/myDatabase" id="dataSource" resource-ref="true"                         proxy-interface="javax.sql.DataSource"/>    </beans></beans>

Q:怎么激活 profile?

Spring 在确定哪个 profile 处于激活状态时,需要依赖两个独立的属性:spring.profile.activespring.profile.default。Spring 会先查找 active 再找 default,如果两个均没有设置的话,那就没有激活的 profile,因此只会创建哪些没有定义的在 profile 中的 bean。

Q:有哪些方式是来设置这两个属性呢?
A:

  • 作为 DispatcherServlet 的初始化参数;
  • 作为 Web 应用的上下文参数;
  • 作为 JNDI 条目;
  • 作为环境变量;
  • 作为 JVM 的系统属性;
  • 在集成测试上,使用 @ActiveProfiles 注解设置。

在 Web 应用中,设置 spring.profiles.default 的 web.xml 文件如下所示
这里采用的方法:使用 DispatcherServlet 的初始化参数将 spring.profiles.default 设置为开发环境的 profile,我会在 Servlet 上下文中进行设置(为了兼顾到 ContextLoaderListener)。

<?xml version="1.0" encoding="UTF-8"?><web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"         version="3.1">    <context-param>        <param-name>contextConfigLocation</param-name>        <!-- bean 配置所在的 xml -->        <param-value>classpath:datesource.xml</param-value>    </context-param>    <!-- 为上下文设置默认的 profile:为了兼顾到 ContextLoaderListener -->    <context-param>        <param-name>spring.profiles.default</param-name>        <param-value>dev</param-value>    </context-param>    <!-- 需要导入 spring-web -->    <listener>        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>    </listener>    <!-- 需要导入 spring-webmvc -->    <servlet>        <servlet-name>appServlet</servlet-name>        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>        <init-param>            <!-- 为 servlet 设置默认的 profile -->            <param-name>spring.profiles.default</param-name>            <param-value>dev</param-value>        </init-param>        <load-on-startup>1</load-on-startup>    </servlet>    <servlet-mapping>        <servlet-name>appServlet</servlet-name>        <url-pattern>/</url-pattern>    </servlet-mapping></web-app>

然后,当应用程序部署到 QA、生产或其他环境之中时,负责部署的人根据情况使用系统属性环境变量JNDI 设置 spring.profiles.active 即可。当设置了 active 以后,default 的值就无所谓了。

Q:怎么同时激活多个 profile?
A:通过列出多个 profile 名称,并以逗号分隔来实现。

当运行继承测试时,通常会希望采用与生产环境(或者是生产环境的部分子集)相同的配置进行测试。
如果配置中的 bean 定义在了 profile 中,那么在运行测试时,就需要一种方式来启用合适的 profile。

Q:如何使用 profile 进行测试?
Spring 提供了@ActiveProfiles 注解,我们可以使用它来指定运行测试时需要激活哪个 profile。在集成测试时,通常想要激活的是开发环境的 profile。

/** * 使用 @ActiveProfiles 激活 dev profile * <p> * SpringJUnit4ClassRunner 需要导入 spring-test 包 */@RunWith(SpringJUnit4ClassRunner.class)// 设置配置文件@ContextConfiguration("classpath:datesource.xml")//@ContextConfiguration(classes = {PersistenceTestConfig.class})@ActiveProfiles("dev")public class PersistenceTest {}

在条件化创建 bean 方面,Spring 的 profile 机制是一种很棒的方法,这里的条件要基于哪个 profile 处于激活状态来判断。
Spring 4.0 中提供了一种更为通用的机制来实现条件化的 bean 定义,在这种机制中,条件完全由你来确定。那就是 @Conditional 注解定义条件化的 bean

上一篇:自动装配、JavaConfig、XML 三种方案之间,怎么导入和混合配置?
下一篇:高级装配 —— 条件化的 bean

原创粉丝点击