一、前言

本文承接上一节:Spring_总结_04_高级配置(一)之Profile

在上一节,我们了解到 Profile 为不同环境下使用不同的配置提供了支持,那么Profile到底是如何实现的呢?其实Profile正是通过条件注解来实现的。

条件注解的应用场景举例:

(1)希望一个或多个 bean 只有在应用的类路径下包含特定的库时才创建

(2)希望某个bean只有当另外某个特定bean也声明了之后才创建

(3)希望只有某个特定的环境变量设置之后,才会创建某个bean

上述场景能让我们联想到springboot的自动化配置、Profile以及配置文件等。

二、概述

1.定义

@Conditionnal 能根据特定条件来控制Bean的创建行为。

2.用法

@Conditional注解可以用到带有@Bean注解的方法上,如果给定的条件计算结果为true,就会创建这个bean,否则,忽略这个bean。

3.用处

可以用来进行一些自动化配置,如上述三个应用场景。

三、使用实例

1.创建Condition实现类

Condition接口如下,只有一个 matches 方法,用来判断是否满足条件。

@FunctionalInterface
public interface Condition { /**
* Determine if the condition matches.
* @param context the condition context
* @param metadata metadata of the {@link org.springframework.core.type.AnnotationMetadata class}
* or {@link org.springframework.core.type.MethodMetadata method} being checked
* @return {@code true} if the condition matches and the component can be registered,
* or {@code false} to veto the annotated component's registration
*/
boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata); }
Condition实现类 MagicExistsCondition
public class MagicExistsCondition  implements Condition {

    /**
* 1.判断是否满足条件
* @param context
* @param metadata
* @return
*/
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
Environment environment = context.getEnvironment();
/**
* 检查环境中是否存在magic属性,若存在则满足条件
*/
return environment.containsProperty("magic");
}
}

2.使用@Conditional注解

如以下代码,在装配Bean的时候,使用@conditional注解,以便只有在满足条件地情况下才创建此bean

    /**
* 条件化地创建Bean
* 若满足MagicExistsCondition的条件,则创建此Bean,否则忽略此bean.
* @return
*/
@Conditional(MagicExistsCondition.class)
@Bean
public MagicBean magicBean(){
return new MagicBean();
}

四、Condition接口分析

Condition接口如下,只有一个 matches 方法,用来判断是否满足条件。

@FunctionalInterface
public interface Condition { /**
* Determine if the condition matches.
* @param context the condition context
* @param metadata metadata of the {@link org.springframework.core.type.AnnotationMetadata class}
* or {@link org.springframework.core.type.MethodMetadata method} being checked
* @return {@code true} if the condition matches and the component can be registered,
* or {@code false} to veto the annotated component's registration
*/
boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata); }

在设置条件时,可用 ConditionContext  和 AnnotatedTypeMetadata 来进行条件地设置。

1.ConditionContext

源码如下:

/**
* Context information for use by {@link Condition}s.
*
* @author Phillip Webb
* @author Juergen Hoeller
* @since 4.0
*/
public interface ConditionContext { /**
* Return the {@link BeanDefinitionRegistry} that will hold the bean definition
* should the condition match.
* @throws IllegalStateException if no registry is available (which is unusual:
* only the case with a plain {@link ClassPathScanningCandidateComponentProvider})
*/
BeanDefinitionRegistry getRegistry(); /**
* Return the {@link ConfigurableListableBeanFactory} that will hold the bean
* definition should the condition match, or {@code null} if the bean factory is
* not available (or not downcastable to {@code ConfigurableListableBeanFactory}).
*/
@Nullable
ConfigurableListableBeanFactory getBeanFactory(); /**
* Return the {@link Environment} for which the current application is running.
*/
Environment getEnvironment(); /**
* Return the {@link ResourceLoader} currently being used.
*/
ResourceLoader getResourceLoader(); /**
* Return the {@link ClassLoader} that should be used to load additional classes
* (only {@code null} if even the system ClassLoader isn't accessible).
* @see org.springframework.util.ClassUtils#forName(String, ClassLoader)
*/
@Nullable
ClassLoader getClassLoader(); }

作用如下:

1
getRegistry
利用返回的 BeanDefinitionRegistry 来检查bean定义
2
getBeanFactory
利用返回的 ConfigurableListableBeanFactory来检查bean是否存在,甚至探查bean的属性
3
getEnvironment
利用返回的 Environment 检查环境变量是否存在以及它的值是什么
4
getResourceLoader
读取并探查返回的 ResourceLoader 所加载的资源
5
getClassLoader
借助返回的ClassLoader加载并检查类是否存在

2.AnnotatedTypeMetadata

源码如下:

