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方法】。

原文地址:https://www.cnblogs.com/shirui/p/9427045.html

时间: 2024-08-04 01:38:30

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

Spring_总结_04_高级配置(三)之处理歧义

一.前言 本文承接上一节:Spring_总结_04_高级配置(二)之条件注解@Conditional 我们前面装配bean时,在Spring容器中,都是只有一个bean能匹配所需的结果. 如果有多个bean能匹配结果的话,Spring就将无法做出选择,这就是自动装配的歧义性. 这一节我们将解决自动装配的歧义性 二.歧义实例 1.实体类 如下,有三个类都继承自Dessert @Component public class Cake implements Dessert {...} @Compone

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

一.前言 本文承接上一节:Spring_总结_04_高级配置(四)_bean的作用域 当讨论依赖注入的时候,我们通常所讨论的是将一个bean引用注入到另一个bean的属性或者构造参数中.它通常指的是将一个对象与另一个对象进行关联. 但是bean装配的另一个方面指的是将一个值注入到bean的属性或者构造函数中. spring提供了两种在运行时求值的方式: (1)属性占位符 (Property placeholder) (2)Spring表达式语言( SpEL) 原文地址:https://www.c

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

一.前言 本文承接上一节:Spring_总结_04_高级配置(五)_运行时注入值 原文地址:https://www.cnblogs.com/shirui/p/9551412.html

条件注解@Conditional

通过活动的profile,可以获得不同的Bean.Spring4提供了一个更通用的基于条件的Bean的创建,即使用@Conditonal注解 @Conditional根据满足某一个特定条件创建一个特定的Bean. 比如:当某一个jar包在一个类路径下的时候,自动配置一个或多个Bean:或者只有某个Bean被创建才会创建另外一个Bean. 总的来说,就是根据特定条件来控制Bean的创建行为,利用这个特性可以进行一些自动的配置. 在SpringBoot中将会大量应用到条件注解. 原文地址:https

Spring条件注解@Conditional

@Conditional 基于条件的Bean的创建,根据满足某一个特定条件创建特定的Bean,通过实现Condition接口,并重写matches接口来构造判断条件. 下面的实例通过将以不同操作系统作为条件,我们将通过实现Condition接口,并重写其matchess方法来构造判断条件.若在Windows系统下运行程序,则输出列表命令为dir:若在Linux操作系统下运行程序,则输出列表命令为ls1.判断条件定义(1)判断Windows的条件 package com.lwh.highlight

SpringBoot条件注解@Conditional

@Conditional是啥呀? @Conditional注解是个什么东西呢,它可以根据代码中设置的条件装载不同的bean,比如说当一个接口有两个实现类时,我们要把这个接口交给Spring管理时通常会只选择实现其中一个实现类,这个时候我们总不能使用if-else吧,所以这个@Conditional的注解就出现了. 我怎么用它呢? 在SpringBoot中,@Conditional注解并不是孤军奋斗,它们是一个家族,我们来看一下它们其中的几个成员,又都是怎么用的 @ConditionalOnBea

通过条件注解@Conditional细粒度的选择bean实例

在进行spring进行开发时,当某个接口有多种实现方式并且我们只想让一种生效时,比如声明如下一个接口和两个实现: public interface LanggageService { String say(); } public class ChineseServiceImpl implements LanggageService { @Override public String say() { return "你好"; } } public class EnglishService

JavaEE开发之Spring中的条件注解、组合注解与元注解

上篇博客我们详细的聊了<JavaEE开发之Spring中的多线程编程以及任务定时器详解>,本篇博客我们就来聊聊条件注解@Conditional以及组合条件.条件注解说简单点就是根据特定的条件来选择Bean对象的创建.条件注解就是可以根据不同的条件来做出不同的事情.在Spring中条件注解可以说是设计模式中状态模式的一种体现方式,同时也是面向对象编程中多态的应用部分.而组合注解就是将现有的注解进行组合.下方会给出具体的介绍和实例. 一.条件注解[email protected] 本篇博客的本部分

证书解析(二):windows 2012 r2证书服务安装与高级配置

本着应用隔离的原则,建议把证书服务部署在一台独立的windows server 2012 r2虚拟机之中.证书服务器可以不用考虑高可用,因证书服务宕掉后,除了不能继续颁发证书和不能访问证书吊销信息,并不影响证书的其他验证. 证书服务的安装 证书服务的安装很简单,运行服务器管理器,添加角色与功能,选择"Active Directory证书服务", 在角色服务中选择"证书颁发机构"与"证书颁发机构WEB注册" (不是证书注册web服务)两项,证书颁发