SpringBoot
1.创建SpringBoot文件
1.1 官网创建文件并下载,使用IDEA打开
1.2 IDEA创建
- New Project -> Spring Initializr(本质也是从官网下载的模板格式);
- 添加 java web 依赖;
2.文件简析
2.1 pom.xml
-
配置文件,主要有四个部分:
- 项目元数据信息:创建时输入的Project Metadata部分,也就是Meaven项目的基本元素,包括groupId、artifactId、version、name、description等;
- parent:继承
spring-boot-starter-parent
的依赖管理、控制版本和打包等内容; - dependencies:项目具体依赖,下面包含了
spring-boot-starter-web
用于实现HTTP接口(该依赖包含了Spring MVC,且使用Tomcat作为默认嵌入式容器),spring-boot-starter-test
是用于编写后续单元测试的依赖包:
<!-- 在这个标签中添加 --> <dependencies> <!-- 添加依赖,格式为 spring-boot-starter- 这是springboot启动器的标准格式 --> <!-- web依赖,tomcat --> <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>
- build:构件配置部分,默认使用了
spring-boot-maven-plugin
,即打jar包插件;
2.2 application.properties
src/main/javaresource/application.properties
目录核心配置文件;- 重写服务属性,稍后详述;
- 编辑对象、定义属性;
- 常用小服务:
# 直接更改项目端口号即可,修改之后再次访问该端口才能成功
server.port=8081
2.3 banner.txt(小彩蛋)
- 自建文件,重写启动器banner服务,
src/main/javaresource/banner.txt
目录文件; - 网站:https://www.bootschool.net/ascii
- 例:
// _ooOoo_ //
// o8888888o //
// 88" . "88 //
// (| ^_^ |) //
// O\ = /O //
// ____/`---'\____ //
// .' \\| |// `. //
// / \\||| : |||// \ //
// / _||||| -:- |||||- \ //
// | | \\\ - /// | | //
// | \_| ''\---/'' | | //
// \ .-\__ `-` ___/-. / //
// ___`. .' /--.--\ `. . ___ //
// ."" '< `.___\_<|>_/___.' >'"". //
// | | : `- \`.;`\ _ /`;.`/ - ` : | | //
// \ \ `-. \_ __\ /__ _/ .-` / / //
// ========`-.____`-.___\_____/___.-`____.-'======== //
// `=---=' //
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ //
// 佛祖保佑 永不宕机 永无BUG //
2.4 XXXApplication.java
src/main/java/com/kun/XXXApplication.java
目录文件;
//标注这是一个SpringBoot应用,加载所有资源类
@SpringBootApplication
public class Demo01Application {
//程序入口
//run,开启了一个服务,参数一:应用入口的类,参数二:命令行输入的参数类
/*SpringApplication实例化,做了以下四项工作:
1.判断该应用类型是普通java项目还是Web项目
2.查找并加载所有可用初始化器,设置到initializers属性中
3.找出所有的应用程序监听器,设置到listeners属性中
4.推断并设置main方法的定义类,找到运行的主类
* */
public static void main(String[] args) {
SpringApplication.run(Demo01Application.class, args);
}
}
2.5 XXXApplicationTests.java
test/java/com/kun/XXXApplicationTests.java
目录文件;- 实现单元测试功能;
2.6 自建框架层
- 在
src/main/java/com/kun/
最底层包目录下新建config
、controller
、dao
、pojo
等包,这些具体用到再详细讲解,也可以参考博客简略了解一下:https://www.cnblogs.com/tooyi/p/13340374.html; - 为说明几个常用注解以及习惯注解的用法,新建
controller
包,并在该目录下新建HelloController.java
文件:
//使用Controller注解标注说明并被加载
@Controller
//使用Mapping类注解来映射请求,即请求/hello时响应回该类
@RequestMapping("/hello")
public class HelloController {
//在/hello地址请求之下再请求地址/hello即可获得下列响应
@GetMapping("/hello")
//当请求/hello/hello时,响应主体部分为hello()方法
@ResponseBody
public String hello(){
return "hello";
}
}
- 注解和反射机制请参考博客:[https://kunpw.cn/2020/10/23/java/%E6%B3%A8%E8%A7%A3%E5%92%8C%E5%8F%8D%E5%B0%84/](https://kunpw.cn/2020/10/23/java/%E6%B3%A8%E8%A7%A3%E5%92%8C%E5%8F%8D%E5%B0%84/)
3.yaml(yml)语法
- yaml文件是对properties文件的替代,但比properties更实用,所以一般使用yaml;
- 删除application.properties文件,新建application.yaml(.yml);
3.1 常用语法:
# 1.基础语法
# 修改自动配置的默认值,其中 : 后必须接空格
# yaml 对空格很敏感,很像python 而properties只能保存键值对
server:
port: 8081
# yaml基本语法
# 定义参数使用key-value
name: zhaoxiaoan
# 定义对象
student:
name: zhaoxiaoan
age: 3
# 对象的行内写法
teacher: {name: zhao,age 30}
# 定义数组
pets:
- cat
- dog
- pig
# 数组的行内写法
pet: [cat,dog,pig]
# EL表达式
age: ${random.int}
# 占位符
person:
hello: happy
dog:
name: ${person.hello:hello}_旺财
# 如果没有就直接为空,有则占位显示,即happy_旺财
3.2 重点:给实体类赋值:
- 新建数据类:新建pojo/Dog.java、pojo/Person.java类
package com.kun.pojo;
import org.springframework.stereotype.Component;
@Component
public class Dog {
private String name;
private Integer age;
//有参无参构造器、get/set方法以及toString方法,后续这些方法不再细列
public Dog() {
}
public Dog(String name, Integer age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public String toString() {
return "Dog{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
package com.kun.pojo;
import java.util.Date;
import java.util.List;
import java.util.Map;
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;
//省略各方法
}
-
赋值:
- 直接new对象;
-
原生注解赋值:
//1.0 public class Dog { @Value("zhaoxiaoan") private String name; @Value("3") private Integer age; }
- yaml文件赋值:
# 2.0 person: name: zhaoxiaoan age: 3 happy: true birth: 2020/10/22 maps: {k1: v1,k2: v2} lists: - code - music - study dog: name: 旺财 age: 3
@Component /*@PropertySource(value = "classpath:application.yaml") 通过该注解设置装配文件 */ //通过ConfigurationProperties(prefix = "person")注解绑定映射yaml文件中person对象 @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; }
- 单元测试:
//1.0
@SpringBootTest
class DemoApplicationTests {
//自动装配类
//如果有多个对象Dog,可以通过@Qualifier选择具体的对象
@Autowired
private Dog dog;
@Test
void contextLoads() {
System.out.println(dog);
}
}
//输出为:
Dog{name=‘旺财’, age=3}
```java
@SpringBootTest
class DemoApplicationTests {
@Autowired
private Person person;
@Test
void contextLoads() {
System.out.println(person);
}
}
//输出为:
Person{name='zhaoxiaoan', age=3, happy=true, birth=Thu Oct 22 00:00:00 CST 2020, maps={k1=v1, k2=v2}, lists=[code, music, study], dog=Dog{name='旺财', age=3}}
3.3 yaml-ConfigurationProperties与原properties-value比较图示:
-
功能:即yaml可以直接映射到对象而properties需要使用@Value一个一个赋值;
-
松散绑定:即yaml中的last-name和类中的lastName是一样的,
-
后接的字母转为大写; -
JSR303数据校验:可以在字段增加一层过滤验证,保证数据合法性;
-
复杂类型封装:yml中可以封装对象,使用@Value就不支持;
-
JSR303数据校验(类似于html中input标签中选中的url、email等功能,可以自动校验输入):
@Component //@PropertySource(value = "classpath:application.yaml") @ConfigurationProperties(prefix = "person") @Validated//开启数据校验 public class Person { @Email//对email赋值开启Email数据校验,假设对name属性开启Email验证 private String name; private Integer age; private Boolean happy; private Date birth; private Map<String,Object> maps; private List<Object> lists; private Dog dog; }
-
注意,在SpringBoot2.3以后版本由于spring-boot-starter-web的依赖项已经去除了validate依赖,所以需要手动添加依赖才能生效:
<dependency> <groupId>org.hibernate.validator</groupId> <artifactId>hibernate-validator</artifactId> <version>6.0.17.Final</version> <scope>compile</scope> </dependency>
-
再次测试则会得到报错结果:
Description: Binding to target org.springframework.boot.context.properties.bind.BindException: Failed to bind properties under 'person' to com.kun.pojo.Person failed: Property: person.name Value: zhaoxiaoan Origin: class path resource [application.yaml]:2:9 Reason: 不是一个合法的电子邮件地址
- 修改name属性赋值即可;
- JSR303校验注解类:
- 小技巧:可以进入注解原文件查看各种注解值并自主修改;
-
4.yaml配置文件位置和多环境配置
4.1 配置文件
file:./config/
file:./
classpath:/config/
classpath:/
-
file即项目目录,classpath即
src/main/resources
目录; -
这既是yaml配置文件可存放的位置,也是其配置文件加载的优先级权重顺序,即按照权重覆盖相同属性及服务配置;
4.2 多环境配置
-
现实开发中可能会有三种环境版本:
- 普通环境:application.properties配置文件;
- 测试环境:application-test.properties配置文件;
- 开发环境:application-dev.properties配置文件;
-
yaml格式和properties格式配置环境对比:
-
在properties文件格式下徐亚单独配置多个文件才能生效;
-
而在yaml格式中则实现多文档模块,步骤更为简便:
server: port: 8081 # 通过`---`符号分割不同配置环境,并使用spring.profiles属性说明即可 --- server: port: 8082 spring: profiles: dev --- server: port: 8083 spring: profiles: test
-
5.自动装配原理(重点)
5.1 原理
-
从
src/main/java/com/kun/XXXApplication.java
程序入口文件@SpringBootApplication
注解出发,依次查看父级文件,了解其自动装配原理:- @ComponentScan:其父级文件注解,用来实现扫描当前主启动类同级的包;
- @SpringBootConfiguration——>@Configuration——>@Component:分别实现springboot配置、spring配置和基本组件;
- @EnableAutoConfiguration:自动装配,核心包:
—>@AutoConfigurationPackage:自动配置包 ->@Import({Registrar.class}):自动配置包注册 ->@Import({AutoConfigurationImportSelector.class}):自动配置导入选择器 ->getAutoConfigurationEntry():获得自动配置的实体入口 ->getCandidateConfigurations():获取候选配置 ->protected Class<?> getSpringFactoriesLoaderFactoryClass() { return EnableAutoConfiguration.class; //标注了EnableAutoConfiguration注解类的所有配置 } ->loadFactoryNames():获取所有的加载配置名字 ->loadSpringFactories():加载所有的Spring工厂 ->classLoader.getResources("META-INF/spring.factories"):加载项目资源 ->ClassLoader.getSystemResources("META-INF/spring.factories"):加载系统资源 ->META-INF/spring.factories:从该文件获取资源,自动装配的核心文件
-
META-INF/spring.factories
文件:存在于External Libraries中
-
文件内容:
-
所有已经配置好的依赖都在这里,其它只能自己配置;
-
随便进入一个源文件(可能只是.class文件,根据提示下载源java文件查看):
@ConditionalOnXXX //系列核心注解,其中每个文件都有,用来判断是否满足一系列条件才加载该文件
-
-
结论一:SpringBoot所有自动装配都是在启动的时候扫描并加载的,其中所有的自动装配类都在
spring.factories
文件中,只有在pom.xml文件中添加了依赖才满足生效条件并且自动装配生效;
5.2 yaml文件与自动装配
-
yaml文件内能够重写的服务实质上和spring.factories文件相关:
@EnableConfigurationProperties //某些自动装配文件中含有此注解,即标注可以在properties配置文件中重写属性值
- 在构造器类中:
//举例如下: public HttpEncodingAutoConfiguration(ServerProperties properties) { this.properties = properties.getServlet().getEncoding(); } //其中ServerProperties文件中的属性即与yaml配置文件绑定,可以重写配置
- ServerProperties:
@ConfigurationProperties(prefix = "server", ignoreUnknownFields = true) //该注解中prefix值表示yaml文件中重写配置该类属性的前缀
- 同样可以从yaml文件中根据配置属性返回到上级XXXProperties文件查看所有同类属性;
-
结论二:springfactories中定义好的自动装配类XXXAutoConfiguration加载XXXProperties文件,而XXXProperties文件则与配置文件.properties/.yaml一一绑定,即可通过.yaml配置文件修改配置并被加载;
-
开启Debug,查看具体哪些配置类被加载:
# 在application.yaml文件中开启Spring调试类 debug: true
- 测试结果:
Positive matches: 已经启用生效的类 Negative matches: 没有开启生效的类 Unconditional classes: 没有条件的类
5.3 SpringApplication.run启动
-
src/main/java/com/kun/XXXApplication.java
文件中:SpringApplication.run(DemoApplication.class, args);
-
该方法主要分两部分:
- SpringApplication实例化;
- run方法执行;
-
做了四项工作:
- 判断该应用类型是普通java项目还是Web项目;
- 查找并加载所有可用初始化器,设置到initializers属性中;
- 找出所有的应用程序监听器,设置到listeners属性中;
- 推断并设置main方法的定义类,找到运行的主类;
-
开启Debug,查看具体哪些配置类被加载:
# 在application.yaml文件中开启Spring调试类
debug: true
- 测试结果:
Positive matches: 已经启用生效的类
Negative matches: 没有开启生效的类
Unconditional classes: 没有条件的类
5.3 SpringApplication.run启动
-
src/main/java/com/kun/XXXApplication.java
文件中:SpringApplication.run(DemoApplication.class, args);
-
该方法主要分两部分:
- SpringApplication实例化;
- run方法执行;
-
做了四项工作:
- 判断该应用类型是普通java项目还是Web项目;
- 查找并加载所有可用初始化器,设置到initializers属性中;
- 找出所有的应用程序监听器,设置到listeners属性中;
- 推断并设置main方法的定义类,找到运行的主类;
-