springboot注解--基础--2.4--ConditionalOnMissingBean、ConditionalOnBean、ConditionalOnSingleCandidate

springboot注解–基础–2.4–ConditionalOnMissingBean、ConditionalOnBean、ConditionalOnSingleCandidate


代码位置
https://gitee.com/DanShenGuiZu/learnDemo/tree/master/annotation-learn/annotation-learn1

1、介绍

  1. ConditionalOnMissingBean

    1. 该注解规定的类不存在于 spring容器中时,使用该注解的config或者bean声明才会被实例化到容器中
  2. ConditionalOnBean

    1. 该注解规定的类存在于 spring容器中时,使用该注解的config或者bean声明才会被实例化到容器中
  3. ConditionalOnSingleCandidate

    1. 当指定Bean在容器中只有一个,或者虽然有多个但是指定首选Bean,使用该注解的config或者bean声明才会被实例化到容器中

1.2、ConditionalOnBean注解

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

	/**
	 *   需要作为条件的类Class对象数组
	 */
	Class<?>[] value() default {};

	/**
	 * 需要作为条件的类Name,Class.getName()
	 */
	String[] type() default {};

	/**
	 * (用指定注解修饰的bean)条件所需的注解类
	 */
	Class<? extends Annotation>[] annotation() default {};

	/**
	 * spring容器中bean的名字
	 */
	String[] name() default {};

	/**
	 * 搜索容器层级:当前容器,父容器,默认所有
	 */
	SearchStrategy search() default SearchStrategy.ALL;

	/**
	 * 
	 * 可能在其泛型参数中包含指定bean类型的其他类

	 */
	Class<?>[] parameterizedContainer() default {};

1.3、ConditionalOnMissingBean注解

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

	/**
	 *   需要作为条件的类Class对象数组
	 */
	Class<?>[] value() default {};

	/**
	 * 需要作为条件的类Name,Class.getName()
	 */
	String[] type() default {};

	/**
	 * (用指定注解修饰的bean)条件所需的注解类
	 */
	Class<? extends Annotation>[] annotation() default {};

	/**
	 * spring容器中bean的名字
	 */
	String[] name() default {};

	/**
	 * 搜索容器层级:当前容器,父容器,默认所有
	 */
	SearchStrategy search() default SearchStrategy.ALL;

	/**
	 * 
	 * 可能在其泛型参数中包含指定bean类型的其他类

	 */
	Class<?>[] parameterizedContainer() default {}; 
	/**
	 * 标识匹配bean时应忽略的bean的class类型。
	 */
	Class<?>[] ignored() default {};

	/**
	 * 标识匹配项时应忽略的bean的class类型名称 
	 */
	String[] ignoredType() default {};

}

1.4、ConditionalOnSingleCandidate注解

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

	/**
	 * 需要作为条件的类Class对象 
	 */
	Class<?> value() default Object.class;

	/**
	 * 需要作为条件的类Name,Class.getName()
	 */
	String type() default "";

	/**
	 *  搜索容器层级:当前容器,父容器,默认所有
	 */
	SearchStrategy search() default SearchStrategy.ALL;

}

1.5、OnClassCondition类关系

在这里插入图片描述

2、测试

在这里插入图片描述

2.1、代码

 

@Configuration
public class TestConfig {
    
    @Bean
    @ConditionalOnMissingBean(Test1.class)
    Test1 test1() {
        System.out.println("---------Test1 实例化bean不存在,将Test1注册到容器中---------");
        return new Test1();
    }
    
    @Bean
    @ConditionalOnBean(Test1.class)
    Test2 test2() {
        System.out.println("---------Test1 实例化bean存在,将Test2注册到容器中---------");
        return new Test2();
    }
    
    @Bean
    @ConditionalOnSingleCandidate(Test1.class)
    Test2 test3() {
        System.out.println("---------当指定Bean在容器中只有一个,或者虽然有多个但是指定首选Bean,就注册test3到容器中---------");
        return new Test2();
    }
    
    public class Test1 {
    }
    
    public class Test2 {
    }
    
    public static void main(String[] args) {
        
        // 设置日志级别,去掉我不要的信息
        LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory();
        List<Logger> loggerList = loggerContext.getLoggerList();
        loggerList.forEach(logger -> {
            logger.setLevel(Level.ERROR);
        });
        
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(TestConfig.class);
        
    }
}

