前言:

  在上一章装配Bean中,我们看到了一些最为核心的bean装配技术。你可能会发现上一章学到的知识有很大的用处。但是,bean装配所涉及的领域并不仅仅局限于上一章 所学习到的内容。Spring提供了多种技巧,借助它们可以实现更为高级的bean装配功能。在本章中,我们将会深入介绍一些这样的高级技术。

使用情景:

  在开发软件的时候,有一个很大的挑战就是将应用程序从一个环境迁移到另外一个环境。开发阶段中,某些环境相关做法可能并不适合迁移到生产环境中,甚至即便迁移过去也无法正常工作。数据库配置、加密算法以及与外部系统的集成是跨环境部署时会发生变化的几个典型例子。

一、配置profile bean

  Spring需要根据环境决定该创建哪个bean和不创建哪个bean。不过Spring并不是在构建的时候做出这样的决策,而是等到运行时再来确定。这样的结果就是同一个部署单元(可能会是WAR文件)能够适用于所有的环境,没有必要进行重新构建。

使用profile步骤:

  1. 你首先要将所有不同的bean定义整理到一个或多个profile之中,在Java配置中,可以使用@Profile注解指定某个bean属于哪一个profile。
  2. 在将应用部署到每个环境时,要确保对应的profile处于激活(active)的状态,设置spring.profiles.active。

开发环境和生产环境通常采用不同的数据库连接方式,例如在开发环境可以采用嵌入式,而生产环境中采用jndi连接池,所以要根据不同环境配置不同的bean,Spring中提供了profile来实现动态生成相应的bean:

1、@Profile配置在类级别上

嵌入式DataSource:

它会告诉Spring这个配置类中的bean只有在dev profile激活时才会创建。如果dev profile没有激活的话,那么带有@Bean注解的方法都会被忽略掉。

同时,你可能还需要有一个适用于生产环境的配置,如下所示:

在本例中,只有prod profile激活的时候,才会创建对应的bean;

2、@Profile配置在类方法级别上

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

这里有个问题需要注意,尽管每个DataSource bean都被声明在一个profile中,并且只有当规定的profile激活时,相应的bean才会被创建,但是可能会有其他的bean并没有声明在一个给定的profile范围内。没有指定profile的bean始终都会被创建,与激活哪个profile没有关系。

3、在XML中配置profile

我们也可以通过<beans>元素的profile属性,在XML中配置profile bean。例如,为了在XML中定义适用于开发阶段的嵌入式数据库DataSourcebean,我们可以创建如下所示的XML文件:

你还可以在根<beans>元素中嵌套定义<beans>元素,而不是为每个环境都创建一个profile XML文件。这能够将所有的profile bean定义放到同一个XML文件中,如下所示:

除了所有的bean定义到了同一个XML文件之中,这种配置方式与定义在单独的XML文件中的实际效果是一样的。这里有三个bean,类型都是javax.sql.DataSource,并且ID都是dataSource。但是在运行时,只会创建一个bean,这取决于处于激活状态的是哪个profile。

二、激活profile

通过profile标记不同的环境,但是如何激活它呢,可以通过设置spring.profiles.active和spring.profiles.default。如果设置了active,default便失去了作用。如果没有设置active就会去找default的值。如果两个都没有设置,那么那些定义在profiles的bean都不会生成。

有多种方式来设置这两个属性:

  • 作为DispatcherServlet的初始化参数;
  • 作为web应用的上下文参数;
  • 作为JNDI条目;
  • 作为环境变量;(指linux\window系统中的环境配置修改)
  • 作为JVM的系统属性;
  • 在集成测试类上,使用@ActiveProfiles注解配置。

以上面的前两种方式举例,设置spring.profiles.default的web.xml文件会如下所示:

按照这种方式设置spring.profiles.default,使用开发环境的设置(如嵌入式数据库)运行代码,而不需要任何额外的配置。

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

补充:在系统的环境变量里的设置的优先级高于application.properties里的spring.profiles.active的设置:

使用profile进行测试

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

