Springboot3

1.JDK 关注的新特性

1.1 有用的新特性

JDK8-19 新增了不少新特性,这里我们把实际常用的新特性,给大家介绍一下。包括以下几个方面:
 Java Record
 Swich 开关表达式
 Text Block 文本块
 var 声明局部变量
 sealed 密封类

1.2.1 Java Record

Java14 中预览的新特性叫做 Record,在 Java 中,Record 是一种特殊类型的 Java 类。可用来创建不可变类,语法
简短。参考JEP 395. Jackson 2.12支持Record类

任何时候创建 Java 类,都会创建大量的样板代码,我们可能做如下:
 每个字段的 set,get 方法
 公共的构造方法
 重写 hashCode, toString(), equals()方

Java Record 避免上述的样板代码,如下特点:
 带有全部参数的构造方法
 public 访问器
 toString(),hashCode(),equals()
 无 set,get 方法。没有遵循 Bean 的命名规范
 final 类,不能继承 Record,Record 为隐士的 final 类。除此之外与普通类一样
 不可变类,通过构造创建 Record
 final 属性,不可修改
 不能声明实例属性(不能用get、set方法),能声明 static 成员

1.2.1.1 看Record怎么用

IDEA 新建 Class,选择类 Record
step1: 创建 Student Record

public record Student(Integer id,String name,String email,Integer age) {
}```

step2:创建 Record 对象

```java
public static void main(String[] args) {
Student lisi = new Student(1001, "lisi","lisi@qq.com",20);
System.out.println("lisi = " + lisi.toString());
Student zhangsan = new Student(1002, "zhangsan","lisi@qq.com",20);
System.out.println("zhangsan = " + zhangsan.toString());
System.out.println("lisi.equals(zhangsan) = " + lisi.equals(zhangsan));
System.out.println("lisi.name() = " + lisi.name());
System.out.println("zhangsan.name() = " + zhangsan.name());
}

现在能查看控制台输出:

lisi = Student[id=1001, name=lisi, email=lisi@qq.com, age=20]
zhangsan = Student[id=1002, name=zhangsan, email=lisi@qq.com, age=20]
lisi.equals(zhangsan) = false
lisi.name() = lisi
zhangsan.name() = zhangsa

1.2.1.2 Instance Methods

Record 是 Java 类,和普通 Java 类一样定义方法。下面定义方法 concat,将姓名和年龄一起打印输出。
我们创建普通的方法 concat,将 name 和 age 连接为一个字符

step1:创建实例方法

public record Student(Integer id,String name,String email,Integer age) {
public String concat(){
return String.format("姓名:%s,年龄是:%d", this.name,this.age);
}
}

step2: 调用实例方法

public static void main(String[] args) {
Student lisi = new Student(1001, "lisi","lisi@qq.com",20);
String nameAndAge = lisi.concat();
System.out.println( nameAndAge);
}

1.2.1.3 静态方法 Static Method

Record 类定义静态方法,试用静态方法与普通类一样。
step1: 创建静态方法

public record Student(Integer id,String name,String email,Integer age) {
public String concat(){
return String.format("姓名:%s,年龄是:%d", this.name,this.age);
}
/** 静态方法 */
public static String emailUpperCase(String email){
return Optional.ofNullable(email).orElse("no email").toUpperCase();
}
}

step2:测试静态方法

public static void main(String[] args) {
String emailUpperCase = Student.emailUpperCase("lisi@163.com");
System.out.println("emailUpperCase = " + emailUpperCase);
}

1.2.1.4 Record 的构造方法

我们可以在 Record 中添加构造方法, 有三种类型的构造方法分别:是紧凑的,规范的和定制构造方法
 紧凑型构造方法没有任何参数,甚至没有括号。
 规范构造方法是以所有成员作为参数
 定制构造方法是自定义参数个数

step1: 紧凑和定制构造方法

public record Student(Integer id,String name,String email,Integer age) {
/*紧凑构造方法*/
public Student {
System.out.println("id"+ id );
if( id < 1 ){
throw new RuntimeException("ok");
}
}
/*自定义构造方法*/
public Student(Integer id, String name) {
this(id, name, null, null);
}
}

