type
status
date
slug
summary
tags
category
icon
password
URL

自动装配原理

自动装配简单来说就是自动去把第三方组件的bean装载到IOC容器里面,不需要开发人员再写相关的bean的配置,springboot 中在启动类上加上@SpringbootApplication就可以去实现自动装配,@SpringBootApplication是复合注解,起作用的是@EnableAutoConfiguration,自动装配主要靠3个核心技术
  1. 引入starter这个组件里面必须包含@Configuration配置类,而这个配置类里需要声明@Bean来把需要装配到IOC容器里面的bean的对象
  1. 这个配置类是放在第三方jar包里面,然后通过约定优于配置的设计理念,去把这个配置类的全路径放在classpath:/META-INF/spring.factories文件里面,这样springboot就可以知道了第三方jar包的配置类的位置,主要是用到springFactoriesLoader来完成
  1. spring拿到三方包的配置类后,再通过spring提供的importSelector这样的接口来实现对这些配置类的动态加载,从而完成自动装配这样的1个动作
 

DeferredImportSelector接口

DeferredImportSelector和ImportSelector的区别在于:
  1. 在解析ImportSelector时,所导入的配置类会被直接解析,而DeferredImportSelector导入的配置类会延迟进行解析(延迟在其他配置类都解析完之后)
  1. DeferredImportSelector支持分组,可以实现getImportGroup方法以及定义Group对象,就相当于指定了DeferredImportSelector所导入进来的配置类所属的组,比如SpringBoot就把所有自动配置类单独做了分组AutoConfigurationGroup

常用条件注解

SpringBoot中的常用条件注解有:
  1. ConditionalOnBean:是否存在某个某类或某个名字的Bean
  1. ConditionalOnMissingBean:是否缺失某个某类或某个名字的Bean
  1. ConditionalOnSingleCandidate:是否符合指定类型的Bean只有一个
  1. ConditionalOnClass:是否存在某个类
  1. ConditionalOnMissingClass:是否缺失某个类
  1. ConditionalOnExpression:指定的表达式返回的是true还是false
  1. ConditionalOnJava:判断Java版本
  1. ConditionalOnWebApplication:当前应用是不是一个Web应用
  1. ConditionalOnNotWebApplication:当前应用不是一个Web应用
  1. ConditionalOnProperty:Environment中是否存在某个属性
 
当然我们也可以利用@Conditional来自定义条件注解。
条件注解是可以写在类上和方法上的,如果某个条件注解写在了自动配置类上,那该自动配置类会不会生效就要看当前条件能不能符合,或者条件注解写在某个@Bean修饰的方法上,那这个Bean生不生效就看当前条件符不符合。
具体原理是:
  1. Spring在解析某个自动配置类时,会先检查该自动配置类上是否有条件注解,如果有,则进一步判断该条件注解所指定的条件当前能不能满足,如果满足了则继续解析该配置类,如果不满足则不进行解析了,也就是配置类所定义的Bean都得不到解析,也就是相当于没有这些Bean了。
  1. 同理,Spring在解析某个@Bean的方法时,也会先判断方法上是否有条件注解,然后进行解析,如果不满足条件,则该Bean不会生效
我们可以发现,SpringBoot的自动配置,实际上就是SpringBoot的源码中预先写好了一些配置类,预先定义好了一些Bean,我们在用SpringBoot时,这些配置类就已经在我们项目的依赖中了,而这些自动配置类或自动配置Bean到底生不生效,就看具体所指定的条件了。
 

自定义条件注解

SpringBoot中众多的条件注解,都是基于Spring中的@Conditional来实现的,所以我们先来用一下@Conditional注解。
先来看下@Conditional注解的定义:
 
 
根据定义我们在用@Conditional注解时,需要指定一个或多个Condition的实现类,所以我们先来提供一个实现类:
很明显,我们可以在matches方法中来定义条件逻辑:
  1. ConditionContext:表示条件上下文,可以通过ConditionContext获取到当前的类加载器、BeanFactory、Environment环境变量对象
  1. AnnotatedTypeMetadata:表示当前正在进行条件判断的Bean所对应的类信息,或方法信息(比如@Bean定义的一个Bean),可以通过AnnotatedTypeMetadata获取到当前类或方法相关的信息,从而就可以拿到条件注解的信息,当然如果一个Bean上使用了多个条件注解,那么在解析过程中都可以获取到,同时也能获取Bean上定义的其他注解信息
 

