前言:
概述
什么是springboot
Spring Boot是由Pivotal团队提供的脚手架,其设计目的是用来简化新Spring应用的初始搭建以及开发过程。该框架使用了特定的方式来进行配置,从而使开发人员不再需要定义样板化的配置
Spring Boot 基于 Spring 开发,Spirng Boot 本身并不提供 Spring 框架的核心特性以及扩展功能,只是用于快速、敏捷地开发新一代基于 Spring 框架的应用程序。也就是说,它并不是用来替代 Spring 的解决方案,而是和 Spring 框架紧密结合用于提升 Spring 开发者体验的工具。Spring Boot 以约定大于配置的核心思想,默认帮我们进行了很多设置,多数 Spring Boot 应用只需要很少的 Spring 配置。同时它集成了大量常用的第三方库配置(例如 Redis、MongoDB、Jpa、RabbitMQ、Quartz 等等),Spring Boot 应用中这些第三方库几乎可以零配置的开箱即用。
springboot的优点
- 为所有Spring开发者更快的入门
- 开箱即用,提供各种默认配置来简化项目配置
- 内嵌式容器简化Web项目
- 没有冗余代码生成和XML配置的要求
什么是微服务
微服务是一种架构风格,它要求我们在开发一个应用的时候,这个应用必须构建成―系列小服务的组合;可以通过http的方式进行互通。要说微服务架构,先得说说过去我们的单体应用架构。
单体架构
所谓单体应用架构(allin one)足指,我们将一个应用中的所有服务都制装在一个应用中。
无论是ERP,CRM或是其他什么系统,你都把数据库访问,web访问,等等各个功能放到一个war包内。
- 这样做的好处是,易干开发和测试: 他十分方便部署:当需要扩底时,只需要将war复制多份,然后放到多个服务器上,再做个负载均衡就可以了.
- 单体应用架构的缺点是,哪怕我要修改一个非常小的地方,我都需要停掉整个服务,重新打包、部署这个应用war包,特别是对于一个大型应用,我们不可能吧所有内容都放在一个应用里面,我们如何维护、如何分工合作都是问题。
微服务架构
all in one的架构方式,我们把所有的功能单元放在一个应用里面。然后我们把整个应用部署到服务器上。如果负载能力不行,我们将整个应用进行水平复制,进行扩展,然后在负载均衡。
所谓微服务架构,就是打破之前all in one的架构方式,把每个功能元素独立出来。把独立出来的功能元素的动态组合,需要的功能元素才去拿来组合,需要多一些时可以整合多个功熊元素。所以微服务架构是对功能元素进行复制,而没有对整个应用进行复制。
SpringBoot快速上手
初识SpringBoot
Spring官方提供了非常方便的工具让我们快速构建应用
官网地址:
Spring Initializr:https://start.spring.io/
如果访问缓慢可以使用阿里云镜像地址:
https://start.aliyun.com/
创建项目
项目创建方式一 : 使用Spring Initializr 的 Web页面创建项目
1、打开 https://start.spring.io/
2、填写项目信息
3、点击”Generate Project“按钮生成项目;下载此项目
4、解压项目包,并用IDEA以Maven项目导入,一路下一步即可,直到项目导入完毕。
5、如果是第一次使用,可能速度会比较慢,包比较多、需要耐心等待一切就绪。
项目创建方式二:使用 IDEA 直接创建项目
1、创建一个新项目
2、选择spring initalizr , 可以看到默认就是去官网的快速构建工具那里实现
3、填写项目信息
4、选择初始化的组件(初学勾选 Web 即可)
5、填写项目路径
6、等待项目构建成功
编写一个http接口
1、在主程序的同级目录下,新建一个controller
包,一定要在同级目录下,否则识别不到
2、在包中新建一个HelloController
类
@RestController
public class HelloController {
@RequestMapping("/hello")
public String hello() {
return "Hello World";
}
}
3、编写完毕后,从主程序启动项目,浏览器发起请求,看页面返回;控制台输出了 Tomcat 访问的端口号!
简单几步,就完成了一个web接口的开发,SpringBoot就是这么简单。所以我们常用它来建立我们的微服务项目!
将项目打成jar包
点击 maven的 package
如果遇到以上错误,可以配置打包时 跳过项目运行测试用例
<!--
在工作中,很多情况下我们打包是不想执行测试用例的
可能是测试用例不完事,或是测试用例会影响数据库数据
跳过测试用例执
-->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<!--跳过项目运行测试用例-->
<skipTests>true</skipTests>
</configuration>
</plugin>
如果打包成功,则会在target目录下生成一个 jar 包
彩蛋
如何更改启动时显示的字符拼成的字母,SpringBoot呢?也就是 banner 图案;
只需一步:到项目下的 resources 目录下新建一个banner.txt
即可。
图案可以到:https://www.bootschool.net/ascii 这个网站生成,然后拷贝到文件中即可!
自动装配原理
我们之前写的HelloSpringBoot,到底是怎么运行的呢,Maven项目,我们一般从pom.xml
文件探究起;
pom.xml
项目父级依赖
其中它主要是依赖一个父项目,主要是管理项目的资源过滤及插件!
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.5.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
点进去,发现还有一个父依赖
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.2.5.RELEASE</version>
<relativePath>../../spring-boot-dependencies</relativePath>
</parent>
这里才是真正管理SpringBoot应用里面所有依赖版本的地方,SpringBoot的版本控制中心;
以后我们导入依赖默认是不需要写版本;但是如果导入的包没有在依赖中管理着就需要手动配置版本了;
启动器
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
springboot-boot-starter-xxx
:就是spring-boot的场景启动器
spring-boot-starter-web
:帮我们导入了web模块正常运行所依赖的组件;
SpringBoot将所有的功能场景都抽取出来,做成一个个的starter (启动器),只需要在项目中引入这些starter即可,所有相关的依赖都会导入进来 , 我们要用什么功能就导入什么样的场景启动器即可 ;我们未来也可以自己自定义 starter;
主启动类
//@SpringBootApplication 来标注一个主程序类
//说明这是一个Spring Boot应用
@SpringBootApplication
public class SpringBootStudyApplication {
public static void main(String[] args) {
//将springboot 应用启动
SpringApplication.run(SpringBootStudyApplication.class, args);
}
}
但是一个简单的启动类并不简单!我们来分析一下这些注解都干了什么
@SpringBootApplication
进入这个注解:可以看到上面还有很多其他注解!
@ComponentScan
扫描主启动类同级的包
@SpringBootConfiguration
@EnableAutoConfiguration
开启自动配置功能
以前我们需要自己配置的东西,而现在SpringBoot可以自动帮我们配置 ;@EnableAutoConfiguration告诉SpringBoot开启自动配置功能,这样自动配置才能生效
点进注解接续查看:
@AutoConfigurationPackage :自动配置包
- Registrar.class 作用:将主启动类的所在包及包下面所有子包里面的所有组件扫描到Spring容器 ;
@Import({AutoConfigurationImportSelector.class}) :给容器导入组件 ;
AutoConfigurationImportSelector :自动配置导入选择器,那么它会导入哪些组件的选择器呢?我们点击去这个类看源码
1.这个类中有一个这样的方法
// 获得候选的配置
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.getBeanClassLoader());
Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you are using a custom packaging, make sure that file is correct.");
return configurations;
}
protected Class<?> getSpringFactoriesLoaderFactoryClass() {
// 启动类下的所有资源被导入
return EnableAutoConfiguration.class;
}
1.1 进入loadFactoryNames()方法
public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
//这里它又调用了 loadSpringFactories 方法
String factoryTypeName = factoryType.getName();
return (List)loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
}
1.2 进入loadSpringFactories ()方法
private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
//获得classLoader , 我们返回可以看到这里得到的就是EnableAutoConfiguration标注的类本身
MultiValueMap<String, String> result = (MultiValueMap)cache.get(classLoader);
if (result != null) {
return result;
} else {
try {
//去获取一个资源 "META-INF/spring.factories"
Enumeration<URL> urls = classLoader != null ? classLoader.getResources("META-INF/spring.factories") : ClassLoader.getSystemResources("META-INF/spring.factories");
LinkedMultiValueMap result = new LinkedMultiValueMap();
//将读取到的资源遍历,封装成为一个Properties
while(urls.hasMoreElements()) {
URL url = (URL)urls.nextElement();
UrlResource resource = new UrlResource(url);
Properties properties = PropertiesLoaderUtils.loadProperties(resource);
Iterator var6 = properties.entrySet().iterator();
while(var6.hasNext()) {
Entry<?, ?> entry = (Entry)var6.next();
String factoryClassName = ((String)entry.getKey()).trim();
String[] var9 = StringUtils.commaDelimitedListToStringArray((String)entry.getValue());
int var10 = var9.length;
for(int var11 = 0; var11 < var10; ++var11) {
String factoryName = var9[var11];
result.add(factoryClassName, factoryName.trim());
}
}
}
cache.put(classLoader, result);
return result;
} catch (IOException var13) {
throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var13);
}
}
}
2.发现一个多次出现的文件:spring.factories
,全局搜索它
WebMvcAutoConfiguration
我们在上面的自动配置类随便找一个打开看看,比如 :WebMvcAutoConfiguration
这里就是关于SpringMVC的相关配置
所以,自动配置真正实现是从classpath中搜寻所有的META-INF/spring.factories
配置文件 ,并将其中对应的 org.springframework.boot.autoconfigure. 包下的配置项,通过反射实例化为对应标注了 @Configuration的JavaConfig形式的IOC容器配置类 , 然后将这些都汇总成为一个实例并加载到IOC容器中。
结论
- SpringBoot在启动的时候从类路径下的
META-INF/spring.factories
中获取EnableAutoConfiguration
指定的值 - 将这些值作为自动配置类导入容器 , 自动配置类就生效 , 帮我们进行自动配置工作;
- 整个J2EE的整体解决方案和自动配置都在
springboot-autoconfigure
的jar包中; - 它会给容器中导入非常多的自动配置类 (xxxAutoConfiguration), 就是给容器中导入这个场景需要的所有组件 , 并配置好这些组件 ;
- 有了自动配置类 , 免去了我们手动编写配置注入功能组件等的工作;
思考
spring.factories的配置类那么多,为什么只生效了部分?
需要导入start才能生效
核心注解:@ConditionalOnClass
括号内的条件满足才会生效@ConditionalOnClass
相关注解:
SpringApplication
我最初以为就是运行了一个main方法,没想到却开启了一个服务;
@SpringBootApplication
public class SpringbootApplication {
public static void main(String[] args) {
//启动了一个服务
SpringApplication.run(SpringbootApplication.class, args);
}
}
分析该方法主要分两部分,
一部分是SpringApplication的实例化,二是run方法的执行;
这个类主要做了以下四件事情:
1、推断应用的类型是普通的项目还是Web项目
2、查找并加载所有可用初始化器 , 设置到initializers属性中
3、找出所有的应用程序监听器,设置到listeners属性中
4、推断并设置main方法的定义类,找到运行的主类
流程分析
总结
springboot是通过main方法下的SpringApplication.run方法启动的,启动的时候他会调用refshContext方法,先刷新容器,然后根据解析注解或者解析配置文件的形式注册bean,而它是通过启动类的SpringBootApplication注解进行开始解析的,他会根据EnableAutoConfiguration开启自动化配置,里面有个核心方法ImportSelect选择性的导入,根据loadFanctoryNames根据classpash路径以MATA-INF/spring.factorces下面以什么什么EnableAutoConfiguration开头的key去加载里面所有对应的自动化配置,他并不是把这一百二十多个自动化配置全部导入,在他每个自动化配置里面都有条件判断注解,先判断是否引入相互的jar包,再判断容器是否有bean再进行注入到bean容器
yml配置文件详解
SpringBoot使用一个全局的配置文件,配置文件名称是固定的- application.properties
- 语法结构: key=value application.yml
- 语法结构: key:空格value
配置文件的作用︰修改SpringBoot自动配置的默认值,因为SpringBoot在底层都给我们自动配置好了;
application.properties写法
# 应用名称
spring.application.name=springBootStudy
# 应用服务 WEB 访问端口
server.port=8080
application.yml写法
# 对空格要求十分高 格式 key:空格 value
# 应用名称
spring:
application:
name: springBootStudy
# 应用服务 WEB 访问端口
server:
port: 8080
# 行内写法
student: {name: 秦疆,age: 3}
# 数组
pets: [cat,dog,pig]
属性赋值
yml的强大之处是可以给属性赋值
以下是原来给属性赋值的方式:
@Data
@NoArgsConstructor
@AllArgsConstructor
@Component //被spring管理
public class Dog {
//通过@Value 为属性赋值
@Value("周去")
private String name;
@Value("3")
private int age;
}
当我们使用yml之后可以给属性这样赋值:
方式一
使用@ConfigurationProperties
- 将配置文件中配置的每一个属性的值,映射到这个组件中;
- 告诉SpringBoot将本类中的所有属性和配置文件中相关的配置进行绑定
- 参数 prefix = "person”:将配置文件中的person下面的所有属性——对应
- 只有这个组件是容器中的组件,才能使用容器提供的@configurationProperties功能
@Data
@NoArgsConstructor
@AllArgsConstructor
@Component//spring管理
@ConfigurationProperties(prefix = "person") //此注解将yml配置文件的值赋值给属性
public class Person {
private String name;
private int age;
private Boolean happy;
private Date date;
private Map<String,Object> map;
private List<Object> list;
private Dog dog;
}
yml配置文件如下:
# 属性赋值
Person:
name: 陈平安
age: 3
happy: true
date: 2011/11/02
map: {k1: v1,K2: v2}
list:
- java
- python
- js
dog:
name: 剑来
age: 1000
使用@ConfigurationProperties
注解会爆红,可根据官方提示在pom.xml配置文件中添加如下依赖:
<dependency>
<groupId> org.springframework.boot </ groupId>
<artifactId> spring-boot-configuration-processor </ artifactId>
<optional> true </ optional>
</dependency>
重启IDEA即可,当然不添加此依赖不会影响程序正常运行
方式二
可以自定义properties
配置文件为属性赋值
# zhouqu.properties
Dog.name=周去
Dog.age=3
@Data
@NoArgsConstructor
@AllArgsConstructor
@Component //被spring管理
//绑定配置文件
@PropertySource(value = "classpath:zhouqu.properties")
public class Dog {
@Value("${Dog.name}")
private String name;
@Value("${Dog.age}")
private int age;
}
需要在IDEA settings中设置properties
UTF-8编码以防出现中文乱码
两种方式对比
@ConfigurationProperties | @properties | |
功能 | 批量注入配置文件中的属性 | 使用Value一个个指定 |
松散绑定(松散语法) | 支持 | 不支持 |
SpEL | 部支持 | 支持 |
JSR303数据校验 | 支持 | 不支持 |
复杂类型封装 | 支持 | 不支持 |
这里我们看到一个松散绑定,这个是什么意思呢
比如我的yml中写的last-name
,这个和lastName
是一样的, –
后面跟着的字母默认是大写的。这就是松散绑定
跟与mybatis中的驼峰命名规则映射有点相似
配置文件占位符
配置文件还可以编写占位符生成随机数
person:
name: qinjiang${random.uuid} # 随机uuid
age: ${random.int} # 随机int
happy: false
birth: 2000/01/01
maps: {k1: v1,k2: v2}
lists:
- code
- girl
- music
dog:
name: ${person.hello:other}_旺财
age: 1
配置文件可以配什么?
前情回顾:
SpringBoot自动装配会在启动的时候从类路径下的META-INF/spring.factories
中获取EnableAutoConfiguration
指定的值
我们用HttpEncodingAutoConfiguration
来举例说明
// 标注自动装配类
@Configuration(
proxyBeanMethods = false
)
//自动装配的属性 ServerProperties.class
@EnableConfigurationProperties({ServerProperties.class})
//spring底层注解 根据不同的条件 来判断当前配置类是否生效
@ConditionalOnWebApplication(
type = ConditionalOnWebApplication.Type.SERVLET
)
@ConditionalOnClass({CharacterEncodingFilter.class})
@ConditionalOnProperty(
prefix = "server.servlet.encoding",
value = {"enabled"},
matchIfMissing = true
)
public class HttpEncodingAutoConfiguration {
private final Encoding properties;
public HttpEncodingAutoConfiguration(ServerProperties properties) {
this.properties = properties.getServlet().getEncoding();
}
自动配置类可以配置哪些内容,我们完全可以找到@EnableConfigurationProperties({ServerProperties.class})
注解后标注的class
点击ServerProperties.class
在application.yml
配置文件中,使用server能点出来的内容都可以在ServerProperties.class
中找到!
结论
在其中我们发现了一些规律,所有能点出来的配置都会有一个XXXXProperties.class
这个类又会和XXXXAutoConfiguration
自动配置类绑定(默认值) 我们可以通过改变XXXXProperties.class
来实现自定义配置
由此我们可以得到自动配置原理:
- SpringBoot启动会加载大量的自动配置类
- 我们看我们需要的功能有没有在SpringBoot默认写好的自动配置类当中;
- 我们再来看这个自动配置类中到底配置了哪些组件;(只要我们要用的组件存在在其中,我们就不需要再手动配置了)
- 给容器中自动配置类添加组件的时候,会从properties类中获取某些属性。我们只需要在配置文件中指定这些属性的值即可;
xxxAutoConfigurartion:自动配置类;给容器中添加组件
xxxxProperties:封装配置文件中相关属性;
如何知道哪些自动配置类是否生效?
# 在application.yml中配置
# 可以在日志中查看到哪些自动配置类是否生效
debug: true
JSR303数据校验
spring-boot中可以用@validated
来校验数据,如果数据异常则会统一抛出异常,方便异常中心统—处理。我们这里来写个注解让我们的name
只能支持Email
格式
@Data
@NoArgsConstructor
@AllArgsConstructor
@Component //被spring管理
@PropertySource(value = "classpath:zhouqu.properties")//绑定配置文件
@Validated//数据校验
public class Dog {
@Email(message="不是一个邮箱!")
private String email;
}
如果@Email
注解报红 是因为新版本需要validation启动器
解决方法:在pom.xml
加入下面依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
使用数据校验,可以保证数据的正确性;
常见参数:
otNull(message="名字不能为空")
private String userName;
@Max(value=120,message="年龄最大不能查过120")
private int age;
@Email(message="邮箱格式错误")
private String email;
//空检查
@Null //验证对象是否为null
@NotNull //验证对象是否不为null, 无法查检长度为0的字符串
@NotBlank //检查约束字符串是不是Null还有被Trim的长度是否大于0,只对字符串,且会去掉前后空格.
@NotEmpty //检查约束元素是否为NULL或者是EMPTY.
//Booelan检查
@AssertTrue //验证 Boolean 对象是否为 true
@AssertFalse //验证 Boolean 对象是否为 false
//长度检查
@Size(min=, max=) //验证对象(Array,Collection,Map,String)长度是否在给定的范围之内
@Length(min=, max=) //string is between min and max included.
//日期检查
@Past //验证 Date 和 Calendar 对象是否在当前时间之前
@Future //验证 Date 和 Calendar 对象是否在当前时间之后
@Pattern //验证 String 对象是否符合正则表达式的规则
多环境配置
配置文件加载位置
从官网了解配置文件可以放在四个地方
他们的优先级是:
- file: ./ config/
- file: ./
- classpath : /config/
- classpath:/
多环境切换
准备三个配置文件
方式一:
在application.yml
配置文件中选择激活哪个配置文件
# SpringBoot多环境配置可以选择激活哪一个配置文件
spring:
profiles:
active: dev #只需要写 - 后面的名字 application-dev.yml
方式二:
如果觉得分成三个配置文件较为麻烦,可以都写在application.yml
这一个配置文件中
# 多个环境通过 ---分割
spring:
profiles:
active: dev
---
server:
port: 8002
spring:
profiles: dev
---
server:
port: 8003
spring:
profiles: test