/**
* Defines access to the annotations of a specific type ({@link AnnotationMetadata class}
* or {@link MethodMetadata method}), in a form that does not necessarily require the
* class-loading.
*
* @author Juergen Hoeller
* @author Mark Fisher
* @author Mark Pollack
* @author Chris Beams
* @author Phillip Webb
* @author Sam Brannen
* @since 4.0
* @see AnnotationMetadata
* @see MethodMetadata
*/
public interface AnnotatedTypeMetadata { /**
* Determine whether the underlying element has an annotation or meta-annotation
* of the given type defined.
* <p>If this method returns {@code true}, then
* {@link #getAnnotationAttributes} will return a non-null Map.
* @param annotationName the fully qualified class name of the annotation
* type to look for
* @return whether a matching annotation is defined
*/
boolean isAnnotated(String annotationName); /**
* Retrieve the attributes of the annotation of the given type, if any (i.e. if
* defined on the underlying element, as direct annotation or meta-annotation),
* also taking attribute overrides on composed annotations into account.
* @param annotationName the fully qualified class name of the annotation
* type to look for
* @return a Map of attributes, with the attribute name as key (e.g. "value")
* and the defined attribute value as Map value. This return value will be
* {@code null} if no matching annotation is defined.
*/
@Nullable
Map<String, Object> getAnnotationAttributes(String annotationName); /**
* Retrieve the attributes of the annotation of the given type, if any (i.e. if
* defined on the underlying element, as direct annotation or meta-annotation),
* also taking attribute overrides on composed annotations into account.
* @param annotationName the fully qualified class name of the annotation
* type to look for
* @param classValuesAsString whether to convert class references to String
* class names for exposure as values in the returned Map, instead of Class
* references which might potentially have to be loaded first
* @return a Map of attributes, with the attribute name as key (e.g. "value")
* and the defined attribute value as Map value. This return value will be
* {@code null} if no matching annotation is defined.
*/
@Nullable
Map<String, Object> getAnnotationAttributes(String annotationName, boolean classValuesAsString); /**
* Retrieve all attributes of all annotations of the given type, if any (i.e. if
* defined on the underlying element, as direct annotation or meta-annotation).
* Note that this variant does <i>not</i> take attribute overrides into account.
* @param annotationName the fully qualified class name of the annotation
* type to look for
* @return a MultiMap of attributes, with the attribute name as key (e.g. "value")
* and a list of the defined attribute values as Map value. This return value will
* be {@code null} if no matching annotation is defined.
* @see #getAllAnnotationAttributes(String, boolean)
*/
@Nullable
MultiValueMap<String, Object> getAllAnnotationAttributes(String annotationName); /**
* Retrieve all attributes of all annotations of the given type, if any (i.e. if
* defined on the underlying element, as direct annotation or meta-annotation).
* Note that this variant does <i>not</i> take attribute overrides into account.
* @param annotationName the fully qualified class name of the annotation
* type to look for
* @param classValuesAsString whether to convert class references to String
* @return a MultiMap of attributes, with the attribute name as key (e.g. "value")
* and a list of the defined attribute values as Map value. This return value will
* be {@code null} if no matching annotation is defined.
* @see #getAllAnnotationAttributes(String)
*/
@Nullable
MultiValueMap<String, Object> getAllAnnotationAttributes(String annotationName, boolean classValuesAsString); }
通过 AnnotatedTypeMetadata 可以检查 @Bean 注解的方法上还有什么其他注解。

五、@Profile注解的实现原理

@Profile注解是基于@Conditional 和 condition 实现的

1.Profile注解类

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional({ProfileCondition.class})
public @interface Profile {
String[] value();
}

可以看到,@Profile 注解本身也使用了 @Conditional 注解,而Condition实现类为ProfileCondition

2.ProfileCondition

/**
* {@link Condition} that matches based on the value of a {@link Profile @Profile}
* annotation.
*
* @author Chris Beams
* @author Phillip Webb
* @author Juergen Hoeller
* @since 4.0
*/
class ProfileCondition implements Condition { @Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
MultiValueMap<String, Object> attrs = metadata.getAllAnnotationAttributes(Profile.class.getName());
if (attrs != null) {
for (Object value : attrs.get("value")) {
if (context.getEnvironment().acceptsProfiles((String[]) value)) {
return true;
}
}
return false;
}
return true;
} }

可以看到, 此类主要是

(1)通过 AnnotatedTypeMetadata  获取到了 @Profile  的所有属性。

(2)然后检查value属性值,该属性包含了bean的profile名称

(3)根据 通过 ConditionContext 得到的Environment来检查 该profile是否处于激活状态【借助 acceptsProfiles方法】。

