springboot的自动装配原理

目录

1.springboot启动类样例

2. springboot自动装配原理分析

3.总结图

4.springboot 自动装配用到的条件化配置

4.1 @ConditionalOnBean 使用例子

4.2  @ConditionalOnBean 源码分析



 

1.springboot启动类样例

 

package com.chr.test.testspringboot;


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

@SpringBootApplication
public class TestSpringbootApplication {

    public static void main(String[] args) {

        SpringApplication.run(TestSpringbootApplication.class, args);
    }

}

 

2. springboot自动装配原理分析

   从上面的启动类上看,有个@SpringBootApplication的很重要的注解,我们就从这个注解开始分析springboot自动装配的原理。

从图上我们看到注解是在spring-boot-autoconfigure jar包里实现的。

我们看到@SpringBootApplication上面还有很多组合的注解:

@SpringBootConfiguration  (表明这是一个配置类,此注解最终会用到 @Configuration注解)
@EnableAutoConfiguration   (开启自动装配,重要,我们接下来哟啊重点分析的注解)
@ComponentScan(            (扫描主类所在的同级包以及下级包里的Bean)
    excludeFilters = {@Filter(
    type = FilterType.CUSTOM,
    classes = {TypeExcludeFilter.class}
), @Filter(
    type = FilterType.CUSTOM,
    classes = {AutoConfigurationExcludeFilter.class}
)}
)
public @interface SpringBootApplication 

 

接下来我们重点分析@EnableAutoConfiguration注解。

@EnableAutoConfiguration可以帮助SpringBoot应用将所有符合条件的@Configuration配置都加载到当前SpringBoot创建并使用的IoC容器:

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {
    String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";

    Class<?>[] exclude() default {};

    String[] excludeName() default {};
}

我们看到了重要的注解:

@Import({AutoConfigurationImportSelector.class})

Import注解的原理分析可以参考我的上篇文章:

https://blog.csdn.net/hairongok353043059/article/details/111473425

 

在这里通过@Import注解引入了AutoConfigurationImportSelector这个类到spring容器中。

我们来分析AutoConfigurationImportSelector这个类:

 我们看到AutoConfigurationImportSelector实现了DeferredImportSelector接口,而DeferredImportSelector实现自ImportSelector接口。

在分析@Import注解的时候,我们分析了通过ImportSelector这种方式引入对象来ioc容器中,是通过在实现方法selectImports方法中实现的。

public String[] selectImports(AnnotationMetadata annotationMetadata) {
    if (!this.isEnabled(annotationMetadata)) {
        return NO_IMPORTS;
    } else {
        //在这里收集需要自动装配的类
        AutoConfigurationImportSelector.AutoConfigurationEntry autoConfigurationEntry = this.getAutoConfigurationEntry(annotationMetadata);
        return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
    }
}

进入 this.getAutoConfigurationEntry(annotationMetadata);这个方法:

protected AutoConfigurationImportSelector.AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
        if (!this.isEnabled(annotationMetadata)) {
            return EMPTY_ENTRY;
        } else {
            AnnotationAttributes attributes = this.getAttributes(annotationMetadata);
            //在这里找到所有需要自动装配的候选配置类集合
            List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
            configurations = this.removeDuplicates(configurations);
            Set<String> exclusions = this.getExclusions(annotationMetadata, attributes);
            this.checkExcludedClasses(configurations, exclusions);
            configurations.removeAll(exclusions);
            configurations = this.getConfigurationClassFilter().filter(configurations);
            this.fireAutoConfigurationImportEvents(configurations, exclusions);
            return new AutoConfigurationImportSelector.AutoConfigurationEntry(configurations, exclusions);
        }
    }

  进入getCandidateConfigurations()方法: 

protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
      //通过SpringFactoriesLoader来加载需要自动装配的配置类
    List<String> configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.getBeanClassLoader());
    Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you are using a custom packaging, make sure that file is correct.");
    return configurations;
}

