SpringBoot自动配置

自动配置到底配了什么?

SpringBoot中的自动配置,更多的是配置各种Bean,对于端口号这些配置,SpringBoot也是会提供一种默认值的,也相当于一种自动配置。

自动配置类

ServletWebServerFactoryAutoConfiguration

配置了Servlet Web场景中所需要的一些Bean

TransactionAutoConfiguration

配置了事务场景中所需要的一些Bean

AopAutoConfiguration

配置了AOP场景中所需要的一些Bean

RabbitAutoConfiguration

配置了Rabbitmq场景中所需要的一些Bean

条件注解

ConditionalOnBean

是否存在某个某类或某个名字的Bean

ConditionalOnMissingBean

是否缺失某个某类或某个名字的Bean

ConditionalOnClass

是否存在某个类

ConditionalOnSingleCandidate:是否符合指定类型的Bean只有一个
ConditionalOnMissingClass:是否缺失某个类
ConditionalOnExpression:指定的表达式返回的是true还是false
ConditionalOnJava:判断Java版本
ConditionalOnJndi:JNDI指定的资源是否存在
ConditionalOnWebApplication:当前应用是一个Web应用
ConditionalOnNotWebApplication:当前应用不是一个Web应用
ConditionalOnProperty:Environment中是否存在某个属性
ConditionalOnResource:指定的资源是否存在
ConditionalOnWarDeployment:当前项目是不是以War包部署的方式运行
ConditionalOnCloudPlatform:是不是在某个云平台上

条件注解是可以写在类上和方法上的,如果某个条件注解写在了自动配置类上,那该自动配置类会不会生效就要看当前条件能不能符合,条件注解写在某个@Bean修饰的方法上,那这个Bean生不生效就看当前条件符不符合。

Spring在解析某个自动配置类时,会先检查该自动配置类上是否有条件注解,如果有,则进一步判断该条件注解所指定的条件当前能不能满足,如果满足了则继续解析该配置类,如果不满足则不进行解析了

Starter机制

一个Starter,就是一个Maven依赖,当我们在项目的pom.xml文件中添加某个Starter依赖时,其实就是简单的添加了很多其他的依赖。通过@ConditionalOnClass这个条件注解,因为这个条件注解的作用就是用来判断当前应用的依赖中是否存在某个类或某些类

@ConditionalOnClass({ Servlet.class, Tomcat.class, UpgradeProtocol.class })

用来判断项目中是否存在Servlet.class、Tomcat.class、UpgradeProtocol.class这三个类,如果存在就满足当前条件,如果项目中引入了spring-boot-starter-tomcat,那就有这三个类,如果没有spring-boot-starter-tomcat那就可能没有这三个类(除非你自己单独引入了Tomcat相关的依赖)。

项目中要用Tomcat,那就依赖spring-boot-starter-web就够了,因为它默认依赖了spring-boot-starter-tomcat,从而依赖了Tomcat,从而Tomcat相关的Bean能生效。

不想用Tomcat,那就得这么写

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    <exclusions>
        <exclusion>
            <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-tomcat</artifactId>
      </exclusion>
    </exclusions>
</dependency>

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-jetty</artifactId>
</dependency>

@ConditionalOnMissingBean

用来判断某个Bean是否缺失,如果不存在某个Bean,那就符合该条件,主要就是顺序问题

@ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)

利用这个注解来决定到底用用户自己的Bean还是用SpringBoot自动配置的。

SpringBoot启动时,@EnableAutoConfiguration:通过@import注解导入AutoConfigurationImportSelector这个配置类,因为它实现了DeferredImportSelector接口,所以Spring会在把其他配置类都解析完之后才解析AutoConfigurationImportSelector,这个类的作用就是用来解析SpringBoot的自动配置类

spring.factories

SpringBoot会提供一个spring.factories文件,并把所有自动配置类的名字记录在这个文件中,到时候启动过程中解析这个文件就知道有哪些自动配置类了,并且这件事也是发生在解析完用户的配置类之后的。

自动配置开启原理

@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
        @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })

@SpringBootApplication是一个三合一注解

@EnableAutoConfiguration

@Import(AutoConfigurationImportSelector.class)

