spring学习总结——高级装配学习一(profile与@Conditional)

前言:java

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

 

使用情景:web

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

 

1、配置profile bean

  Spring须要根据环境决定该建立哪一个bean和不建立哪一个bean。不过Spring并非在构建的时候作出这样的决策,而是等到运行时再来肯定。这样的结果就是同一个部署单元(可能会是WAR文件)可以适用于全部的环境,没有必要进行从新构建。spring

 

使用profile步骤:sql

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

 

开发环境和生产环境一般采用不一样的数据库链接方式,例如在开发环境能够采用嵌入式,而生产环境中采用jndi链接池,因此要根据不一样环境配置不一样的bean,Spring中提供了profile来实现动态生成相应的bean:数据库

一、@Profile配置在类级别上

嵌入式DataSource:app

 

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

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

 

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

 

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

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

 

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

 

三、在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。

 

2、激活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:

 

 

3、条件化的bean

经过活动的profile,咱们能够得到不一样的Bean。Spring 4提供了一个更通用的基于条件的Bean的建立方式,即便用@Conditional注解。

@Conditional根据知足某个特定的条件建立一个特定的Bean。好比,当某一个jar包在一个类路径下时,自动配置一个或者多个Bean。或者只有一个Bean建立时,才会建立另外一个Bean。总的来讲,就是根据特定条件来控制Bean的建立行为,这样咱们能够利用这个特性进行一些自动配置。

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

 

一、条件化地配置bean

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

 

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

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

 

介绍matches()方法 

提供ConditionContext和AnnotatedTypeMetadata对象;

1.一、ConditionContext接口以下:

经过ConditionContext,咱们能够作到以下几点:

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

 

1.二、AnnotatedTypeMetadata

AnnotatedTypeMetadata则可以让咱们检查带有@Bean注解的方法上还有什么其余的注解。像ConditionContext同样,AnnotatedTypeMetadata也是一个接口。它以下所示:

 

借助isAnnotated()方法,咱们可以判断带有@Bean注解的方法是否是还有其余特定的注解。借助其余的那些方法,咱们可以检查@Bean注解的方法上其余注解的属性。

 

 三、使用@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是否处于激活状态。

相关文章
相关标签/搜索