2.2、测试

在这里插入图片描述

3、原理分析

我们通过类关系图找到Condition接口有个实现抽象类SpringBootCondition,SpringBoot中所有条件注解对应的条件类都继承这个抽象类。它实现了matches方法

在这里插入图片描述

getMatchOutcome是个抽象方法,需要子类去实现
@Override
public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {
	ConditionMessage matchMessage = ConditionMessage.empty();
	MergedAnnotations annotations = metadata.getAnnotations();
	//ConditionalOnBean注解 的处理方法
	if (annotations.isPresent(ConditionalOnBean.class)) {
		 // 将 ConditionalOnBean 注解属性封装进Spec对象中
		Spec<ConditionalOnBean> spec = new Spec<>(context, metadata, annotations, ConditionalOnBean.class);
		// 获取匹配的的bean
		MatchResult matchResult = getMatchingBeans(context, spec);
		// 注解上所有的属性都不匹配
		if (!matchResult.isAllMatched()) {
			String reason = createOnBeanNoMatchReason(matchResult);
			// 返回匹配失败的信息
			return ConditionOutcome.noMatch(spec.message().because(reason));
		}
		// 构建匹配成功的信息
		matchMessage = spec.message(matchMessage).found("bean", "beans").items(Style.QUOTE,
				matchResult.getNamesOfAllMatches());
	}
	 //ConditionalOnSingleCandidate注解 的处理方法
	if (metadata.isAnnotated(ConditionalOnSingleCandidate.class.getName())) {
		// 将 ConditionalOnSingleCandidate 注解属性封装进Spec对象中
		Spec<ConditionalOnSingleCandidate> spec = new SingleCandidateSpec(context, metadata, annotations);
		MatchResult matchResult = getMatchingBeans(context, spec);
		// 注解上所有的属性都不匹配
		if (!matchResult.isAllMatched()) {
			// 返回匹配失败的信息
			return ConditionOutcome.noMatch(spec.message().didNotFind("any beans").atAll());
		}
		// 如果bean不是单例或者多实例情况下,没有Primary表示,返回匹配失败 
		else if (!hasSingleAutowireCandidate(context.getBeanFactory(), matchResult.getNamesOfAllMatches(),
				spec.getStrategy() == SearchStrategy.ALL)) {
			// 返回匹配失败的信息
			return ConditionOutcome.noMatch(spec.message().didNotFind("a primary bean from beans")
					.items(Style.QUOTE, matchResult.getNamesOfAllMatches()));
		}
		// 构建匹配成功的信息
		matchMessage = spec.message(matchMessage).found("a primary bean from beans").items(Style.QUOTE,
				matchResult.getNamesOfAllMatches());
	}
	//ConditionalOnMissingBean注解 的处理方法
	if (metadata.isAnnotated(ConditionalOnMissingBean.class.getName())) {
		// 将 ConditionalOnMissingBean 注解属性封装进Spec对象中
		Spec<ConditionalOnMissingBean> spec = new Spec<>(context, metadata, annotations,
				ConditionalOnMissingBean.class);
		// 获取匹配的的bean
		MatchResult matchResult = getMatchingBeans(context, spec);
		// 没有一个匹配成功,返回匹配失败 
		if (matchResult.isAnyMatched()) {
			String reason = createOnMissingBeanNoMatchReason(matchResult);
			// 返回匹配失败的信息
			return ConditionOutcome.noMatch(spec.message().because(reason));
		}
		// 构建匹配成功的信息
		matchMessage = spec.message(matchMessage).didNotFind("any beans").atAll();
	}
	//返回成功信息
	return ConditionOutcome.match(matchMessage);
}

