《SpringBoot系列十二》:如何自定义条件装配(由@ConditionalOnClass推导)

简介: 《SpringBoot系列十二》:如何自定义条件装配(由@ConditionalOnClass推导)

一、前言

在博文《SpringBoot系列十一》:精讲如何使用@Conditional系列注解做条件装配中我们讨论了如何使用@Conditional系列注解做条件装配,假如我想自定义条件装配改怎么做呢?

本文就如何自定义条件装配展开讨论。

二、@Conditional介绍

@Conditional注解是从spring4.0版本才有的,其是一个条件装配注解,可以用在任何类型或者方法上面,以指定的条件形式限制bean的创建;即当所有条件都满足的时候,被@Conditional标注的目标才会被spring容器处理。

  1. @Conditional本身也是一个父注解,从SpringBoot1.0版本开始派生出了大量的子注解;用于Bean的按需加载。
  2. @Conditional注解和其所有子注解必须依托于被@Component衍生注解标注的类,即Spring要能扫描到@Conditional衍生注解所在的类,才能做进一步判断。
  3. @Conditional衍生注解可以加在类 或 类的方法上;加在类上表示类的所有方法都做条件装配、加在方法上则表示只有当前方法做条件装配。

1、@Conditional源码

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Conditional {

    /**
     * All {@link Condition} classes that must {@linkplain Condition#matches match}
     * in order for the component to be registered.
     */
    Class<? extends Condition>[] value();

}

@Conditional注解只有一个value参数,类型是:Condition类型的数组;而Condition是一个接口,其表示一个条件判断,内部的matches()方法返回true或false;当所有Condition都成立时,@Conditional的条件判断才成立。

1)Condition接口

@FunctionalInterface
public interface Condition {

    /**
     * 判断条件是否匹配
     * @param context 条件判断上下文
     * @param metadata 注解元数据
     * @return 如果条件匹配返回TRUE,否者返回FALSE
     */
    boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata);

}

Condition是一个函数式接口,其内部只有一个matches()方法,用来判断条件是否成立的,方法有2个入参:

context:条件上下文,ConditionContext接口类型,用来获取容器中的信息
metadata:用来获取被@Conditional标注的对象上的所有注解信息

2)ConditionContext接口

public interface ConditionContext {

    /**
     * 返回bean定义注册器BeanDefinitionRegistry,通过注册器获取bean定义的各种配置信息
     */
    BeanDefinitionRegistry getRegistry();

    /**
     * 返回ConfigurableListableBeanFactory类型的bean工厂,相当于一个ioc容器对象
     */
    @Nullable
    ConfigurableListableBeanFactory getBeanFactory();

    /**
     * 返回当前spring容器的环境配置信息对象
     */
    Environment getEnvironment();

    /**
     * 返回正在使用的资源加载器
     */
    ResourceLoader getResourceLoader();

    /**
     * 返回类加载器
     */
    @Nullable
    ClassLoader getClassLoader();

}

ConditionContext接口中提供了一些常用的方法,用于获取spring容器中的各种信息。

三、自定义条件装配

先看看@ConditionalOnClass注解是怎么做的?

1、@ConditionalOnClass

在这里插入图片描述
@ConditionalOnClass注解被@Conditional(OnClassCondition.class)注解标注,结合上面@Conditional注解的介绍来看。可以知道@ConditionalOnClass的条件装配判断逻辑依赖于Condition接口的实现类OnClassCondition实现。

再看OnClassCondition的类图:
在这里插入图片描述
理论上只需要OnClassCondition重写Condition接口的matches(ConditionContext, AnnotatedTypeMetadata)方法即可自定义条件装配的判定逻辑。然而OnClassCondition并没有,matches()方法的重写由OnClassCondition的父类SpringBootCondition来完成。
在这里插入图片描述
下面模仿其自定义一个《系统属性名称与属性值匹配的条件注解@ConditionalOnSystemProperty》。

2、自定义条件装配注解@ConditionalOnSystemProperty

注解@ConditionalOnSystemProperty注解的作用是获取到 启动jar程序命令行中 的Program arguments属性的名称与属性值做匹配进而决定是否装配类。

对于如何在IDEA中设置Program arguments,参考博文:https://blog.csdn.net/Saintmm/article/details/124603343

整体代码目录结构如下:
在这里插入图片描述

1> 自定义Condition

package com.saint.autoconfigure.condition;

import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.type.AnnotatedTypeMetadata;
import org.springframework.util.MultiValueMap;

import java.util.Objects;

/**
 * 系统属性值与值匹配条件
 *
 * @author Saint
 */
public class OnSystemPropertyCondition implements Condition {
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        MultiValueMap<String, Object> attributes =
                metadata.getAllAnnotationAttributes(ConditionalOnSystemProperty.class.getName());
        String name = (String) attributes.getFirst("name");
        String value = (String) attributes.getFirst("value");
        String systemPropertyValue = System.getProperty(name);
        // 比较系统属性值和方法的值
        if (Objects.equals(systemPropertyValue, value)) {
            System.out.printf("系统属性【名称: %s】找到匹配值:%s \n", name, value);
            return true;
        }
        return false;
    }
}