Spring提供了@ActiveProfiles注解,我们可以使用它来指定运行测试时要激活哪个profile。在集成测试时,通常想要激活的是开发环境的profile。例如,下面的测试类片段展现了使用@ActiveProfiles激活dev profile:

三、条件化的bean

通过活动的profile,我们可以获得不同的Bean。Spring 4提供了一个更通用的基于条件的Bean的创建方式,即使用@Conditional注解。

@Conditional根据满足某个特定的条件创建一个特定的Bean。比如,当某一个jar包在一个类路径下时,自动配置一个或者多个Bean。或者只有一个Bean创建时,才会创建另一个Bean。总的来说,就是根据特定条件来控制Bean的创建行为,这样我们可以利用这个特性进行一些自动配置。

下面的示例将以“环境中是否存在magic属性”作为条件,我们将通过实现Condition接口,并重写其matches方法来构造判断条件。

1、条件化地配置bean

可以看到,@Conditional中给定了一个Class,它指明了条件,也就是MagicExistsCondition。

2、判断条件定义(实现Condition接口)

在上面的程序清单中,matches()方法很简单但功能强大。它通过给定的ConditionContext对象进而得到Environment对象,并使用这个对象检查环境中是否存在名为magic的环境属性。如果满足这个条件的话,matches()方法就会返回true。所有@Conditional注解上引用MagicExistsCondition的bean都会被创建。如果这个属性不存在的话,就无法满足条件,matches()方法会返回false,这些bean都不会被创建。

介绍matches()方法 

提供ConditionContext和AnnotatedTypeMetadata对象;

1.1、ConditionContext接口如下:

通过ConditionContext,我们可以做到如下几点:

  • 借助getRegistry()返回的BeanDefinitionRegistry检查bean定义;
  • 借助getBeanFactory()返回的ConfigurableListableBeanFactory检查bean是否存在,甚至探查bean的属性;
  • 借助getEnvironment()返回的Environment检查环境变量是否存在以及它的值是什么;
  • 读取并探查getResourceLoader()返回的ResourceLoader所加载的资源;
  • 借助getClassLoader()返回的ClassLoader加载并检查类是否存在。

1.2、AnnotatedTypeMetadata

AnnotatedTypeMetadata则能够让我们检查带有@Bean注解的方法上还有什么其他的注解。像ConditionContext一样,AnnotatedTypeMetadata也是一个接口。它如下所示:

借助isAnnotated()方法,我们能够判断带有@Bean注解的方法是不是还有其他特定的注解。借助其他的那些方法,我们能够检查@Bean注解的方法上其他注解的属性。

3、使用@Conditional重构@Profile

从Spring 4开始,@Profile注解进行了重构,使其基于@Conditional和Condition实现。我们来看一下在Spring 4中,@Profile是如何实现的。

@Profile注解如下所示:

注意:@Profile本身也使用了@Conditional注解,并且引用ProfileCondition作为Condition实现了Condition接口,并且在做出决策的过程中,考虑到了ConditionContext和AnnotatedTypeMetadata中的多个因素。

条件:ProfileCondition检查某个bean profile是否可用

我们可以看到,ProfileCondition通过AnnotatedTypeMetadata得到了用于@Profile注解的所有属性。借助该信息,它会明确地检查value属性,该属性包含了bean的profile名称。然后,它根据通过ConditionContext得到的Environment来检查[借助acceptsProfiles()方法]该profile是否处于激活状态。