step2:编译 Student.java -> Student.class

public record Student(Integer id, String name, String email, Integer age) {
/** 紧凑构造方法和规范构造方法合并了 */
public Student(Integer id, String name, String email, Integer age) {
System.out.println("id" + id);
if (id < 1) {
throw new RuntimeException("ok"); }
else {
this.id = id;
this.name = name;
this.email = email;
this.age = age;
}
}
public Student(Integer id, String name) {
this(id, name, (String)null, (Integer)null);
}
}

1.2.1.5 Record 与 Lombok

Java Record 是创建不可变类且减少样板代码的好方法。Lombok 是一种减少样板代码的工具。两者有表面上的重叠部
分。可能有人会说 Java Record 会代替 Lombok. 两者是有不同用途的工具。
Lombok 提供语法的便利性,通常预装一些代码模板,根据您加入到类中的注解自动执行代码模板。这样的库纯粹是
为了方便实现 POJO 类。通过预编译代码。将代码的模板加入到 class 中。
Java Record 是语言级别的,一种语义特性,为了建模而用,数据聚合。简单说就是提供了通用的数据类,充当“数据
载体",用于在类和应用程序之间进行数据传输

1.2.1.6 Record 实现接口

Java Record 可以与普通类一样实现接口,重写接口的方法。
step1: 创建新的接口,定义一个规范方法。

public interface PrintInterface {
/** 输出自定义描述信息 */
void print();
}

step2: 创建新的 Record 实现接口,重写接口的方法,实现当前 Record 有关的业务逻辑

public record ProductRecord(String id,String name,Integer qty)
implements PrintInterface {
@Override
public void print() {
String productDesc = String.join("-", id, name, qty.toString());
System.out.println("商品信息 = " + productDesc);
}
}

step3:测试 print 方法

public static void main(String[] args) {
ProductRecord product = new ProductRecord("P001", "手机", 100);
product.print();
}

1.2.1.7 Local Record

Record 可以作为局部对象使用。在代码块中定义并使用 Record,下面定义一个 SaleRecord

step1:定义 Local Record

public static void main(String[] args) {
//定义 Java Record
record SaleRecord(String saleId,String productName,Double money){};
//创建 Local Record
SaleRecord saleRecord = new SaleRecord("S22020301", "手机", 3000.0);
//使用 SaleRecor
System.out.println("销售记录 = " + saleRecord.toString());
}

控制台输出:
销售记录 = SaleRecord[saleId=S22020301, productName=手机, money=3000.0]

1.2.1.8 嵌套 Record

多个 Record 可以组合定义, 一个 Record 能够包含其他的 Record。
我们定义 Record 为 Customer,存储客户信息,包含了 Address 和 PhoneNumber

step1:定义 Record

public record Address(String city,String address,String zipcode) {}
public record PhoneNumber(String areaCode,String number) {}
public record Customer(String id, String name, PhoneNumber phoneNumber,
Address address) {}

step2: 创建 Customer 对象

public static void main(String[] args) {
Address address = new Address("北京", "大兴区凉水河二街-8号10栋三层", "100176");
PhoneNumber phoneNumber = new PhoneNumber("010", "400-8080-105");
Customer customer = new Customer("C1001", "李项", phoneNumber, address);
System.out.println("客户 = " + customer.toString());
}

控制台输出:
客户 = Customer[id=C1001, name=李项, phoneNumber=PhoneNumber[areaCode=010, number=400-8080-105], address=Address[city=北京, address=大兴区凉水河二街 9 号 10 栋三层, zipcode=100176]]

1.2.1.9 instanceof 判断 Record 类型

instanceof 能够与 Java Record 一起使用。编译器知道记录组件的确切数量和类型。
step1:声明 Person Record,拥有两个属性 name 和 age

public record Person(String name,Integer age) {
}

step2: 在一个业务方法判断当是 Record 类型时,继续判断 age 年龄是否满足 18 岁。

public class SomeService {
public boolean isEligible(Object obj){
// 判断 obj 为 Person 记录类型
if( obj instanceof Person(String name, Integer age)){
return age >= 18;
}
return false;
}
}

