一、Spring Boot概述
1.1 什么是Spring Boot?
不会吧??!!不会真有人不知道SpringBoot是什么吧??!!
哈哈哈,不知道SpringBoot也是很正常的,毕竟我也是今天才知道什么是Spring Boot!不用不知道,用过才知道,艾玛呀!Spring Boot可真是太香了!
学习过SSM的小伙伴都知道,这套框架真的是太特么优秀了,使用这套框架能够快速的为我们搭建一套web骨架,让我们能更加方便的进行开发(CRUD),而不用一直忙于注册各种Servlet等等,很长的一段时间里,我都在SSM的开发便捷和各种XML配置文件的编写的痛苦中来回遨游,虽然这套框架很香,但编程的思绪在业务实现和逻辑实现中来回切换,非常心累,经常完成了业务代码却忘记了web.xml某个部分的配置,后来知道我学习了Spring Boot,我终于不用再为文件配置感到烦恼了。
Spring Boot也是Spring家族的一个重要成员,是一个基于Spring开发出的一个全新的框架,它采用“约定大于配置”的思想来摆脱传统SSM项目中各种复杂的手动配置。 它的出现并不是为了替代Spring,而是和Spring框架紧密结合提升Spring开发者体验的工具,并且SpringBoot集合了许多其他常用的第三方库(例如 Jackson、JDBC)等,可以达到一个几乎开箱即用的效果。
1.2 Spring Boot出生的时代背景
前面提到,Spring框架虽然轻量级的,但是它的配置却是重量级的,早期的Spring版本专注于XML配置,开发一个程序需要各种XML配置文件,为了简便开发,在Spring 2.x版本开始引入了少量的注解,如@Component、@Service等,但当时的注解功能并不是很完善,大多只用于辅助使用。
随着生产中敏捷开发的需要以及注解功能的完善,到了Spring 4.x版本基本可以脱离XML配置文件进行开发,注解逐渐占据了开发中的主流地位。与此同时,Pivotal团队在原有Spring框架的基础上通过注解的方式进一步的简化了Spring框架,并基于Spring框架开发出了全新的Spring Boot框架,于2014年4月正式推出了Spring Boot 1.0版本,继而在2018年推出了Spring Boot 2.0版本。 Spring Boot 2.x版本在Spring Boot 1.x版本上进行了诸多功能的改进和扩展,同时进行了大量代码的重构。
1.2 SpringBoot的优点
与传统的Spring相比,Spring Boot 具有一下的优点。
1)、内嵌Tomcat、Jetty、Undertow服务器
传统的Spring应用部署时,通常会将应用打包成WAR包的形式并部署到Tomcat等服务器上,Spring Boot框架内嵌了Tomcat、Jetty和Undertow服务器,只要我们启动项目引导类,就可以自动将项目打包,并在运行时部署到服务器中。
2)、起步依赖
在Spring Boot项目构建中,我们无需再像Spring那样,在pom.xml中准备各种jar文件,我们只需要在构建项目的时候根据开发场景需求选择对应的依赖启动器即可,例如我们在Web开发的时候,只需要选择Web场景依赖启动器spring-boot-starter-web,Spring Boot便会自动帮我们导入spring-webmvc、spring-web、spring-boot-starter-tomcat等子依赖,并且我们无需再担心版本冲突这一个让人头疼的问题。
3)、自动化配置
Spring Boot充分考虑到了与Spring框架以及其他第三方库融合的场景,再提供了各种场合场景依赖启动器的基础之上,内部还默认提供了各种自动话配置类,再引入某个场景的依赖启动器的时候,这些内部默认自动化配置就会生效,我们终于不需要再自己手动进行相关的配置了!!!(我终于可以专心的CRUD了!!)
二、Spring Boot核心配置与注解
2.1 SpringBoot两种全局配置文件
全局配置文件的路径:src:main/resource目录或者类路径下的/config
注意:两种配置文件中appication.properties优先级高于application.yaml!!!
1)、application.properties配置文件
server.address=80
server.port=8443
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.config.additional-location=
spring.config.location=
spring.config.name=application
2)、application.yaml配置文件
YAML文件格式是Spring Boot支持的一种JSON超集文件格式。
相较于传统的Properties配置文件,YAML文件以数据为核心,是一种更为直观且容易被电脑识别的数据序列化格式。
application.yaml文件的工作原理和application.properties一样。
server:
port: 8081
path: /hello
2.2 配置文件属性注入
在全局配置文件application.properties中配置如下内容
person.id=6
person.name=lisi
person.hobby=[dda,ddd]
1)、使用@Value注解
@value注解是Spring框架提供的,用于读取配置文件中的属性值并注入bean对象对应的属性中,由于Spring Boot对Spring的@value注解进行了默认继承,所以Spring Boot也可以使用该注解读取和注入配置文件属性值。
public class Person {
@Value("${person.id}")
private int id;
@Value("${person.hobby}")
private List<String> hobby;
@Value("${person.name}")
private String name;
}
2)、使用@ConfigurationProperties注解
Spring Boot提供@ConfigurationPproperties注解用来快速、方便的将配置文件中的自定义属性值批量的注入到某个bean对象多个对应属性中。
@Component
@ConfigurationProperties(prefix = "person")
public class Person {
private int id;
private List<String> hobby;
private String name;
//省略set方法
}
除了以上两种属性注入的方式之外,还可以使用构造方法和@EnableConfigurationProperties注解来完成属性注入。
3)、两种注解对比
对比点 | @ConfigurationProperties | @Value |
---|---|---|
底层框架 | Spring Boor | Spring |
功能 | 批量注入配置文件中的属性 | 单个注入 |
Setter方法 | 需要 | 不需要 |
复杂类型属性注入 | 支持 | 不支持 |
松散绑定 | 支持 | 不支持 |
JSR303数据校验 | 支持 | 不支持 |
SpEL表达式 | 不支持 | 支持 |
复杂的数据类型:@ConfigurationProperties能够支持任意数据类型的属性注入,而@Value只能支持注入基本数据类型的属性
松散类型绑定:@configurationProperties注解在注入属性值的时候,支持松散绑定语法。例如Person中有一个属性firstName,在配置文件中我们可以采用如下配置方式
person.firstName=zhangsan
person.first-name=zhangsan
person.first_name=zhangsan
person.FIRST_NAME=zhangsan
JSR303数据校验
@ConfigurationProperties注解在进行配置为你教案属性注入时,支持JSR303数据校验,其主要作用就是校验注入Bean对象的属性值是否符合相关值的规则,当有一个属性值为Email类型的时候,可如下校验:
@Component
@ConfigurationProperties(prefix = "person")
@Validated //引入Spring框架支持的数据校验规则
public class Person {
@Email
private String email;
public void setEmail(String email) {
this.email = email;
}
}
2.3 Spring Boot自定义配置
1)、使用 @PropertySource加载配置文件
有时候由于实际开发场景的需求,我们需要手动配置一个配置文件,但是自定义的配置文件时Spring Boot无法识别的,所以我们需要在使用到这个配置文件的地方手动加载,用到的注解是@ @PropertySource(“classpath:xxx.properties”),示例如下
student.properties
student.sno=2020
student.name=xiaominxxx
student.age=18
Student.java
@Component
@ConfigurationProperties(prefix = "student")
@PropertySource("classpath:student.properties")
public class Student {
private String sno;
private String name;
private int age;
//省略setter方法
}
2)、使用@ImportResource加载XML配置文件
Spring Boot在4.0的基础上进行了改进,默认不在使用xml配置文件,如果需要用到xml文件进行配置,可以使用 @ImportResource进行配置。通常加载项目应到类上即可。
1、自定义一个实体类Teacher
public class Teacher {
private String name;
//省略toString和setter方法
}
2、配置bean.xml配置文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="teacher" class="com.gzgs.domain.Teacher">
<property name="name" value="zhangsansan"></property>
</bean>
</beans>
3、在项目引导类上加入 @ImportResource(“classpath:bean.xml”)
@SpringBootApplication
@ImportResource("classpath:bean.xml")
public class Demo02Application {
public static void main(String[] args) {
SpringApplication.run(Demo02Application.class, args);
}
}
3)、使用@Configuration编写自定义配置类
前面讲了如何自定义一个xml文件进行配置,但这种方式是十分少用的,在Spring Boot的开发中,“约定大于配置”的思想更推荐我们使用配置类的方式来替代xml文件配置。
声明一个配置类
@Configuration //声明该类是一个配置类
public class MyConfig {
@Bean
public Employer test(){
return new Employer();
}
}
这样这个配置类的test方法所返回的值就会自动注入到Spring容器中。
三、Spring Boot原理分析
3.1 Spring Boot怎么内嵌Tomcat?
众所周知,Spring Boot有一个很牛逼的功能,就是它内嵌了3个容器,而tomcat就是其中之一。
止不住好奇的心扒了一下Spring Boot的源码,从项目引导类出发,一系列操作之后找到了AutoConfigurationImportSelector 中的getCandidateConfigurations()方法
发现它是从springboot的jar包中的META-INF/spring.factory配置文件中加载相关配置类,于是又顺藤摸瓜找到了这个配置文件,继续打开spring.factories配置文件,找到tomcat所在的类,tomcat加载在ServletWebServerFactoryAutoConfiguration配置类中
再一系列操作之后最终找到了TomcatServletWebServerFactory类里面的getWebServer()这个关键方法
找到了!!看到了熟悉的new方法,太感动了!SpringBoot就是在这里实现了内嵌tomcat的方法,也是在这个方法中设置了tomcat的端口号以及其他的一些参数配置。
总结一下Spring Boot实现内嵌tomcat的原理,就是引入了tomcat的jar包之后,在TomcatServletWebServerFactory类里面的getWebServer()方法里new 了一个tomcat对象,并通过对象设置了它的基本参数,在启动项目引导类的时候,这个tomcat也会被随之启动!!
3.1 Spring Boot的依赖管理
使用过SpringBoot的小伙伴肯定都被Spring Boot的依赖管理深深的折服了,我们开发需要导入什么jar包,只需要根据实际的开发场景导入相应的依赖启动器即可,可是Spring Boot是怎么实现这个功能的呢?
在pom.xml中一个核心的依赖如下:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.3.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
这段依赖的意思就是将spring-boot-starter-parent作为Spring Boot项目的统一父项目依赖管理,并将项目版本统一为2.3.3.RELEASE版本,这也是Spring Boot项目的一个重要标识。
点进去spring-boot-starter-parent的源码一看,发现它也继承一个了父类spring-boot-dependencies,在进去这个父类的源码,找到了Spring Boot对项目进行依赖版本管理的关键,足足200多行的版本号控制代码,实属厉害,难怪使用Spring Boot能减少许多版本冲突的问题。
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.3.3.RELEASE</version>
</parent>
从上面可以看出,源码通过< properties >标签对一些常用的技术框架的依赖文件进行了统一的版本号管理,这些版本号都于Spring Boot的版本适配,这就是pom.xml引入依赖文件不需要指定依赖文件版本号的原因,需要注意的是,当引入的依赖文件不是由spring-boot-starter-parent所管理的时候,需要自行指定依赖文件版本号。
3.2 SpringBoot的核心注解
3.3 Spring Boot的自动配置
Spring Boot之所以能够在如此短的时间内成为一个十分火爆而且非常优秀的一个框架,就是因为它这样一个强大的功能,即自动配置,他让我们可以更加专注业务逻辑的实现而不用配置web.xml和bean.xml等配置文件。那么Spring Boot又是如何实现这个功能的呢?
这还得从项目引导类说起,因为Spring Boot项目的入口就是项目引导类的main方法,在项目引导类中,有一个重要的注解,就是 @SpringBootApplication,可以说,这个注解标记在哪个类,那么这个类就可以被认为是这个项目的项目引导类,既然它的功能这么强大,那么肯定是要看一看它的源码的了,进入这个源码一看,发现其实它是一个组合注解,如下:
@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}
)}
可以看到,Spring Boot在@SpringBootApplication中,除了几个元注解外,还有@SpringBootConfiguration、@EnableAutoConfiguration、@ComponentScan这三个重要的注解,SpringBoot进行自动化配置也是与这个三个注解息息相关。下面就来看看这三个注解。
1)、@SpringBootConfiguration
@SpringBootConfiguration注解表示Spring Boot配置类,查看它的源代码,发现如下:
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
可以看到,@SpringBootConfiguration内部有一个核心的注解就是@Configuration,该注解是Spring框架提供的,表示当前类是一个配置类,并且可以被组件扫描器扫描,由此可知,@SpringBootConfiguration其实就是Spring Boot对@Configuration进行重新封装命名而已。
2)、@EnableAutoConfiguration
@EnableAutoConfiguration表示开启自动配置功能,它也是SpringBoot最重要的一个注解,也是实现自动化配置的注解,他的核心代码如下:
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class})
可以看到它也是一个组合注解,它的核心注解有@AutoConfigurationPackage和@Import。
1.@AutoConfigurationPackage
它的核心代码如下:
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import({Registrar.class})
从上面可以看出,@AutoConfigurationPackage的功能是由@Import注解实现的,作用是向容器导入注册的所有组件,至于导入何种组件,由Registrar决定,那么同样看看Registrar的源码,如下:
static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
Registrar() {
}
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
AutoConfigurationPackages.register(registry, (String[])(new AutoConfigurationPackages.PackageImports(metadata)).getPackageNames().toArray(new String[0]));
}
Registrar类就是通过registerBeanDefinitions()这个方法来获取主程序的所在目录,也就是说,@AutoConfigurationPackage的主要作用就是获取项目主程序启动类所在的根目录,从而指定后续组件扫描器要扫描的包路径,这也就是为什么我们在写Spring Boot的项目的时候,通常要求将主程序启动类定义在最外层的根目录的位置,这样才能保证定义的类能够被组件扫描器扫描。
2.@Import({AutoConfigurationImportSelector.class})
同样也是查看它的源码,找到了 getAutoConfigurationEntry()这个方法,如下:
protected AutoConfigurationImportSelector.AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
if (!this.isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
} else {
AnnotationAttributes attributes = this.getAttributes(annotationMetadata);
//获取所有SpringBoot提供的后续自动配置类xxAutoConfiguration
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);
//筛选并过滤当前应用环境下需要的自动配置类xxAutoConfiguration
configurations = this.getConfigurationClassFilter().filter(configurations);
this.fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationImportSelector.AutoConfigurationEntry(configurations, exclusions);
}
}
这个方法的作用就是根据上下文筛选出需要启动的自动配置类xxAutoConfiguration,从而实现项目所需要的自动配置环境。
3)、@ComponentScan
@ComponentScan是组件包扫描器,用于指定包中的注解类自动装配到Spring的Bean容器中,它具体扫描的包路径有项目引导类所在的包路径决定,在扫描的过程中由前面介绍的@AutoConfigurationPackage进行解析,从而得到程序启动类所在的包的位置。
四、SpringBoot启动流程
从项目引导类的run()方法的源码可以看出,它的内部执行了两个操作,分别是SpringApplication实例的初始化和调用run启动项目。