Spring_总结_04_高级配置(二)_条件注解@Conditional
一、前言
本文承接上一节: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的更多相关文章
- Spring_总结_04_高级配置(三)_处理歧义
一.前言 本文承接上一节:Spring_总结_04_高级配置(二)之条件注解@Conditional 我们前面装配bean时,在Spring容器中,都是只有一个bean能匹配所需的结果. 如果有多个b ...
- Spring_总结_04_高级配置(五)_运行时注入值
一.前言 本文承接上一节:Spring_总结_04_高级配置(四)_bean的作用域 当讨论依赖注入的时候,我们通常所讨论的是将一个bean引用注入到另一个bean的属性或者构造参数中.它通常指的是将 ...
- Spring_总结_04_高级配置(六)_Bean的初始化和销毁
一.前言 本文承接上一节:Spring_总结_04_高级配置(五)_运行时注入值
- Spring_总结_04_高级配置(四)_bean的作用域
一.前言 本文承接上一节:Spring_总结_04_高级配置(三)之处理歧义 1.单例bean Spring应用上下文中所有的bean默认都是单例的.也就是说,不管一个bean被注入到其他bean多少 ...
- [读书笔记] 二、条件注解@Conditional,组合注解,元注解
一.条件注解@Conditional,组合注解,元注解 1. @Conditional:满足特定条件创建一个Bean,SpringBoot就是利用这个特性进行自动配置的. 例子: 首先,两个Condi ...
- Spring_总结_04_高级配置(一)_Profile
一.前言 本文承接上一节:Spring_总结_03_装配Bean(四)之导入与混合配置 这一节,来总结一下profile. 我们在开发软件时,通常会进行跨环境部署.而在跨环境部署时,经常会遇到某些环境 ...
- Spring Boot实战笔记(八)-- Spring高级话题(条件注解@Conditional)
一.条件注解@Conditional 在之前的学习中,通过活动的profile,我们可以获得不同的Bean.Spring4提供了一个更通用的基于条件的Bean的创建,即使用@Conditional注解 ...
- 条件注解@Conditional
通过活动的profile,可以获得不同的Bean.Spring4提供了一个更通用的基于条件的Bean的创建,即使用@Conditonal注解 @Conditional根据满足某一个特定条件创建一个特定 ...
- springboot自动装配(3)---条件注解@Conditional
之前有说到springboot自动装配的时候,都是去寻找一个XXXAutoConfiguration的配置类,然而我们的springboot的spring.factories文件中有各种组件的自动装配 ...
随机推荐
- 20170411 debug窗口执行文件
[FUNCTION] Command=/H Title=Debugger Type=SystemCommand
- 曾经跳过的坑----js截取字符串substr与substring 和 trim
不废话直接代码.自己理解...... > "abcdefg".substring(1,6)> "bcdef"> "abcdefg&q ...
- vue框架(一)
一.介绍 1.Vue是什么? Vue.js (读音 /vjuː/,类似于 view) 是一套构建用户界面的渐进式框架.与其他重量级框架不同的是,Vue 采用自底向上增量开发的设计.Vue 的核心库只关 ...
- Centos7.2安装bacula及bacula-web
serverd端安装(centos7) bacula的安装很简单,但是配置文件内容很多,配置不正确服务就启动不了,所以需要用webmin来配置. 1.安装基础软件包: 关闭SElinux(重要)set ...
- CodeForces - 691E Xor-sequences 【矩阵快速幂】
题目链接 http://codeforces.com/problemset/problem/691/E 题意 给出一个长度为n的序列,从其中选择k个数 组成长度为k的序列,因为(k 有可能 > ...
- PAT 天梯赛 L1-032. Left-pad 【字符串】
题目链接 https://www.patest.cn/contests/gplt/L1-032 思路 要分两种情况处理 ①字符串长度 <= 填充长度 就在字符串前面输出(填充长度 - 字符串长度 ...
- iOS Xcode 8 快捷键 (注释 失效 处理)
在升级后,好用的VVDocumment 插件不能用了.(但是苹果这次内置了好多好用的插件,也有自己的注释功能了 AddDocumentation) 上网上有查到 传播很广泛的一条信息 "这个 ...
- 025_MapReduce样例Hadoop TopKey算法
1.需求说明
- 20145231《Java程序设计》第五次实验报告
实验五 Java网络编程及安全 实验内容 1.掌握Socket程序的编写: 2.掌握密码技术的使用: 3.设计安全传输系统. 实验要求 基于Java Socket实现安全传输 基于TCP实现客户端和服 ...
- VS中一个强大的功能,将Json或者XML黏贴为类
有时候需要传递json,或者是json结构复杂,看的杂乱无章,我们可以将这个json复制下来,然后将它写成类的形式,VS中已经帮我们很好的实现了这个功能,我们只需要选择 编辑===>> ...