springboot原理篇(三)

目录

三.自动配置原理

3.1 什么是 SpringBoot 自动配置

3.2 springboot是如何实现自动配置的

3.3 变更自动配置

3.3.1 自定义自动配置

3.3.2 控制SpringBoot内置自动配置类加载


三.自动配置原理

3.1 什么是 SpringBoot 自动配置

        没有 Spring Boot 的情况下,我们引入第三方依赖之后,需要手动配置,比如需要手动将引入的第三方依赖通过 xml 配置或注解的方式注入到 Ioc 容器中,并可能需要对注入到Ioc容器中的bean进行一些配置,非常麻烦。但是,在Spring Boot 中,我们直接引入一个 starter 即可。

        SpringBoot 定义了一套接口规范,这套规范规定:SpringBoot 在启动时会扫描外部引用 jar 包中的 META-INF/spring.factories 文件,将文件中配置的类型信息加载到 Spring 容器(此处涉及到 JVM 类加载机制与 Spring 的容器知识),并执行类中定义的各种操作。对于外部 jar 来说,只需要按照 SpringBoot 定义的标准,就能将自己的功能装置进 SpringBoot。

        没有 Spring Boot 的情况下,如果我们需要引入第三方依赖,需要手动配置,非常麻烦。但是,Spring Boot 中,我们直接引入一个 starter 即可。比如你想要在项目中使用 redis 的话,直接在项目中引入对应的 starter 即可。

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

引入 starter 之后,我们通过少量注解和一些简单的配置就能使用第三方组件提供的功能了。

所以说,其实自动装配可以简单的理解为:通过注解或者一些简单的配置就能在spring boot的帮助下实现某款功能。

3.2 springboot是如何实现自动配置的

首先我们先来看一些 springboot 的核心注解 @SpringBootApplication 的类:

点击 @SpringBootConfiguration 注解,发现这个注解其实就是一个配置注解,SpringBoot 把 @Configuration 注解做一个包装。 

所以说 @SpringBootApplication 是一个复合注解,大概就可以把 @SpringBootApplication 看作是 @Configuration、@EnableAutoConfiguration、@ComponentScan 注解的集合。根据 SpringBoot 官网,这三个注解的作用分别是:

  • @EnableAutoConfiguration:启用 SpringBoot 的自动配置机制。
  • @Configuration:允许在上下文中注册额外的 bean 或导入其他配置类,作用与 applicationContext.xml 的功能相同。
  • @ComponentScan: 扫描包下的类中添加了@Component (@Service,@Controller,@Repostory,@RestController)注解的类 ,并添加的到spring的容器中,可以自定义不扫描某些 bean。如下图所示,容器中将排除TypeExcludeFilter和AutoConfigurationExcludeFilter。

@EnableAutoConfiguration:实现自动装配的核心注解

EnableAutoConfiguration 只是一个简单地注解,自动装配核心功能的实现实际是通过 AutoConfigurationImportSelector 类。

我们现在重点分析下 AutoConfigurationImportSelector 类到底做了什么?

AutoConfigurationImportSelector:加载自动装配类

AutoConfigurationImportSelector 类的继承体系如下:

可以看出,AutoConfigurationImportSelector 类实现了 ImportSelector 接口,也就实现了这个接口中的 selectImports 方法,该方法主要用于获取所有符合条件的类的全限定类名,这些类需要被加载到 IoC 容器中

这里我们需要重点关注一下 getAutoConfigurationEntry方法,这个方法主要负责加载自动配置类的。

该方法调用链如下:

现在我们结合 getAutoConfigurationEntry 方法的源码来详细分析一下:

protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
    //第1步:判断自动装配开关是否打开
   if (!isEnabled(annotationMetadata)) {
      return EMPTY_ENTRY;
   }
    //第2步:用于获取注解中的exclude和excludeName。
    //获取注解属性
   AnnotationAttributes attributes = getAttributes(annotationMetadata); 
    //第3步:获取需要自动装配的所有配置类,读取META-INF/spring.factories
    //读取所有预配置类
   List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
    //第4步:符合条件加载
    //去掉重复的配置类
   configurations = removeDuplicates(configurations);
    //执行
   Set<String> exclusions = getExclusions(annotationMetadata, attributes);
    //校验
   checkExcludedClasses(configurations, exclusions);
    //删除
   configurations.removeAll(exclusions);
    //过滤
   configurations = getConfigurationClassFilter().filter(configurations);
   fireAutoConfigurationImportEvents(configurations, exclusions);
    //创建自动配置的对象
   return new AutoConfigurationEntry(configurations, exclusions);
}