AutoConfigurationImportSelector实现了DeferredImportSelector这个接口,Spring容器在启动时,会在解析完其他所有程序员定义的配置类之后,来调用AutoConfigurationImportSelector中的selectImports方法,然后把该方法返回的类名对应的类作为配置类进行解析。

获取类名

  List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);

会利用SpringFactoriesLoader找到所有的META-INF/spring.factories文件中key为EnableAutoConfiguration.class的value值,也就是众多自动配置类的类名

去重

configurations = removeDuplicates(configurations);

排除

// 获取需要排除的AutoConfiguration,可以通过@EnableAutoConfiguration注解的exclude属性,或者spring.autoconfigure.exclude来配置
  Set<String> exclusions = getExclusions(annotationMetadata, attributes);
checkExcludedClasses(configurations, exclusions);
  configurations.removeAll(exclusions);

看是否存在某些自动配置类需要排除

筛选

继续利用ConfigurationClassFilter对自动配置类进行进一步筛选,利用AutoConfigurationMetadata进行筛选,对应的是"META-INF/spring-autoconfigure-metadata.properties"文件中的内容,是一种加快SpringBoot启动速度的机制,默认是开启了的

// 获取spring.factories中的AutoConfigurationImportFilter对AutoConfiguration进行过滤

  // 默认会拿到OnBeanCondition、OnClassCondition、OnWebApplicationCondition

  // 这三个会去判断上面的AutoConfiguration是否符合它们自身所要求的条件,不符合的会过滤掉,表示不会进行解析了

  // 会利用spring-autoconfigure-metadata.properties中的配置来进行过滤

  // spring-autoconfigure-metadata.properties文件中的内容是利用Java中的AbstractProcessor技术在编译时生成出来的

  configurations = getConfigurationClassFilter().filter(configurations);

内容格式为:自动配置类名.条件注解=条件

只会判断所需要的类是否存在,如果需要的类,或者需要的Bean对应的类,都不存在,那么肯定不符合条件了
对于像@ConditionalOnMissingBean这样的条件,在 这一步是不会去判断的,最后条件匹配成功的自动配置类就会记录下来,并最终返回给Spring容器

日志

过滤的过程中,如果日志级别等于trace级别,那么会把所有条件不匹配的自动配置类记录到日志中,如果日志框架配置了打印到控制台,那就会打印到控制台

配置debug=true,只要开启了这个配置,那么Spring在解析每一个自动配置类时,就会将是否匹配的结果进行记录

匹配结果分别记录了:

  • 哪些自动配置类的条件是匹配的

  • 哪些自动配置类的条件是不匹配的,并且具体原因也会打印出来,比如是哪个类不存在

  • 哪些自动配置类是无条件的

Spring解析具体的自动配置类上的各种条件注解的时候统计的,每解析一个条件注解,就 会把结果记录ConditionEvaluationReport对象中,当Spring容器启动完成后,会 发布一个ContextRefreshedEvent事件,而SpringBoot提供了一个 ConditionEvaluationReportLoggingListener处理这个事件,接收到这个事件后就会把统计结果进行打印。

自动配置类解析的大体流程

  1. 读取spring.factories中的所有自动配置类

  1. 看是否配置了需要排除的自动配置类,进行排除

  1. 利用spring-autoconfigure-metadata.properties文件来过滤掉一些自动配置类

  1. 判断自动配置类所有的条件注解条件全部符合才会真正去解析自动配置类上的其他内容

Group

用了group会执行group里的process方法,然后所有的这个group里的都执行完了 会执行AutoConfigurationGroup.selectImports,按照@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE) @AutoConfigureAfter()@AutoConfigureBefore()进行排序

然后返回

@AutoConfigurationPackage

当前的包路径注册为BeanDefinition,比如mybatis的扫描就可以直接使用

public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
   register(registry, new PackageImports(metadata).getPackageNames().toArray(new String[0]));
  }

@ComponentScan

@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
      @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })

TypeExcludeFilter

从Spring容器中获取TypeExcludeFilter,然后进行匹配,匹配的则不解析

for (TypeExcludeFilter delegate : getDelegates()) {
   if (delegate.match(metadataReader, metadataReaderFactory)) {
      return true;
   }
}

