Java Web 开发:SpringBoot
[TOC]
前言
学习目标
- 掌握IDEA中创建SpringBoot应用程序
- 理解SpringBoot中key=value形式配置文件
- 理解SpringBoot应用中的starter依赖功能
- 理解SpringBoot约定大于配置原理(面试拔高)
- 熟悉搭建SpringBoot应用后SpringMVC和MyBatis的使用
前置知识准备
- Spring配置类注册组件 → @Bean
- SpringMVC配置类和WebMvcConfigurer接口
- maven父工程中的dependencyManagement标签
- SpringMVC静态资源映射配置
SpringBoot介绍
Spring阶段最困扰大家的事情是什么? 配置 → 配置魔鬼
- 快速搭建一个独立的生产级别的Spring应用
- 快速引入项目相关依赖
- 开箱即用,约定大于配置,大多数应用只需要极少的Spring配置
- 内置JavaEE容器,可以以Jar包的方式启动
核心点约定大于配置
提供一些约定项(其实就是默认值),在应用程序启动过程中,向容器中注册默认组件
早餐案例
wtr对wyf ,明早帮我带一碗热干面和一杯豆浆,wyf会给他带什么早餐
wtr对wyf说,以后都帮我带早餐,如果没告诉你买什么,就默认给我热干面和豆浆 → 约定
wtr对wyf说,明早带个早餐,wyf会给他带什么早餐? 热干面和豆浆
几天后
wtr对wyf说,明早带个豆皮和面窝,wyf会给他带什么早餐?豆皮和面窝
创建一个SpringBoot应用
官网
start.spring.io选择groupId、ArtifactId、版本号、扫描包、JDK版本、项目构建方式、开发语言、引入的其他依赖来创建SpringBoot应用,点击Generate会下载一个zip压缩包,解压开就是一个SpringBoot应用,同时也是一个Maven工程

解压后会包含这样的文件,包含src目录、pom.xml文件、帮助文档、Git忽略管理配置文件、Maven相关文件

IDEA
其实需要的配置项和在官网上创建是完全一致的,只不过选择是在IDEA中选择,另外可以选择Project和Module的路径。
新建一个新的Project,其中starter service URL就是Spring官网创建SpringBoot应用的链接

选择基本的配置项
图片缺失:C:/Users/stone/AppData/Roaming/Typora/typora-user-images/1629704422525.png
选择依赖和SpringBoot应用的版本

配置Project和Module路径

选择Finish的话,就会在对应的目录创建文件夹,并且将下载下来的zip压缩包解压到指定目录下,通过在IDEA中打开对应的应用。
pom.xml文件
parent标签
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.7.7</version> <relativePath/> <!-- lookup parent from repository --></parent>父工程
所有的SpringBoot应用都有这样的一个父工程,parent标签里有一个version标签,SpringBoot应用的版本号;修改version标签里的值就是修改使用的使用SpringBoot版本
使用父工程的话,可以共享父工程里的配置 → 相同配置的解耦
父工程打包方式是pom,可以在本地仓库里找到这个文件
父工程中的标签
- dependencies → 子工程里会引用父工程里依赖,SpringBoot应用中其实没有用到这个标签
- dependencyManagement → 写的dependency标签的写法和我们前面的写法是一致的 → 提供的是依赖的版本信息,如果父工程中写了一个依赖,而子工程中也写了相同的依赖(groupid和artifactId一致)
- 如果子工程中的依赖没有写版本号,复用父工程中的版本号
- 如果子工程中的依赖写了版本号,使用自定义的这个版本号
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId></dependency>和我们之前写的dependency标签不一样的点 → 没有写版本号
但实际上有版本信息 → 父工程(的父工程)中的dependencyManagement中的来
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-dependencies</artifactId> <version>2.7.7</version></parent>当前SpringBoot有一个爷爷工程,这个爷爷工程就是专门管理依赖的版本信息的
<dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <version>2.7.7</version> </dependency> </dependencies></dependencyManagement>这是不是约定大于配置?是
在引入一个依赖 mysql-connector-java
-
不写版本号的情况下,版本信息是什么?
-
<dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId></dependency>
-

