zoukankan      html  css  js  c++  java
  • SpringBoot @ConditionalOnBean、@ConditionalOnMissingBean注解源码分析与示例

    前言:

    Spring4推出了@Conditional注解,方便程序根据当前环境或者容器情况来动态注入bean,对@Conditional注解不熟悉的朋友可移步至 Spring @Conditional注解 详细讲解及示例 这篇博客进行学习。

    继@Conditional注解后,又基于此注解推出了很多派生注解,比如@ConditionalOnBean、@ConditionalOnMissingBean、@ConditionalOnExpression、@ConditionalOnClass......动态注入bean变得更方便了。本篇将讲解@ConditionalOnBean注解。

    配置类中有两个Computer类的bean,一个是笔记本电脑,一个是备用电脑。如果当前容器中已经有电脑bean了,就不注入备用电脑,如果没有,则注入备用电脑,这里需要使用到@ConditionalOnMissingBean。

    1. @Configuration
    2. public class BeanConfig {
    3. @Bean(name = "notebookPC")
    4. public Computer computer1(){
    5. return new Computer("笔记本电脑");
    6. }
    7. @ConditionalOnMissingBean(Computer.class)
    8. @Bean("reservePC")
    9. public Computer computer2(){
    10. return new Computer("备用电脑");
    11. }
    12. }

    这个注解就实现了功能,这个@ConditionalOnMissingBean为我们做了什么呢?我们来一探究竟.。

    一探究竟:

    首先,来看@ConditionalOnMissingBean的声明:

    1. //可以标注在类和方法上
    2. @Target({ElementType.TYPE, ElementType.METHOD})
    3. @Retention(RetentionPolicy.RUNTIME)
    4. @Documented
    5. //使用了@Conditional注解,条件类是OnBeanCondition
    6. @Conditional({OnBeanCondition.class})
    7. public @interface ConditionalOnMissingBean {
    8. Class<?>[] value() default {};
    9. String[] type() default {};
    10. Class<?>[] ignored() default {};
    11. String[] ignoredType() default {};
    12. Class<? extends Annotation>[] annotation() default {};
    13. String[] name() default {};
    14. SearchStrategy search() default SearchStrategy.ALL;
    15. }

    这时候,我们就看到了我们熟悉的@Conditional注解,OnBeanCondition作为条件类。

    OnBeanCondition类的声明:

    1. //定义带注释的组件的排序顺序,2147483647即为默认值
    2. @Order(2147483647)
    3. class OnBeanCondition extends SpringBootCondition implements ConfigurationCondition {

    它继承了SpringBootCondition类,OnBeanCondition类中没有matches方法,而SpringBootCondition类中有实现matches方法。OnBeanCondition还实现了ConfigurationCondition,ConfigurationCondition接口不熟悉的读者可以到Spring ConfigurationCondition接口详解 了解接口。OnBeanCondition类重写了getConfigurationPhase()方法,表示在注册bean的时候注解生效:

    1. public ConfigurationPhase getConfigurationPhase() {
    2. return ConfigurationPhase.REGISTER_BEAN;
    3. }

    就从matches方法开始:

    1. //SpringBootCondition类中的matches方法
    2. public final boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
    3. //获取当前的类名或者方法名(由标注的位置决定)
    4. String classOrMethodName = getClassOrMethodName(metadata);
    5. try {
    6. //关键代码:这里就会判断出结果
    7. ConditionOutcome outcome = this.getMatchOutcome(context, metadata);
    8. //存入日志
    9. this.logOutcome(classOrMethodName, outcome);
    10. //存入记录
    11. this.recordEvaluation(context, classOrMethodName, outcome);
    12. //最后返回ConditionOutcome的isMatch就是返回boolean类型结果
    13. return outcome.isMatch();
    14. } catch (NoClassDefFoundError var5) {
    15. throw new IllegalStateException("Could not evaluate condition on " + classOrMethodName + " due to " + var5.getMessage() + " not found. Make sure your own configuration does not rely on that class. This can also happen if you are @ComponentScanning a springframework package (e.g. if you put a @ComponentScan in the default package by mistake)", var5);
    16. } catch (RuntimeException var6) {
    17. throw new IllegalStateException("Error processing condition on " + this.getName(metadata), var6);
    18. }
    19. }

    关键代码在OnBeanCondition的getMatchOutcome方法上:

    1. /**
    2. * 获得判断结果的方法,ConditionOutcome类中存着boolean类型的结果
    3. */
    4. public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {
    5. //返回一个新的ConditionMessage
    6. ConditionMessage matchMessage = ConditionMessage.empty();
    7. OnBeanCondition.BeanSearchSpec spec;
    8. List matching;
    9. //这是metadata会调用isAnnotated方法判断当前标注的注解是不是ConditionalOnMissingBean
    10. //其实@ConditionalOnBean、@ConditionalOnMissingBean和@ConditionalOnSingleCandidate都是使用这个条件类,所以这里做判断
    11. if (metadata.isAnnotated(ConditionalOnBean.class.getName())) {
    12. spec = new OnBeanCondition.BeanSearchSpec(context, metadata, ConditionalOnBean.class);
    13. matching = this.getMatchingBeans(context, spec);
    14. if (matching.isEmpty()) {
    15. return ConditionOutcome.noMatch(ConditionMessage.forCondition(ConditionalOnBean.class, new Object[]{spec}).didNotFind("any beans").atAll());
    16. }
    17. matchMessage = matchMessage.andCondition(ConditionalOnBean.class, new Object[]{spec}).found("bean", "beans").items(Style.QUOTE, matching);
    18. }
    19. if (metadata.isAnnotated(ConditionalOnSingleCandidate.class.getName())) {
    20. OnBeanCondition.BeanSearchSpec spec = new OnBeanCondition.SingleCandidateBeanSearchSpec(context, metadata, ConditionalOnSingleCandidate.class);
    21. matching = this.getMatchingBeans(context, spec);
    22. if (matching.isEmpty()) {
    23. return ConditionOutcome.noMatch(ConditionMessage.forCondition(ConditionalOnSingleCandidate.class, new Object[]{spec}).didNotFind("any beans").atAll());
    24. }
    25. if (!this.hasSingleAutowireCandidate(context.getBeanFactory(), matching, spec.getStrategy() == SearchStrategy.ALL)) {
    26. return ConditionOutcome.noMatch(ConditionMessage.forCondition(ConditionalOnSingleCandidate.class, new Object[]{spec}).didNotFind("a primary bean from beans").items(Style.QUOTE, matching));
    27. }
    28. matchMessage = matchMessage.andCondition(ConditionalOnSingleCandidate.class, new Object[]{spec}).found("a primary bean from beans").items(Style.QUOTE, matching);
    29. }
    30. //如果当前注入的bean是@ConditionalOnMissingBean
    31. if (metadata.isAnnotated(ConditionalOnMissingBean.class.getName())) {
    32. //返回一个spec(说明),这里的spec规定了搜索的内容,比如搜索策略、需要搜索的类名......
    33. spec = new OnBeanCondition.BeanSearchSpec(context, metadata, ConditionalOnMissingBean.class);
    34. //主要的搜索实现在这个方法里,最后返回一个list
    35. matching = this.getMatchingBeans(context, spec);
    36. //判断搜索出来的结果
    37. if (!matching.isEmpty()) {
    38. return ConditionOutcome.noMatch(ConditionMessage.forCondition(ConditionalOnMissingBean.class, new Object[]{spec}).found("bean", "beans").items(Style.QUOTE, matching));
    39. }
    40. matchMessage = matchMessage.andCondition(ConditionalOnMissingBean.class, new Object[]{spec}).didNotFind("any beans").atAll();
    41. }
    42. return ConditionOutcome.match(matchMessage);
    43. }

    spec = new OnBeanCondition.BeanSearchSpec(context, metadata, ConditionalOnBean.class);

    这句中,相当于从内部类中将标注@ConditionalOnMissingBean注解时的属性都取出来:

    1. BeanSearchSpec(ConditionContext context, AnnotatedTypeMetadata metadata, Class<?> annotationType) {
    2. this.annotationType = annotationType;
    3. MultiValueMap<String, Object> attributes = metadata.getAllAnnotationAttributes(annotationType.getName(), true);
    4. //将attributes这个map中的数据放到对应的list成员变量中
    5. this.collect(attributes, "name", this.names);
    6. this.collect(attributes, "value", this.types);
    7. this.collect(attributes, "type", this.types);
    8. this.collect(attributes, "annotation", this.annotations);
    9. this.collect(attributes, "ignored", this.ignoredTypes);
    10. this.collect(attributes, "ignoredType", this.ignoredTypes);
    11. this.strategy = (SearchStrategy)metadata.getAnnotationAttributes(annotationType.getName()).get("search");
    12. OnBeanCondition.BeanTypeDeductionException deductionException = null;
    13. try {
    14. if (this.types.isEmpty() && this.names.isEmpty()) {
    15. this.addDeducedBeanType(context, metadata, this.types);
    16. }
    17. } catch (OnBeanCondition.BeanTypeDeductionException var7) {
    18. deductionException = var7;
    19. }
    20. this.validate(deductionException);
    21. }
    22. //验证的方法
    23. protected void validate(OnBeanCondition.BeanTypeDeductionException ex) {
    24. if (!this.hasAtLeastOne(this.types, this.names, this.annotations)) {
    25. String message = this.annotationName() + " did not specify a bean using type, name or annotation";
    26. if (ex == null) {
    27. throw new IllegalStateException(message);
    28. } else {
    29. throw new IllegalStateException(message + " and the attempt to deduce the bean's type failed", ex);
    30. }
    31. }
    32. }

    看一下OnBeanCondition类中的getMatchingBeans方法,里面有用到搜索策略,详见搜索策略介绍

    1. private List<String> getMatchingBeans(ConditionContext context, OnBeanCondition.BeanSearchSpec beans) {
    2. //获得当前bean工厂
    3. ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
    4. //判断当前的搜索策略是否是PARENTS或者ANCESTORS,默认是ALL
    5. if (beans.getStrategy() == SearchStrategy.PARENTS || beans.getStrategy() == SearchStrategy.ANCESTORS) {
    6. BeanFactory parent = beanFactory.getParentBeanFactory();
    7. Assert.isInstanceOf(ConfigurableListableBeanFactory.class, parent, "Unable to use SearchStrategy.PARENTS");
    8. //如果是PARENTS或者ANCESTORS,当前bean工厂就用父工厂
    9. beanFactory = (ConfigurableListableBeanFactory)parent;
    10. }
    11. if (beanFactory == null) {
    12. return Collections.emptyList();
    13. } else {
    14. List<String> beanNames = new ArrayList();
    15. //如果当前搜索策略等于CURRENT,为true
    16. boolean considerHierarchy = beans.getStrategy() != SearchStrategy.CURRENT;
    17. //这里的type就是需要查找的bean的类型
    18. //下面,会从属性中找bean
    19. Iterator var6 = beans.getTypes().iterator();
    20. String beanName;
    21. while(var6.hasNext()) {
    22. beanName = (String)var6.next();
    23. //如果找到了类型,接下来就是根据类型找bean的实例名,找示例名的方法在下方,实际上就是一个getNamesForType
    24. beanNames.addAll(this.getBeanNamesForType(beanFactory, beanName, context.getClassLoader(), considerHierarchy));
    25. }
    26. var6 = beans.getIgnoredTypes().iterator();
    27. while(var6.hasNext()) {
    28. beanName = (String)var6.next();
    29. beanNames.removeAll(this.getBeanNamesForType(beanFactory, beanName, context.getClassLoader(), considerHierarchy));
    30. }
    31. var6 = beans.getAnnotations().iterator();
    32. while(var6.hasNext()) {
    33. beanName = (String)var6.next();
    34. beanNames.addAll(Arrays.asList(this.getBeanNamesForAnnotation(beanFactory, beanName, context.getClassLoader(), considerHierarchy)));
    35. }
    36. var6 = beans.getNames().iterator();
    37. while(var6.hasNext()) {
    38. beanName = (String)var6.next();
    39. if (this.containsBean(beanFactory, beanName, considerHierarchy)) {
    40. beanNames.add(beanName);
    41. }
    42. }
    43. //将存放bean实例名的list返回
    44. return beanNames;
    45. }
    46. }
    47. //根据类型获取bean的name
    48. private Collection<String> getBeanNamesForType(ListableBeanFactory beanFactory, String type, ClassLoader classLoader, boolean considerHierarchy) throws LinkageError {
    49. try {
    50. Set<String> result = new LinkedHashSet();
    51. this.collectBeanNamesForType(result, beanFactory, ClassUtils.forName(type, classLoader), considerHierarchy);
    52. return result;
    53. } catch (ClassNotFoundException var6) {
    54. return Collections.emptySet();
    55. } catch (NoClassDefFoundError var7) {
    56. return Collections.emptySet();
    57. }
    58. }
    59. private void collectBeanNamesForType(Set<String> result, ListableBeanFactory beanFactory, Class<?> type, boolean considerHierarchy) {
    60. result.addAll(BeanTypeRegistry.get(beanFactory).getNamesForType(type));
    61. if (considerHierarchy && beanFactory instanceof HierarchicalBeanFactory) {
    62. BeanFactory parent = ((HierarchicalBeanFactory)beanFactory).getParentBeanFactory();
    63. if (parent instanceof ListableBeanFactory) {
    64. this.collectBeanNamesForType(result, (ListableBeanFactory)parent, type, considerHierarchy);
    65. }
    66. }
    67. }

    找完bean了之后,回到刚才的代码里:

    1. //如果当前注入的bean是@ConditionalOnMissingBean
    2. if (metadata.isAnnotated(ConditionalOnMissingBean.class.getName())) {
    3. //返回一个spec(说明),这里的spec规定了搜索的内容,比如搜索策略、需要搜索的类名......
    4. spec = new OnBeanCondition.BeanSearchSpec(context, metadata, ConditionalOnMissingBean.class);
    5. matching = this.getMatchingBeans(context, spec);
    6. if (!matching.isEmpty()) {
    7. return ConditionOutcome.noMatch(ConditionMessage.forCondition(ConditionalOnMissingBean.class, new Object[]{spec}).found("bean", "beans").items(Style.QUOTE, matching));
    8. }
    9. matchMessage = matchMessage.andCondition(ConditionalOnMissingBean.class, new Object[]{spec}).didNotFind("any beans").atAll();
    10. }

    如果第5行返回的list不是空的,就会返回ConditionOutcome对象noMatch方法,表示不匹配。ConditionOutcome类用于存放过滤结果,只有两个变量:

    1. /**
    2. * 过滤结果类
    3. */
    4. public class ConditionOutcome {
    5. /**
    6. * 匹配结果 true or false
    7. */
    8. private final boolean match;
    9. /**
    10. * 匹配结果信息
    11. */
    12. private final ConditionMessage message;

    两者区别:

    @ConditionOnBean在判断list的时候,如果list没有值,返回false,否则返回true

    @ConditionOnMissingBean在判断list的时候,如果list没有值,返回true,否则返回false,其他逻辑都一样

    例子:

    • @ConditionalOnBean(javax.sql.DataSource.class)    
      Spring容器或者所有父容器中需要存在至少一个javax.sql.DataSource类的实例
       

     

     

  • 相关阅读:
    fedora上部署ASP.NET——(卡带式电脑跑.NET WEB服务器)
    SQL Server 请求失败或服务未及时响应。有关详细信息,请参见事件日志或其它适合的错误日志
    8086CPU的出栈(pop)和入栈(push) 都是以字为单位进行的
    FTP 服务搭建后不能访问问题解决
    指定的 DSN 中,驱动程序和应用程序之间的体系结构不匹配
    Linux 安装MongoDB 并设置防火墙,使用远程客户端访问
    svn Please execute the 'Cleanup' command. 问题解决
    .net 操作MongoDB 基础
    oracle 使用绑定变量极大的提升性能
    尝试加载 Oracle 客户端库时引发 BadImageFormatException。如果在安装 32 位 Oracle 客户端组件的情况下以 64 位模式运行,将出现此问题。
  • 原文地址:https://www.cnblogs.com/jpfss/p/10870441.html
Copyright © 2011-2022 走看看