instanceof 还可以下面的方式

if( obj instanceof Person(String name, Integer age) person){
return person.age() >= 18;
}
//或者
if( obj instanceof Person p){
return p.age() >= 18;
}

step3: 测试代码

public static void main(String[] args) {
SomeService service = new SomeService();
boolean flag = service.isEligible(new Person("李四", 20));
System.out.println("年龄符合吗?" + flag);
}

控制台输出:
控制台输出 flag 为 true

处理判断中 Record 为 null
Java Record 能够自动处理 null。
step1:record 为 null

处理判断中 Record 为 null
Java Record 能够自动处理 null。
step1:record 为 null

public static void main(String[] args) {
SomeService service = new SomeService();
boolean eligible = service.isEligible(null);
System.out.println("年龄符合吗?" + eligible)
}

控制台输出 eligible 为 false ,Debug 调试代码,发现 if 语句判断为 false,不执行

总结

 abstract 类 java.lang.Record 是所有 Record 的父类。
 有对于 equals(),hashCode(),toString()方法的定义说明
 Record 类能够实现 java.io.Serializable 序列化或反序列化
 Record 支持泛型,例如 record Gif( T t ) { }
 java.lang.Class 类与 Record 类有关的两个方法:
boolean isRecord() : 判断一个类是否是 Record 类型
RecordComponent[] getRecordComponents():Record 的数组,表示此记录类的所有记录组件

Customer customer = new Customer(....);
RecordComponent[] recordComponents = customer.getClass().getRecordComponents();
for (RecordComponent recordComponent : recordComponents) {
System.out.println("recordComponent = " + recordComponent);
}
boolean record = customer.getClass().isRecord();
System.out.println("record = " + record);

1.2.2 Switch

Switch 的三个方面,参考:JEP 361
 支持箭头表达式
 支持 yied 返回值
 支持 Java

1.2.2.1 箭头表达式,新的 case 标签

Switch 新的语法,case label -> 表达式|throw 语句|block
case label_1, label_2, …, label_n -> expression;|throw-statement;|block

public static void main(String[] args) {
int week = 7;
String memo = "";
switch (week){
case 1 -> memo = "星期日,休息";
case 2,3,4,5,6-> memo="工作日";
case 7 -> memo="星期六,休息";
default -> throw new IllegalArgumentException("无效的日期:");
}
System.out.println("week = " + memo);
}

yeild 返回

yeild 让 switch 作为表达式,能够返回值
语法
变量 = switch(value) { case v1: yield 结果值; case v2: yield 结果值;case v3,v4,v5.. yield 结果值

示例:yield 返回值,跳出 switch 块

public static void main(String[] args) {
int week = 2;
//yield 是 switch 的返回值, yield 跳出当前 switch 块
String memo = switch (week){
case 1: yield "星期日,休息";
case 2,3,4,5,6: yield "工作日";
case 7: yield "星期六,休息";
default: yield "无效日期";
};
System.out.println("week = " + memo);
}

无需中间变量, switch 作为表达式计算,可以得到结果。yield 是表达式的返回

public static void main(String[] args) {
int week = 1;
//yield 是 switch 的返回值, yield 跳出当前 switch 块
String memo = switch (week){
case 1 ->{
System.out.println("week=1 的 表达式部分");
yield "星期日,休息";
}
case 2,3,4,5,6 ->{
System.out.println("week=2,3,4,5,6 的 表达式部分");
yield "工作日";
}
case 7 -> {
System.out.println("week=7 的 表达式部分");
yield "星期六,休息";
}
default -> {
System.out.println("其他语句");
yield "无效日期";
}
};
System.out.println("week = " + memo);
}

提示:

case 标签-> 与 case 标签:不能混用。 一个 switch 语句块中使用一种语法格式。
switch 作为表达式,赋值给变量,需要 yield 或者 case 标签-> 表达式。->右侧表达式为 case 返回值

示例:

public static void main(String[] args) {
int week = 1;
//yield 是 switch 的返回值, yield 跳出当前 switch 块
String memo = switch (week){
case 1 ->{
System.out.println("week=1 的 表达式部分");
yield "星期日,休息";
}
case 2,3,4,5,6 ->System.out.println("week=2,3,4,5,6 的 表达式部分");
yield "工作日";
}
case 7 -> "星期六,休息";
default -> "无效日期";
};
System.out.println("week = " + memo);
}

