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。

  1. @RunWith(SpringJUnit4ClassRunner.class)
  2. @ContextConfiguration(classes={PersistenceTestConfig.class})
  3. @ActiveProfiles("dev")
  4. public class PersistenceTest {
  5.  
  6. }

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

Spring高级装配(一) profile的更多相关文章

  1. Spring高级装配

    Spring高级装配 目录 一.Profile(根据开发环境创建对应的bean) 二.条件化的创建bean(根据条件创建bean) 三.处理自动装配歧义性(指定首选bean.限定符限制bean) 四. ...

  2. spring对bean的高级装配之profile机制

    最近在读spring实战一书,个人感觉内容通俗易懂,学到了一些之前并不知道的知识,于是打算在博客里记录一下这些知识点便于后期记忆: 今天要记录的就是spring的条件化创建bean,针对条件化创建be ...

  3. (一)spring 高级装配-@Profile

    1.环境与profile 示例:数据库配置 a:通过@Bean注解,通过EmbeddedDatabaseBuilder创建数据源 @Bean(destroyMethod="shutdown& ...

  4. Spring实战(四)Spring高级装配中的bean profile

    profile的原意为轮廓.剖面等,软件开发中可以译为“配置”. 在3.1版本中,Spring引入了bean profile的功能.要使用profile,首先要将所有不同的bean定义整理到一个或多个 ...

  5. Spring高级装配bean

    目录 spring profile 条件化的bean声明 自动装配与歧义性 bean的作用域 Spring表达式语言 一.环境与profile 配置profile  bean 在软件开发的时候,有一个 ...

  6. Spring高级装配(二) 条件化的bean

    如果你希望一个bean在特定的条件下才会出现: 应用的类路径下包含特定的库时才创建 只有当某个特定的bean也声明之后才会创建 某个特定的环境变量设定之后才创建某个bean 在Spring 4之前,很 ...

  7. 第3章—高级装配—配置profile bean

    配置profile bean 3.1.@profile注解是spring提供的一个用来标明当前运行环境的注解. 我们正常开发的过程中经常遇到的问题是,开发环境是一套环境,qa测试是一套环境,线上部署又 ...

  8. (二)spring 高级装配-Condition -条件化的bean

    Condition:满足某个特定条件的情况下创建bean 条件化配置bean: a:@Conditional 指定一个class ,它指明了通过条件对比的类.如果没有指定class则通过Condito ...

  9. Spring——自动装配(@Autowired/@Profile/底层组件)

    本文介绍Spring中关于自动装配的方法和规则,以及@Profile动态激活的用法和一个例子. 一.@Autowired自动装配 @Autowired注解可以加在构造器.属性.方法.方法参数上. 自动 ...

随机推荐

  1. nmapport状态解析

  2. Python 文件 truncate() 方法

    概述 Python 文件 truncate() 方法用于截断文件并返回截断的字节长度. 指定长度的话,就从文件的开头开始截断指定长度,其余内容删除:不指定长度的话,就从文件开头开始截断到当前位置,其余 ...

  3. 三角函数 与 JavaScript

    三角函数   canvas 和 JavaScript 中所有与角相关的API如Math.sin().Math.cos().Math.tan(),都需要以弧度为单位值.但大部分人还是习惯以角度单位.所以 ...

  4. jQuery学习笔记(DOM操作)

    DOM操作的分类 一般来说,DOM操作分为3个方面,即DOM Core.HTML-DOM和CSS-DOM. 1. DOM Core DOM Core并不专属于JavaScript,任何一种支持DOM的 ...

  5. [hihoCoder] 第五十二周: 连通性·一

    题目1 : 连通性·一 时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描述 还记得上次小Hi和小Ho学校被黑客攻击的事情么,那一次攻击最后造成了学校网络数据的丢失.为了避免再 ...

  6. 学习排序算法(一):单文档方法 Pointwise

    学习排序算法(一):单文档方法 Pointwise 1. 基本思想 这样的方法主要是将搜索结果的文档变为特征向量,然后将排序问题转化成了机器学习中的常规的分类问题,并且是个多类分类问题. 2. 方法流 ...

  7. MYSQL拒绝访问:not allowed to connect解决方法

    分享下MYSQL拒绝访问报错not allowed to connect的解决方法. 可以在其它任何的主机上以root身份登录 mysql报如下错误,截取部分, message from server ...

  8. Shiro系列(1) - 权限管理的介绍与原理

    1. 什么是权限管理 一般来说,只要有用户参与,那么该系统都会需要权限管理,权限管理实现了对用户访问系统  指定功能的限制,按照管理员定义的安全规则或权限策略,限制用户只能访问自己被授权的那些资源路径 ...

  9. IOS 启动画面和图标设置(适配IOS7 and Xcode5)

    关于IOS程序设置启动画面以及图标的设备目前主要为:IPhone设备 和IPad设备 IPhone启动画面以及图标的设置 目前IPhone的分辨率为:320X480.640X960.640X1136. ...

  10. jar依赖

    https://blog.csdn.net/honghailiang888/article/details/53019635