spring-boot 配置生效条件处理(三)

1 查找配置是否生效处理代码

上一编可以看到EnableAutoConfiguration 加载的配置类就有92个,但是这个配置类都有效吗,显然是不一定的,因为很多相关的依懒都没有加引进来,又怎么生效呢?哪问题来了,spring-boot 是如何去处理的呢,带着这些疑问又该怎么入手分析?
a、首先确定处理的代码范围,逐渐缩小范围,b或先观察配置类身上的信息
在前面分析ConfigurationClassPostProcessor.processConfigBeanDefinitions()方法内可以看到这么一下段代码

//parser.getConfigurationClasses() 从spring-factories获取获取到的配置类信息
Set<ConfigurationClass> configClasses = new LinkedHashSet<ConfigurationClass>(parser.getConfigurationClasses());
configClasses.removeAll(alreadyParsed);

// Read the model and create bean definitions based on its content
if (this.reader == null) {
    this.reader = new ConfigurationClassBeanDefinitionReader(
            registry, this.sourceExtractor, this.resourceLoader, this.environment,
            this.importBeanNameGenerator, parser.getImportRegistry());
}
//ConfigurationClassBeanDefinitionReader 这里疑似是把配置类转换成beandefinition再注册到beanfactory里
this.reader.loadBeanDefinitions(configClasses);

继续跟下去查看一下ConfigurationClassBeanDefinitionReader这个里面有什么

/**
 * Reads a given fully-populated set of ConfigurationClass instances, registering bean
 * definitions with the given {@link BeanDefinitionRegistry} based on its contents.

上面的意思读取所有配置类实例,并根据其内容器注册beandefinitions

2 处理是否有效配置

/**
 * Read a particular {@link ConfigurationClass}, registering bean definitions
 * for the class itself and all of its {@link Bean} methods.
 */
private void loadBeanDefinitionsForConfigurationClass(ConfigurationClass configClass,
                                                      TrackedConditionEvaluator trackedConditionEvaluator) {
    //1.根据配置类身上的Conditional是否满足(依懒是否有引入)注册到beanfactory里,这是重点
    if (trackedConditionEvaluator.shouldSkip(configClass)) {
        String beanName = configClass.getBeanName();
        if (StringUtils.hasLength(beanName) && this.registry.containsBeanDefinition(beanName)) {
            this.registry.removeBeanDefinition(beanName);
        }
        this.importRegistry.removeImportingClassFor(configClass.getMetadata().getClassName());
        return;
    }
    //2.如果是导入类,直接注册其自身
    if (configClass.isImported()) {
        registerBeanDefinitionForImportedConfigurationClass(configClass);
    }
    //3.遍历配置类上所有的@Bean方法返回的bean并注册
    for (BeanMethod beanMethod : configClass.getBeanMethods()) {
        loadBeanDefinitionsForBeanMethod(beanMethod);
    }
    //4.导入资源配置如:@ImportResource(locations={"classpath:spring-beans.xml"})
    loadBeanDefinitionsFromImportedResources(configClass.getImportedResources());
    loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());
}

3 @conditional 处理

回头到1.1去观一下配置类的都一些特点,配置类身上都有@Conditional注解或其子类,好像上面那段代码第1点注释就是处理@Conditional注解的

private class TrackedConditionEvaluator {

   private final Map<ConfigurationClass, Boolean> skipped = new HashMap<ConfigurationClass, Boolean>();

   public boolean shouldSkip(ConfigurationClass configClass) {
      Boolean skip = this.skipped.get(configClass);
      if (skip == null) {
         if (configClass.isImported()) {
            boolean allSkipped = true;
            for (ConfigurationClass importedBy : configClass.getImportedBy()) {
               if (!shouldSkip(importedBy)) {
                  allSkipped = false;
                  break;
               }
            }
            if (allSkipped) {
               // The config classes that imported this one were all skipped, therefore we are skipped...
               skip = true;
            }
         }
         if (skip == null) {
            skip = conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN);
         }
         this.skipped.put(configClass, skip);
      }
      return skip;
   }
}