1.2.2.3 Java Record

switch 表达式中使用 record,结合 case 标签-> 表达式,yield 实现复杂的计算
step1: 准备三个 Record

public record Line(int x,int y) {
}
public record Rectangle(int width,int height) {
}
public record Shape(int width,int height) {
}

step2: switch record

public static void main(String[] args) {
Line line = new Line(10,100);
Rectangle rectangle = new Rectangle(100,200);
Shape shape = new Shape(200,200);
Object obj = rectangle;
int result = switch (obj){
case Line(int x,int y) -> {
System.out.println("图形是线, X:"+x+",Y:"+y);
yield x+y;
}
case Rectangle(int w,int h) -> w * h;
case Shape(int w,int h) ->{
System.out.println("这是图形,要计算周长");
yield 2* (w + h);
}
default -> throw new IllegalStateException("无效的对象:" + obj);
};
System.out.println("result = " + result);
}

case Line , Rectangle,Shape 在代码块执行多条语句,或者箭头->表达式。

1.2.3 Text Block

Text Block 处理多行文本十分方便,省时省力。无需连接 “+”,单引号,换行符等。Java 15 ,参考 JEP 378

1.2.3.1 认识文本块

String name = """lisi"""; //Error 不能将文本块放在单行上
String name= """lisi
20"""; //Error 文本块的内容不能在没有中间行结束符的情况下跟随三
个开头双引号
String myname= """
zhangsan
20
"""; //正确

文本块定义要求:
 文本块以三个双引号字符开始,后跟一个行结束符。
 不能将文本块放在单行上
 文本块的内容也不能在没有中间行结束符的情况下跟随三个开头双引号
三个双引号字符"“” 与两个双引号""的字符串处理是一样的。与普通字符串一样使用。例如 equals() , “==” , 连接字符串(”+“), 作为方法的参数等。

1.2.3.2 文本块与普通的双引号字符串一样

Text Block 使用方式与普通字符串一样,==,equals 比较,调用 String 类的方法

step1:字符串比较与方法

public void fun1() {
String s1= """
lisi
""";
String s2 = """
lisi
""";
//比较字符串
boolean b1 = s1.equals(s2);
System.out.println("b1 = " + b1);
//使用 == 的比较
boolean b2 = s1 == s2;
System.out.println("b2 = " + b2);
String msg = """
hello world""";
//字符串方法 substring
String sub = msg.substring(0, 5);
System.out.println("sub = " + sub);
}
b1 = true
b2 = true
sub = hello