我们继续进入SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.getBeanClassLoader());

 public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
        ClassLoader classLoaderToUse = classLoader;
        if (classLoader == null) {
            classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
        }

        String factoryTypeName = factoryType.getName();
        //开始加载配置类
        return (List)loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList());
    }

    private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader) {
        Map<String, List<String>> result = (Map)cache.get(classLoader);
        if (result != null) {
            return result;
        } else {
            HashMap result = new HashMap();

            try {
                //类加载器找到META-INF/spring.factories目录下的文件,开始解析数据。吧对应的类放入list中
                Enumeration urls = classLoader.getResources("META-INF/spring.factories");

                while(urls.hasMoreElements()) {
                    URL url = (URL)urls.nextElement();
                    UrlResource resource = new UrlResource(url);
                    Properties properties = PropertiesLoaderUtils.loadProperties(resource);
                    Iterator var6 = properties.entrySet().iterator();

                    while(var6.hasNext()) {
                        Entry<?, ?> entry = (Entry)var6.next();
                        String factoryTypeName = ((String)entry.getKey()).trim();
                        String[] factoryImplementationNames = StringUtils.commaDelimitedListToStringArray((String)entry.getValue());
                        String[] var10 = factoryImplementationNames;
                        int var11 = factoryImplementationNames.length;

                        for(int var12 = 0; var12 < var11; ++var12) {
                            String factoryImplementationName = var10[var12];
                            ((List)result.computeIfAbsent(factoryTypeName, (key) -> {
                                return new ArrayList();
                            })).add(factoryImplementationName.trim());
                        }
                    }
                }

                result.replaceAll((factoryType, implementations) -> {
                    return (List)implementations.stream().distinct().collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList));
                });
                cache.put(classLoader, result);
                return result;
            } catch (IOException var14) {
                throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var14);
            }
        }
    }

AutoConfigurationImportSelector中的方法getCandidateConfigurations,得到待配置的class的类名集合,这个集合就是所有需要进行自动配置的类,而是否配置的关键在于META-INF/spring.factories文件中是否存在该配置信息

  protected List<String> getCandidateConfigurations(AnnotationMetadata metadata,
            AnnotationAttributes attributes) {
        List<String> configurations = SpringFactoriesLoader.loadFactoryNames(
                getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader());
        Assert.notEmpty(configurations,
                "No auto configuration classes found in META-INF/spring.factories. If you "
                        + "are using a custom packaging, make sure that file is correct.");
        return configurations;
    }

打开,如下图可以看到所有需要配置的类全路径都在文件中,每行一个配置,多个类名逗号分隔,而\表示忽略换行。

3.总结图

如下图来源于网络:

在这里插入图片描述
SpringBoot自动化配置关键组件关系图
mybatis-spring-boot-starter、spring-boot-starter-web等组件的META-INF文件下均含有spring.factories文件,自动配置模块中,SpringFactoriesLoader收集到文件中的类全名并返回一个类全名的数组,返回的类全名通过反射被实例化,就形成了具体的工厂实例,工厂实例来生成组件具体需要的bean。

 

4.springboot 自动装配用到的条件化配置

SpringBoot运用条件化配置的方法,定义多个特殊的条件化注解,并将其用到配置类上。

SpringBoot提供的条件化注解:

自动配置中使用的条件化注解
条件化注解配置生效条件
@ConditionalOnBean配置了某个特定Bean
@ConditionalOnMissingBean没有配置特定的Bean
@ConditionalOnClassClasspath里有指定的类
@ConditionalOnMissingClassClasspath里缺少指定的类
@ConditionalOnExpression给定的SpEL表达式计算结果为true
@ConditionalOnJavaJava的版本匹配特定值或者一个范围值
@ConditionalOnJndi参数中给定的JNDI位置必须存在一个,如果没有参数,则需要JNDI InitialContext
@ConditionalOnProperty指定的配置属性要有一个明确的值
@ConditionalOnResourceClasspath里有指定的资源
@ConditionalOnWebApplication这是一个Web应用程序
@ConditionalOnNotWebApplication这不是一个Web应用程序