spring学习总结——高级装配学习一(profile与@Conditional)的更多相关文章

  1. spring学习总结——高级装配学习四(运行时:值注入、spring表达式)

    前言: 当讨论依赖注入的时候,我们通常所讨论的是将一个bean引用注入到另一个bean的属性或构造器参数中.bean装配的另外一个方面指的是将一个值注入到bean的属性或者构造器参数中.在没有学习使用 ...

  2. spring学习总结——高级装配学习三(Bean的作用域)

    一.bean的作用域 在默认情况下,Spring应用上下文中所有bean都是作为以单例(singleton)的形式创建的.也就是说,不管给定的一个bean被注入到其他bean多少次,每次所注入的都是同 ...

  3. spring学习总结——高级装配学习二(处理自动装配的歧义性)

    我们已经看到如何使用自动装配让Spring完全负责将bean引用注入到构造参数和属性中.自动装配能够提供很大的帮助.不过,spring容器中仅有一个bean匹配所需的结果时,自动装配才是有效的.如果不 ...

  4. Spring学习(三)--高级装配

    一.Spring profile 在开发软件的时候,有一个很大的挑战就是将应用程序从一个环境迁 移到另外一个环境.开发阶段中,某些环境相关做法可能并不适合迁 移到生产环境中,甚至即便迁移过去也无法正常 ...

  5. Spring学习笔记-高级装配-03

    主要内容: ●Spring profile ●条件化的bean声明 ●自动装配与歧义性 ● Spring表达式语言 本章介绍一些高级的装配技术,可实现更为高级的装配功能. 环境与profile 软件开 ...

  6. 【Spring 核心】高级装配

    高级装配用来适应开发和生产 不同环境下的软切换 一.环境与profile 1.开发环境下的profile package com.bonc.config; import javax.sql.DataS ...

  7. 【Spring】高级装配

    前言 前面讲解了bean的核心装配技术,其可应付很多中装配情况,但Spring提供了高级装配技术,以此实现更为高级的bean装配功能. 高级装配 配置profile bean 将所有不同bean定义放 ...

  8. mybatis 高级映射和spring整合之高级映射(4)

    mybatis 高级映射和spring整合之高级映射 ----------------学习结构-------------------- 0.0 对订单商品数据模型进行分析 1.0 高级映射 1.1 一 ...

  9. Spring学习总结之高级装配

    1.  profile profile可以决定bean在什么环境下才被装配(开发环境.测试环境.线上环境等) @Profile(“dev”)可以用在class之前,也可以用在类之前(Spring3.2 ...

随机推荐

  1. [Swift]LeetCode795. 区间子数组个数 | Number of Subarrays with Bounded Maximum

    We are given an array A of positive integers, and two positive integers L and R (L <= R). Return ...

  2. [Swift]LeetCode1025. 除数博弈 | Divisor Game

    Alice and Bob take turns playing a game, with Alice starting first. Initially, there is a number N o ...

  3. php设计模式2

    代理模式 <?php /** * 代理模式:为其他对象提供一个代理以控制这个对象的访问 它是给某一个对象提供一个替代者,使之在client对象和subject对象之间编码更有效率. 代理可以提供 ...

  4. 一个老程序员是如何手写Spring MVC的

    人见人爱的Spring已然不仅仅只是一个框架了.如今,Spring已然成为了一个生态.但深入了解Spring的却寥寥无几.这里,我带大家一起来看看,我是如何手写Spring的.我将结合对Spring十 ...

  5. Python内置函数(3)——any

    英文文档: any(iterable) Return True if any element of the iterable is true. If the iterable is empty, re ...

  6. 【译】如何高效的使用 Git

    原文链接 代码昨天还是运行好好的今天就不行了. 代码被删了. 突然出现了一个奇怪的 bug,但是没人知道怎么回事. 如果你出现过上面的任何一种情况,那本篇文章就是为你准备的. 除了知道 git add ...

  7. redhat 6.5安装ansible

    安装epel 源: rpm -ivh https://dl.fedoraproject.org/pub/epel/epel-release-latest-6.noarch.rpm 安装ansible ...

  8. localStorage之本地储存

    一.定义json var employees = [{ "firstName":"Bill" , "lastName":"Gate ...

  9. Config非对称加解密

    对称加密和非对称加密区别1. 对称加密对称加密指的就是加密和解密使用同一个秘钥,所以叫做对称加密.对称加密只有一个秘钥,作为私钥. 常见的对称加密算法:DES,AES,3DES等等. 2. 非对称加密 ...

  10. 设计模式总结篇系列:享元模式(Flyweight)

    我们都知道,Java中的String类具有如下特性:String是一个不可变类,当直通过用字符串方式使用String对象时,Jvm实际上在内存中只存有一份,且存在字符串常量池中.当对字符串直接进行修改 ...