1.2.3.3 空白

 JEP 378 中包含空格处理的详细算法说明。
 Text Block 中的缩进会自动去除,左侧和右侧的。
 要保留左侧的缩进,空格。将文本块的内容向左移动(tab 键

示例:

public void fun2(){
//按 tab 向右移动,保留左侧空格
String html= """
<html>
<body>动力节点,Java 黄埔军校</body>
</html>
""";
System.out.println( html);
}

示例 2:indent()方法

public void fun3(){
String colors= """
red
green
blue
""";
System.out.println( colors);
//indent(int space)包含缩进,space 空格的数量
String indent = colors.indent(5);
System.out.println( indent)
}
输出:
red
green
blue
		red
		green
		blue

1.2.3.4 文本块的方法

Text Block 的格式方法 formatted()

public void fun4(){
String info= """
Name:%s
Phone:%s
Age:%d
""".formatted("张三","13800000000",20);
System.out.println("info = " + info);
}

String stripIndent():删除每行开头和结尾的空白
String translateEscapes() :转义序列转换为字符串字面量

1.2.3.5 转义字符

新的转义字符"",表示隐士换行符,这个转义字符被 Text Block 转义为空格。通常用于是拆分非常长的字符串文
本 ,串联多个较小子字符串,包装为多行生成字符串。
新的转义字符,组合非常长的字符串。
示例

public void fun5(){
String str= """
Spring Boot 是一个快速开发框架 \
基于\"Spring\"框架,创建 Spring 应用 
内嵌 Web 服务器,以 jar 或 war 方式运行 \
""";
System.out.println("str = " + str);
}

总结:
1.多行字符串,应该使用 Text Block
2.当 Text Block 可以提高代码的清晰度时,推荐使用。比如代码中嵌入 SQL 语句
3.避免不必要的缩进,开头和结尾部分。
4.使用空格或仅使用制表符 文本块的缩进。混合空白将导致不规则的缩进。
5.对于大多数多行字符串, 分隔符位于上一行的右端,并将结束分隔符位于文本块单独行上。例如:

String colors= """	
				red
				green
				blue
				""";

2. 掌握SpringBoot 基础篇

2.1 SpringBoot

Spring Boot 是目前流行的微服务框架 倡导 约定优先于配置” 其设 目的是 用来简化新 Spring 应用的初 始化搭建以及开发过程。 Spring Boot 提供了很多核心的功 能,比如自动化配置 starter(启动器) 简化 Maven 配置、内嵌 Servlet 容器、应用监控等功能, 让我们可以快速构建企业级应用程序。

特性:

  1. 创建独立的 Spring 应用程序。
  2. 嵌入式 Tomcat、 Jetty、 Undertow 容器(jar)
  3. 提供的 starters 简化构建配置(简化依赖管理和版本控制)
  4. 尽可能自动配置 spring 应用和第三方库
  5. 提供生产指标,例如指标、健壮检查和外部化配置
  6. 没有代码生成,无需 XML 配
SpringBoot 同时提供 “开箱即用”,“约定优于配置”的特性。
开箱即用:Spring Boot 应用无需从 0 开始,使用脚手架创建项目。基础配置已经完成。 集成大部分第三方
库对象,无需配置就能使用。例如在 Spring Boot 项目中使用 MyBatis。可以直接使用 XXXMapper 对象, 调用
方法执行 sql 语句。
约定优于配置:Spring Boot 定义了常用类,包的位置和结构,默认的设置。代码不需要做调整,项目能够
按照预期运行。比如启动类在根包的路径下,使用了@SpringBooApplication 注解。创建了默认的测试类。controller,
service,dao 应该放在根包的子包中。application 为默认的配置文件。
脚手架(spring 提供的一个 web 应用,帮助开发人员,创建 springboot 项目)
SpringBoot3 最小 jdk17, 支持 17-20. Spring Boot 理念“约定优于配置”,也可称为按约定编

2.1.1 与 Spring 关系

Spring 框架:

Spring Boot 创建的是 Spring 应用,对于这点非常重要。也就是使用 Spring 框架创建的应用程序。这里的 Spring
是指 Spring Framework。 我们常说的 Spring,一般指 Spring 家族,包括 Spring Boot、Spring Framework 、Spring
Data ,Spring Security,Spring Batch , Spring Shell, Spring for Apache Kafka …。
2004 年 3 月,Spring Framework1.0 发布。2006 年 10 月,Spring Framework2.0 发布
2006 年后开始,国内项目渐渐的开始应用 Spring 框架,2009 年 12 月,Spring3.0 发布。这时国内已经比较注重
Spring 使用了。项目多数转移到 Spring 框架了。 我是在 2007 开始知道渐渐了解 Spring 框架。 那个时候用 Struts
或者就是 jsp+servlet+jdbc 比较多。当时研发项目也没什么烦恼, 就一,两个技术可以用。没什么可选择的。现
在的框架,技术太多了。2017 年 09 月,Spring Framework5.0 发布。 2022 年 11 月 Spring Framework6.0 发布。
第一个版本 1.0 的 blog:https://spring.io/blog/2004/03/24/spring-framework-1-0-final-released
Spring 的核心功能:IoC , AOP , 事务管理,JDBC,SpringMVC , Spring WebFlux,集成第三方框架
MyBatis,Hibernate, Kafka , 消息队列… Spring 包含 SpringMVC, SpringMVC 作为 web 开发的强有力框架,是 Spring 中的一个模块。
首先明确一点,Spring Boot 和 Spring Framework 都是创建的 Spring 应用程序。Spring Boot 是一个新的框架,
看做是 Spring 框架的扩展,它消除了设置 Spring 应用程序所需的 XML 配置,为更快,更高效的创建 Spring
应用提供了基础平台。Spring Boot 能够快速创建基于 Spring ,SpringMVC 的普通应用以及 Web 项目。
SpringBoot 是包含了 Spring 、SpringMVC 的高级的框架,提供了自动功能,短平快。能够更快的创建 Spring
应用。消除了 Spring 的 XML 配置文件,提供了开发效率,消除 Spring 应用的臃肿。避免了大量的样板代码。
所以学习 Spring Boot 的建议:了解 Spring + SpringMVC 核心功能,基本应用是最好的,能够更快的上手 Spring
Boot。一般的 Spring Boot 课程中默认听众是会 Spring ,SpringMVC的。

在这里插入图片描述
Spring Boot 在现在 Java 开发人员必须掌握的框架。Spring Boot 是掌握 Spring Cloud 的基础。

2.1.2 与 SpringCloud关系

微服务:微服务(Microservices Architecture)是一种架构和组织方法,微服务是指单个小型的但有业务功能的服务,
每个服务都有自己的处理和轻量通讯机制,可以部署在单个或多个服务器上。
将一个大型应用的功能,依据业务功能类型,抽象出相对独立的功能,称为服务。每个服务就上一个应用程序,
有自己的业务功能,通过轻量级的通信机制与其他服务通信(通常是基于 HTTP 的 RESTful API),协调其
他服务完成业务请求的处理。 这样的服务是独立的,与其他服务是隔离的, 可以独立部署,运行。与其
他服务解耦合。
微服务看做是模块化的应用,将一个大型应用,分成多个独立的服务,通过 http 或 rpc 将多个部分联系起
来。请求沿着一定的请求路径,完成服务处理
在这里插入图片描述
项目规模大,服务多。要构建大型的分布式应用,保证应用的稳定,高效,不间断的提供服务。Spring Cloud
是对分布式项目提供了,有力的支持。
Spring Cloud 是一系列框架的有序的组合,为开发人员提供了快速构建分布式系统中常用工具(例如,配置管
理、服务发现、断路器、智能路由、微代理、控制总线、一次性令牌、全局锁、领导选举、分布式会话、集群状
态)。开发人员使用使用 Spring Cloud 这套框架和工具的集合可以快速建立实现分布式服务。这些框架需要使用
Spring Boot 作为基础开发平台。
学好了 Spring Boot,才能使用这些框架,创建良好的 Spring Cloud 应用。分布式应用.
Spring Cloud 包含的这些框架和工具各负其职,例如 Spring Cloud Config 提供配置中心的能力,给分布式多
个服务提供动态的数据配置,像数据库的 url,用户名和密码等,第三方接口数据等。 Spring Cloud Gateway 网
关,提供服务统一入口,鉴权,路由等功能。
学习 Spring Colud 难度比较大,里面框架,工具比较多。有多个框架需要学习,在把框架组合起来又是一个
难度.

2.1.3 最新的 Spring Boot3

2022 年 11 月 24 日。Spring Boot3 发布,里程碑的重大发布。这个版本应该是未来 5 年的使用主力。Spring
官网支持 Spring Boot3.0.X 版本到 2025 年。
SpringBoot3 中的重大变化:

1)JDK 最小 Java 17,能够支持 17-20. 2)Spring Boot 3.0 已将所有底层依赖项从 Java EE 迁移到了 Jakarta EE API。原来 javax 开头的包名,
修改为 jakarta。 例如 jakarta.servlet.http.HttpServlet 原来 javax.servlet.http.HttpServlet
3)支持 GraalVM 原生镜像。将 Java 应用编译为本机代码,提供显著的内存和启动性能改进。
4)对第三方库,更新了版本支持。
5)自动配置文件的修改。
6)提供新的声明式 Http 服务,在接口方法上声明@HttpExchange 获取 http 远程访问的能力。代替 OpenFeign
7)Spring HTTP 客户端提供基于 Micrometer 的可观察性. 跟踪服务,记录服务运行状态等
8)AOT(预先编译) 支持 Ahead Of Time,指运行前编译
9)Servlet6.0 规范
10)支持 Jackson 2.14。
11)Spring MVC :默认情况下使用的 PathPatternParser。删除过时的文件和 FreeMarker 、JSP 支持。
伴随着 Spring Boot3 的发布,还有 Spring Framework 6.0 的发布(2022-11-16),先于 Spring Boot 发布

