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文件中有各种组件的自动装配 ...
随机推荐
- Redis分布式锁的python实现
案例1: #!/usr/bin/env python # coding=utf-8 import time import redis class RedisLock(object): def __in ...
- Android:日常学习笔记(2)——分析第一个Android应用程序
Android:日常学习笔记(2)——分析第一个Android应用程序 Android项目结构 整体目录结构分析 说明: 除了APP目录外,其他目录都是自动生成的.APP目录的下的内容才是我们的工作重 ...
- css的继承性理解
1) 所有的text 相关属性都被继承: 如 font-family font-size; font-style;font-weight;font;font-variant;letter-spacin ...
- [转]springmvc中的常用的返回
package com.boventech.learning.controller; import java.util.HashMap; import java.util.Map; import or ...
- PHP操作MongoDB数据库的示例
http://www.jquerycn.cn/a_8137 本节内容:PHP操作MongoDB数据库的简单示例. Mongodb的常用操作参看手册,php官方的http://us2.php.net/m ...
- window7 3G/4G拨号操作
Win7系统Modem拨号操作指导:https://wenku.baidu.com/view/bb855b1dc77da26925c5b0e1.html 拨号上网设置APN,拨号号码,帐号和密码:ht ...
- linux下查找文件或目录(which,whereis,locate,find)
命令 查找对象 查找速度 备注 which 可执行文件 根据PATH变量的内容去寻找可执行文件 不同的PATH配置会有不一样的结果 whereis 程序名的搜索 根据数据库来寻找,速度快 ...
- 五、golang实现排序
实现排序: 1.实现一个冒泡排序 2.实现一个选择排序 3.实现一个插入排序 4.实现一个快速排序 冒泡排序 package main import( "fmt" ) func b ...
- ubuntu关闭631(cups)端口
在ubuntu17.04环境下使用nmap扫描自己机器,发现631端口处于开启状态,将其输入到浏览器,可以看出是网络打印机的服务: 这个端口开着总是那么的刺眼,(5.12全球爆发的勒索病毒让人不寒而栗 ...
- 如何检查BioPerl是否正确安装
如果是Linux系统,随意打开一个终端:如果用的是Windows系统,那么打开命令提示符. 输入以下命令: perldoc Bio::SeqIO 以上命令的作用是查看Bio::SeqIO模块的文档是否 ...