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文件中有各种组件的自动装配 ...
随机推荐
- mysql大致学习路径
- android studio DELETE_FAILED_INTERNAL_ERROR Error while Installing APKs
DELETE_FAILED_INTERNAL_ERROR Error while Installing APKs 参考https://stackoverflow.com/questions/38892 ...
- 使用Kotlin开发Android应用(IV):自定义视图和Android扩展
在读完扩展函数和默认值这篇文章之后,那么接下来要介绍什么呢?在本系列第一篇文章中我们说过,Kotlin使得Android开发更加简单,本文我们将进一步作介绍. 自定义视图 你应该还记得,在说到Kotl ...
- python并发编程之多线程2---(死锁与递归锁,信号量等)
一.死锁现象与递归锁 进程也是有死锁的 所谓死锁: 是指两个或两个以上的进程或线程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用, 它们都将无法推进下去.此时称系统处于死锁状态或系统 ...
- uboot相关的几篇好文
http://www.eeworld.com.cn/mcu/2015/0727/article_21246.html http://blog.csdn.net/kernel_yx/article/de ...
- linux基础四----samba&&nginx
一 samba 1环境配置: a.确保linux下防火墙关闭比或开放共享目录权限 iPtables -F b.确保setlinux关闭:setenforce 0 c.配置iP 2安装软件包:yum i ...
- 封装一个既能遍历数组又能遍历对象的的forEach函数
function newforEach(obj,fn) { var key; if(obj instanceof Array){ obj.forEach(function(item,index){ f ...
- mysql下merge分表
1.merge简介分表就是把N条记录的表,分成若干个分表,各个分表记录的总和仍为N. 分表的方法有很多,用merge来分表,是最简单的一种方式.merge是mysql的一种存储引擎,它把一组MyISA ...
- C++中容器的使用(一)
C++中有两种类型的容器:顺序容器和关联容器. 顺序容器主要有vector.list.deque等.其中vector表示一段连续的内存,基于数组实现,list表示非连续的内存,基于链表实现,deque ...
- Mybatis入门2-动态代理实现CRUD
MyBatis动态代理生成DAO的步骤: 1) 编写数据管理的接口XxxMapper 2) 编写该接口对应的Mapper.xml a) namespace必须与Mapper接口全名一致 b) stat ...