zoukankan      html  css  js  c++  java
  • Spring高级装配(一) profile

    Spring高级装配要学习的内容包括:

    • Spring profile
    • 条件化的bean声明
    • 自动装配与歧义性
    • bean的作用域
    • Spring表达式语言

    以上属于高级一点的bean装配技术,如果你没有啥特别的需求的话用的还比较少。但是用于解决变态一点的需求还是要学一下留个备份。

    环境与Profile

    直接上情形吧,一个项目现在有三个阶段,不同阶段使用的dataSource的来源不一样,分别是:

    1. 开发阶段:使用嵌入式的Hypersonic数据库
    2. QA阶段:使用不同DataSource配置,比如Common DBCP连接池
    3. 生产阶段:从JNDI容器中获取一个DataSource

    这三种DataSource bean的生成代码分别是:

    嵌入式的Hypersonic数据库:

    1 @Bean(destroyMethod="shutdown")
    2 public DataSource dataSource() {
    3     return new EmbeddedDataSourceBuilder()
    4         .addScript("classpath:schema.sql")
    5         .addScript("classpath:test-data.sql")
    6         .build();
    7 }

     JNDI:

    1 @Bean
    2 public DataSource dataSource() {
    3     JndiObjectFactoryBean jndiObjectFactoryBean = new JndiObjectFactoryBean();
    4     jndiObjectFactoryBean.setJndiName("jdbc/myDS");
    5     jndiObjectFactoryBean.setResourceRef(true);
    6     jndiObjectFactoryBean.setProxyInterface(javax.sql.DataSource.class);
    7     return (DataSource) jndiObjectFactoryBean.getObject();
    8 }

    Common DBCP:

     1 @Bean(destroyMethod="close")
     2 public DataSource dataSource() {
     3     BasicDataSource dataSource = new BasicDataSource();
     4     dataSource.setUrl("jdbc:h2:tcp://dbserver/~/test");
     5     dataSource.setDriverClassName("org.h2.Driver");
     6     dataSource.setUserName("sa");
     7     dataSource.setPassword("password");
     8     dataSource.setInitialSize(20);
     9     dataSource.setMaxActive(30);
    10     return dataSource;
    11 }

    也就是说每个阶段都是用了完全不同的策略来生成DataSource的bean。现在有一个需求是:如何优雅地切换这三种DataSource?

    如果只用到基础的Spring bean的装配知识的话,我们必须每次手动的加上要转入的阶段对应的DataSource bean定义代码。这样的话容易引入bug,而且不优雅。这种情况其实可以抽象一下:根据不同的情况,生成不同的bean

    Spring针对这种根据环境来决定创建哪个bean和不创建哪个bean提供了了一种解决方案:profile。profile使用的大致流程:

    配置profile bean

    Spring利用profile来感觉环境决定创建哪个bean和不创建哪个bean,并不是在构建的时候做出决策,而是在运行时再决定。这样的话代码就可以适用于所有的环境,而不是需要额外重构。

    在使用profile的时候(since 3.1),首先要把不同的bean定义整理到一个或者多个profile中,在将应用部署到每个环境时,要确保对应的profile处于激活(active)状态。

    在Java配置中使用@Profile指定某个bean属于哪个profile。先来一个直接一点的例子:

     1 @Configuration
     2 @Profile("dev")
     3 public class DevelopmentProfileConfig {
     4 
     5     @Bean(destroyMethod="shutdown")
     6     public DataSource dataSource() {
     7         return new EmbeddedDataSourceBuilder()
     8             .addScript("classpath:schema.sql")
     9             .addScript("classpath:test-data.sql")
    10             .build();
    11     }
    12 }

    解释说明:

    • @Profile应用在了类级别上
    • 这个配置类中的bean只有在dev profile被激活的时候才会被创建。
    • 如果dev profile没有被激活,那么带有@Bean注解的方法都会被忽略。

    在给出一个适用于生产环境的配置:

     1 @Configuration
     2 @Profile("prod")
     3 public class ProductionProfileConfig {
     4     @Bean
     5     public DataSource dataSource() {
     6         JndiObjectFactoryBean jndiObjectFactoryBean = new JndiObjectFactoryBean();
     7         jndiObjectFactoryBean.setJndiName("jdbc/myDS");
     8         jndiObjectFactoryBean.setResourceRef(true);
     9         jndiObjectFactoryBean.setProxyInterface(javax.sql.DataSource.class);
    10         return (DataSource) jndiObjectFactoryBean.getObject();
    11     }
    12 }

    在Spring 3.1中只能在类级别上使用@Profile注解,3.2开始,也可以在方法级别上使用@Profile注解,与@Bean注解一同使用;这样的话可以把这两个bean的声明放到同一个配置类中:

     1 @Configuration
     2 public class DataSourceConfig {
     3     @Bean(destroyMethod="shutdown")
     4     @Profile("dev")
     5     public DataSource dataSource() {
     6         return new EmbeddedDataSourceBuilder()
     7             .addScript("classpath:schema.sql")
     8             .addScript("classpath:test-data.sql")
     9             .build();
    10     }
    11 
    12     @Bean
    13     @Profile("prod")
    14     public DataSource dataSource() {
    15         JndiObjectFactoryBean jndiObjectFactoryBean = new JndiObjectFactoryBean();
    16         jndiObjectFactoryBean.setJndiName("jdbc/myDS");
    17         jndiObjectFactoryBean.setResourceRef(true);
    18         jndiObjectFactoryBean.setProxyInterface(javax.sql.DataSource.class);
    19         return (DataSource) jndiObjectFactoryBean.getObject();
    20     }
    21 }

    这样一来每个DataSource的bean都被声明在配置类中,并且只有当规定的profile激活时,相应的bean才会被创建;没有指定profile的bean始终都会被创建,与激活哪个profile没有关系。

    在XML中配置profile

    通过<beans>元素的profile属性,在XML中配置profile bean:

    1 <beans profile="dev">
    2     <jdbc:embedded-database id="dataSource">
    3         <jdbc:script location="classpath:schema.sql" />
    4         <jdbc:script location="classpath:test-data.sql" />
    5     </jdbc:embedded-database>
    6 </beans>

    同理,可以通过把profile设置为prod,创建适用于生产环境的从JNDI获取的DataSource bean;也可以创建基于连接池定义的dataSource bean,将其放在另一个XML文件中,并标注为qa profile。所有的配置文件都会放在部署单元之中(如WAR文件),但是只有profile属性与当前激活的profile相匹配的配置文件才会被用到。

    如果觉得定义的配置文件太多,你可以在根<beans>中嵌套定义<beans>元素,而是不是为每个环境创建一个profile XML文件,配置代码如下:

     1 <beans>
     2 
     3     <beans profile="dev">
     4         <jdbc:embedded-database id="dataSource">
     5             <jdbc:script location="classpath:schema.sql" />
     6             <jdbc:script location="classpath:test-data.sql" />
     7         </jdbc:embedded-database>
     8     </beans>
     9 
    10     <beans profile="qa">
    11         <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"
    12             p:url="jdbc:h2:tcp://dbserver/~/test"
    13             p:driverClassName="org.h2.Driver"
    14             p:username="sa"
    15             p:password="password"
    16             p:initialSize="20"
    17             p:maxActive="30" />
    18     </beans>
    19 
    20     <beans profile="prod">
    21         <jee:jndi-lookup id="dataSource" jndi-name="jdbc/myDatabase"
    22             resource-ref="true" proxy-interface="javax.sql.DataSource" />
    23     </beans>
    24 
    25 </beans>

    激活profle

    把profile配置好了之后,问题是怎么激活这些profile?

    Spring在确定哪个profile处于激活状态时,需要依赖两个独立的属性:

    • spring.profiles.active
    • spring.profiles.default

    如果设置了spring.profiles.active属性的话,那么它的值就会用来确定哪个profile是激活的。

    如果没有设置spring.profiles.active属性的话,那Spring将会查找spring.profiles.default的值。

    如果active和default都没有设置,那么就没有激活的profile,因此只会激活那些没有定义在profile中的bean。

    设置激活属性的方法:

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

    推荐的方式使用时DispatcherServlet的参数将spring.profiles.default设置为开发环境的profile,会在Servlet上下文中进行设置,在web.xml中:

     1 <web-app>
     2 
     3     <!-- 为上下文设置默认的profile -->
     4     <context-param>
     5         <param-name>spring.profiles.default</param-name>
     6         <param-value>dev</param-value>
     7     </context-param>
     8     
     9     <!-- 为Servlet设置默认的profile -->
    10     <servlet>
    11         <servlet-name>appServlet</servlet-name>
    12         <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    13         <init-param>
    14             <param-name>spring.profiles.default</param-name>
    15             <param-value>dev</param-value>
    16         </init-param>
    17         <load-on-startup>1</load-on-startup>
    18 </web-app>

    可以通过列出多个profile名称并以逗号分隔来同时激活多个profile。不过同时启用dev和prod可能没有太大的意义,但是可以同时设置多个彼此不相关的profile。

    集成测试时使用@ActiveProfiles注解来指定测试时要激活的profile。

    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration(classes={PersistenceTestConfig.class})
    @ActiveProfiles("dev")
    public class PersistenceTest {
    
    }

    Spring的profile提供了一种很好的条件化创建bean的方法,这里的条件是基于哪个profile处于激活状态来判断。Spring 4.0提供了一种更为通用的机制来实现条件化的bean定义。

  • 相关阅读:
    javascsript 去除数组重复数据
    javascript监听事件兼容
    javascript紧接上一张for循环的问题,我自己的理解
    javascript解决for循环中i取值的问题(转载)
    javascript 模仿 html5 placeholder
    javascript构造函数+原形继承+作用域扩充
    css画下图
    jquery商城类封装插件
    JEECG01-开发入门环境搭建成功
    Perl学习笔记(九)--文件(四)
  • 原文地址:https://www.cnblogs.com/tuhooo/p/9244524.html
Copyright © 2011-2022 走看看