springboot 条件装配原理,我们以@ConditionalOnBean为例开始分析此注解。

@ConditionalOnBean注解的作用是:当给定的在bean存在时,则实例化当前Bean,否则不实例化。

4.1 @ConditionalOnBean 使用例子

    city类需要用到province类对象。

   pojo类:

import lombok.*;

/**
 * @author chenhairong3
 * @description 省份
 * @date 2020/12/28
 */
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class Province {

    /**
     * 省份名称
     */
    private String name;

}
import lombok.*;

/**
 * @author chenhairong3
 * @description 城市
 * @date 2020/12/28
 */
@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class City {

    /**
     * city需要provice的一个对象
     */
    private Province province;

    /**
     * 城市名称
     */
    private String name;


}

 配置类:

import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * @author chenhairong3
 * @description
 * @date 2020/12/28
 */
@Configuration
public class ConfigTest {

    @Bean
    public Province provice() {
        Province province = new Province();
        province.setName("陕西省");
        return province;
    }


    @Bean
    public City city(Province province) {
        province.setName("陕西省1");
        return new City(province,"西安市");
    }
}

 springboot启动类:

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

/**
 * @author chenhairong3
 * @description
 * @date 2020/12/28
 */
@SpringBootApplication
public class ApplicaitonConfig {

    public static void main(String[] args) {
        SpringApplication.run(ApplicaitonConfig.class,args);
    }
}

编写测试类:

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

/**
 * @author chenhairong3
 * @description
 * @date 2020/12/28
 */
@SpringBootTest(classes = ApplicaitonConfig.class)
@RunWith(SpringRunner.class)
public class TestMain {

    /**
     * 注意这里,就是一旦使用@Autowired那就默认代表当前Bean一定是已经存在的,如果为null,会报错。
     * 所以这里要修改下。@Autowired(required=false) //required=false 的意思就是允许当前的Bean对象为null。
     */
    @Autowired(required=false)
    private City city;

    @Test
    public void test() {
        System.out.println("= = = = = = = = = = = = = ");
        System.out.println("city = " + city);
        System.out.println("= = = = = = = = = = = = = ");
    }
}

输出结果:

= = = = = = = = = = = = = 
city = City(province=Province(name=陕西省1), name=西安市)
= = = = = = = = = = = = = 

 

现在我们注释掉配置类里面province 这个bean的代码:

再次执行测试类代码:

发现报错,提示需要定义个Province类型的bean.

现在我们给city这个bean上面加上@ConditionalOnBean这个注解:

再来执行测试类看输出结果:

发现不再报错,输出了city=null.说明注解起作用了。当给定的在bean(这里是Province)不存在时,则不实例化当前对象(这里是city)实例。

4.2  @ConditionalOnBean 源码分析

      

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional({OnBeanCondition.class})
public @interface ConditionalOnBean {
    Class<?>[] value() default {};

    String[] type() default {};

    Class<? extends Annotation>[] annotation() default {};

    String[] name() default {};

    SearchStrategy search() default SearchStrategy.ALL;
}

ConditionalOnBean本质上用到了Conditional注解。

我们再看看下ConditionalOnBean的用法,我们看到了熟悉的bean注解。

我们先来猜一下springboot到底会如何实现?我们看到了Bean注解,在之前分析configuration注解的时候,我们知道有一个很重要的类:ConfigurationClassPostProcessor类。

在这个类的parse方法中会解析bean注解等。然后loadBeanDefinitions 加载并注册beanDefinition.

如果感兴趣Configuration注解的原理,可以看以前的文章:

Configuration注解分析原文链接:

https://blog.csdn.net/hairongok353043059/article/details/111329646

我们来看ConfigurationClassPostProcessor类:

public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
        List<BeanDefinitionHolder> configCandidates = new ArrayList();
        String[] candidateNames = registry.getBeanDefinitionNames();
        String[] var4 = candidateNames;
        int var5 = candidateNames.length;

        for(int var6 = 0; var6 < var5; ++var6) {
            String beanName = var4[var6];
            BeanDefinition beanDef = registry.getBeanDefinition(beanName);
            if (!ConfigurationClassUtils.isFullConfigurationClass(beanDef) && !ConfigurationClassUtils.isLiteConfigurationClass(beanDef)) {
                if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {
                    configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
                }
            } else if (this.logger.isDebugEnabled()) {
                this.logger.debug("Bean definition has already been processed as a configuration class: " + beanDef);
            }
        }

        if (!configCandidates.isEmpty()) {
            configCandidates.sort((bd1, bd2) -> {
                int i1 = ConfigurationClassUtils.getOrder(bd1.getBeanDefinition());
                int i2 = ConfigurationClassUtils.getOrder(bd2.getBeanDefinition());
                return Integer.compare(i1, i2);
            });
            SingletonBeanRegistry sbr = null;
            if (registry instanceof SingletonBeanRegistry) {
                sbr = (SingletonBeanRegistry)registry;
                if (!this.localBeanNameGeneratorSet) {
                    BeanNameGenerator generator = (BeanNameGenerator)sbr.getSingleton("org.springframework.context.annotation.internalConfigurationBeanNameGenerator");
                    if (generator != null) {
                        this.componentScanBeanNameGenerator = generator;
                        this.importBeanNameGenerator = generator;
                    }
                }
            }

            if (this.environment == null) {
                this.environment = new StandardEnvironment();
            }

            ConfigurationClassParser parser = new ConfigurationClassParser(this.metadataReaderFactory, this.problemReporter, this.environment, this.resourceLoader, this.componentScanBeanNameGenerator, registry);
            Set<BeanDefinitionHolder> candidates = new LinkedHashSet(configCandidates);
            HashSet alreadyParsed = new HashSet(configCandidates.size());

            do {
                //在parse方法中解析bean注解的方法。
                parser.parse(candidates);
                parser.validate();
                Set<ConfigurationClass> configClasses = new LinkedHashSet(parser.getConfigurationClasses());
                configClasses.removeAll(alreadyParsed);
                if (this.reader == null) {
                    this.reader = new ConfigurationClassBeanDefinitionReader(registry, this.sourceExtractor, this.resourceLoader, this.environment, this.importBeanNameGenerator, parser.getImportRegistry());
                }

                //在这里加载beanDefinition
                this.reader.loadBeanDefinitions(configClasses);
                alreadyParsed.addAll(configClasses);
                candidates.clear();
                if (registry.getBeanDefinitionCount() > candidateNames.length) {
                    String[] newCandidateNames = registry.getBeanDefinitionNames();
                    Set<String> oldCandidateNames = new HashSet(Arrays.asList(candidateNames));
                    Set<String> alreadyParsedClasses = new HashSet();
                    Iterator var12 = alreadyParsed.iterator();

                    while(var12.hasNext()) {
                        ConfigurationClass configurationClass = (ConfigurationClass)var12.next();
                        alreadyParsedClasses.add(configurationClass.getMetadata().getClassName());
                    }

                    String[] var23 = newCandidateNames;
                    int var24 = newCandidateNames.length;

                    for(int var14 = 0; var14 < var24; ++var14) {
                        String candidateName = var23[var14];
                        if (!oldCandidateNames.contains(candidateName)) {
                            BeanDefinition bd = registry.getBeanDefinition(candidateName);
                            if (ConfigurationClassUtils.checkConfigurationClassCandidate(bd, this.metadataReaderFactory) && !alreadyParsedClasses.contains(bd.getBeanClassName())) {
                                candidates.add(new BeanDefinitionHolder(bd, candidateName));
                            }
                        }
                    }

                    candidateNames = newCandidateNames;
                }
            } while(!candidates.isEmpty());

            if (sbr != null && !sbr.containsSingleton(IMPORT_REGISTRY_BEAN_NAME)) {
                sbr.registerSingleton(IMPORT_REGISTRY_BEAN_NAME, parser.getImportRegistry());
            }

            if (this.metadataReaderFactory instanceof CachingMetadataReaderFactory) {
                ((CachingMetadataReaderFactory)this.metadataReaderFactory).clearCache();
            }

        }
    }

 

