微服务、Spring Boot入门、自动装配部分
1、简介
1.1、微服务阶段
各阶段学习要点:
- JavaSE:学习面向对象的思想
- MySQL:学会将数据持久化
- HTML + CSS + jQuery + 框架:学会能够看懂前端的代码,并掌握部分部分前端
- Java Web:可以独立的开发一个简单的拥有MVC三层架构的网站
- SSM:简化了我们的整个开发流程,但是配置却变得越来越复杂(打包时变成war包,再在tomcat上运行)
- Spring Boot:微服务架构(内嵌Tomcat,打包时变成jar包,对外仅需要提供接口)
- Spring Cloud:当服务越来越多
总结:Spring Boot的开发,就类似于将之前的一个大的SSM系统中的各种服务都进拆分,在Spring Boot中只需要提供对外的接口和编写对应的视图层。然后在打包成jar包,当整个个大业务需要运行的时候,每个jar包都将展示自己对外的接口,继而使得整个程序得以运行。
1.2、什么是springboot
开发一个web应用,最初开始接触Servlet结合Tomcat, 得到一个Hello Wolrld程序,需要经历很多的步骤; 后来出现了Structs,再后来出现了Spring MVC,到了现在的Spring Boot。
那什么是Spring Boot呢?
它也是一个Java Web的开发框架,和Spring MVC类似,对比其他Java Web框架的好处,官方的说是简化开发,约定大于配置, you can “just run”,能迅速的开发web应用,几行代码开发一个http接口。
Java企业级应用的发展:J2EE->spring->Spring Boot
随着 Spring 不断的发展,涉及的领域越来越多,项目整合开发需要配合各种各样的文件,慢慢变得不那么易用简单,违背了最初的理念,甚至人称配置地狱。Spring Boot 就是在这样的背景下被抽象出来的开发框架,目的为了让大家更容易的使用 Spring 、更容易的集成各种常用的中间件、开源软件;
Spring Boot 基于 Spring 开发,Spring Boot 本身并不提供 Spring框架的核心特性以及扩展功能,只是用于快速、敏捷地开发新一代基于 Spring 框架的应用程序。也就是说,它并不是用来替代 Spring 的解决方案,而是和 Spring 框架紧密结合用于提升 Spring 开发者体验的工具。Spring Boot 以约定大于配置的核心思想,默认帮我们进行了很多设置,多数 Spring Boot 应用只需要很少的 Spring 配置。同时它集成了大量常用的第三方库配置(例如 Redis、MongoDB、Jpa、RabbitMQ、Quartz 等等),Spring Boot 应用中这些第三方库几乎可以零配置的开箱即用,
简单来说就是Spring Boot其实不是什么新的框架,它默认配置了很多框架的使用方式,就像maven整合了所有的jar包,Spring Boot整合了所有的框架 。
Spring Boot 出生名门,从一开始就站在一个比较高的起点,又经过这几年的发展,生态足够完善,Spring Boot 已经当之无愧成为 Java 领域最热门的技术。
Spring Boot的主要优点:
- 为所有Spring开发者更快的入门
- 开箱即用,提供各种默认配置来简化项目配置
- 内嵌式容器简化Web项目
- 没有冗余代码生成和XML配置的要求
1.3、Spring Boot阶段及接下来的学习大纲
2、第一个SpringBoot程序
2.1、创建Spring Boot程序方式一(官网)
1、在Spring官网的项目中选择Spring Boot项目
2、下拉Spring Boot的介绍信息,在最后选择 “Quickstart Your Project”下的Spring Initializr选项
3、设置对应的选项
4、下载到桌面后解压,使用IDEA打开即可
2.2、创建Spring Boot程序方式二(IDEA)
1、使用IDEA新建一个项目
2、填写对应的配置信息
3、选择对用的依赖
4、再点击finish,即完成
2.3、Spring Boot程序内容简介
我删除了暂时无用的五个文件
-
本身的目录结构
-
新建文件的目录结构
-
maven依赖介绍
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <!--说明它有一个父项目--> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.2.5.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>pers.mobian</groupId> <artifactId>springboot01</artifactId> <version>0.0.1-SNAPSHOT</version> <name>springboot01</name> <description>Demo project for Spring Boot</description> <properties> <java.version>11</java.version> </properties> <dependencies> <!--这是一个Spring Boot的web项目的jar包--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <!--此处可以省略版本信息,使其直接继承父依赖--> </dependency> <!--这是一个Spring Boot的测试的jar包--> <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> <build> <!--这是打jar包的插件--> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <configuration> <source>11</source> <target>11</target> </configuration> </plugin> </plugins> </build> </project>
-
由于内嵌了Tomcat服务器,所以能够直接启动
-
将程序打包成jar包
-
执行对应的jar包
-
网页测试访问
-
修改默认端口号
2.4、修改启动的图标
个人推荐这个网站:https://www.bootschool.net/
在资源目录下application.properties的同级目录下,新建一个banner.txt文件
将你想要显示的信息,放入其中,启动服务即可。
3、原理初探
自动配置
3.1、pom.xml
- pom.xml:部分插件,可以显式的定义对应的依赖(及对应的启动器)
- pom.xml的父项目为:spring-boot-starter-parent(配置了资源过滤器,及插件)
- pom.xml的父项目的父项目:spring-boot-dependencies(配置了很多的版本信息和启动器。当我们在pom.xml中配置依赖时,可以不用指定对应的版本信息,因为这些版本都可以在这里中找到)
3.2、启动器
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<version>2.2.5.RELEASE</version>
</dependency>
- 启动器就类似于Spring Boot的启动场景
- 比如:spring-boot-starter-web,系统就会帮我们自动导入web环境相关的所有的依赖
- Spring Boot会将所有的功能场景,都变成一个个启动器
- 我们需要做的就是找到对应的启动器
3.2、主程序
package pers.mobian.springboot01;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
//标注这是一个Spring Boot的应用
@SpringBootApplication
public class Springboot01Application {
public static void main(String[] args) {
//启动Spring Boot项目,run为静态方法,通过反射调用
SpringApplication.run(Springboot01Application.class, args);
}
}
@SpringBootApplication自动装配的流程
层次结构图
自动配置的核心文件
结论:Spring Boot所有的自动配置都是在启动的时候扫描并加载:spring.factories
所有的自动配置类都在这里,但是不一定生效,要判断条件是否成立,只要导入对应的star,就有对应的启动器了,有了启动器,我们的自动装配就会生效,然后配置成功!
- Spring Boot在启动的时候,从路径/META-INF/spring.factories获取指定的值
- 将这些自动装配的类导入容器中,自动装配就会生效,帮我们进行自动装配
- 以前我们呢需要自动配置的东西,现在
- 整个JavaEE,解决方案和自动装配的东西都在
spring-boot-autoconfigure-2.2.5.RELEASE.jar
下 - 它会把所有的需要导入的组件,以类名的方式返回,这些组件就会被添加到容器中
- 容器中也会存在非常多的xxxAutoConfig的文件(@Bean),就是这些类给容器中导入了这个场景需要的所有组件;并自动配置,@Configuration,JavaConfig
- 有了自动配置类,就可以免去了我们手写配置
3.3、run
开启了一个服务
@SpringBootApplication
public class SpringbootDemo02Application {
public static void main(String[] args) {
//该方法返回一个ConfigurableApplicationContext对象
//参数一:应用入口的类 参数类:命令行参数
SpringApplication.run(SpringbootDemo02Application.class, args);
}
}
SpringApplication.run分析
分析该方法主要分两部分,一部分是SpringApplication的实例化,二是run方法的执行;
1、SpringApplication
这个类主要做了以下四件事情
- 推断应用的类型是普通的项目还是Web项目
- 查找并加载所有可用初始化器 , 设置到initializers属性中
- 找出所有的应用程序监听器,设置到listeners属性中
- 推断并设置main方法的定义类,找到运行的主类
查看构造器
public SpringApplication(ResourceLoader resourceLoader, Class... primarySources) {
this.sources = new LinkedHashSet();
this.bannerMode = Mode.CONSOLE;
this.logStartupInfo = true;
this.addCommandLineProperties = true;
this.addConversionService = true;
this.headless = true;
this.registerShutdownHook = true;
this.additionalProfiles = new HashSet();
this.isCustomEnvironment = false;
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
this.primarySources = new LinkedHashSet(Arrays.asList(primarySources));
this.webApplicationType = WebApplicationType.deduceFromClasspath();
this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
this.mainApplicationClass = this.deduceMainApplicationClass();
}
2、run方法的执行流程
4、Spring Boot配置
4.1、yaml语法概述
YAML是 “YAML Ain’t a Markup Language” (YAML不是一种标记语言)的递归缩写。在开发的这种语言时,YAML 的意思其实是:“Yet Another Markup Language”(仍是一种标记语言)
该以数据作为中心,而不是以标记语言为重点!
以前的配置文件,大多数都是使用xml来配置,但是Spring Boot更推荐我们使用yaml。
三种文件格式的配置:xml、properties、yaml
<server>
<port>8081<port>
</server>
server.port=8081
server:
port: 8081
1、传统的注入属性方式
@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
@Component
public class Dog {
@Value("阿黄")
private String name;
@Value("3")
private int age;
}
@SpringBootTest
class Springboot01ApplicationTests {
@Autowired
Dog dog;
@Test
void contextLoads() {
System.out.println(dog.toString());
}
}
2、使用yaml方式注入(重点)
两个实体类
@Data
@AllArgsConstructor
@ToString
@NoArgsConstructor
@Component
public class Dog {
private String name;
private int age;
}
@AllArgsConstructor
@NoArgsConstructor
@ToString
@Data
@Component
//该注解用于绑定对应的对象名
@ConfigurationProperties(prefix = "person")
public class Person {
private String name;
private Integer age;
private Boolean happy;
private Date birth;
private Map<String,Object> maps;
private List<Object> lists;
private Dog dog;
}
yaml格式的配置文件
person:
name: 默辨
age: 23
happy: true
birth: 2020/03/16
maps: {key1: value1,key2: value2}
lists:
- eat
- sleep
- play
dog:
name: 小黄
age: 11
测试:
@SpringBootTest
class DemoApplicationTests {
@Autowired
private Person person;
@Test
void contextLoads() {
System.out.println(person);
}
}
3、yaml配置文件的占位符用法
person:
name: 默辨${random.uuid} # 随机uuid
age: ${random.int} # 随机int
happy: true
birth: 2020/03/16
maps: {key1: value1,key2: value2}
lists:
- eat
- sleep
- play
dog:
name: ${person.hello:other}_小黄 #如果有值就输出,没有就使用默认值,再拼接_阿黄
age: 11
注意:时间的格式不要错
4、使用properties方式注入
如果你的配置文件中含有中文,建议先修改文件的字符编码,不然就会出现
这样的乱码。
修改方式如下:
实体类:
@AllArgsConstructor
@NoArgsConstructor
@Data
@ToString
//该注解,可以用来绑定我们自定义的配置文件
@PropertySource(value = "classpath:cat.properties")
@Component
public class Cat {
@Value("${cat.name}") //可以从配置文件中取值
private String name;
@Value("#{2*3}") // #{SPEL} Spring表达式
private int age;
}
自定义的cat.properties:
cat.name=咪咪
cat.age=1
测试类:
@SpringBootTest
class DemoApplicationTests {
@Autowired
private Cat cat;
@Test
void contextLoads() {
System.out.println(cat);
}
}
执行结果:
Cat(name=咪咪, age=6)
5、@ConfigurationProperties与@Value的对比
@ConfigurationProperties | @Value | |
---|---|---|
功能 | 批量注入被指文件中的属性 | 一个一个注入 |
松散绑定(松散语法) | 支持 | 不支持 |
SpEL | 不支持 | 支持 |
JSR303数据校验 | 支持 | 不支持 |
复杂类型数据的封装 | 支持 | 不支持 |
补充:
松散绑定:我的yml中写的last-name,这个和lastName是一样的, - 后面跟着的字母默认是大写的。这就是松散绑定。
JSR303校验:Spring Boot中可以用@validated来校验数据,如果数据异常则会统一抛出异常,方便异常中心统一处理
@AllArgsConstructor
@NoArgsConstructor
@Data
@ToString
@PropertySource(value = "classpath:cat.properties")
@Component
@Validated
public class Cat {
//使用了JSR303校验后,系统会多一层判断
@Email(message = "邮箱格式错误")
@Value("${cat.name}")
private String name;
@Value("#{2*3}")
private int age;
}
常见的异常参数
@NotNull(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 对象是否符合正则表达式的规则
.......等等
除此以外,我们还可以自定义一些数据校验规则
该注解的在包中的位置
6、配置文件优先级
配置文件分别可以创建在:file:./config/
、file:./
、classpath:/config/
、classpath:/
目录下,且其对应的优先级为8081–>8082–>8083–>8084
7、配置环境的任意切换
在主配置文件编写的时候,文件名可以是 application-{profile}.properties/yml , 用来指定多个环境版本;
例如:
application-test.properties 代表测试环境配置
application-dev.properties 代表开发环境配置
但是Springboot并不会直接启动这些配置文件,它默认使用application.properties主配置文件;
我们需要通过一个配置来选择需要激活的环境:
#比如在配置文件中指定使用dev环境,我们可以通过设置不同的端口号进行测试;
#我们启动SpringBoot,就可以看到已经切换到dev下的配置了;
spring.profiles.active=dev
yaml方式
server:
port: 8081
#选择要激活那个环境块
spring:
profiles:
active: prod
---
server:
port: 8083
spring:
profiles: dev #配置环境的名称
---
server:
port: 8084
spring:
profiles: prod #配置环境的名称
注意:如果yml和properties同时都配置了端口,并且没有激活其他环境 , 默认会使用properties配置文件的!
4.2、核心配置文件与配置类文件之间的关系
1、两者联系
根据当前不同的条件判断,决定这个配置类是否生效!
- 一但这个配置类生效;这个配置类就会给容器中添加各种组件;
- 这些组件的属性是从对应的配置类中获取的,配置类中每一个属性又是和核心配置文件一一对应;
- 所有在核心配置文件中能配置的属性都是在配置类中封装着;
- 核心配置文件能配置什么就可以参照某个功能对应的这个属性配置类
//从配置文件中获取指定的值和bean的属性进行绑定
@ConfigurationProperties(prefix = "spring.http")
public class HttpProperties {
// .....
}
这就是自动装配的原理!
核心要领
1、SpringBoot启动会加载大量的自动配置类
2、我们首先看我们需要的功能有没有在Spring Boot默认写好的自动配置类当中;
3、我们再看这个自动配置类中到底配置了哪些组件;(只要我们要用的组件存在在其中,我们就不需要再手动配置了)
4、给容器中自动配置类添加组件的时候,会从配置类中获取某些属性。我们只需要在核心配置文件中指定这些属性的值即可;
xxxxAutoConfigurartion:自动配置类;,给容器中添加组件
xxxxProperties:封装配置文件中相关属性;
2、查看是否生效
我们加载了所有的配置类,但并不是所有的配置都生效的,可以在核心配置文件中进行对应的设置。这样就能够在控制台查看哪些配置类生效,哪些没有生效。(只需要配置对应的启动器,就可以让没有生效的也生效)
#开启springboot的调试类
debug=true
Positive matches:(自动配置类启用的:正匹配)
Negative matches:(没有启动,没有匹配成功的自动配置类:负匹配)
Unconditional classes: (没有条件的类)
3、补充注解
@Conditional派生注解(Spring注解版原生的@Conditional作用)
作用:必须是@Conditional指定的条件成立,才给容器中添加组件,配置配里面的所有内容才生效;
@ConditionalOn扩展注解 | 作用(判断是否满足当前指定的条件) |
---|---|
@ConditionalOnJava | 系统的java版本是否符合要求 |
@ConditionalOnBean | 容器中存在指定Bean |
@ConditionalOnMissingBean | 容器中不存在指定Bean |
@ConditionalOnExpression | 满足SpEL表达式指令 |
@ConditionalOnClass | 系统中有指定的类 |
@ConditionalOnMissingClass | 系统中没有指定的类 |
@ConditionalOnSingleCandidate | 容器中只有一个指定的Bean,或者这个Bean是首选Bean |
@ConditionalOnProperty | 系统中指定的属性是否有指定的值 |
@ConditionalOnResource | 类路径下是否存在指定资源文件 |
@ConditionalOnWebApplication | 当前是web环境 |
@ConditionalOnNotWebApplication | 当前不是web环境 |
@ConditionalOnJndi | JNDI存在指定项 |