getMatchingBeans方法
protected final MatchResult getMatchingBeans(ConditionContext context, Spec<?> spec) {
	//类加载器
	ClassLoader classLoader = context.getClassLoader();
	//bean工厂
	ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
	// 搜索容器层级:是否搜索当前容器
	boolean considerHierarchy = spec.getStrategy() != SearchStrategy.CURRENT;
	// 泛型参数中包含指定bean类型的其他类
	Set<Class<?>> parameterizedContainers = spec.getParameterizedContainers();
	// 如果搜索的是父容器
	if (spec.getStrategy() == SearchStrategy.ANCESTORS) {
		//获取父bean工厂
		BeanFactory parent = beanFactory.getParentBeanFactory();
		Assert.isInstanceOf(ConfigurableListableBeanFactory.class, parent,
				"Unable to use SearchStrategy.ANCESTORS"); 
		beanFactory = (ConfigurableListableBeanFactory) parent;
	}
	//构建匹配结果类
	MatchResult result = new MatchResult();
	// 获取忽略的bean计划
	Set<String> beansIgnoredByType = getNamesOfBeansIgnoredByType(classLoader, beanFactory, considerHierarchy,
			spec.getIgnoredTypes(), parameterizedContainers);
			
	// 解析注解的value,type属性 
	for (String type : spec.getTypes()) {
		// 通过注解value,type属性,在bean工厂中获取所有匹配的bean
		Collection<String> typeMatches = getBeanNamesForType(classLoader, considerHierarchy, beanFactory, type,
				parameterizedContainers);
		// 删除忽略的bean
		typeMatches.removeAll(beansIgnoredByType);
		if (typeMatches.isEmpty()) {
			// 注解value,type属性,没有匹配到bean
			result.recordUnmatchedType(type);
		}
		else {
			//注解value,type属性,匹配到bean
			result.recordMatchedType(type, typeMatches);
		}
	}
	// 解析注解的annotation属性 
	for (String annotation : spec.getAnnotations()) {
		// 通过注解annotation属性,在bean工厂中获取所有匹配的bean
		Set<String> annotationMatches = getBeanNamesForAnnotation(classLoader, beanFactory, annotation,
				considerHierarchy);
		// 删除忽略的bean
		annotationMatches.removeAll(beansIgnoredByType);
		if (annotationMatches.isEmpty()) {
			// 注解annotation属性 ,没有匹配到bean
			result.recordUnmatchedAnnotation(annotation);
		}
		else {
			// 注解annotation属性 ,匹配到bean
			result.recordMatchedAnnotation(annotation, annotationMatches);
		}
	}
	// 解析注解的name属性 
	for (String beanName : spec.getNames()) {
		// 如果忽略的属性没有name,且容器中包含name
		if (!beansIgnoredByType.contains(beanName) && containsBean(beanFactory, beanName, considerHierarchy)) {
				// 注解name属性 ,匹配到bean
			result.recordMatchedName(beanName);
		}
		else {
			// 注解name属性,没有匹配到bean
			result.recordUnmatchedName(beanName);
		}
	}
	//返回结果
	return result;
}
hasSingleAutowireCandidate方法

private boolean hasSingleAutowireCandidate(ConfigurableListableBeanFactory beanFactory, Set<String> beanNames,
		boolean considerHierarchy) {
	// 单个实例或者又Primary注解,返回true
	return (beanNames.size() == 1 || getPrimaryBeans(beanFactory, beanNames, considerHierarchy).size() == 1);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
springboot vue-simple-uploader 是一个前后端分离的文件上传插件,在使用前需要进行相关配置和代码的编写。 首先,在后端部分,我们使用的是Spring Boot框架。需要导入spring-boot-starter-web依赖,并在配置文件中配置相关参数,例如设置文件上传临时目录、文件上传大小限制等。然后,我们需要编写一个处理文件上传请求的Controller类,使用@RequestBody注解接收前端传递的文件信息,并使用multipartFile.transferTo()方法保存文件到指定目录中。 在前端部分,我们使用的是Vue.js框架,并引入vue-simple-uploader插件。首先,我们需要安装该插件,可以使用npm安装或者直接引入插件的CDN地址。然后,在Vue实例中,我们可以通过配置uploaderOptions对象来进行插件的相关配置,例如设置上传的url、自定义headers、文件的最大数量和大小限制等。然后,在需要上传文件的组件中,我们可以通过引入uploader组件,并使用v-model绑定上传的文件列表。 通过上述配置和代码编写,我们就可以实现前后端分离的文件上传功能了。当用户选择上传的文件后,前端会将文件信息发送给后端,后端接收到请求后进行文件保存操作,并返回相应的结果给前端,例如文件的保存路径或者上传成功的提示信息。 总结一下,springboot vue-simple-uploader是一个支持前后端分离的文件上传插件,通过在后端配置文件上传参数和编写Controller类,在前端通过配置uploaderOptions对象和使用uploader组件,我们可以实现文件的上传和保存功能。这样,我们就可以方便地在Spring Boot和Vue.js项目中实现文件上传的需求。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值