继续进入标红的parse方法:最终调到如下的方法:
protected void processConfigurationClass(ConfigurationClass configClass) throws IOException {
        if (!this.conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION)) {
            ConfigurationClass existingClass = (ConfigurationClass)this.configurationClasses.get(configClass);
            if (existingClass != null) {
                if (configClass.isImported()) {
                    if (existingClass.isImported()) {
                        existingClass.mergeImportedBy(configClass);
                    }

                    return;
                }

                this.configurationClasses.remove(configClass);
                this.knownSuperclasses.values().removeIf(configClass::equals);
            }

            ConfigurationClassParser.SourceClass sourceClass = this.asSourceClass(configClass);

            do {
                //重点关注doProcessConfigurationClass方法
                sourceClass = this.doProcessConfigurationClass(configClass, sourceClass);
            } while(sourceClass != null);

            this.configurationClasses.put(configClass, configClass);
        }
    }

在doProcessConfigurationClass方法中:

@Nullable
    protected final ConfigurationClassParser.SourceClass doProcessConfigurationClass(ConfigurationClass configClass, ConfigurationClassParser.SourceClass sourceClass) throws IOException {
        this.processMemberClasses(configClass, sourceClass);
        Iterator var3 = AnnotationConfigUtils.attributesForRepeatable(sourceClass.getMetadata(), PropertySources.class, PropertySource.class).iterator();

        AnnotationAttributes importResource;
        while(var3.hasNext()) {
            importResource = (AnnotationAttributes)var3.next();
            if (this.environment instanceof ConfigurableEnvironment) {
                this.processPropertySource(importResource);
            } else {
                this.logger.warn("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() + "]. Reason: Environment must implement ConfigurableEnvironment");
            }
        }

        Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);
        if (!componentScans.isEmpty() && !this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) {
            Iterator var13 = componentScans.iterator();

            while(var13.hasNext()) {
                AnnotationAttributes componentScan = (AnnotationAttributes)var13.next();
                Set<BeanDefinitionHolder> scannedBeanDefinitions = this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());
                Iterator var7 = scannedBeanDefinitions.iterator();

                while(var7.hasNext()) {
                    BeanDefinitionHolder holder = (BeanDefinitionHolder)var7.next();
                    BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition();
                    if (bdCand == null) {
                        bdCand = holder.getBeanDefinition();
                    }

                    if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {
                        this.parse(bdCand.getBeanClassName(), holder.getBeanName());
                    }
                }
            }
        }

        this.processImports(configClass, sourceClass, this.getImports(sourceClass), true);
        importResource = AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class);
        if (importResource != null) {
            String[] resources = importResource.getStringArray("locations");
            Class<? extends BeanDefinitionReader> readerClass = importResource.getClass("reader");
            String[] var19 = resources;
            int var21 = resources.length;

            for(int var22 = 0; var22 < var21; ++var22) {
                String resource = var19[var22];
                String resolvedResource = this.environment.resolveRequiredPlaceholders(resource);
                configClass.addImportedResource(resolvedResource, readerClass);
            }
        }

        //解析bean注解的方法,我们例子中的city bean就是这个时候被解析被放入configClass.addBeanMethod中。
        Set<MethodMetadata> beanMethods = this.retrieveBeanMethodMetadata(sourceClass);
        Iterator var17 = beanMethods.iterator();

        while(var17.hasNext()) {
            MethodMetadata methodMetadata = (MethodMetadata)var17.next();
            configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
        }

        this.processInterfaces(configClass, sourceClass);
        if (sourceClass.getMetadata().hasSuperClass()) {
            String superclass = sourceClass.getMetadata().getSuperClassName();
            if (superclass != null && !superclass.startsWith("java") && !this.knownSuperclasses.containsKey(superclass)) {
                this.knownSuperclasses.put(superclass, configClass);
                return sourceClass.getSuperClass();
            }
        }

        return null;
    }

 