2.2 代码结构

2.2.1.1 单一模块

com.example.模块名称
+----Application.java 启动类
+----controller 控制器包
			---StudentController.java
			---ScoreController.java
+----service 业务层包
			---inter 业务层接口
			---impl 接口实现包
+----repository 持久层包
+----model 模型包
			---entity 实体类
			---dto 数据传输包
			---vo 视图数据

2.2.1.2 多个模块

一个 Spring Boot 中多个模块。在根包下创建每个模块的子包, 子包中可以按“单一模块”包结构定义。
创建包含多个功能的单体 Spring Boot。 Lession05-crm (activity, sale, customer 等模块)

2.3.1.3 包和主类

我们通常建议您将主应用程序类定位在其他类之上的根包中。@SpringBootApplication 注释通常放在主类上,它 隐式地为某些项定义了一个基本的“搜索包”。例如,如果您正在编写一个 JPA 应用程序,则使用 @SpringBootApplication 注释类的包来搜索@Entity 项。使用根包还允许组件扫描只应用于您的项目。

Spring Boot 支持基于 java 的配置。尽管可以将 SpringApplication 与 XML 源一起使用,但我们通常建议您的主
源是单个@Configuration 类。通常,定义主方法的类可以作为主@Configuration 类

2.2.1.4 spring-boot-starter-parent