@ConditionalOnClass的底层工作原理

先来看一个案例:
我们在ZhouyuConfiguration这个类上使用了两个条件注解:
  1. @ConditionalOnClass(name = "com.zhouyu.Jetty"):条件是项目依赖中存在"com.zhouyu.Jetty"这个类,则表示符合条件
  1. @ConditionalOnMissingClass(value = "com.zhouyu.Tomcat"):条件是项目依赖中不存在"com.zhouyu.Tomcat"这个类,则表示符合条件
这两个注解对应的都是@Conditional(OnClassCondition.class),那在OnClassCondition类中是如何对这两个注解进行区分的呢?
Spring在解析到ZhouyuConfiguration这个配置时,发现该类上用到了条件注解就会进行条件解析,相关源码如下:
 
conditions中保存了两个OnClassCondition对象,这段代码会依次调用OnClassCondition对象的matches方法进行条件匹配,一旦某一个条件不匹配就不会进行下一个条件的判断了,这里return的是true,但是这段代码所在的方法叫做shouldSkip,所以true表示忽略。
我们继续看OnClassCondition的matches()方法的实现。
OnClassCondition类继承了FilteringSpringBootCondition,FilteringSpringBootCondition类又继承了SpringBootCondition,而SpringBootCondition实现了Condition接口,matches()方法也是在SpringBootCondition这个类中实现的:
 
所以具体的条件匹配逻辑在getMatchOutcome方法中,而SpringBootCondition类中的getMatchOutcome方法是一个抽象方法,具体的实现逻辑就在子类OnClassCondition中:
 
在getMatchOutcome方法中的逻辑为:
  1. 如果类或方法上有@ConditionalOnClass注解,则获取@ConditionalOnClass注解中的value属性,也就是要判断是否存在的类名
  1. 利用ClassNameFilter.MISSING来判断这些类是否缺失,把缺失的类的类名存入missing集合
  1. 如果missing不为空,则表示有类缺失,则表示不匹配,并利用ConditionMessage记录哪些类是缺失的,直接return,表示条件不匹配
  1. 否则,则表示条件匹配,继续执行代码
  1. 如果类或方法上有ConditionalOnMissingClass注解,则获取ConditionalOnMissingClass注解中的value属性,也就是要判断是否缺失的类名
  1. 利用ClassNameFilter.PRESENT来判断这些类是否存在,把存在的类的类名存入present集合
  1. 如果present不为空,则表示有类存在,则表示不匹配,并利用ConditionMessage记录哪些类是存在的,直接return,表示条件不匹配
  1. 否则,则表示条件匹配,继续执行代码
  1. return,表示条件匹配
 
因为ConditionalOnClass注解和ConditionalOnMissingClass注解的逻辑是比较类似的,所以在源码中都是在OnClassCondition这个类中实现的,假如一个类上即有@ConditionalOnClass,也有@ConditionalOnMissingClass,比如以下代码:
 
1. 如果@ConditionalOnClass条件匹配、@ConditionalOnMissingClass条件也匹配,那么getMatchOutcome方法会执行两次 2. 如果@ConditionalOnClass条件不匹配,那么getMatchOutcome方法会执行一次 3. 如果@ConditionalOnClass条件匹配、@ConditionalOnMissingClass条件不匹配,那么getMatchOutcome方法也只会执行一次,因为在getMatchOutcome方法处理了这种情况
 
上面提到的ClassNameFilter.MISSING和ClassNameFilter.PRESENT也比较简单,代码如下:
 
主要就是用类加载器,来判断类是否存在。
 