image-20221227112141555 -
<properties><mysql.version>8.0.31</mysql.version></properties><dependencyManagement><dependencies><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>${mysql.version}</version><exclusions><exclusion><groupId>com.google.protobuf</groupId><artifactId>protobuf-java</artifactId></exclusion></exclusions></dependency></dependencies></dependencyManagement>
-
-
写了版本号的情况下,版本信息是什么?
-
<dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>5.1.47</version></dependency>
-
提供默认的版本号信息给我们带来什么好处?
开发方便一些,兼容性
starter依赖
引入的依赖中,artifactid中有一个starter这样的一个词,这样的依赖就叫其starter依赖
- spring-boot-starter SpringBoot本身的依赖,所有的SpringBoot应用都有这个依赖
- spring-boot-starter-xxx SpringBoot官方提供的依赖(groupid → org.springframework.boot),提供的是SpringBoot对xxx技术的支持
- 比如spring-boot-starter-web 就是SpringBoot对web技术的支持
- 比如spring-boot-starter-tomcat就是SpringBoot对Tomcat的支持
- 比如spring-boot-starter-json 就是SpringBoot对Json的支持
- xxx-spring-boot-starter 第三方框架提供的依赖,提供的是SpringBoot对xxx技术的支持
- 比如mybatis-spring-boot-starter,SpringBoot对MyBatis技术的支持
- 比如pagehelper-spring-boot-starter
通常在SpringBoot中要使用某一项技术,只需要引入其starter依赖就可以了
为什么引入其starter依赖就可以了?
- starter依赖中关联了其他依赖,当我们引入starter依赖的时候,会将该技术所需要的其他的依赖一同引入进来
- 举个例子:使用mybatis的话,引入mybatis-spring-boot-starter,mybatis、mybatis-spring、spring-jdbc都会被引入进来
- starter依赖中通常会包含另外一个依赖autoconfigure依赖
- autoconfigure依赖能够帮我们做自动配置,自动配置里最主要的是自动注册默认的组件
启动类
SpringBoot应用最终打包为Jar包,packaging的默认值也是jar
运行jar包的命令是
java -jar frontend.jar执行的jar包的main方法
jar包里的文件里的内容
Start-Class: com.cskaoyan.frontend.FrontendApplication启动类:main方法所处的类 → 启动类的包目录就是SpringBoot应用默认的扫描包目录
整合SpringMVC
spring-boot-starter-web
整合配置类
@ComponentScan("com.cskaoyan.controller")@EnableWebMvcpublic class MvcConfiguration implements WebMvcConfigurer{
}// SpringMVC阶段我们写的配置类配置类在SpringBoot阶段是可以使用的,但是有些内容产生了变化
- 不需要写扫描包目录了 → springboot提供的默认的扫描包目录:启动类所在的包目录
- 在配置类上增加@EnableWebMvc或@Configuration
- 如果使用@EnableWebMvc意味着全面接管SpringMVC的相关配置,默认配置失效
- 如果使用@Configuration意味着做的是配置项的补充 → 建议使用
SpringBoot阶段的
//@ComponentScan() // 默认的扫描包是启动类所在的包目录//@EnableWebMvc // 全面接管SpringMVC的配置,默认配置失效 → 我们通常不全面接管@Configuration // 配置项的增量补充,存量(默认配置)是自动配置public class WebConfiguration implements WebMvcConfigurer { @Override public void addInterceptors(InterceptorRegistry registry) {
}
@Override public void addResourceHandlers(ResourceHandlerRegistry registry) {
}}静态资源处理
WebMvcConfigurer接口中的addResourceHandlers(registry)
registry.addResourceHandler(“映射范围”).addResourceLocations(“静态资源所处的位置”)
上面这种方式可以在配置类中使用
SpringBoot也给我们做了默认的配置
- mapping映射范围:/**
- location资源所处的位置:classpath:/public/、classpath:/static/、classpath:/META-INF/等
SpringBoot给我们提供了默认配置使用的是默认值;我们仍然可以使用其默认的配置,我们可以指定自定义的值
在SpringBoot的配置文件中可以提供指定的值
配置文件是properties → key=value
我们通过指定的key提供value,SpringBoot可以自动读取这些key对应的值
spring.web.resources.static-locations=file:d:/tmp/spring.mvc.static-path-pattern=/pic/**静态资源处理
- 啥都不做采用默认值
- 配置文件中按照指定的key来提供对应的值
- 也可以写配置类

Filter
前面做Javaconfig的时候是在AACDSI,在SpringBoot应用中只需要注册到容器中就生效
仍然可以使用OncePerRequestFilter,使用Filter直接将其注册为容器中的组件就会生效
有什么好处?
配置起来方便;也可以使用容器中的其他组件
Filter这么方便,HandlerInterceptor应该也挺方便的吧?
还是配置类的配置方式 → addInterceptors方法
Tomcat配置
端口号:server.port
上下文路径:server.servlet.context-path

#Tomcat配置server.servlet.context-path=/demo2server.port=8090其他配置
- 配置类
- 配置文件
- prefix为:spring.web
- prefix为:spring.mvc
- 2早期的一些SpringBoot版本:spring.resources → 它现在变了 spring.web.resources
jackson的配置也可以直接在配置文件中配置
spring.jackson.date-format=yyyy-MM-dd HH:mm:ssspring.jackson.time-zone=GMT+8小结
spring-boot-starter-web
静态资源处理配置 → 尤其关注location要写file路径
端口号配置
整合MyBatis
mybatis-spring-boot-starter
<dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>2.2.2</version></dependency><dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.47</version></dependency>应用程序中引入mybatis-spring-boot-starter这个依赖,启动程序的时候报错了
先思考一个问题:starter依赖的功能是什么?
- 引入mybatis这项技术所需要的依赖
- spring-boot-autoconfigure依赖 → 自动配置
刚刚报错的原因就是因为自动配置;自动配置MyBatis过程中需要注册一些组件,这些组件会被自动注册,其中有一个组件DataSource → datasource.set值的时候,发现你没有给他提供值
解决这个问题,提供数据源的值就行
spring.datasource.driver-class-name=com.mysql.jdbc.Driverspring.datasource.url=jdbc:mysql://localhost:3306/cskaoyan_db?useUnicode=true&characterEncoding=utf-8spring.datasource.username=rootspring.datasource.password=123456SpringBoot会根据这些默认向容器中注册DataSource组件:Hikari
如果要修改数据源的类型:spring.datasource.type
# 将数据源修改为了Druidspring.datasource.type=com.alibaba.druid.pool.DruidDataSource还需要提供:Mapper接口的包目录
@MapperScan → 写在启动类上就行
@SpringBootApplication@MapperScan("com.cskaoyan.mapper")public class Demo3MybatisApplication {}注意一个点:写Mapper包目录的时候,就写Mapper接口的包目录;写com.cskaoyan好不好,行不行 ?不行
如果要做额外的人配置:prefix为mybatis
mybatis: type-handlers-package: com.cskaoyan.typehandler configuration: cache-enabled: true lazy-loading-enabled: true约定大于配置原理
配置文件中的值的获取
我们现在自己向容器中注册一个DataSource,想把driverClassName、url、username、password这样的一些值写在配置文件中
没有从配置文件中取值
@Configurationpublic class DataSourceConfiguration1 {
String driverClassName = "com.mysql.jdbc.Driver"; String url = "jdbc:mysql://localhost:3306/cskaoyan_db?useUnicode=true&characterEncoding=utf-8"; String username = "root"; String password = "123456"; @Bean public DataSource dataSource1() { DruidDataSource dataSource = new DruidDataSource(); dataSource.setDriverClassName(driverClassName); dataSource.setUrl(url); dataSource.setUsername(username); dataSource.setPassword(password); return dataSource; }}魔法值:Magic value
@Value
@Configurationpublic class DataSourceConfiguration2 { @Value("${cskaoyan.datasource2.driver-class-name}") //SpEL String driverClassName; // = "com.mysql.jdbc.Driver"; @Value("${cskaoyan.datasource2.url}") String url; // = "jdbc:mysql://localhost:3306/cskaoyan_db?useUnicode=true&characterEncoding=utf-8"; @Value("${cskaoyan.datasource2.username}") String username; // = "root"; @Value("${cskaoyan.datasource2.password}") String password; // = "123456"; @Bean public DataSource dataSource2() { DruidDataSource dataSource = new DruidDataSource(); dataSource.setDriverClassName(driverClassName); dataSource.setUrl(url); dataSource.setUsername(username); dataSource.setPassword(password); return dataSource; }}${} 引用配置文件中的值 给@Value注解对应的成员变量赋值
注意事项:在容器中的组件中才可以使用@Value
@ConfigurationProperties
使用set方法给成员变量赋值
注意事项:在容器中的组件中才可以使用
@ConfigurationProperties仍然是从配置文件中根据key来获得对应的值 → key=@ConfigurationProperties注解的prefix属性值 + 成员变量名
@Configuration@Data@ConfigurationProperties(prefix = "cskaoyan.datasource3")public class DataSourceConfiguration3 { String driverClassName; //cskaoyan.datasource3.driver-class-name String url; String username; String password; @Bean public DataSource dataSource3() { DruidDataSource dataSource = new DruidDataSource(); dataSource.setDriverClassName(driverClassName); dataSource.setUrl(url); dataSource.setUsername(username); dataSource.setPassword(password); return dataSource; }}还可以进一步的解耦,可以提供一个(参数)组件,专门用来接收来自于配置文件中的值
@Configurationpublic class DataSourceConfiguration4 {
/*@Autowired DataSourceProperties4 dataSourceProperties4;*/
DataSourceProperties4 dataSourceProperties4; public DataSourceConfiguration4(DataSourceProperties4 dataSourceProperties4) { this.dataSourceProperties4 = dataSourceProperties4; }
@Bean public DataSource dataSource4() { DruidDataSource dataSource = new DruidDataSource(); dataSource.setDriverClassName(dataSourceProperties4.getDriverClassName()); dataSource.setUrl(dataSourceProperties4.getUrl()); dataSource.setUsername(dataSourceProperties4.getUsername()); dataSource.setPassword(dataSourceProperties4.getPassword()); return dataSource; }}@Component@ConfigurationProperties(prefix = "cskaoyan.datasource4")@Datapublic class DataSourceProperties4 { String driverClassName; //cskaoyan.datasource4.driver-class-name String url; String username; String password;}@EnableConfigurationProperties和@ConfigurationProperties
和上面最后一个案例,只有Properties组件注册过程有差别,其他的均一致
@ConfigurationProperties(prefix = "cskaoyan.datasource5")@Datapublic class DataSourceProperties5 { String driverClassName; //cskaoyan.datasource5.driver-class-name String url; String username; String password;}@Configuration@EnableConfigurationProperties(DataSourceProperties5.class)public class DataSourceConfiguration5 {
DataSourceProperties5 dataSourceProperties5; public DataSourceConfiguration5(DataSourceProperties5 dataSourceProperties5) { this.dataSourceProperties5 = dataSourceProperties5; }
@Bean public DataSource dataSource5() { DruidDataSource dataSource = new DruidDataSource(); dataSource.setDriverClassName(dataSourceProperties5.getDriverClassName()); dataSource.setUrl(dataSourceProperties5.getUrl()); dataSource.setUsername(dataSourceProperties5.getUsername()); dataSource.setPassword(dataSourceProperties5.getPassword()); return dataSource; }}@EnableConfigurationProperties注解写在配置上,其value属性写的Class数组,将Class数组对应的类注册为容器中的组件
yml配置文件
和properties一样都是key=value形式的配置文件,只不过语法上有区别
可以将前面的properties配置文件修改为yml配置文件
- 如果key是多级的,比如spring.datasource.driver-clsss-name ,这个就是3级,yml 要写 冒号、换行、空格缩进(几个空格都可以,但是同一级要对齐)
- 等于 yml 要写 冒号 空格
- 如果有相同的前缀,省略掉重复的前缀
约定大于配置说明
SpringBoot实现约定大于配置主要做的事情是帮我们注册一些默认的组件,而默认的组件是自动自动配置类来进行配置的
那么SpringBoot应用加载哪一些自动配置类呢,主要加载的使用autoconfigure依赖中的**/META-INF/spring.factories**文件里提供了自动配置类的列表
通过org.springframework.boot.autoconfigure.EnableAutoConfiguration这个key找到对应的value就是自动配置类的列表
# /META-INF/spring.factories中的文件# Auto Configureorg.springframework.boot.autoconfigure.EnableAutoConfiguration=\org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration# 截取了其中一部分最近的几个版本的SpringBoot也提供了另外的一个位置的配置类
/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfigurationorg.springframework.boot.autoconfigure.aop.AopAutoConfigurationorg.springframework.boot.autoconfigure.amqp.RabbitAutoConfigurationorg.springframework.boot.autoconfigure.batch.BatchAutoConfigurationorg.springframework.boot.autoconfigure.cache.CacheAutoConfigurationorg.springframework.boot.autoconfigure.cassandra.CassandraAutoConfigurationorg.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfigurationorg.springframework.boot.autoconfigure.context.LifecycleAutoConfiguration@EnableAutoConfiguration注解上有一个注解
@Import({AutoConfigurationImportSelector.class})- @Import注解可以直接写配置类Class → 把这个配置类注册为容器中的组件
- @Import注解写的Class实现了Selector接口 → selectImports方法 返回值String[] → 配置类的全限定类名
public interface ImportSelector { String[] selectImports(AnnotationMetadata importingClassMetadata);
@Nullable default Predicate<String> getExclusionFilter() { return null; }}public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {
public String[] selectImports(AnnotationMetadata annotationMetadata) { // 读取/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports // 读取/META-INF/spring.factories 里的 org.springframework.boot.autoconfigure.EnableAutoConfiguration这个key对应的值 }}注解
@ConditionalOnXXX → 满足XXX条件其他的注解生效
@ConditionalOnMissingXXX → 满足缺少XXX条件,其他的注解生效
// 应用程序中要引入对应的类 → 要有对应的依赖 → starter引入的@ConditionalOnClass({SqlSessionFactory.class, SqlSessionFactoryBean.class})
// 容器中DataSource类型的组件只有一个@ConditionalOnSingleCandidate(DataSource.class)自动配置类
文件
配置类信息:
autoconfigure依赖/META-INF文件夹
- spring.factories → key “xxxAutoConfiguration” 对应的值是字符串的列表,这些字符串是 自动配置类的全限定类名
- spring文件夹/文件(文件名很长) → 这个文件里面的值 字符串的列表,这些字符串是 自动配置类的全限定类名


# Auto Configureorg.springframework.boot.autoconfigure.EnableAutoConfiguration=\org.mybatis.spring.boot.autoconfigure.MybatisLanguageDriverAutoConfiguration,\org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration加载过程
首先来看启动类,启动类上包含了@SpringBootApplication注解
@SpringBootApplication@MapperScan("com.cskaoyan.mapper")public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); }}在@SpringBootApplication注解中包含了@EnableAutoConfiguration注解
@Target({ElementType.TYPE})@Retention(RetentionPolicy.RUNTIME)@Documented@Inherited@SpringBootConfiguration@EnableAutoConfiguration@ComponentScan( excludeFilters = {@Filter( type = FilterType.CUSTOM, classes = {TypeExcludeFilter.class}), @Filter( type = FilterType.CUSTOM, classes = {AutoConfigurationExcludeFilter.class})})public @interface SpringBootApplication {在@EnableAutoConfiguration中包含了@Import({AutoConfigurationImportSelector.class}),通过Selector选择器找到对应的自动配置类
@Target({ElementType.TYPE})@Retention(RetentionPolicy.RUNTIME)@Documented@Inherited@AutoConfigurationPackage@Import({AutoConfigurationImportSelector.class})public @interface EnableAutoConfiguration {在AutoConfigurationImportSelector中包含了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()); }}然后再看selectImports方法里的this.getAutoConfigurationEntry(annotationMetadata),在该方法中包含这样的一行代码,获得配置类的字符串的List
protected AutoConfigurationImportSelector.AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) { if (!this.isEnabled(annotationMetadata)) { return EMPTY_ENTRY; } else { AnnotationAttributes attributes = this.getAttributes(annotationMetadata); //这一行就是获得配置类(全限定类名)的List信息 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) { // 加载/META-INF/spring.factories文件中的自动配置类 List<String> configurations = new ArrayList(SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.getBeanClassLoader())); // 加载/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports文件中的自动配置类 ImportCandidates.load(AutoConfiguration.class, this.getBeanClassLoader()).forEach(configurations::add); Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories nor 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;}要看/META-INF/spring.factories接下来进入到loadFactoryNames这个方法中
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());}最后来看loadSpringFactories这个方法,这个方法的返回值为Map,这个Map的key为字符串,value为字符串列表List
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 Enumeration urls = classLoader.getResources("META-INF/spring.factories"); //略掉很多代码 } }}到这里大家其实可以看到最终加载的就是/META-INF/spring.factories文件
要看/META-INF/spring/xxx.imports
public static ImportCandidates load(Class<?> annotation, ClassLoader classLoader) { Assert.notNull(annotation, "'annotation' must not be null"); ClassLoader classLoaderToUse = decideClassloader(classLoader); String location = String.format("META-INF/spring/%s.imports", annotation.getName()); Enumeration<URL> urls = findUrlsInClasspath(classLoaderToUse, location); ArrayList importCandidates = new ArrayList();
while(urls.hasMoreElements()) { URL url = (URL)urls.nextElement(); importCandidates.addAll(readCandidateConfigurations(url)); }
return new ImportCandidates(importCandidates);}自动配置类
加载自动配置类,并且配置的生效是有条件的,包含@ConditionalOnXXX和@ConditionalOnMissingXXX这样的注解,满足一定的条件时生效和不满足一定的条件的时候生效
比如@ConditionalOnClass也就是包含对应的类的时候生效(也就是导包以后生效)
@ConditionalOnMissingBean当没有某个组件的时候生效,生效就会导致注册一个默认组件;如果自行注册组件,那么这个默认组件失效;其实这个就是约定大于配置
@Configuration( proxyBeanMethods = false)@ConditionalOnClass({JdbcTemplate.class, TransactionManager.class})@AutoConfigureOrder(2147483647)@EnableConfigurationProperties({DataSourceProperties.class})public class DataSourceTransactionManagerAutoConfiguration { public DataSourceTransactionManagerAutoConfiguration() { }
@Configuration( proxyBeanMethods = false ) @ConditionalOnSingleCandidate(DataSource.class) static class JdbcTransactionManagerConfiguration { JdbcTransactionManagerConfiguration() { }
@Bean @ConditionalOnMissingBean({TransactionManager.class}) DataSourceTransactionManager transactionManager(Environment environment, DataSource dataSource, ObjectProvider<TransactionManagerCustomizers> transactionManagerCustomizers) { DataSourceTransactionManager transactionManager = this.createTransactionManager(environment, dataSource); transactionManagerCustomizers.ifAvailable((customizers) -> { customizers.customize(transactionManager); }); return transactionManager; }
private DataSourceTransactionManager createTransactionManager(Environment environment, DataSource dataSource) { return (DataSourceTransactionManager)((Boolean)environment.getProperty("spring.dao.exceptiontranslation.enabled", Boolean.class, Boolean.TRUE) ? new JdbcTransactionManager(dataSource) : new DataSourceTransactionManager(dataSource)); } }}@Bean和@ConditionalOnMissingBean通常同时出现
如果你没有注册组件,那么@Bean生效,注册默认组件
如果你注册了组件,那么@Bean不会生效,就不会注册默认组件,以你注册的组件为准
配置文件配置项
都在autoconfigure依赖中的/META-INF路径下,包含了(additional-)spring-configuration-metadata.json文件,这个文件中包含了你可以做的配置项有哪些,并且这些配置的描述、默认值、值的格式等

值的格式
{ "name": "spring.web.resources.static-locations", "type": "java.lang.String[]", "description": "Locations of static resources. Defaults to classpath:[\/META-INF\/resources\/, \/resources\/, \/static\/, \/public\/].", "sourceType": "org.springframework.boot.autoconfigure.web.WebProperties$Resources", "defaultValue": [ "classpath:\/META-INF\/resources\/", "classpath:\/resources\/", "classpath:\/static\/", "classpath:\/public\/" ]}但是呢,让你写json文件,是为难你,引入一个依赖,这个依赖会帮我们新增对应的json
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-configuration-processor</artifactId> <optional>true</optional></dependency>rerun你的应用程序,重新启动一下
如果没有生成:在resources目录下新增一个文件夹 META-INF
完结撒花
彩蛋banner
文章分享
如果这篇文章对你有帮助,欢迎分享给更多人!