前言
学完Spring和SpringMVC,直接就开始学这个框架,不多BB。
传统的SSM框架的劣势是什么?其实也不是很劣势啦,只是配置有那么一内内繁琐(其实我个人觉得真的只是一点点繁琐而已),而springboot则是大大简化了这些繁琐。
Quick Start
直接用idea生成一个最最普通的maven结构,然后上 https://start.spring.io/ 获取到依赖关系,然后复制粘贴进自己的项目,稍等一会依赖就搞定了。当然你也可以使用idea自带的工具来生成,也很方便。
然后只需要最最简单的代码就可以搞定了:
1 |
|
直接运行就可以跑起来了,甚至连Tomcat都不需要自己配。
最后部署的话,只需要生成jar包即可,到目标服务器中运行这个jar包就完成啦。果然是轻松简单的很。
稍微探究一下
依赖关系
1 | <parent> |
那么如果我不想使用springboot提供的呢,那么可以直接在自己的项目的pom中直接指定(因为就近原理):
1 | <properties> |
这样就会覆盖掉自动版本裁定了。
1 | <dependency> |
仔细观察会发现上面两个都是spring-boot-starter-*,称为场景启动器,里面有web需要的东西。String boot将这些常用的功能场景都抽取了出来,只需要导入starter,那么项目中就会都存在这些依赖了。
一般官方的启动器都是以spring-boot-starter开头的,而一般第三方的都是以这个结尾。
所有的这些starter,底层依赖的都是spring-boot-starter,这个之后会详细展开讲。
自动配置
springboot之所以可以直接启动,因为它为我们配置好了一些东西,比如:
- 内置的Tomcat容器,引入了相关的依赖,并且进行了配置(之后详细看)
- 配置好了SpringMVC,也是引入了相关的依赖,并且在容器中配置了相关的组件,比如DispatcherServlet这个类等。
- 配置好了包结构,自带了包扫描功能,主程序所在的包及其子包,所有的组件都会被扫描并加入到容器中。这个可以通过
scanBasePackages来进行指定,或者可以通过@ComponentScan来指定。 - 各种配置都有默认配置,这些默认配置都是映射到某个类上面的。也就是配置文件会对应一个类,而这个类的对象会在容器中存在。
- 所有配置按需加载。只有引入了对应的场景,才会开启对应的自动配置。所有自动配置功能其实都是通过
spring-boot-autoconfigure来实现的。 - 其他种种。
主程序
@SpringBootApplication标志着这个类是总配置类。打开它的源码查看一下:
1 |
可以看到其实这个注解非常的麻烦,慢慢分析:
- 首先是
@SpringBootConfiguration,这个从名字就知道这个注解是用来注解配置类的,这个注解在深入,其实就是@Configuration这个注解。需要注意配置类也是容器中的一个组件。 @EnableAutoConfiguration开启自动配置功能,把我们以前需要配置的东西,让spring boot帮我们配置。- 其底部又包括
@AutoConfigurationPackage自动配置包,底层用的是@Import,给容器中导入一个组件。debug追踪一下可以发现,其实就是导入主配置类所在的包,所以记得要把你要导入的组件(controller、service等)放到配置类的同级或者对应的子包下。 - 还有一个
@Import({AutoConfigurationImportSelector.class}),从字面意思就可以知道,是导入哪些组件的选择器。跟踪源代码可以发现,是需要导入组件的全类名。会给容器中导入非常非常多的自动配置类,而这些自动配置类就能够给容器中导入该场景需要的所有组件,并配置好这些组件。其本质上就是从META-INF/spring.factories中获取了EnableAutoConfiguration的值。
- 其底部又包括
好了初步探究到这里就差不多了。
容器功能
springboot本质上还是一个spring容器,那我们首先来看看之前基础的spring是怎么做的。
spring的话我们需要在类路径下创建好xml,并且配置好各种bean。像这样子:
1 |
|
如果你之前的项目是spring的,然后想切换到springboot,可以直接使用一个ImportResource把这个对应的文件制定好就可以了。
导入组件
springboot则相对来说要变通很多——我们一般是在config包下,创建对应的配置类,这个类就相当于对应的配置文件,配置类中的bean对应的就是一个一个方法,返回值就是对应的bean对象,而方法名就是对象的id。
1 |
|
上面这段代码会为容器增加两个组件,一个是MyConfig类的对象(更精确的说,是被spring cglib增强过后的对象),因为配置类自身也是一个组件;还有一个是User类的对象,而且是单实例对象。
springboot2之后新增了一个proxyBeanMethods,默认是true。因为上面说了配置类本身也是一个组件,那么在获取了这个组件之后,我们可以调用其方法创建对象。这个对象仍然是从容器中获取的,而且不论多少次都是从容器中获取的,那么就一直是同一个对象。也就是这个属性的本身含义,代理bean方法,spring容器代理了这个方法,导致其一直从容器中获取相同的对象。
但是如果指定成了false之后,那么容器中的MyConfig对象就不再是被spring增强的对象了,那么调用它的方法所创建出来的User对象自然不是单实例的了。为什么会有这样子的设计,就是因为在配置类中,方法之间可能需要互相调用来创建不同的对象,如果是true的话就一直是相同的对象。如果是false就可以跳过容器中的检查,加速程序。
@Import
直接导入对应的类。会自动调用对应类的无参构造器创建出对应的对象(默认组件的名字是全限定类名),并放入到容器中。
@Conditional
当满足某些条件的时候,才会给容器中放入组件。注意一般和@Bean一起使用。
配置绑定
一般我们会在我们的配置文件中配置好对应的信息,然后通过读取这个配置文件,生成对应的java类,然后再通过这个java类来进行操作。
那么我们之前是如何做到的呢?
1 | 1. 准备好一个外部的文件,如config文件 |
但是这样子比较麻烦,springboot为我们提供了一个很好的注解,叫@ConfigurationProperties(当然你也必须将其放入到容器中才能生效),然后可以直接到对应的配置文件下面,写好对应的配置信息即可。
当然我们一般是会有一个专门的配置类进行配置的,所以实际中一般是到配置类上面去,写上@EnableConfigurationProperties(xxx.class)来将对应的类加入容器,并开启配置绑定功能。
自动配置原理入门
在主程序上有一个注解,这个注解本质上是三个注解的合集,即:
1 |
@SpringBootConfiguration
点进去非常容易,其实本质上就是一个@Configuration,也就是主程序所在的类,本质上也是一个配置类,所以你可以直接在里面写对应的bean,当然非常不推荐这么做。
@ComponentScan
显然是用来做包扫描的,并不是重点,可以先跳过。
@EnableAutoConfiguration
看名字也知道,这个注解才是真正的核心所在。它本质上是两个注解的合成,分别是@AutoConfigurationPackage和@Import(AutoConfigurationImportSelector.class)。而第一个注解本质上是@Import(AutoConfigurationPackages.Registrar.class),所以其实本质上,就是为容器新增了两个组件而已。
AutoConfigurationPackages.Registrar
那首先来看看Registrar这个静态内部类的代码:
1 | static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports { |
注意第四行,获取了metadata的包名,而我们的这个注解是标注在主程序上的,相当于把整个主程序所在的包的所有组件都进行了注册。这也解释了为什么包扫描的默认是主程序所在的包下面。
AutoConfigurationImportSelector
核心的方法在于:
1 |
|
显然是使用了getAutoConfigurationEntry()方法来获取元数据,然后根据元数据把对应的组件导入到容器中了。
1 | protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) { |
这个方法也很简单,仔细看其实就是对configurations进行了一堆的处理,然后得到最终需要的。
1 | protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) { |
跟踪下去,其本质上其实就是获取了一个META-INF下面的spring.factories文件下的东西。而这个其实就是到spring-boot-autoconfigure-2.3.4.RELEASE.jar包里面的META-INF/spring.factories下获取。
虽然这些自动配置类都写好了,但是并不是真正加入到容器中。靠什么?之前讲过的@ConditionalOnClass这类的注解。
配置文件
之所以我们需要研究怎么来写配置文件,一来是为了让我们能够更好的使用配置文件,二来也是希望能够通过自己使用配置文件,来搞清楚springboot是如何自动化进行配置的。
虽然springboot简化了操作,但是必要的配置文件还是并不可少的。springboot使用这两个配置文件:application.yml和application.properties,这个配置文件就是用来修改springboot的默认值。properties这个文件应该非常熟悉了,下面简单介绍一下yaml这种文件格式。
YAML
yaml的核心是数据为中心,所以配置就是键值对,用冒号分割,冒号后面必须加一个空格。缩进相同的代表属于同一级。
字面量
- 数字,直接写
- 字符串,直接写;如果用双引号引起来,特殊字符就是特殊字符,例如
"\n"就是换行;如果单引号引起来,那么里面是什么东西,它会原封不动给你输出出来。
对象
1 | person: |
数组
1 | animal: |
通过实际演示使用一下就行了。首先定义一个Person类:
1 | public class Person { |
然后对应的yaml文件即为:
1 | person: |
上面请特别特别注意一下最后一行,是man而不是isMan哦。因为实际是根据setter的函数名字来决定的,而idea最后设置的setter是setMan,所以是man。(如果还是有疑问,可以妙用idea的提示功能,万无一失)
最后回到类上面,声明一下即可。
1 |
注意还需要加入一个依赖:
1 | <dependency> |
然后就大功告成了。相比于@Value一个一个指定,显然这个一次性导入比较方便。
Properties
1 | =18 |
如果出现中文乱码的情况,记得把idea的file encoding有个Transparent native-to-ascii conversion,勾选上即可。这个的意思就是把你输入的所有字符转换成Unicode序列码保存,即变成了下面这个样子:
1 | =18 |
最后当Java去读取properties时,也会将自动将\uxxx的Unicode转成对应的char,不会出现乱码问题。
注入值校验
在类上面使用@Validated,则可以启用注入值校验,它自带了一些校验规则,可以直接使用。
ImportResource
由于springboot里面没有Spring的配置文件,所以需要这个注解来导入spring的配置文件并且生效。当然springboot本身是推荐使用全注解的方式来实现的。
多配置
我们可能针对不同的环境(开发、测试、生产等)或者根据不同的语言(中文、英文等)需要实现不同的配置文件,那么可以通过类似 application-{profile}.properties来实现;如果用yml的话,可以用多文档块的方式直接配置。
在使用的时候,当然需要直接指定需要激活哪个配置文件:
- 直接在配置文件中指定:spring.profiles.active=dev
- 在启动的时候在命令行中指定:java -jar …jav –spring.profiles.active=dev;
- 在虚拟机参数中加入:-Dspring.profiles.active=dev
配置文件的加载顺序
由于springboot是约定大于配置,所以默认会从下面的位置加载文件名为application.properties或者的application.yml文件:
file:./config/
file:./
classpath:/config/
classpath:/
优先级从高到低,需要注意的是,高优先级会覆盖掉低优先级的配置,且不会因为在某个位置进行了而短路掉其它的配置,利用这个特性我们就可以实现配置的互补了。
当然我们还可以通过在启动项目的时候额外指定配置文件,这样就能够和之前的配置形成互补了。
外部配置
请参考:参考官方文档
自动配置的原理
首先我们在主配置类里面写了@SpringBootApplication,而这个里面即开启了自动配置的功能。然后就能通过配置文件来获得需要开启哪些xxAutoConfiguration类。下面以HttpEncodingAutoConfiguration这个类为案例来分析下自动配置的原理。
1 |
|
首先要做的是,判断下目前这个配置类它是否生效,生效就会给容器中加东西,加入的东西都是通过配置文件来获取的。
所以我们不难发现,springboot启动的时候,它其实是加载了各种各样的自动配置类,然后这些自动配置类会替我们加载组件,如果我们需要的组件已经被添加了,那直接拿来用就可以了。自动配置类增加组件的方法就是从properties中获取属性,而这些属性恰恰是配置文件里指定的。
日志
日志框架
框架主要分成两种:抽象类和实现类,抽象类包含了JCL、SLF4j以及Jboss-logging,而实现则有Log4j、Log4j2、Logback以及Java自带的JUL
springboot选择的是SLF4j+Logback,而spring使用的则是JCL。
SLF4j的使用
只需要导入抽象类和实现类的包,下面是示例代码:
1 | import org.slf4j.Logger; |
特别的简单。这里需要注意的是,由于日志框架参差不齐,有些项目用的是log4j,有的用的jul,我们就需要对它们进行转换,思路其实也特别简单,只需要一个转化的jar包,这个jar包会让那些使用log4j等jar包的以为自己用的就是原来的日志包,但是实际上用的却是logback包。
所以如果我们要同时使用各类框架,而这些框架的实现类各不相同,那么我们只需要:把原来的框架使用的jar排除,然后用中间的包来替换就可以了。

使用
1 |
|
你会发现默认情况下,trace和debug是不会输出的,我们需要到application.properties文件中去配置,配置以logging开头。具体可以修改输出内容的格式、级别以及输出到哪里等。当然你也可以在类路径下面放上每个框架的对应配置文件,下面是springboot中各种对应的日志配置文件
| Logging System | Customization |
|---|---|
| Logback | logback-spring.xml, logback-spring.groovy, logback.xml or logback.groovy |
| Log4j2 | log4j2-spring.xml or log4j2.xml |
| JDK (Java Util Logging) | logging.properties |
如果用的是带-spring这个后缀的,则不是直接被对应的日志框架识别,而是先被springboot识别,这样就可以针对不同的profile来实现针对不同环境拥有不同的日志记录功能:比如我在开发的情况下需要事无巨细的记录下各种日志,而到了生产中只需要error和warn就可以了。
切换
每个日志框架其实都对应了自己的核心包,以及别的框架使用自己的包,只需要根据自己的需要对应的添加排除信息即可。
Web
SpringBoot创建web应用还是非常方便的,我们只需要在初始化的时候选好自己需要的模块,然后自己再加那么一内内的配置,然后就可以把重心放到业务代码上面了。
静态资源的映射
我们的一些js文件、css文件、图标等都属于静态文件,那springboot是如何加载它们的呢?利用的是WebMvcAutoConfiguration这个类。
访问jar包内容
1 | public void addResourceHandlers(ResourceHandlerRegistry registry) { |
可以看到,在/webjars下面的所有资源,它都会去classpath:/META-INF/resources/webjars目录下找资源,而我们可以通过引入相对应的jar包,里面的资源就可以直接被我们所使用了,这些jar包可以通过这个网站查找。
比如我希望我的项目中用到jQuery,那么可以直接到该网站找到它,并且引入到项目里面:
1 | <dependency> |
然后可以通过包的结构分析发现:

这个和上面的类路径下面的路径完全一致,直接拿来用就可以了,现在只需要访问:http://localhost:8080/webjars/jquery/3.5.0/jquery.js 就可以看到结果了。
访问当前项目的资源
如果要访问当前项目的任何资源,那么会去这五个路径下寻找:
- classpath:/META-INF/resources/
- classpath:/resources/
- classpath:/static/
- classpath:/public/
- /:当前项目的根路径
模板引擎
我们熟悉的jsp就是一个模板引擎,通过特定的占位符替换内容,组装出我们需要的html页面。
而在SpringBoot中,强力推荐的Thymeleaf,因为它的语法更简单,功能更强大;如果你对jsp比较熟悉,上手它还是比较容易的,这里就不多做介绍了。
SpringMVC自动配置
springboot本身自己自动配置好了springmvc,下面是一些详细信息:
- 注册了
ContentNegotiatingViewResolver和BeanNameViewResolver这两个组件。 - 解决了静态资源的位置
- 解决了静态首页的访问
- 配置了页面的图标
- 自动注册了
Converter,GenericConverter,Formatter。Converter:把String转成对应的类型Formatter:格式化内容,比如日期文本转成Date
- 等等等等
扩展使用SpringMVC
自动配置肯定是不尽如人意的,就需要我们手动来解决。我们可以创建一个类来继承WebMvcConfigurerAdapter,然后自己编写相对应的方法,就可以完成我们需要的“定制版”的springMVC了。
1 | // 扩展SpringMVC的功能 |
全面接管
我不需要springboot帮我做任何springMVC的功能,我希望我自己来,此时只需要在上面那个类上加上@EnableWebMvc,springboot配置的springmvc就会失效,此时你就需要自己来做组件了。原理就是springboot在自动配置springmvc的时候会判断下组件里有没有WebMvcConfigurationSupport这个类,有了就不启用;而@EnableWebMvc就是给容器中加入这个类(确切的说是它的子类)。
修改springboot的默认配置
虽然前面提到了我们可以全面接管springMVC,但是其实相辅相成才是最好的。此时我们就需要来修改它。
springboot在自动配置的时候,会先确认下有没有用户配置的,如果有就不自动配置了,没有才会自动配置。当然如果有些组件可以存在多份,那么springboot就会把自动配置的,和我们配置的组合起来用。
同时springboot也提供了很好的扩展性,比如xxxConfigurer帮我们进行扩展配置,而xxxCustomizer帮助我们进行定制配置。
简单的CRUD
默认访问首页,我们只需要把首页放在上面说的几个静态文件夹里面即可。PS:如果确定没问题,但是又频繁404,不妨重启下(或者maven和项目都清理下)。
国际化
我们首先需要编写好国际化的配置文件,springboot为我们自动配置了国际化资源文件,叫MessageSourceAutoConfiguration,然后使用特定的语法,就可以针对浏览器头部发送的Accept-Language信息来判断了。
针对国际化,我们有一个对象是Locale,而springboot里面有一个LocaleResolver来根据请求头的信息来使用对应的语言,所以我们只需要自己实现这个接口,并将它加入到容器中,我们就可以自己根据请求头中的内容来进行国际化内容的输出了。
数据库集成部分
Druid配置
首先是导入jdbc和Druid的jar包,然后只需要在application.properties里面写上:
1 | =root |
然后再配置一下即可使用:
1 |
|
这里特别注意一下,是spring.datasource.username而不是spring.datasource.data-username。
Mybatis
Springboot中的mybatis除了需要对应的mybatis的starter外,还需要mysql的jdbc连接的依赖,以及配置好对应的jdbc的设置。
1 | <dependency> |
以及对应的四项即可:
1 | spring.datasource.driverClassName= com.mysql.cj.jdbc.Driver |