在spring容器扫描之前就将TypeExcludeFilter的这个放到容器中才会生效

spring.factories

org.springframework.context.ApplicationContextInitializer=\
com.zhouyu.ZhouyuApplicationContextInitializer
public class TestApplicationContextInitializer implements ApplicationContextInitializer {
 @Override
 public void initialize(ConfigurableApplicationContext applicationContext) {
  applicationContext.getBeanFactory().registerSingleton("testTypeExcludeFilter", new TestTypeExcludeFilter());
 }
}
 
@Component
public class TestTypeExcludeFilter extends TypeExcludeFilter {
 
 @Override
 public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
  return metadataReader.getAnnotationMetadata().getClassName()
    .equals("com.test.service.UserService");
 }
}

在springboot启动的new SpringApplication(primarySources) 的

setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
从"META-INF/spring.factories"中读取key为ApplicationContextInitializer类型的扩展点,并实例化出对应扩展点对象。
顾名思义,ApplicationContextInitializer是用来初始化Spring容器ApplicationContext对象的,比如可以利用ApplicationContextInitializer来 向Spring容器中添加ApplicationListener

AutoConfigurationExcludeFilter

扫描到的配置类名字如果在自动配置类名集合中,就不解析

判断是否是配置类
private boolean isConfiguration(MetadataReader metadataReader) {
  return metadataReader.getAnnotationMetadata().isAnnotated(Configuration.class.getName());
 }
 
判断是否是自动配置类
private boolean isAutoConfiguration(MetadataReader metadataReader) {
  return getAutoConfigurations().contains(metadataReader.getClassMetadata().getClassName());
 }
 
 protected List<String> getAutoConfigurations() {
  if (this.autoConfigurations == null) {
   this.autoConfigurations = SpringFactoriesLoader.loadFactoryNames(EnableAutoConfiguration.class,
     this.beanClassLoader);
  }
  return this.autoConfigurations;
 }

Spring Boot整合Tomcat底层原理

添加的starter为:spring-boot-starter-web,那么我们启动项目时,SpringBoot就会自动启动一个Tomcat。

ServletWebServerFactoryAutoConfiguration

@Configuration(proxyBeanMethods = false)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@ConditionalOnClass(ServletRequest.class)
@ConditionalOnWebApplication(type = Type.SERVLET)
@EnableConfigurationProperties(ServerProperties.class)
@Import({ ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class,
        ServletWebServerFactoryConfiguration.EmbeddedTomcat.class,
        ServletWebServerFactoryConfiguration.EmbeddedJetty.class,
        ServletWebServerFactoryConfiguration.EmbeddedUndertow.class })
public class ServletWebServerFactoryAutoConfiguration {
    // ...
}
  • ConditionalOnClass(ServletRequest.class):表示项目依赖中要有ServletRequest类(server api)

  • ConditionalOnWebApplication(type = Type.SERVLET):表示项目应用类型得是SpringMVC

spring-boot-starter-web中,其实还间接的引入了spring-web、spring-webmvc等依赖,这就使得第二个条件满足;
tomcat-embed-core这个jar包中是存在这个类的,这是因为Tomcat在自己的源码中把Servlet规范中的一些代码也包含进去。

自动配置的两个条件都符合,那么Spring就能去解析它,这个类Import进来了三个类:

  • ServletWebServerFactoryConfiguration.EmbeddedTomcat.class

  • ServletWebServerFactoryConfiguration.EmbeddedJetty.class

  • ServletWebServerFactoryConfiguration.EmbeddedUndertow.class

EmbeddedTomcat