2> 自定义条件装配注解

package com.saint.autoconfigure.condition;

import org.springframework.context.annotation.Conditional;

import java.lang.annotation.*;

/**
 * 系统属性名称与属性值匹配条件注解
 *
 * @author Saint
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(OnSystemPropertyCondition.class)
public @interface ConditionalOnSystemProperty {

    /**
     * 属性名
     */
    String name();

    /**
     * 属性值
     */
    String value();
}

3> 定义 使用条件装配注解的配置类

package com.saint.config;

import com.saint.autoconfigure.condition.ConditionalOnSystemProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * 条件配置类
 *
 * @author Saint
 */
@Configuration
public class MyConditionalConfiguration {

    /**
     * 当存在key为language,value为Chinese的property属性时,加载当前方法
     *
     * @return
     */
    @ConditionalOnSystemProperty(name = "language", value = "Chinese")
    @Bean("message")
    public String chinese() {
        return "你好,世界!";
    }

    /**
     * 当存在key为language,value为English的property属性时,加载当前方法
     *
     * @return
     */
    @ConditionalOnSystemProperty(name = "language", value = "English")
    @Bean("message")
    public String english() {
        return "Hello, World";
    }
}

4> 在启动类中测试

package com.saint;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;

@SpringBootApplication
public class SaintSpringbootApplication {

    public static void main(String[] args) {
        // 设置language属性值,也可以通过--language=English在Program arguments中设置
        System.setProperty("language", "English");

        ConfigurableApplicationContext context = SpringApplication.run(SaintSpringbootApplication.class, args);

        // 从Spring上下文中获取beanName为message的类
        String message = context.getBean("message", String.class);
        System.out.println("当前message类型为:" + message);
    }

}

运行结果输出如下:
在这里插入图片描述
从结果可以看出,@ConditionalOnSystemProperty注解条件装配成功生效。

相关文章
|
26天前
|
消息中间件 Java 数据库
解密Spring Boot:深入理解条件装配与条件注解
Spring Boot中的条件装配与条件注解提供了强大的工具,使得应用程序可以根据不同的条件动态装配Bean,从而实现灵活的配置和管理。通过合理使用这些条件注解,开发者可以根据实际需求动态调整应用的行为,提升代码的可维护性和可扩展性。希望本文能够帮助你深入理解Spring Boot中的条件装配与条件注解,在实际开发中更好地应用这些功能。
31 2
|
6月前
|
Java Spring 容器
在 Spring Boot 中,条件装配(Conditional Configuration)和条件注解(Conditional Annotations)
在 Spring Boot 中,条件装配(Conditional Configuration)和条件注解(Conditional Annotations)
132 1
|
7月前
|
XML Java 数据库
【SpringBoot:详解Bean装配】
【SpringBoot:详解Bean装配】
|
7月前
|
Java
SpringBoot之@Conditional衍生条件装配详解
SpringBoot之@Conditional衍生条件装配详解
112 0
|
7月前
|
消息中间件 NoSQL Java
springboot - 条件注解@ConditionalOnClass原理
springboot - 条件注解@ConditionalOnClass原理
springboot - 条件注解@ConditionalOnClass原理
|
7月前
|
XML Java API
Springboot的自动装配解读
Spring Framework本身有一个IOC容器,该容器会统一管理其中的Bean对象,Bean对象可以理解为组件,要理解组件装配,首先要理解组件的概念。
|
7月前
|
存储 Java 开发工具
SpringBoot中Bean的条件装配
本文总结了在SpringBoot中常用的bean装配方法: * profile * conditional * ConditionalOn
81 1
|
7月前
|
XML Java 数据库
SpringBoot:详解Bean装配
SpringBoot:详解Bean装配
218 1
|
2月前
|
JavaScript 安全 Java
如何使用 Spring Boot 和 Ant Design Pro Vue 实现动态路由和菜单功能,快速搭建前后端分离的应用框架
本文介绍了如何使用 Spring Boot 和 Ant Design Pro Vue 实现动态路由和菜单功能,快速搭建前后端分离的应用框架。首先,确保开发环境已安装必要的工具,然后创建并配置 Spring Boot 项目,包括添加依赖和配置 Spring Security。接着,创建后端 API 和前端项目,配置动态路由和菜单。最后,运行项目并分享实践心得,包括版本兼容性、安全性、性能调优等方面。
178 1
|
1月前
|
JavaScript 安全 Java
如何使用 Spring Boot 和 Ant Design Pro Vue 构建一个具有动态路由和菜单功能的前后端分离应用。
本文介绍了如何使用 Spring Boot 和 Ant Design Pro Vue 构建一个具有动态路由和菜单功能的前后端分离应用。首先,创建并配置 Spring Boot 项目,实现后端 API;然后,使用 Ant Design Pro Vue 创建前端项目,配置动态路由和菜单。通过具体案例,展示了如何快速搭建高效、易维护的项目框架。
115 62
下一篇
DataWorks