Spring_总结_04_高级配置(二)_条件注解@Conditional的更多相关文章

  1. Spring_总结_04_高级配置(三)_处理歧义

    一.前言 本文承接上一节:Spring_总结_04_高级配置(二)之条件注解@Conditional 我们前面装配bean时,在Spring容器中,都是只有一个bean能匹配所需的结果. 如果有多个b ...

  2. Spring_总结_04_高级配置(五)_运行时注入值

    一.前言 本文承接上一节:Spring_总结_04_高级配置(四)_bean的作用域 当讨论依赖注入的时候,我们通常所讨论的是将一个bean引用注入到另一个bean的属性或者构造参数中.它通常指的是将 ...

  3. Spring_总结_04_高级配置(六)_Bean的初始化和销毁

    一.前言 本文承接上一节:Spring_总结_04_高级配置(五)_运行时注入值

  4. Spring_总结_04_高级配置(四)_bean的作用域

    一.前言 本文承接上一节:Spring_总结_04_高级配置(三)之处理歧义 1.单例bean Spring应用上下文中所有的bean默认都是单例的.也就是说,不管一个bean被注入到其他bean多少 ...

  5. [读书笔记] 二、条件注解@Conditional,组合注解,元注解

    一.条件注解@Conditional,组合注解,元注解 1. @Conditional:满足特定条件创建一个Bean,SpringBoot就是利用这个特性进行自动配置的. 例子: 首先,两个Condi ...

  6. Spring_总结_04_高级配置(一)_Profile

    一.前言 本文承接上一节:Spring_总结_03_装配Bean(四)之导入与混合配置 这一节,来总结一下profile. 我们在开发软件时,通常会进行跨环境部署.而在跨环境部署时,经常会遇到某些环境 ...

  7. Spring Boot实战笔记(八)-- Spring高级话题(条件注解@Conditional)

    一.条件注解@Conditional 在之前的学习中,通过活动的profile,我们可以获得不同的Bean.Spring4提供了一个更通用的基于条件的Bean的创建,即使用@Conditional注解 ...

  8. 条件注解@Conditional

    通过活动的profile,可以获得不同的Bean.Spring4提供了一个更通用的基于条件的Bean的创建,即使用@Conditonal注解 @Conditional根据满足某一个特定条件创建一个特定 ...

  9. springboot自动装配(3)---条件注解@Conditional

    之前有说到springboot自动装配的时候,都是去寻找一个XXXAutoConfiguration的配置类,然而我们的springboot的spring.factories文件中有各种组件的自动装配 ...

随机推荐

  1. 011-git-将tag推送到远端

    1.将tag推送到远端 在使用TortoiseGit过程中,push推送过程中,选择include tag,远端就有次分支.

  2. 《Python数据分析》笔记——数据可视化

    数据可视化 matplotlib绘图入门 为了使用matplotlib来绘制基本图像,需要调用matplotlib.pyplot子库中的plot()函数 import matplotlib.pyplo ...

  3. Oracle学习笔记—归档模式

    什么是归档模式 Oracle数据库有联机重做日志,这个日志是记录对数据库所做的修改,比如插入,删除,更新数据等,对这些操作都会记录在联机重做日志里.一般数据库至少要有2个联机重做日志组.当一个联机重做 ...

  4. applicationContext.xml配置简介

    这里简单介绍一下spring的配置文件applicationContext.xml中的一些配置的作用. <context:component-scan base-package="&q ...

  5. oracle11g参数的简单查看方法

    1.查看processes和sessions参数show parameter processesshow parameter sessions 2.修改processes和sessions值alter ...

  6. Python API:openstack

    OpenStack 是一个越来越流行的.用于部署基础架构即服务 (IaaS) 云的开源解决方案.OpenStack 附带了一个仪表板 Web 应用程序,非常适合执行手动任务,比如启动单个虚拟机 (VM ...

  7. python基础28 -----python中sockserver模块

    一.Python中的sockserver模块 1.该模块与sock模块不同之处是该模块自动帮我们分装好了一些功能,让我们在编程的时候直接调用这些功能就可以了,节省了编程步骤. 2.如图所示 注释:上图 ...

  8. __init__和__new__

    一.__init__方法是什么 __init__方法通常用在初始化一个类实例的时候, class Person(object): """Silly Person" ...

  9. MapInfo 文件格式说明

    MapInfo 文件格式说明(id.map.tab.dat) (1). 属性数据的表结构文件.TAB 属性数据表结构文件定义了地图属性数据的表结构,包括字段数.字段名称.字段类型和字段宽度.索引字段及 ...

  10. win10 chrome 调试

      下载NPAPI版本的flash player: http://www.adobe.com/support/flashplayer/debug_downloads.html#fp13       禁 ...