@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({ Servlet.class, Tomcat.class, UpgradeProtocol.class })
@ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)
static class EmbeddedTomcat {

是一个配置类,所以Spring也会来解析它,项目依赖中有Tomcat的类,ConditionalOnClass条件就符合;

这个配置类的内部就是定义了一个TomcatServletWebServerFactory类型的Bean,如果没有自己定义ServletWebServerFactory类型的Bean,ConditionalOnMissingBean就符合条件,否则生效的就是自己定义的。

TomcatServletWebServerFactory

ServletWebServerFactory用来获得WebServer对象的,而WebServer拥有启动、停止、获取端口等方法,TomcatServletWebServerFactory就是用来生成Tomcat所对应的WebServer对象,具体一点就是TomcatWebServer对象,并且在生成TomcatWebServer对象时会把Tomcat给启动起来

TomcatConnectorCustomizer

@Bean
TomcatServletWebServerFactory tomcatServletWebServerFactory(
    ObjectProvider<TomcatConnectorCustomizer> connectorCustomizers,
    ObjectProvider<TomcatContextCustomizer> contextCustomizers,
    ObjectProvider<TomcatProtocolHandlerCustomizer<?>> protocolHandlerCustomizers) {
    TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory();

构造这个Bean,Spring会从Spring容器中获取到TomcatConnectorCustomizer、TomcatContextCustomizer、TomcatProtocolHandlerCustomizer这三个类型的Bean,然后把它们添加到TomcatServletWebServerFactory对象中去,这三种Bean是用来配置Tomcat的

  • TomcatConnectorCustomizer:是用来配置Tomcat中的Connector组件

  • TomcatContextCustomizer:是用来配置Tomcat中的Context组件的

  • TomcatProtocolHandlerCustomizer:是用来配置Tomcat中的ProtocolHandler组件的

可以通过定义TomcatConnectorCustomizer类型的Bean,来对Tomcat进行配置

@SpringBootApplication
public class MyApplication {

    @Bean
    public TomcatConnectorCustomizer tomcatConnectorCustomizer(){
        return new TomcatConnectorCustomizer() {
            @Override
            public void customize(Connector connector) {
                connector.setPort(8888);
            }
        };
    }

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

}

Tomcat就会绑定8888这个端口。

//遍历tomcatConnectorCustomizers ,包含之前在tomcatServletWebServerFactory里添加的
for (TomcatConnectorCustomizer customizer : this.tomcatConnectorCustomizers) {

   customizer.customize(connector);

  }

createWebServer

在SpringBoot的启动过程中,会执行ServletWebServerApplicationContext的onRefresh()方法,而这个方法会调用createWebServer()方法。

getWebServerFactory

ServletWebServerFactory factory = getWebServerFactory();

负责获取具体的ServletWebServerFactory对象,比如TomcatServletWebServerFactory对象,只能获取到一个,然后调用该对象的getWebServer方法,启动对应的Tomcat。

getWebServerFactory获取Spring容器中的ServletWebServerFactory类型的Bean对象, 如果没有获取到则抛异常,如果找到多个也抛异常,也就是在Spring容器中 只能有一个ServletWebServerFactory类型的Bean对象。

getWebServer

生成一个Tomcat对象,并且利用前面的TomcatConnectorCustomizer等等会对Tomcat对象进行配置,最后启动Tomcat。

SpringBoot并不会根据不同的WebServer设置不同的端口,只会给WebServer设置端口,而不会区分WebServer的不同实现。

customizeConnector(connector);
int port = Math.max(getPort(), 0);

  connector.setPort(port);

ServletWebServerFactoryCustomizer

在自动配置类ServletWebServerFactoryAutoConfiguration中,会定义一个ServletWebServerFactoryCustomizer类型的Bean,是 针对一个ServletWebServerFactory的自定义器,也就是 用来配置TomcatServletWebServerFactory这个Bean的,到时候ServletWebServerFactoryCustomizer就会 利用ServerProperties对象来对TomcatServletWebServerFactory对象进行设置
@Bean
public ServletWebServerFactoryCustomizer servletWebServerFactoryCustomizer(ServerProperties serverProperties,
                                                                           ObjectProvider<WebListenerRegistrar> webListenerRegistrars,
                                                                           ObjectProvider<CookieSameSiteSupplier> cookieSameSiteSuppliers) {

会接收一个ServerProperties的Bean,ServerProperties的Bean对应的就是properties文件中前缀为server的配置,我们可以利用ServerProperties对象的getPort方法获取到我们所配置的server.port的值。

@ConfigurationProperties(prefix = "server", ignoreUnknownFields = true)

public class ServerProperties {

WebServerFactoryCustomizerBeanPostProcessor

ServletWebServerFactoryAutoConfiguration这个自动配置上,Import了一个ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class,这个BeanPostProcessorsRegistrar会向Spring容器中注册一个WebServerFactoryCustomizerBeanPostProcessor类型的Bean

@Import({ ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class
   registerSyntheticBeanIfMissing(registry, "webServerFactoryCustomizerBeanPostProcessor",

是一个BeanPosrtProcessor,它专门用来处理类型为WebServerFactory的Bean对象,创建比如TomcatServletWebServerFactory这个Bean时,会对其进行处理。

  1. 从Spring容器中拿到WebServerFactoryCustomizer类型的Bean,也就是前面说的ServletWebServerFactoryCustomizer对象

  1. 然后调用ServletWebServerFactoryCustomizer对象的customize方法,把TomcatServletWebServerFactory对象传入进去

  1. customize方法中就会从ServerProperties对象获取各种配置,然后设置给TomcatServletWebServerFactory对象

当TomcatServletWebServerFactory这个Bean对象创建完成后,它里面的很多属性,比如port,就已经是所配置的值了,后续执行getWebServer方法时,就直接获取自己的属性,比如port属性,设置给Tomcat,然后再利用TomcatConnectorCustomizer进行处理,最后启动Tomcat。

总结

  • spring-boot-starter-web:会自动引入Tomcat、SpringMVC的依赖

  • ServletWebServerFactoryAutoConfiguration:自动配置类

  • ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar:用来注册WebServerFactoryCustomizerBeanPostProcessor

  • ServletWebServerFactoryConfiguration.EmbeddedTomcat:配置TomcatServletWebServerFactory

  • ServletWebServerFactoryConfiguration.EmbeddedJetty:配置JettyServletWebServerFactory

  • ServletWebServerFactoryConfiguration.EmbeddedUndertow:配置UndertowServletWebServerFactory

  • ServletWebServerFactoryCustomizer:用来配置ServletWebServerFactory

  • WebServerFactoryCustomizerBeanPostProcessor:是一个BeanPostProcessor,利用ServletWebServerFactoryCustomizer来配置ServletWebServerFactory

  • ServletWebServerApplicationContext中的onRefresh()方法:负责启动Tomcat

条件注解

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Conditional {
    
    /**
    * All {@link Condition} classes that must {@linkplain Condition#matches match}
    * in order for the component to be registered.
    */
    Class<? extends Condition>[] value();
    
}

自定义条件注解

用@Conditional注解时,需要指定一个或多个Condition的实现类

public class TestCondition implements Condition {

    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        return false;
    }

}

在matches方法中来定义条件逻辑

ConditionContext:表示条件上下文,可以通过ConditionContext获取到当前的类加载器、BeanFactory、Environment环境变量对象

AnnotatedTypeMetadata:表示当前正在进行条件判断的Bean所对应的类信息,或方法信息(比如@Bean定义的一个Bean),可以通过AnnotatedTypeMetadata获取到当前类或方法相关的信息,从而就可以拿到条件注解的信息,当然如果一个Bean上使用了多个条件注解,那么在解析过程中都可以获取到,同时也能获取Bean上定义的其他注解信息

@ConditionalOnClass的底层工作原理

@Configuration
@ConditionalOnClass(name = "com.test.Jetty")
@ConditionalOnMissingClass(value = "com.test.Tomcat")
public class TestConfiguration {


}

Spring在解析到TestConfiguration这个配置时,发现该类上用到了条件注解就会进行条件解析

for (Condition condition : conditions) {
    ConfigurationPhase requiredPhase = null;
    if (condition instanceof ConfigurationCondition) {
        requiredPhase = ((ConfigurationCondition) condition).getConfigurationPhase();
    }
    
    // 重点在这
    if ((requiredPhase == null || requiredPhase == phase) && !condition.matches(this.context, metadata)) {
        return true;
    }
}

conditions中保存了两个OnClassCondition对象,这段代码会依次调用OnClassCondition对象的matches方法进行条件匹配,一旦某一个条件不匹配就不会进行下一个条件的判断了,这里return的是true,但是这段代码所在的方法叫做shouldSkip,所以true表示忽略。

matches()

OnClassCondition类继承了FilteringSpringBootCondition,FilteringSpringBootCondition类又继承了SpringBootCondition,而SpringBootCondition实现了Condition接口,matches()方法也是在SpringBootCondition这个类中实现的

public final boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
  // 针对每个条件注解进行条件判断

  // 条件注解写在了哪个类上,或哪个方法上
  String classOrMethodName = getClassOrMethodName(metadata);
  try {
   // 条件的判断结果
   ConditionOutcome outcome = getMatchOutcome(context, metadata);

   // 如果log的日志级别为trace,那就直接记录当前条件的判断结果
   logOutcome(classOrMethodName, outcome);

   // 将判断结果记录到ConditionEvaluationReport中
   //ConditionEvaluationReportLoggingListener会在收到ContextRefreshedEvent事件后把判断结果用日志的方式打印出来
   recordEvaluation(context, classOrMethodName, outcome);
   return outcome.isMatch();
  }

getMatchOutcome

条件匹配逻辑在getMatchOutcome方法中,而SpringBootCondition类中的getMatchOutcome方法是一个抽象方法,具体的实现逻辑就在子类OnClassCondition中

  • 如果类或方法上有@ConditionalOnClass注解,则获取@ConditionalOnClass注解中的value属性,也就是要判断是否存在的类名

  • 利用ClassNameFilter.MISSING来判断这些类是否缺失,把缺失的类的类名存入missing集合

  • 如果missing不为空,则表示有类缺失,则表示不匹配,并利用ConditionMessage记录哪些类是缺失的,直接return,表示条件不匹配

  • 否则,则表示条件匹配,继续执行代码

  • 如果类或方法上有ConditionalOnMissingClass注解,则获取ConditionalOnMissingClass注解中的value属性,也就是要判断是否缺失的类名

  • 利用ClassNameFilter.PRESENT来判断这些类是否存在,把存在的类的类名存入present集合

  • 如果present不为空,则表示有类存在,则表示不匹配,并利用ConditionMessage记录哪些类是存在的,直接return,表示条件不匹配

  • 否则,则表示条件匹配,继续执行代码

  • return,表示条件匹配

@ConditionalOnBean的底层工作原理

  • 当前在解析的类或方法上,是否有@ConditionalOnBean注解,如果有则生成对应的Spec对象,该对象中包含了用户指定的,要判断的是否存在的Bean的类型

  • 调用getMatchingBeans方法进行条件判断,MatchResult为条件判断结果

  • 只要判断出来某一个Bean不存在,则return,表示条件不匹配

  • 只要所有Bean都存在,则继续执行下面代码

  • 当前在解析的类或方法上,是否有@ConditionalOnSingleCandidate注解,如果有则生成对应的SingleCandidateSpec对象,该对象中包含了用户指定的,要判断的是否存在的Bean的类型(只能指定一个类型),并且该类型的Bean只能有一个

  • 调用getMatchingBeans方法进行条件判断,MatchResult为条件判断结果

  • 指定类型的Bean如果不存在,则return,表示条件不匹配

  • 如果指定类型的Bean存在,但是存在多个,那就看是否存在主Bean(加了@primary注解的Bean),并且只能有一个主Bean,如果没有,则return,表示条件不匹配

  • 如果只有一个主Bean,则表示条件匹配,继续执行下面代码

  • 当前在解析的类或方法上,是否有@ConditionalOnMissingBean注解,如果有则生成对应的Spec对象,该对象中包含了用户指定的,要判断的是否缺失的Bean的类型

  • 调用getMatchingBeans方法进行条件判断,MatchResult为条件判断结果

  • 只要有任意一个Bean存在,则return,表示条件不匹配

  • 都存在,则表示条件匹配

  • 结束

总结

Spring在解析某个配置类,或某个Bean定义时:

  • 如果发现它们上面用到了条件注解,就会取出所有的条件的条件注解,并生成对应的条件对象,比如OnBeanCondition对象、OnClassCondition对象

  • 从而依次调用条件对象的matches方法,进行条件匹配,看是否符合条件

  • 而条件匹配逻辑中,会拿到@ConditionalOnClass和@ConditionalOnBean等条件注解的信息,比如要判断哪些类存在、哪些Bean存在

  • 然后利用ClassLaoder、BeanFactory来进行判断

  • 最后只有所有条件注解的条件都匹配,那么当前配置类或Bean定义才算符合条件

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值