黑马SpringBoot3+Vue3(基础篇)学习记录 五:SpringBoot自动配置原理
【黑马程序员SpringBoot3+Vue3全套视频教程,springboot+vue企业级全栈开发从基础、实战到面试一套通关】 https://www.bilibili.com/video/BV14z4y1N7pg/?p=11&share_source=copy_web&vd_source=e05c15f0310aaccfeffcd5be6bc8a392
10 11 自动配置原理 自定义starter
记录
1.遵循约定大约配置的原则,在boot程序启动后,起步依赖中的一些bean对象会自动注入到ioc容器
之前从jar包中导入两个类,conuntry和province两个类都不是自动注入IoC容器的,而之前引入Mybatis的时候只要引入Mybatis的起步依赖就可以实现自动注入Bean对象
验证spring-boot-starter-web自动往IoC中注入DispatcherServlet
引入:04代码\后端代码\spring-code
如何引入模块见https://blog.csdn.net/weixin_47708672/article/details/160410082?fromshare=blogdetail&sharetype=blogdetail&sharerId=160410082&sharerefer=PC&sharesource=weixin_47708672&sharefrom=from_link
从@springbootApplication开始看,在SpringbootAutoConfigApplication(要素1)
ctrl+点击@springbootApplication注释
@ComponentScan是bean对象的扫描注解以及讲过
@SpringBootConfiguration
- 是一个组合注解:

- 组合注解中包含@Configuration–说明启动类也是一个配置类
@EnableAutoConfiguration(要素2) - 组合注解

- @Import({AutoConfigurationImportSelector.class})自动配置的重点(要素3)
- @Import导入注解导入了一个AutoConfigurationImportSelector 类,之前有提过,import注解通常用来导入配置类和importSelector实现类
- AutoConfigurationImportSelector 类实现了DeferredImportSelector接口
public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered
- DeferredImportSelector接口继承了ImportSelector接口
public interface DeferredImportSelector extends ImportSelector {
- 说明当前类AutoConfigurationImportSelector实现了ImportSelector接口,因此要重写SelectImport方法
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!this.isEnabled(annotationMetadata)) {
return NO_IMPORTS;
} else {
AutoConfigurationEntry autoConfigurationEntry = this.getAutoConfigurationEntry(annotationMetadata);
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
}
-
SelectImport方法会被SpringBoot自动调用从而得到它返回的全类名的字符串数组(要素4),从而将对应的类的bean对象注入到IoC容器中
-
特别说明: AutoConfigurationImportSelector.class (要素3) 不是直接实现ImportSelector,而是实现DeferredImportSelector,DeferredImportSelector有另外一套很复杂的逻辑,因此重写的SelectImport方法不会被执行!但是通过这个方法去理解自动配置的原理是没有问题的
-
SelectImport方法的全类名不是写死的,因此SpringBoot也会去通过读取一个配置文件去获得这些方法名
-
AutoConfigurationImportSelector.class (要素3) 源码拆解:getAutoConfigurationEntry返回的对象转换成String后可以得到全类名**(要素4)**,说明这个对象应该知道配置文件的位置
protected 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.<String>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 AutoConfigurationEntry(configurations, exclusions);
}
- 返回的是一个新对象AutoConfigurationEntry,我们要找的是配置文件的位置,从名字知道configurations和配置相关而它最开是getCandidateConfigurations返回,那么追踪这个方法得到:
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
List<String> configurations = ImportCandidates.load(AutoConfiguration.class, this.getBeanClassLoader()).getCandidates();
Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports. If you are using a custom packaging, make sure that file is correct.");
return configurations;
}
- load肯定是和加载相关,这里就先跟踪到此为止记住AutoConfiguration.class,
- 看断言Assert:No auto configuration classes found in META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports.这个就是配置文件的位置(要素5)
- 在pom中知道有个核心起步依赖spring-boot-starter,追踪
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
<version>3.1.2</version>
<scope>compile</scope>
</dependency>
- autoconfigure就是自动配置的意思,在外部库中找到autoconfigure


- 配置文件的内容是很多类的全类名

找到DispatcherServletAutoConfiguration(要素6)
继续追踪