pom.xml 中的指定 spring-boot-starter-parent 作为坐标,表示继承 Spring Boot 提供的父项目。从
spring-boot-starter-parent 继承以获得合理的默认值和完整的依赖树,以便快速建立一个 Spring Boot 项目。 父项
目提供以下功能:

 JDK 的基准版本,比如<java.version>17</java.version>
 源码使用 UTF-8 格式编码
 公共依赖的版本
 自动化的资源过滤:默认把 src/main/resources 目录下的文件进行资源打包
 maven 的占位符为‘@’
 对多个 Maven 插件做了默认配置,如 maven-compile-plugin,maven-jar-plugin

快速创建 Spring Boot 项目,同时能够使用父项目带来的便利性,可以采用如下两种方式:

  1. 在项目中,继承 spring-boot-starter-parent
  2. pom.xml 不继承,单独加入 spring-boot-dependencies 依赖
<dependencyManagement>
<dependencies>
	<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-dependencies</artifactId>
			<version>3.0.1</version>
			<type>pom</type>
			<scope>import</scope>
	</dependency>
</dependencies>
</dependencyManagement>

2.4 运行 Spring Boot 项

 开发工具,例如 IDEA 执行 main 方法
 Maven 插件 mvn spring-boot:run
 java -jar jar 文件的路径
Spring Boot 项目可以打包为 jar 或者 war 文件。 因为 Spring Boot 内嵌了 web 服务器,例如 tomcat。能够以
jar 方式运行 web 应用。 无需安装 tomcat 程序。
创建 Lession06-package 演示打包 jar 应用。
更进一步
普通的 jar 与 Spring区别
在这里插入图片描述
在这里插入图片描述

2.5 starter

starter 是一组依赖描述,应用中包含 starter,可以获取 spring 相关技术的一站式的依赖和版本。不必复制、
粘粘代码。通过 starter 能够快速启动并运行项目。
starter 包含:
 依赖坐标、版本
 传递依赖的坐标、版本
 配置类,配置

pom.xml 加入 mybatis 依赖

<dependceny>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.2.2</version>
</dependency>

2.6 外部配置