第 1 步:判断自动装配开关是否打开。默认 spring.boot.enableautoconfiguration = true,可在 application.properties 或 application.yml 中设置

第 2 步用于获取 EnableAutoConfiguration注解中的 exclude 和 excludeName

第 3 步

获取需要自动装配的所有配置类,读取META-INF/spring.factories

先进入 getCandidateConfigurations() 方法中:

进入 loadFactoryNames() 方法中:

再进入 loadSpringFactories() 方法中:

从下图可以看到这个文件的配置内容都被我们读取到了。

不光是这个依赖下的 META-INF/spring.factories 被读取到,所有 Spring Boot Starter 下的 META-INF/spring.factories 都会被读取到。 

第 4 步 : 

到这里可能面试官会问你:spring.factories 中这么多配置,每次启动都要全部加载么?

很明显,这是不现实的。我们 debug 到后面你会发现,configurations 的值变小了。

因为,这一步有经历了一遍筛选过滤,@ConditionOnXXX 中的所有条件都满足,该类才会生效。

Spring Boot 提供的条件注解如下:

  • @ConditionalOnBean:当容器里有指定 Bean 的条件下
  • @ConditionalOnMissingBean:当容器里没有指定 Bean 的情况下
  • @ConditionalOnSingleCandidate:当指定 Bean 在容器中只有一个,或者虽然有多个但是指定首选 Bean
  • @ConditionalOnClass:当类路径下有指定类的条件下
  • @ConditionalOnMissingClass:当类路径下没有指定类的条件下
  • @ConditionalOnProperty:指定的属性是否有指定的值
  • @ConditionalOnResource:类路径是否有指定的值
  • @ConditionalOnExpression:基于 SpEL 表达式作为判断条件
  • @ConditionalOnJava:基于 Java 版本作为判断条件
  • @ConditionalOnJndi:在 JNDI 存在的条件下差在指定的位置
  • @ConditionalOnNotWebApplication:当前项目不是 Web 项目的条件下
  • @ConditionalOnWebApplication:当前项目是 Web 项 目的条件下 

通过一个具体技术说明自动装配原理

第一步:先找到 springboot 项目中的启动类,根据启动类上方的 @SpringBootApplication 注解一层层的找到下图中的类。在这个类中有一个常量,其常量值是spring.factories文件的所在位置,在这个文件中定义了第三方依赖中所有技术的全路径名

第二步:从spring.factories文件中随便找到一个技术,以redis为例,然后在当前springboot项目中双击Shift,在弹出的页面中搜索找到RedisAutoConfiguration类。如下图所示:

        由RedisAutoConfiguration类上面的注解可知,RedisAutoConfiguration类有一个bean加载控制的注解。也就是说,当前类要想加载成bean,必须在当前项目中导入RedisOperations这个类,也就是当前类加载成bean的触发条件,而RedisOperations这个类在我们导入的redis的依赖包中。

        在RedisAutoConfiguration类上方有一个 @EableConfigurationProperties 注解。进入@EableConfigurationProperties 注解里的RedisProperties类中,如下图所示, RedisProperties 类上方有一个 @ConfigurationProperties 注解,此注解用来将配置文件中前缀为 spring.redis 的配置值绑定到类中属性上。

        但是可以发现,RedisProperties类里很多属性已经配置了默认值。也就是说,如果 springboot 配置文件中没有配置值,则 springboot 会采用 RedisProperties 类中属性的默认值来作为redis这项技术的默认配置值。

注意:

spring.factories 功能在 SpringBoot 2.7 已经废弃,并且在 SpringBoot 3.0 移除。但机制还是类似的。

上图中的RedisProperties类为本人书写,类中实际的属性要多的多

3.3 变更自动配置

明白了springboot的自动配置原理之后,我们就可以自己来变更自动配置了。

3.3.1 自定义自动配置

随便创建一个项目,创建完一个项目后按照以下步骤完成自定义自动配置:

第一步:首先在resources目录下创建META-INF文件夹,再在META-INF文件夹下创建spring.factories文件。如下图所示:

第二步:在 spring.factories 文件中书写 CartoonCatAndMouse 类的全路径名,如下图所示:

第三步:运行启动类,查看 CartoonCatAndMouse 类的 bean 是否在容器中。

3.3.2 控制SpringBoot内置自动配置类加载

方法一:在配置文件中添加配置以排除某些技术

方法二:在启动类上方添加注解

在excludeName中添加了某项技术的全路径名之后,这项技术即可被排除掉。

在exclude中添加某项技术的.class文件,这项技术即可被排除掉。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

真滴book理喻

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值