spring @Conditional的使用与扩展源码分析

目录
  • @Conditional的使用
    • WindowsCondition
    • LinuxCondition
  • Conditional的扩展
    • ConditionalOnBean
    • ConditionalOnProperty
  • 源码分析

@Conditional的使用

@Conditional可以根据条件来判断是否注入某些Bean。

package com.morris.spring.config;

import com.morris.spring.condition.LinuxCondition;
import com.morris.spring.condition.WindowsCondition;
import com.morris.spring.entity.User;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
@Configuration
public class ConditionalConfig {
	// 如果是windows系统就注入bill
	@Conditional(WindowsCondition.class)
	@Bean(name = "user")
	public User bill() {
		return new User("bill", 22);
	}
	// 如果是linux系统就注入linus
	@Conditional(LinuxCondition.class)
	public User linus() {
		return new User("linus", 20);
}

WindowsCondition和LinuxCondition都需要实现Condition接口。

WindowsCondition

package com.morris.spring.condition;

import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.type.AnnotatedTypeMetadata;
public class WindowsCondition implements Condition {
    /**
     * 根据条件判断是否注入对应的Bean
     * @param conditionContext 应用上下文
     * @param annotatedTypeMetadata 加了@Conditional注解的方法的元数据信息
     * @return
     */
    @Override
    public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
        String osName = conditionContext.getEnvironment().getProperty("os.name");
        if(osName.contains("Windows")) {
            return true;
        }
        return false;
    }
}

LinuxCondition

package com.morris.spring.condition;

import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.type.AnnotatedTypeMetadata;
public class LinuxCondition implements Condition {
    @Override
    public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
        String osName = conditionContext.getEnvironment().getProperty("os.name");
        if(osName.contains("linux")) {
            return true;
        }
        return false;
    }
}

如果要测试LinuxCondition并不需要再linux系统下运行,只需要的启动时设置环境参数:-Dos.name=linux

Conditional的扩展

ConditionalOnBean

ConditionalOnBeanc.java

package com.morris.spring.condition;

import org.springframework.context.annotation.Conditional;
import java.lang.annotation.*;
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(OnBeanCondition.class)
public @interface ConditionalOnBean {
	Class<?>[] value() default {};
}

OnBeanCondition.java

package com.morris.spring.condition;

import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.type.AnnotatedTypeMetadata;
import java.util.Map;
public class OnBeanCondition implements Condition {
	@Override
	public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
		Map<String, Object> annotationAttributes = metadata.getAnnotationAttributes(ConditionalOnBean.class.getName());
		Class<?>[] clazz = (Class<?>[]) annotationAttributes.get("value");
		for (Class<?> aClass : clazz) {
			Map<String, ?> beans = context.getBeanFactory().getBeansOfType(aClass);
			if(beans.isEmpty()) {
				return false;
			}
		}
		return true;
	}
}

ConditionalOnProperty

ConditionalOnProperty.java

package com.morris.spring.condition;

import org.springframework.context.annotation.Conditional;
import java.lang.annotation.*;
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(OnPropertyCondition.class)
public @interface ConditionalOnProperty {
	String[] value() default {};
}

OnPropertyCondition.java

package com.morris.spring.condition;

import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.type.AnnotatedTypeMetadata;
import java.util.Map;
public class OnPropertyCondition implements Condition {
	@Override
	public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
		Map<String, Object> annotationAttributes = metadata.getAnnotationAttributes(ConditionalOnProperty.class.getName());
		String[] propertyArray = (String[]) annotationAttributes.get("value");
		for (String property : propertyArray) {
			if(!context.getEnvironment().containsProperty(property)) {
				return false;
			}
		}
		return true;
	}
}

源码分析

如果Condition返回的是false,那么spirng就不会对方法或类进行解析。

org.springframework.context.annotation.ConditionEvaluator#shouldSkip