@ConditionalOnBean的底层工作原理
@ConditionalOnBean和@ConditionalOnClass的底层实现应该是差不多的,一个是判断Bean存不存在,一个是判断类存不存在,事实上也确实差不多。
首先@ConditionalOnBean和@ConditionalOnMissingBean对应的都是OnBeanCondition类,OnBeanCondition类也是继承了SpringBootCondition,所以SpringBootCondition类中的getMatchOutcome方法才是匹配逻辑:
逻辑流程为:
  1. 当前在解析的类或方法上,是否有@ConditionalOnBean注解,如果有则生成对应的Spec对象,该对象中包含了用户指定的,要判断的是否存在的Bean的类型
  1. 调用getMatchingBeans方法进行条件判断,MatchResult为条件判断结果
  1. 只要判断出来某一个Bean不存在,则return,表示条件不匹配
  1. 只要所有Bean都存在,则继续执行下面代码
  1. 当前在解析的类或方法上,是否有@ConditionalOnSingleCandidate注解,如果有则生成对应的SingleCandidateSpec对象,该对象中包含了用户指定的,要判断的是否存在的Bean的类型(只能指定一个类型),并且该类型的Bean只能有一个
  1. 调用getMatchingBeans方法进行条件判断,MatchResult为条件判断结果
  1. 指定类型的Bean如果不存在,则return,表示条件不匹配
  1. 如果指定类型的Bean存在,但是存在多个,那就看是否存在主Bean(加了@primary注解的Bean),并且只能有一个主Bean,如果没有,则return,表示条件不匹配
  1. 如果只有一个主Bean,则表示条件匹配,继续执行下面代码
  1. 当前在解析的类或方法上,是否有@ConditionalOnMissingBean注解,如果有则生成对应的Spec对象,该对象中包含了用户指定的,要判断的是否缺失的Bean的类型
  1. 调用getMatchingBeans方法进行条件判断,MatchResult为条件判断结果
  1. 只要有任意一个Bean存在,则return,表示条件不匹配
  1. 都存在,则表示条件匹配
  1. 结束
getMatchingBeans方法中会利用BeanFactory去获取指定类型的Bean,如果没有指定类型的Bean,则会将该类型记录在MatchResult对象的unmatchedTypes集合中,如果有该类型的Bean,则会把该Bean的beanName记录在MatchResult对象的matchedNames集合中,所以MatchResult对象中记录了,哪些类没有对应的Bean,哪些类有对应的Bean。
@ConditionalOnClass和@ConditionalOnBean,这两个条件注解的工作原理就分析到这,总结以下流程就是:
  1. Spring在解析某个配置类,或某个Bean定义时
  1. 如果发现它们上面用到了条件注解,就会取出所有的条件的条件注解,并生成对应的条件对象,比如OnBeanCondition对象、OnClassCondition对象
  1. 从而依次调用条件对象的matches方法,进行条件匹配,看是否符合条件
  1. 而条件匹配逻辑中,会拿到@ConditionalOnClass和@ConditionalOnBean等条件注解的信息,比如要判断哪些类存在、哪些Bean存在
  1. 然后利用ClassLaoder、BeanFactory来进行判断
  1. 最后只有所有条件注解的条件都匹配,那么当前配置类或Bean定义才算符合条件
 

Starter机制

那SpringBoot中的Starter和自动配置又有什么关系呢?
其实首先要明白一个Starter,就是一个Maven依赖,当我们在项目的pom.xml文件中添加某个Starter依赖时,其实就是简单的添加了很多其他的依赖,比如:
  1. spring-boot-starter-web:引入了spring-boot-starter、spring-boot-starter-json、spring-boot-starter-tomcat等和Web开发相关的依赖包
  1. spring-boot-starter-tomcat:引入了tomcat-embed-core、tomcat-embed-el、tomcat-embed-websocket等和Tomcat相关的依赖包
  1. ...
如果硬要把Starter机制和自动配置联系起来,那就是通过@ConditionalOnClass这个条件注解,因为这个条件注解的作用就是用来判断当前应用的依赖中是否存在某个类或某些类,比如:
上面代码中就用到了@ConditionalOnClass,用来判断项目中是否存在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,那就得这么写:
得把spring-boot-starter-tomcat给排除掉,再添加上spring-boot-starter-jetty的依赖,这样Tomcat的Bean就不会生效,Jetty的Bean就能生效,从而项目中用的就是Jetty。
 
Spring Boot AOP自动配置

Spring Boot Mybatis自动配置

Mybatis的自动配置类为MybatisAutoConfiguration,该类中配置了一个SqlSessionFactory和AutoConfiguredMapperScannerRegistrar。
SqlSessionFactory这个Bean是Mybatis需要配置的,AutoConfiguredMapperScannerRegistrar会注册并配置一个MapperScannerConfigurer。
 
 
 
致谢:
💡
有关Notion安装或者使用上的问题,欢迎您在底部评论区留言,一起交流~
 
 
spring循环依赖mybatis基本介绍和基本使用
Loading...