应用程序 = 代码 + 数据(数据库,文件,url)
应用程序的配置文件:Spring Boot 允许在代码之外,提供应用程序运行的数据,以便在不同的环境中使用相
同的应用程序代码。避免硬编码,提供系统的灵活性。可使用各种外部配置源,包括 Java 属性文件、YAML 文
件、环境变量和命令行参数。
项目中经常使用 properties 与 yaml 文件,其次是命令行参数

2.6.1配置文件基础

2.6.1.1配置文件格式

配置文件有两种格式分别:properies 和 yaml(yml)。properties 是 Java 中的常用的一种配置文件格式,
key=value。key 是唯一的,文件扩展名为 properties。
yaml(YAML Ain’t Markup Language)也看做是 yml,是一种做配置文件的数据格式,基本的语法 key:[空格]
值。yml 文件文件扩展名是 yaml 或 yml(常用)

yml 格式特点:
YAML 基本语法规则:
 大小写敏感
 使用缩进表示层级关系
 缩进只可以使用空格,不允许使用 Tab 键
 缩进的空格数目不重要,相同层级的元素左侧对齐即可
 #字符表示注释,只支持单行注释。#放在注释行的第一个字符
YAML 缩进必须使用空格,而且区分大小写,建议编写 YAML 文件只用小写和空格。
YAML 支持三种数据结构
 对象:键值对的集合,又称为映射(mapping)/ 哈希(hashes) / 字典(dictionary)
 数组:一组按次序排列的值,又称为序列(sequence) / 列表(list)
 标量(scalars):单个的、不可再分的值,例如数字、字符串、true|false 等
需要掌握数据结构完整内容,可从 https://yaml.org/type/index.html 获取详细介绍。

2.6.1.2 application 文件

Spring Boot 同时支持 properties 和 yml 格式的配置文件。 配置文件名称默认是 application。我们可以使用
application.properties , application.yml。
读取配置文件的 key 值,注入到 Bean 的属性可用@Value,@Value 一次注入一个 key 的值。将多个 key 值
绑定到 Bean 的多个属性要用到@ConfigurationProperties 注解。在代码中访问属性除了注解,Spring 提供了外部
化配置的抽象对象 Environment。Environment 包含了几乎所有外部配置文件,环境变量,命令行参数等的所有 key
和 value。需要使用 Environment 的注入此对象,调用它的 getProperty(String key)方法即可。
创建新的 Spring Boot 练习配置文件的使用,项目名称 Lession07-config。Maven 作为构建工具,JDK19。包名
com.bjpowernode.config 。不需要其他依赖

Spring Boot 建议使用一种格式的配置文件,如果 properties 和 yml 都存在。properties 文件优先。推荐使用 yml
文件。
application 配置文件的名称和位置都可以修改。约定名称为 application,位置为 resources 目录

2.6.1.3 application.properties

在这里插入图片描述

2.6.1.4 application

在这里插入图片描述
Spring Boot 对 yml 文件的内容进行扁平化处理。将 yml 中的内容看做和 properties 中的格式一样处理。也就是
app: name 看做是 app.name。 所以在程序中 Spring Boot 认为 properties

2.6.1.5 Environment

Environment 是外部化的抽象,是多种数据来源的集合。从中可以读取 application 配置文件,环境变量,系统属 性。使用方式在 Bean 中注入 Environment。调用它的 getProperty(key)方法

step1:创建 ReadConfig 类,注入Environment

@Service
public class Readcordfig{
	@Autowired
	private Environment environment;
	public void print(){
String name = environment.getProperty("app.name");
//key 是否存在
if (environment.containsProperty("app.owner")) {
System.out.println("有 app.owner 配置项");
}
//读取 key 转为需要的类型,提供默认值 8000
Integer port = environment.getProperty("app.port", Integer.class, 8000);
String result = String.format("读取的 name:%s,端口 port:%d", name,port);
System.out.println("result = " + result);
}

}

step2:单元测试

@SpringBootTest
class ReadConfigTest {
@Autowired
private ReadConfig readConfig;
@Test
void test01() {
readConfig.print();
}
}

输出:
有 app.owner 配置项 result = 读取的 name:Lession07-yml,端口 port:8002

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值