文章目录
1. 概述
1.1 简介
Spring Boot 是由 Pivotal 团队提供用来简化 Spring 的搭建和开发过程的全新框架。随着近些年来微服务技术的流行,Spring Boot 也成了时下炙手可热的热点技术。Spring Boot 去除了大量的 xml 配置文件,简化了复杂的依赖管理,配合各种 starter 使用,基本上可以做到自动化配置。Spring 可以做的事情,现在用 Spring boot 都可以做。
从最根本上来讲,Spring Boot 就是一些库的集合,它能够被任意项目的构建系统所使用。它使用 习惯优于配置(项目中存在大量的配置,此外还内置一个习惯性的配置)的理念让你的项目快速运行起来。
Spring Boot在Spring生态中起到了中流砥柱的作用,它是Spring Framework的快速构建实现,同时又是Spring Cloud构建分布式环境的基础。
1.2 核心功能
Spring Boot的核心功能有:
- 独立运行的Spring项目:Spring Boot项目可以通过
java -jar xxx.jar命令以Jar包独立运行 - 内嵌Servlet容器:Spring Boot内嵌了Tomcat、Jetty等,无需再使用war包的形式部署项目
- 提供starter简化maven配置:Spring Boot提供了一系列的starter pom来简化Maven的依赖加载
- 自动配置Spring:Spring Boot 会根据在类路径中的 jar 包、类,为 jar 包里的类自动配置 Bean,这样会极大地减少要使用的配置
- 准生产的应用监控:Spring Boot 提供基于 http、ssh、telnet 对运行时的项目进行监控
- 无代码生成和XML配置:Spring 4.x 提倡使用 Java 配置和注解配置组合,而 Spring Boot 不需要任何 xml 配置即可实现 Spring 的所有配置
1.3 优点
- 为所有Spring开发提供一个更快更广泛的入门体验
- 无冗余代码生成和XML强制配置,遵循约定大于配置
- 集成了大量常用的第三方库的配置,提供了几乎开箱即用的能力
- 提供了一系列大型项目常用的非功能性特征,如嵌入式服务、安全性、度量等
- ……
2. Hello World构建
- 使用Spring Assistant向导逐步完成Spring Boot项目的创建
- 设定相关的信息,如包名、项目名等
- 添加需使用的依赖项,这里只简单的选择Spring Web完成Hello World的创建
- 点击next到finish即可自动创建完成,创建完成后maven会导入相关的依赖项花一点时间,结束后项目目录如下:
其中Spring Boot的启动类如下,@SpringBootApplication注解标识了该项目为一个SpringBoot项目:
@SpringBootApplication
public class HelloWorldApplication {
public static void main(String[] args) {
SpringApplication.run(HelloWorldApplication.class, args);
}
}
最后,创建一个controller来验证项目是否可以正常启动,controller的定义如下。@Controller注解用于将HelloWorldController 交给Ioc容器管理, @RequestMapping用于发送请求, @ResponseBody用于在浏览器中以json格式显示数据。
@Controller
public class HelloWorldController {
@ResponseBody
@RequestMapping("/hello")
public String hello(){
return "Hello World";
}
}
运行Spring Boot项目,在浏览器中输入localhost:8080/hello显示Hello World,表示项目部署成功。如果想要将整个应用打成jar包,需添加如下依赖:
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
2.1 @SpringBootApplication
@SpringBootApplication表示当前类是SpringBoot的主配置类,SpringBoot就应该运行这个类的main方法来启动SpringBoot应用。它的源码定义为:
@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 {
...
}
除去Java中的元注解外,其中比较重要的注解为:
@Configuration:表示当前类为一个Ioc容器的配置类@EnableAutoConfiguration:表示开启自动配置@ComponentScan:表示Spring在创建Ioc容器时要扫描的包,这里没有重新赋值,表示扫描该类所在的包和所有的子包
其中@Configuration和@ComponentScan在Spring中已经学习过,重点看一下@EnableAutoConfiguration,它的源码定义为:
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage // 自动配置包
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {
String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
Class<?>[] exclude() default {};
String[] excludeName() default {};
}
它又有@Import和@AutoConfigurationPackage两个注解标识,借助这两个注解,@EnableAutoConfiguration将所有符合自动配置条件的bean定义加载到Ioc容器中。其中最主要的是借助 EnableAutoConfigurationImportSelector,@EnableAutoConfiguration 可以帮助 SpringBoot 应用将所有符合条件的 @Configuration 配置都加载到当前 SpringBoot 创建并使用的 IoC 容器。
protected List<AutoConfigurationImportFilter> getAutoConfigurationImportFilters() {
return SpringFactoriesLoader.loadFactories(AutoConfigurationImportFilter.class, this.beanClassLoader);
}
2.2 @AutoConfigurationPackage
@AutoConfigurationPackag表示开启自动配置包,它又使用了@Import({Registrar.class})给容器中导入一个组件;导入的组件由AutoConfigurationPackages.Registrar.class;将主配置类(@SpringBootApplication标注的类)的所在包及下面所有子包里面的所有组件扫描到Spring容器。
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import({Registrar.class})
public @interface AutoConfigurationPackage {
String[] basePackages() default {};
Class<?>[] basePackageClasses() default {};
}
2.2 SpringFactoriesLoader
SpringFactoriesLoader主要功能就是从指定的配置文件META-INF/spring.factories 加载配置,spring.factories 是一个典型的 java properties 文件,配置的格式为 Key=Value 形式,只不过 Key 和 Value 都是 Java 类型的完整类名(Fully qualified name)。例如在spring-boot-autoconfigure-2.3.1.RELEASE.jar/META_INF/spring.factories中关于auto configuration的内容如下:
# Auto Configure
org.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,\
org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,\
org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration,\
org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration,\
org.springframework.boot.autoconfigure.context.LifecycleAutoConfiguration,\
org.springframework.boot.autoconfigure.context.MessageSourceAutoConfiguration,\
总之,Spring Boot会从 classpath 中搜寻所有 META-INF/spring.factories 配置文件,并将其中org.spring-framework.boot.autoconfigure.EnableAutoConfiguration对应的配置项通过反射实例化为对应的标注了@Configuration的JavaConfig形式的 IoC 容器配置类,然后汇总为一个并加载到 IoC 容器。
2.3 pom文件分析
整个项目的pom.xml中导入的依赖为
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
其中spring-boot-starter-web就是Spring Boot的启动器, 它帮助程序导入了web模块正常运行所依赖的组件。例如,spring-boot-starter-web导入的依赖如下所示:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<version>2.3.1.RELEASE</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-json</artifactId>
<version>2.3.1.RELEASE</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<version>2.3.1.RELEASE</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<scope>compile</scope>
</dependency>
</dependencies>
Spring Boot将所有的功能场景都抽取出来,做成一个个的starters(启动器),只需要在项目里面引入这些starter相关场景的所有依赖都会导入进来,要用什么功能就导入什么场景的启动器。
3. 配置文件
SpringBoot使用一个全局的配置文件,配置文件名是固定的application.xxx,其中文件的格式有properties和yaml两种;
- application.properties
- application.yml
它们都可以用来修改Spring Boot自动配置的默认值,如果在配置文件中执行了修改操作,那么Spring Boot在底层会自动配置修改后的值。
3.1 YAML
3.1.1 概述
YAML(YAML Ain’t Markup Language)是一种以数据为中心的标记语言,相比于之前的json、xml等形式的文件更适合做配置文件使用。例如,配置端口号为8081,xml文件中需写成:
<server>
<port>8081</port>
</server>
而YAML只需要写成:
server:
port: 8081
它的基本语法为:
-
k:(空格)v:表示一对键值对,不要忘记v和:之间的空格。以空格的缩进来控制层级关系(类似Python的层级控制),只要是左对齐的一类数据都是同一个层级server: port: 8080 path: /hello! 其中属性和值对大小写敏感
3.1.2 值的写法
对于字面量[普通的值(数字,字符串,布尔)]来说,可以直接写
- 字符串默认不用加上单引号或者双引号;
\"":双引号;不会转义字符串里面的特殊字符;特殊字符会作为本身想表示的意思,如name: "zhangsan \n lisi"输出zhangsan 换行 lisi
\'':单引号;会转义特殊字符,特殊字符最终只是一个普通的字符串数据,如name: ‘zhangsan \n lisi’输出zhangsan \n lisi
对于对象、Map(属性值、键值对)来说,在下一行来写对象的属性和值的关系。如:
person:
name: forlogen
age: 18
如果想写在单行内可写作:person: {name: Forlgoen,age: 18}。
对于数组(list、set)来说,用- 值表示数组中的一个元素,如:
pets:
- cat
- dog
同样可写成单行形式:pets: [cat,dog]
3.2 值的注入
首先编写实体类
@Component
@ConfigurationProperties(prefix = "person")
public class Person {
private String name;
private Integer age;
private Pet pet;
...
}
其中
@ConfigurationProperties:告诉SpringBoot将本类中的所有属性和默认配置文件(application.properties或application.yml)中相关的配置进行绑定;prefix用于指定属性的前缀名- 只有该组件是容器中的组件,才能使用
@ConfigurationProperties功能,因此还用到了@Component,将其注册为Ioc容器中的一个组件
然后在application.properties中或是application.yml中添加配置信息:
person.name=Forlogen
person.age=18
person.pet.name=coco
最后在测试类中进行单元测试:
@SpringBootTest
class HelloWorldApplicationTests {
@Autowired
Person person;
@Test
void contextLoads() {
System.out.println(person);
}
}
输出信息为:
Person{name='Forlogen', age=18, pet=Pet{name='coco'}}
另外可以编写配置文件person.properties,并添加相同的属性值,然后再Person类中使用@PropertySource(value = {"classpath:person.properties"})注解,需要使用classpath来指定自定义的配置文件;以及使用@Value完成值的注入,配置文件中对应属性值的获取使用${xxx}表达式,同样可以使用相同的功能。
@Component
@PropertySource(value = {"classpath:person.properties"})
public class Person {
@Value("${person.name}")
private String name;
@Value("${person.age}")
private Integer age;
private Pet pet;
...
}
Person{name='Forlogen', age=18, pet=null}
@Value获取值和@ConfigurationProperties获取值比较
| @ConfigurationProperties | @Value | |
|---|---|---|
| 功能 | 批量注入配置文件中的属性 | 逐个指定 |
| 松散绑定(松散语法) | 支持 | 不支持 |
| SpEL | 不支持 | 支持 |
| JSR303数据校验 | 支持 | 不支持 |
| 复杂类型封装 | 支持 | 不支持 |
总之:
- 如果只是在某个业务逻辑中需要获取一下配置文件中的某项值,使用
@Value; - 如果专门编写了一个javaBean来和配置文件进行映射,我们就直接使用
@ConfigurationProperties
3.3 @Validated
@Validated可用于类中属性值的校验。
3.4 @PropertySource
@PropertySource用于从指定的classpath下加载配置文件
3.5 @ImportResource
@ImportResource:导入Spring的配置文件,让配置文件里面的内容生效。Spring Boot里面没有Spring的配置文件,我们自己编写的配置文件,也不能自动识别。想让Spring的配置文件生效,加载进来,需将@ImportResource标注在一个配置类上
@ImportResource(locations = {"classpath:beans.xml"})
SpringBoot推荐给容器中添加组件的方式;推荐使用全注解的方式
-
配置类
@Configuration------>Spring配置文件 -
使用
@Bean给容器中添加组件public class HelloService { }@Configuration public class MyConfig { @Bean // //将方法的返回值添加到容器中;容器中这个组件默认的id就是方法名 public HelloService helloService(){ return new HelloService(); } } -
测试
@SpringBootTest class HelloWorldApplicationTests { @Autowired Person person; @Autowired ApplicationContext ac; @Test public void testHelloService(){ final boolean helloService = ac.containsBean("helloService"); System.out.println(helloService); } }输出为true,表示成功将其添加到了Ioc容器中。
3.6 配置文件占位符
3.6.1 随机数
${random.value}、${random.int}、${random.long}
${random.int(10)}、${random.int[1024,65536]}
3.6.2 占位符获取之前配置的值
如果没有可以是用:指定默认值
person.name=Forlogen
person.age=${random.int}
person.pet.name=${person.name}_coco
4. Profile
我们在编写主配置文件的时候,文件名可以是application-{profile}.properties/yml,默认使用application.properties的配置。这样做的目的是为了在不同的环境中使用不同的配置文件,例如开发环境和生成环境有很多配置项不同,使用Profile就可以很方便的进行配置的切换。
profile的激活方式有:
- 在application.properties/yml中指定
spring.profiles.active=dev - 命令行:
java -jar xxxx.jar --spring.profiles.active=dev; - 虚拟机参数:
-Dspring.profiles.active=dev
5.配置文件的加载
5.1 内部配置文件
springboot启动会扫描以下位置的application.properties或者application.yml文件作为Spring boot的默认配置文件
–file:./config/–file:./–classpath:/config/–classpath:/
优先由高到底,高优先级的配置会覆盖低优先级的配置。SpringBoot会从这四个位置全部加载主配置文件;互补配置。我们还可以通过spring.config.location来改变默认的配置文件位置。
项目打包好以后,我们可以使用命令行参数的形式,启动项目的时候来指定配置文件的新位置;指定配置文件和默认加载的这些配置文件共同起作用形成互补配置;java -jar xxx.jar --spring.config.location=xxx。
5.2 外部配置文件
SpringBoot也可以从以下位置加载配置; 优先级从高到低;高优先级的配置覆盖低优先级的配置,所有的配置会形成互补配置
- 命令行参数:所有的配置都可以在命令行上进行指定,如
java -jar spring-boot-02-config-02-0.0.1-SNAPSHOT.jar --server.port=8087 --server.context-path=/abc,多个配置用空格分开; –配置项=值 - 来自java:comp/env的JNDI属性
- Java系统属性(System.getProperties())
- 操作系统环境变量
- RandomValuePropertySource配置的random.*属性值
*由jar包外向jar包内进行寻找, *优先加载带profile
- jar包外部的application-{profile}.properties或application.yml(带spring.profile)配置文件**
- jar包内部的application-{profile}.properties或application.yml(带spring.profile)配置文件**
再来加载不带profile
- jar包外部的application.properties或application.yml(不带spring.profile)配置文件
- jar包内部的application.properties或application.yml(不带spring.profile)配置文件
- @Configuration注解类上的@PropertySource
- 通过SpringApplication.setDefaultProperties指定的默认属性
6. 自动配置原理
6.1 实现流程
自动配置原理的实现步骤:
- SpringBoot启动的时候加载主配置类,开启了自动配置功能
@EnableAutoConfiguration @EnableAutoConfiguration作用:
- 利用EnableAutoConfigurationImportSelector给容器中导入一些组件
- 查看selectImports()方法的内容
List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes):获取候选的配置SpringFactoriesLoader.loadFactoryNames()扫描所有jar包类路径下META-INF/spring.factories
把扫描到的这些文件的内容包装成properties对象,从properties中获取到EnableAutoConfiguration.class类(类名)对应的值,然后把他们添加在容器中。将 类路径下 META-INF/spring.factories 里面配置的所有EnableAutoConfiguration的值加入到了容器中;
# Auto Configure
org.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,\
org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,\
org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration,\
org.springframework.boot.autoconfigure.cloud.CloudAutoConfiguration,\
org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration,\
org.springframework.boot.autoconfigure.context.MessageSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration,\
org.springframework.boot.autoconfigure.couchbase.CouchbaseAutoConfiguration,\
org.springframework.boot.autoconfigure.dao.PersistenceExceptionTranslationAutoConfiguration,\
org.springframework.boot.autoconfigure.data.cassandra.CassandraDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.cassandra.CassandraRepositoriesAutoConfiguration,\
...
每一个这样的xxxAutoConfiguration类都是容器中的一个组件,它们均加入到ioc容器中,由Spring来进行自动配置。下面以HttpEncodingAutoConfiguration为例解释自动配置原理:
//表示这是一个配置类,以前编写的配置文件一样,也可以给容器中添加组件
@Configuration
//启动指定类的ConfigurationProperties功能;将配置文件中对应的值和HttpEncodingProperties绑定起来;
//并把HttpEncodingProperties加入到ioc容器中
@EnableConfigurationProperties(HttpEncodingProperties.class)
// Spring底层@Conditional注解,根据不同的条件,如果满足指定的条件,整个配置类里面的配置就会生效
// 判断当前应用是否是web应用,如果是,当前配置类生效
@ConditionalOnWebApplication
//判断当前项目有没有这个类CharacterEncodingFilter;SpringMVC中进行乱码解决的过滤器;
@ConditionalOnClass(CharacterEncodingFilter.class)
//判断配置文件中是否存在某个配置 spring.http.encoding.enabled;如果不存在,判断也是成立的
//即使我们配置文件中不配置pring.http.encoding.enabled=true,也是默认生效的;
@ConditionalOnProperty(prefix = "spring.http.encoding", value = "enabled", matchIfMissing = true)
public class HttpEncodingAutoConfiguration {
//它已经和SpringBoot的配置文件映射了
private final HttpEncodingProperties properties;
//只有一个有参构造器的情况下,参数的值就会从容器中拿
public HttpEncodingAutoConfiguration(HttpEncodingProperties properties) {
this.properties = properties;
}
//给容器中添加一个组件,这个组件的某些值需要从properties中获取
@Bean
//判断容器没有这个组件
@ConditionalOnMissingBean(CharacterEncodingFilter.class)
public CharacterEncodingFilter characterEncodingFilter() {
CharacterEncodingFilter filter = new OrderedCharacterEncodingFilter();
filter.setEncoding(this.properties.getCharset().name());
filter.setForceRequestEncoding(this.properties.shouldForce(Type.REQUEST));
filter.setForceResponseEncoding(this.properties.shouldForce(Type.RESPONSE));
return filter;
}
根据当前不同的条件判断,决定这个配置类是否生效。一但这个配置类生效,它就会给容器中添加各种组件。这些组件的属性是从对应的properties类中获取的,这些类里面的每一个属性又是和配置文件绑定的。
所有在配置文件中能配置的属性都是在xxxxProperties类中封装者。配置文件能配置什么就可以参照某个功能对应的这个属性类。
@ConfigurationProperties(prefix = "spring.http.encoding")
public class HttpEncodingProperties {
public static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8");
精髓:
1)SpringBoot启动会加载大量的自动配置类
2)使用时看需要的功能有没有SpringBoot默认写好的自动配置类
3)再来看这个自动配置类中到底配置了哪些组件;(只要用的组件有,就不需要重复配置)
4)给容器中自动配置类添加组件的时候,会从properties类中获取某些属性,我们就可以在配置文件中指定这些属性的值
7. @Conditional派生注解
@Conditional作用:必须是@Conditional指定的条件成立,才给容器中添加组件,配置配里面的所有内容才生效;
| @Conditional扩展注解 | 作用(判断是否满足当前指定条件) |
|---|---|
| @ConditionalOnJava | 系统的java版本是否符合要求 |
| @ConditionalOnBean | 容器中存在指定Bean |
| @ConditionalOnMissingBean | 容器中不存在指定Bean |
| @ConditionalOnExpression | 满足SpEL表达式指定 |
| @ConditionalOnClass | 系统中有指定的类 |
| @ConditionalOnMissingClass | 系统中没有指定的类 |
| @ConditionalOnSingleCandidate | 容器中只有一个指定的Bean,或者这个Bean是首选Bean |
| @ConditionalOnProperty | 系统中指定的属性是否有指定的值 |
| @ConditionalOnResource | 类路径下是否存在指定资源文件 |
| @ConditionalOnWebApplication | 当前是web环境 |
| @ConditionalOnNotWebApplication | 当前不是web环境 |
| @ConditionalOnJndi | JNDI存在指定项 |
自动配置类必须在一定的条件下才能生效。通过启用 debug=true属性;来让控制台打印自动配置报告,这样就可以很方便的知道哪些自动配置类生效;
=========================
AUTO-CONFIGURATION REPORT
=========================
Positive matches:(自动配置类启用的)
-----------------
DispatcherServletAutoConfiguration matched:
- @ConditionalOnClass found required class 'org.springframework.web.servlet.DispatcherServlet'; @ConditionalOnMissingClass did not find unwanted class (OnClassCondition)
- @ConditionalOnWebApplication (required) found StandardServletEnvironment (OnWebApplicationCondition)
Negative matches:(没有启动,没有匹配成功的自动配置类)
-----------------
ActiveMQAutoConfiguration:
Did not match:
- @ConditionalOnClass did not find required classes 'javax.jms.ConnectionFactory', 'org.apache.activemq.ActiveMQConnectionFactory' (OnClassCondition)
AopAutoConfiguration:
Did not match:
- @ConditionalOnClass did not find required classes 'org.aspectj.lang.annotation.Aspect', 'org.aspectj.lang.reflect.Advice' (OnClassCondition)