public boolean shouldSkip(@Nullable AnnotatedTypeMetadata metadata, @Nullable ConfigurationPhase phase) {
	// 判断类或方法上面是否有@Conditional注解
	if (metadata == null || !metadata.isAnnotated(Conditional.class.getName())) {
		return false;
	}
	if (phase == null) {
		if (metadata instanceof AnnotationMetadata &&
				ConfigurationClassUtils.isConfigurationCandidate((AnnotationMetadata) metadata)) {
			return shouldSkip(metadata, ConfigurationPhase.PARSE_CONFIGURATION);
		}
		return shouldSkip(metadata, ConfigurationPhase.REGISTER_BEAN);
	List<Condition> conditions = new ArrayList<>();
	for (String[] conditionClasses : getConditionClasses(metadata)) {
		for (String conditionClass : conditionClasses) {
			Condition condition = getCondition(conditionClass, this.context.getClassLoader());
			conditions.add(condition);
	AnnotationAwareOrderComparator.sort(conditions);
	for (Condition condition : conditions) {
		ConfigurationPhase requiredPhase = null;
		if (condition instanceof ConfigurationCondition) {
			requiredPhase = ((ConfigurationCondition) condition).getConfigurationPhase();
		// 调用condition.matches方法
		if ((requiredPhase == null || requiredPhase == phase) && !condition.matches(this.context, metadata)) {
			return true;
	return false;
}

到此这篇关于spring @Conditional的使用与扩展的文章就介绍到这了,更多相关spring @Conditional使用内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Spring Boot中@ConditionalOnProperty的使用方法

    前言 在Spring Boot的自动配置中经常看到@ConditionalOnProperty注解的使用,本篇文章带大家来了解一下该注解的功能.下面话不多说了,来一起看看详细的介绍吧. Spring Boot中的使用 在Spring Boot的源码中,比如涉及到Http编码的自动配置.数据源类型的自动配置等大量的使用到了@ConditionalOnProperty的注解. HttpEncodingAutoConfiguration类中部分源代码: @Configuration(proxyBean

  • Spring基于@Conditional条件化装配bean

    一 前言 理解spring的如何根据条件装配bean有助于我们更好使用springboot进行开发,和源码理解: 二 @Conditional 装配bean 思路如下 Spring中提供了@Conditional注解实现条件化是否装配bean,这样可以根据条件动态的装配bean: 要判定@Conditional 注解是否该装配bean还需要Condition 接口配合,我们只须实现 Condition 接口,重写matches 方法: 根据matches方法返回的布尔值进行判定@Conditio

  • springboot @ConditionalOnMissingBean注解的作用详解

    @ConditionalOnMissingBean,它是修饰bean的一个注解,主要实现的是,当你的bean被注册之后,如果而注册相同类型的bean,就不会成功,它会保证你的bean只有一个,即你的实例只有一个,当你注册多个相同的bean时,会出现异常,以此来告诉开发人员. 代码演示 @Component public class AutoConfig { @Bean public AConfig aConfig() { return new AConfig("lind"); } @B

  • spring @Conditional的使用与扩展源码分析

    目录 @Conditional的使用 WindowsCondition LinuxCondition Conditional的扩展 ConditionalOnBean ConditionalOnProperty 源码分析 @Conditional的使用 @Conditional可以根据条件来判断是否注入某些Bean. package com.morris.spring.config; import com.morris.spring.condition.LinuxCondition; impor

  • 关于Spring启动时Context加载源码分析

    前言 本文主要给大家介绍了关于Spring启动时Context加载的相关内容,分享出来供大家参考学习,下面话不多说了,来一起看看详细的介绍吧. 测试源码下载test-annotation.zip 有如下的代码 @Component public class HelloWorldService { @Value("${name:World}") private String name; public String getHelloMessage() { return "Hell

  • Spring源码分析容器启动流程

    目录 前言 源码解析 1.初始化流程 流程分析 核心代码剖析 2.刷新流程 流程分析 核心代码剖析 前言 本文基于 Spring 的 5.1.6.RELEASE 版本 Spring的启动流程可以归纳为三个步骤: 1.初始化Spring容器,注册内置的BeanPostProcessor的BeanDefinition到容器中 2.将配置类的BeanDefinition注册到容器中 3.调用refresh()方法刷新容器 Spring Framework 是 Java 语言中影响最为深远的框架之一,其

  • SPRING BOOT启动命令参数及源码详析

    前言 使用过Spring Boot,我们都知道通过java -jar可以快速启动Spring Boot项目.同时,也可以通过在执行jar -jar时传递参数来进行配置.本文带大家系统的了解一下Spring Boot命令行参数相关的功能及相关源码分析. 命令行参数使用 启动Spring Boot项目时,我们可以通过如下方式传递参数: java -jar xxx.jar --server.port=8081 默认情况下Spring Boot使用8080端口,通过上述参数将其修改为8081端口,而且通

  • Spring Cloud集成Nacos Config动态刷新源码剖析

    目录 正文 Nacos Config动态刷新机制 Nacos Config 长轮询源码剖析 ClientWorker构造器初始化线程池 长轮询流程方法 正文 从远端服务器获取变更数据的主要模式有两种:推(push)和拉(pull).Push 模式简单来说就是服务端主动将数据变更信息推送给客户端,这种模式优点是时效性好,服务端数据发生变更可以立马通知到客户端,但这种模式需要服务端维持与客户端的心跳连接,会增加服务端实现的复杂度,服务端也需要占用更多的资源来维持与客户端的连接. 而 Pull 模式则

  • lazy init控制加载在Spring中如何实现源码分析

    目录 一.lazy-init说明 二.lazy-init 属性被设置的地方 三.lazy-init发挥作用的地方 四.问答 一.lazy-init说明 ApplicationContext实现的默认行为就是在启动时将所有singleton bean提前进行实例化(也就是依赖注入). 提前实例化意味着作为初始化过程的一部分,ApplicationContext实例会创建并配置所有的singleton bean. 通常情况下这是件好事,因为这样在配置中的任何错误就会即刻被发现(否则的话可能要花几个小

  • Spring MVC策略模式之MethodArgumentResolver源码解析

    目录 正文 例子 源码分析 RequestParamMethodArgumentResolver: PathVariableMethodArgumentResolver: ModelMethodProcessor: 总结 正文 Spring MVC 是一个基于 MVC 设计模式的Web框架,它的核心就是 DispatcherServlet,它相当于请求的中央处理器.在 DispatcherServlet 中,它使用了 MethodArgumentResolver 来解析方法参数. MethodA

  • 使用dynamic-datasource-spring-boot-starter实现多数据源及源码分析

    简介 前两篇博客介绍了用基本的方式做多数据源,可以应对一般的情况,但是遇到一些复杂的情况就需要扩展下功能了,比如:动态增减数据源.数据源分组,纯粹多库 读写分离 一主多从.从其他数据库或者配置中心读取数据源等等.其实就算没有这些需求,使用这个实现多数据源也比之前使用AbstractRoutingDataSource要便捷的多 dynamic-datasource-spring-boot-starter 是一个基于springboot的快速集成多数据源的启动器. github: https://g

  • 深入理解框架背后的原理及源码分析

    目录 问题1 问题2 总结 近期团队中同学遇到几个问题,想在这儿跟大家分享一波,虽说不是很有难度,但是背后也折射出一些问题,值得思考. 开始之前先简单介绍一下我所在团队的技术栈,基于这个背景再展开后面将提到的几个问题,将会有更深刻的体会. 控制层基于SpringMvc,数据持久层基于JdbcTemplate自己封装了一套类MyBatis的Dao框架,视图层基于Velocity模板技术,其余组件基于SpringCloud全家桶. 问题1 某应用发布以后开始报数据库连接池不够用异常,日志如下: co

  • 使用dynamic datasource springboot starter实现多数据源及源码分析

    目录 简介 实操 基本使用 集成druid连接池 service嵌套 为什么切换数据源不生效或事务不生效? 源码分析 整体结构 自动配置怎么实现的 如何集成众多连接池的 DS注解如何被拦截处理的 多数据源动态切换及如何管理多数据源 数据组的负载均衡怎么做的 如何自定义数据配置来源 如何动态增减数据源 总结 简介 前两篇博客介绍了用基本的方式做多数据源,可以应对一般的情况,但是遇到一些复杂的情况就需要扩展下功能了,比如:动态增减数据源.数据源分组,纯粹多库 读写分离 一主多从.从其他数据库或者配置

随机推荐