ConditionEvaluator 很明显该类是用处理配置类是否满自动配置(jar依懒条件是否被引用等),继续贴代码

/**
 * Determine if an item should be skipped based on {@code @Conditional} annotations.
 * @param metadata the meta data
 * @param phase the phase of the call
 * @return if the item should be skipped
 */
public boolean shouldSkip(AnnotatedTypeMetadata metadata, ConfigurationCondition.ConfigurationPhase phase) {
    //不存注解或没存在Conditional注解返回(不跳过)
    if (metadata == null || !metadata.isAnnotated(Conditional.class.getName())) {
        return false;
    }

    if (phase == null) {
        //因为这里是递归处理,传入的不身上有@Configuration注解,不一定为配置类,所以为判断一下
        if (metadata instanceof AnnotationMetadata &&
                ConfigurationClassUtils.isConfigurationCandidate((AnnotationMetadata) metadata)) {
            //@Configuration 配置类的处理
            return shouldSkip(metadata, ConfigurationCondition.ConfigurationPhase.PARSE_CONFIGURATION);
        }
        //只有@condition注解的处理
        return shouldSkip(metadata, ConfigurationCondition.ConfigurationPhase.REGISTER_BEAN);
    }
    //遍历身上所有@Conditional 注解
    List<Condition> conditions = new ArrayList<Condition>();
    for (String[] conditionClasses : getConditionClasses(metadata)) {
        for (String conditionClass : conditionClasses) {
            Condition condition = getCondition(conditionClass, this.context.getClassLoader());
            conditions.add(condition);
        }
    }

    AnnotationAwareOrderComparator.sort(conditions);
    //匹配所有condition,只有一个条件不满足,该类及就不会被注册到beanfactory里
    for (Condition condition : conditions) {
        ConfigurationCondition.ConfigurationPhase requiredPhase = null;
        if (condition instanceof ConfigurationCondition) {
            requiredPhase = ((ConfigurationCondition) condition).getConfigurationPhase();
        }
        if (requiredPhase == null || requiredPhase == phase) {
            if (!condition.matches(this.context, metadata)) {
                return true;
            }
        }
    }

    return false;
}

到此spring-boot 自动配置疑云应该都解开了

spring-boot-starter-web是一个用于构建Web应用程序的Spring Boot起步依赖。该依赖包含了Spring MVC、Tomcat和其他与Web应用程序相关的库和组件。如果使用了spring-boot-starter-web依赖,却发现Web应用程序未生效,则可能有以下原因: 1. maven或gradle配置问题:在使用Maven或Gradle构建项目时,必须正确地配置依赖关系和版本号。如果Maven或Gradle未正确配置spring-boot-starter-web依赖可能无法正确加载。请检查依赖配置是否正确。 2. 主类问题:Spring Boot应用程序需要一个主类来启动。如果主类的类路径不正确,则Web应用程序可能不会生效。请确保主类的类路径正确并且正确地引入spring-boot-starter-web依赖。 3. 自动配置问题:Spring Boot应用程序使用自动配置来完成大部分配置工作。如果自动配置不正确或不存在,则Web应用程序可能无法正常工作。请检查自动配置是否正确,并根据需要进行修改。 4. 端口冲突问题:Spring Boot使用默认的端口号8080启动Web应用程序。如果端口号被其他程序占用,则Web应用程序无法正常启动。请确保端口号未被其他程序占用。 5. Spring Boot版本问题:如果通过编译并启动程序,但无法通过浏览器访问,则可能是版本不兼容。请确保应用程序和Spring Boot版本兼容。 总之,如果使用了spring-boot-starter-web依赖,但Web应用程序未生效,应该仔细检查配置和代码,并逐个排除以上可能的原因,逐步解决问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值