parse方法看完了,我们回到ConfigurationClassPostProcessor类中的loadBeanDefinitions方法:

继续跟进下去:

 

 终于找到加载beanMethod的地方了:

 我们看到了shouldSkip方法,debug的时候,发现methodName为city的时候,方法进入

然后我们重点进入shouldSkip方法。

public boolean shouldSkip(@Nullable AnnotatedTypeMetadata metadata, @Nullable ConfigurationPhase phase) {
        //metadata.isAnnotated(Conditional.class.getName()) 这里我们看到了熟悉的Conditional注解。这里bean上是否包含Conditional注解,如果是,则获取注解上的conditionClass 。
 构建condition : Condition condition = this.getCondition(conditionClass, this.context.getClassLoader()); 这里判断condition的条件是否满足:condition.matches(this.context, metadata)。来决定是否要加载指定的bean

        if (metadata != null && metadata.isAnnotated(Conditional.class.getName())) {
            if (phase == null) {
                return metadata instanceof AnnotationMetadata && ConfigurationClassUtils.isConfigurationCandidate((AnnotationMetadata)metadata) ? this.shouldSkip(metadata, ConfigurationPhase.PARSE_CONFIGURATION) : this.shouldSkip(metadata, ConfigurationPhase.REGISTER_BEAN);
            } else {
                List<Condition> conditions = new ArrayList();
                Iterator var4 = this.getConditionClasses(metadata).iterator();

                while(var4.hasNext()) {
                    String[] conditionClasses = (String[])var4.next();
                    String[] var6 = conditionClasses;
                    int var7 = conditionClasses.length;

                    for(int var8 = 0; var8 < var7; ++var8) {
                        String conditionClass = var6[var8];
                        Condition condition = this.getCondition(conditionClass, this.context.getClassLoader());
                        conditions.add(condition);
                    }
                }

                AnnotationAwareOrderComparator.sort(conditions);
                var4 = conditions.iterator();

                Condition condition;
                ConfigurationPhase requiredPhase;
                do {
                    do {
                        if (!var4.hasNext()) {
                            return false;
                        }

                        condition = (Condition)var4.next();
                        requiredPhase = null;
                        if (condition instanceof ConfigurationCondition) {
                            requiredPhase = ((ConfigurationCondition)condition).getConfigurationPhase();
                        }
                    } while(requiredPhase != null && requiredPhase != phase);
                } while(condition.matches(this.context, metadata));

                return true;
            }
        } else {
            return false;
        }
    }

 

 

最终 是从beanFactory容器中判断是否存在provice的bean,这里发现不存在,所以不匹配,故而city对应的beanDefinition不会被加载。也就不被创建和实例化了。

 

4.3  @ConditionalOnBean 的注意事项

@Configuration
public class ConfigTest {

    
    @Bean
    @ConditionalOnBean(name ="province")
    public City city(Province province) {
        province.setName("陕西省1");
        return new City(province,"西安市");
    }


    @Bean
    public Province provice() {
        Province province = new Province();
        province.setName("陕西省");
        return province;
    }
}

大家猜猜,我放开了provice的bean代码,这里city一定会被创建吗?

答案是不一定.

通过刚才的代码分析,

spring ioc的过程中,优先解析@Component,@Service,@Controller注解的类。其次解析配置类,也就是@Configuration标注的类。最后开始解析配置类中定义的bean。 

示例代码中city是定义在配置类中的,当执行到配置类解析的时候,@Component,@Service,@Controller ,@Configuration标注的类已经全部扫描,所以这些BeanDifinition已经被同步。 但是city的条件注解依赖的是province,province是被定义的配置类中的,所以此时配置类的解析无法保证先后顺序,就会出现不生效的情况。

如果想要生效:完全可以用@ConditionalOnClass(Province.class)来代替。

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值