@AutoConfigureOrder(Integer.MIN_VALUE)
@AutoConfiguration(
after = {ServletWebServerFactoryAutoConfiguration.class}
)
@ConditionalOnWebApplication(
type = Type.SERVLET
)
@ConditionalOnClass({DispatcherServlet.class})
public class DispatcherServletAutoConfiguration {
- 追踪AutoConfiguration注解–发现@configuration(如下)说明就DispatcherServletAutoConfiguration是一个自动配置类(要素6),这个类就是用于自动配置的,并且DispatcherServletAutoConfiguration上面还有一个注解**@ConditionalOnClass({DispatcherServlet.class})如果有DispatcherServlet.class,就自动注入一个Bean对象,如果环境中没有就不生效,DispatcherServlet.class存在性由之前@Import({AutoConfigurationImportSelector.class})(要素3)实现的DeferredImportSelector获取的全类名(要素4)决定
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration(
proxyBeanMethods = false
)
@AutoConfigureBefore
@AutoConfigureAfter
public @interface AutoConfiguration {
回到DispatcherServletAutoConfiguration 类内,也有一个DispatcherServletRegistrationConfiguration 配置类(要素7),有@Configuration说明也是配置类,这个类里面的方法是核心,在SpringBoot解析这个类的源码结构时发现它内部定义了 DispatcherServletConfiguration 类检查这个内部类是否有 @Configuration 注解如果有,将其作为配置类处理,解析这些类执行Bean方法,注意这里不是扫描包
@Configuration(
proxyBeanMethods = false
)
@Conditional({DispatcherServletRegistrationCondition.class})
@ConditionalOnClass({ServletRegistration.class})
@EnableConfigurationProperties({WebMvcProperties.class})
@Import({DispatcherServletConfiguration.class})
protected static class DispatcherServletRegistrationConfiguration {
@Bean(
name = {"dispatcherServletRegistration"}
)
@ConditionalOnBean(
value = {DispatcherServlet.class},
name = {"dispatcherServlet"}
)
public DispatcherServletRegistrationBean dispatcherServletRegistration(DispatcherServlet dispatcherServlet, WebMvcProperties webMvcProperties, ObjectProvider<MultipartConfigElement> multipartConfig) {
DispatcherServletRegistrationBean registration = new DispatcherServletRegistrationBean(dispatcherServlet, webMvcProperties.getServlet().getPath());
registration.setName("dispatcherServlet");
registration.setLoadOnStartup(webMvcProperties.getServlet().getLoadOnStartup());
Objects.requireNonNull(registration);
multipartConfig.ifAvailable(registration::setMultipartConfig);
return registration;
}
}
- 这个方法中new一个对象并返回让后加上了@Bean注解:,说明是在这里注入的Bean对象
- 这个例子没有特别讲读取配置文件(要素8)

核心在于它把这个类写到核心的配置文件中了,因此只要调用了起步依赖就会调用这个类的方法
只要引入了web起步依赖,就会产生dispatcherServlet类,这个类就会生效,否则不生效(条件注册)
**面试核心就是配置文件!
提供一个自动配置类,然后把类名写到配置文件里面即可
2.自动配置类四要素与配置流程:
- 启动类**(要素1)、@EnableAutoConfiguration (要素2)、整个模块的自动配置类(要素3)、全类名非实体(要素4)、imports文件(要素5)、imports导入自动配置类(要素6)、配置类实体(要素7),配置文件(要素8)**
- 配置流程:
-
① 启动类上的 @SpringBootApplication(要素1)
这是一个组合注解,其中包含 @EnableAutoConfiguration。 -
② @EnableAutoConfiguration(要素2) 通过 @Import(AutoConfigurationImportSelector.class)(要素3)得到全类名(要素4)
该 ImportSelector 会读取 META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 文件(要素5)。 -
注意,要素1-2-3-4的流程都是已经被 @SpringBootApplication写好了!
-
③ imports导入自动配置类(要素6) 被加载
例如 RedisAutoConfiguration、DataSourceAutoConfiguration 。
这些配置类内部通常会:
@EnableConfigurationProperties(XxxProperties.class) → 绑定配置文件属性
@ConditionalOnProperty / @ConditionalOnClass 等条件判断
@Bean 方法创建组件(如 RedisTemplate)
@Import(配置类)(要素7)–如果配置类不在这个类里的话 -
④ 配置类(或具体的 @Bean 方法)(要素7)读取配置文件(要素8)
通过 @ConfigurationProperties 注解的 XxxProperties Bean或者直接在 @Bean 方法中用 @Value / Environment
-
3.自动配置类实现
- 实际上要实现自动配置需要的文件只有配置类(jar包提供)、自动配置类、imports文件并将自动配置类的全类名写入imports文件中即可完成自动配置
(可以在延伸出一个配置文件,给配置类读取)
导入02资料\03_自动配置\common-pojo-2.0-SNAPSHOT.jar
mvn install:install-file -Dfile=C:\Users\Administrator\Desktop\资料\03_自动配置\common-pojo-2.0-SNAPSHOT.jar -DgroupId=cn.itcast -DartifactId=common-pojo -Dversion=2.0 -Dpackaging=jar


自定义Starter
1.通常情况起步依赖由两个工程组成
- XXXAutoConfiguration 自动配置
- XXXStarter 依赖管理,在Starter中引入AutoConfiguration
自定义starter需要实现这两个模块-文件-项目结构-±新建项目-建立两个maven archetype queckstart骨架,一个叫dmybatis-spring-boot-autoconfigure,一个叫dmybatis-spring-boot-starter



同上再建立dmybatis-spring-boot-starter
先观察Mybatis,在Maven工具页面下
在项目dmybatis-spring-boot-autoconfigure下修改pom引入,注意修改父工程,
完整xml:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.rainsweet</groupId>
<artifactId>Practice</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<groupId>org.rainsweet</groupId>
<artifactId>dmybatis-spring-boot-autoconfigure</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>dmybatis-spring-boot-autoconfigure</name>
<url>http://maven.apache.org</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<version>3.1.2</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
<version>3.1.2</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.11</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>3.0.0</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
在项目dmybatis-spring-boot-autoconfigure下新建包config,
在**项目dmybatis-spring-boot-autoconfigure下(图片有误)**新建类MyBatisAutoConfig
添加注解@AutoConfiguration
技巧:当输入的类名为红色的时候可以尝试alt+Enter自动import
因为引入过jdbc的起步依赖,所以可以认到
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
<version>3.1.2</version>
</dependency>
实现自动配置类
package org.rainsweet.config;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.mapper.MapperScannerConfigurer;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.AutoConfigurationPackages;
import org.springframework.context.annotation.Bean;
import javax.sql.DataSource;
import java.util.List;
@AutoConfiguration //表示当前类是一个自动配置类,自动注入两个Bean对象
public class MyBatisAutoConfig {
@Bean
public SqlSessionFactoryBean sqlSessionFactoryBean(DataSource dataSource){
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource(dataSource);
return sqlSessionFactoryBean;
}
@Bean
//在方法签名中声明要用的对象SpringBoot会自动注入
public MapperScannerConfigurer mapperScannerConfigurer(BeanFactory beanFactory){
MapperScannerConfigurer mapperScannerConfigurer = new MapperScannerConfigurer();
//扫描的包 启动类所在的包机器子包--依靠SPringBoot的API在Spring-boot-starter中
//返回一个由包名组成的集合
List<String> packages = AutoConfigurationPackages.get(beanFactory);
//获取第一个包名
String p = packages.get(0);
mapperScannerConfigurer.setBasePackage(p);
//扫描的注解
mapperScannerConfigurer.setAnnotationClass(Mapper.class);
return mapperScannerConfigurer;
}
}
将自动配置类写入imports文件:
查找文件名:
在main文件夹下新建目录resource/META-INF/spring,新建文件文件名要选择对应imports文件名
复制配置类全类名
在imports文件中粘贴
剩下的autoconfiguerAPP、test、gitingore可删除,因为不需要main方法和test
进入dmybatis-spring-boot-starter工程 依赖管理
引入dmybatis-spring-boot-autoconfigure依赖
<dependency>
<groupId>org.rainsweet</groupId>
<artifactId>dmybatis-spring-boot-autoconfigure</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
官方推荐将autoconfigure中的依赖同步复制到starter中一份,方便排除依赖的时候,直接在starter中排除
- xxx-spring-boot-autoconfigure决定了“当什么条件满足时,就自动配置哪些 Bean”。这也是它叫 “autoconfigure” 的原因。
- xxx-spring-boot-starter只做一件事——把 autoconfigure 包和其他必要的第三方依赖(比如数据库驱动、连接池等)打包在一起。这样用户只需要引入 starter 这一个依赖,就能得到全套功能
- Spring Boot 的自动配置是“贪心”的。一个 Starter 为了满足大多数场景,可能会引入很多传递性依赖,比如,官方默认的 spring-boot-starter-web 会自动引入 Tomcat 作为 Web 服务器。但假如你的项目要求必须使用 Jetty 服务器时,你需要告诉 Maven/Gradle:“把那个自动带进来的依赖给我排除 (exclude) 掉。” 这种操作叫 排除依赖(Dependency Exclusion)。
- 原本写:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<!-- 这里没写任何排除,说明 Tomcat 会被自动带进来 -->
</dependency>
- 排除后:
<!-- 第一步:排除 Tomcat -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<!-- 关键写法:exclusion 标签 -->
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- 第二步:手动加入 Jetty -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jetty</artifactId>
</dependency>
- 排除多个依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<!-- 排除 Tomcat -->
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
<!-- 排除 Logback -->
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- 手动加入 Jetty -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jetty</artifactId>
</dependency>
<!-- 手动加入 Log4j2 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-log4j2</artifactId>
</dependency>
整个starter项目,没有写代码,src目录下的都可以删除,只留下pom

回到springboot-mybatis项目中,替换mybatis的起步依赖
<!--mybatis的起步依赖-->
<!-- <dependency>-->
<!-- <groupId>org.mybatis.spring.boot</groupId>-->
<!-- <artifactId>mybatis-spring-boot-starter</artifactId>-->
<!-- <version>3.0.0</version>-->
<!-- </dependency>-->
<!--自定义的mybatis起步依赖-->
<dependency>
<groupId>org.rainsweet</groupId>
<artifactId>dmybatis-spring-boot-starter</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
在AutoConfiguration和starter的pom中插入
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.11.0</version>
<configuration>
<source>21</source>
<target>21</target>
</configuration>
</plugin>
</plugins>
</build>
保持编译的java版本一致的编译插件
更多推荐


所有评论(0)