一、什么是Spring?
Spring 是一个开源框架,2003年兴起的一个轻量级的Java开发框架。
Spring为了解决企业级应用开发的复杂性而创建的,简化开发。
Spring是如何简化开发的?
为了降低Java开发的复杂性,Spring采用了一下四种关键策略:
- 基于POJO的轻量级和最小侵入性编程;
- 通过IOC、依赖注入(DI)和面向接口实现松耦合;
- 基于切面(AOP)和惯例进行声明式编程;
- 通过切面和模板(template)减少样式代码;
二、什么是SpringBoot?
SPring Boot基于 Spring 开发,Spring Boot 本身并不提供 Spring 框架的核心特性以及扩展功能,只是用于快捷,敏捷地开发新一代基于 Spring 框架的应用程序。
SpringBoot 的主要优点:
- 为所有Spring 开发者更快的入门
- 开箱即用,提供各种默认配置来简化项目配置
- 内嵌式容器简化web项目
- 没有冗余代码生成和XML配置的要求
三、第一个SpringBoot程序
1、环境需求:
- jdk1.8
- maven 3.6.1
- springboot 最新版
- IDEA
2、创建项目:(建议使用第二种方法)
第一种方式:通过SPring官网,生成压缩包,可能打不开...
Spring官网:Spring | Home
- 点击 Projects,选择 Spring Boot
- 往下滑,找到 Spring initializr,点击
- 填好需要创建的项目信息,点击 最后一行的 GENERATE,生成zip压缩包
- 解压压缩包,直接导入idea。导入idea 后需加载很长时间(根据网速而定)。
第二种方式:直接使用 idea 创建新项目
- 依次点击 File->new->project
- 点击左边栏 Spring Initializr,进行项目文件信息的设置,设置完成后点击 next
- 设置 SPringBoot版本,打勾 spring web,点击 Finish
- 等待一会儿,项目新建完成!附上项目目录
3、启动项目,在页面上返回一个字符串
- 配置项目结构:
- 在 Controller 包下新建一个 java文件 :HelloControlller.java
package com.yyt.springbootdemo.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
//@Controller // 普通Controller
@RestController //返回字符串的Controller
public class HelloController {
//接口:http://localhost:8080/hello
@RequestMapping("/hello")
public String hello(){
// 调用业务,接收前端的参数
return "hello,Spring Boot!";
}
}
- 运行 SpringBootDemoApplication,在浏览器中输入 localhost:8080/hello,运行成功界面。
四、原理初探
自动配置
pom.xml:
- spring-boot-dependencies:核心依赖在父工程中。
// 依赖管理
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.9</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
//他的父依赖
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.7.9</version>
</parent>
- 在写或者引入一些SpringBoot依赖的时候,不需要指定版本,因为有版本仓库。
启动器:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
- 启动器:就是Springboot的启动场景;
- 比如:spring-boot-starter-web 就会自动导入 web 环境所有的依赖
- springboot会把所有的功能场景,变成一个一个的启动器
- 我们要使用什么功能,只需要找到对应的启动器就可以
starter
,直接在官网上寻找。
主程序:
package com.yyt.springbootdemo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
//程序的主入口
// @SpringBootApplication: 标注这个类是一个 SpringBoot的应用,启动类下的所有zi'yaun
@SpringBootApplication
public class SpringBootDemoApplication {
public static void main(String[] args) {
// 通过 run 方法,将这个程序启动。
// 该方法主要分为两部分,一部分是SpringApplication的实例化,二是run方法的执行。
SpringApplication.run(SpringBootDemoApplication.class, args);
}
}
- 注解
@SpringBootConfiguration:Springboot的配置
@Configuration:spring配置类
@Component:说明这也是一个Spring的组件
@EnableAutoConfiguration:自动配置
@AutoConfigurationPackage: 自动配置包
@Import({Registrar.class}):自动配置`包注册`
@Import({AutoConfigurationImportSelector.class}):自动配置导入选择
//获取所有的配置
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {·······}
META-INF/spring-factories:自动配置的核心文件
Properties properties = PropertiesLoaderUtils.loadProperties(resource);
所有资源加载到配置类中。
自动配置原理梳理:
总结:springboot所有自动配置都是在启动的时候扫描并加载:spring.factories
所有自动配置类都在这里面,但是并不一定生效,要判断 @ConditionalOnxxx里面的条件是否满足。只要导入了对应的 start
,就有对应的启动器,有了启动器,自动装配就会生效,就可以配置成功!
- SpringBoot在启动的时候,从类路径下
/META-INF/spring-factories
获取指定的值。 - 将这些自动配置的类导入容器,自动配置就会生效,帮我们进行自动配置。
- 以前我们需要将自动配置的东西,现在SpringBoot通过上面两步帮我们做了。
- 整合JavaEE,解决方案和自动配置的东西都在 spring-boot-autoconfigure-2.7.9.jar 这个包下
- 它会把所有需要导入的组件,以类名的方式返回,这些组件就会被添加到容器。
- 容器中也会存在非常多的 xxxxAutoConfiguration的文件,就是这些类给容器中导入了这个场景需要的所有组件,并自动配置,@Configuration ,JavaConfig
- 有了自动配置类,免去了我们手动编写配置的过程。
- 启动类
SpringApplication
这个类主要做了以下四件事:
- 推断应用的类型是普通的项目还是Web项目
- 查找并加载所有可用初始化器,设置到
initializers
属性中 - 找出所有的应用程序监听器,设置到 listeners 属性中
- 推断并设置main方法的定义类,找到运行的主类
五、yaml 语法讲解
(1)配置文件
SpringBoot 使用一个全局的配置文件,配置文件名称是固定的
- application.properties
- 语法结构:Key = value
- application.yml
- 语法结构:key:空格 value
配置文件的作用:修改SpringBoot自动配置的默认值,因为SpringBoot在底层都给我们自动配置好了。
(2)yaml概述
YAML是“YAML Ain’t a Markup Language”(YAML不是一种标记语言)的递归缩写。
这种语言以数据作为中心,而不是以标记语言为重点!!
以前的配置文件,大多数都是使用 xml 来配置:比如一个简单的端口配置,对比一下传统的yaml和xml
传统的xml配置:
<server>
<port>8080</port>
</server>
yaml配置:
server:
port: 8080
(3)yaml基本语法
说明:语法要求严格!
- 空格不能省略
- 以缩进来控制层级关系,只要是左边对齐的一列数据都是同一层级的
- 属性和值的大小写都是十分敏感的。
1、字面量:普通的值【数字,布尔值,字符串】
字面量直接写在后面就可以,字符串默认不用加上双引号或者单引号;
# K = V 普通的 key - value
name: yyt
注意:
- “ ”双引号,不会转义字符串里面的特殊字符,特殊字符会作为本身想表示的意思: 比如:name:“yyt \n hello” 输出:yyt 换行 hello
- ‘’单引号,会转义特殊字符,特殊字符最终会变成和普通字符一样输出
比如:name:“yyt \n hello” 输出:yyt \n hello
2、对象、Map(键值对)
#对象、Map格式
k:
v1:
V2:
在下一行来写对象的属性和值的关系,注意缩进。
student:
name: yyt
age: 18
行内写法:
student: {name: yyt,age: 18}
3、数组(List、set)
用 - 值表示数组中的一个元素,比如:
pets:
- cat
- dog
- pig
行内写法:
pets: [cat,dog,pig]
4、修改 SPringBoot 的默认端口号
配置文件中添加,端口号的参数,就可以切换端口:
server:
port: 8080
(4)通过绑定配置文件,注入值的方法(重要!!)
① 通过 yaml 注入配置文件,绑定值
- 在 springboot 项目中的 resources 目录下新建一个文件 application.yml。
在 Springboot 项目中
application.properties
和application.yml
同时存在时,两个文件都是有效的,但是优先级不同,application.properties 的优先级要高于application.yml,但在springboot中推荐使用 yaml文件。 - 编写一个实体类 Dog
- 直接在类中 使用 @Value 给bean注入属性值。
package com.yyt.springbootdemo.pojo;
import lombok.Data;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
/**
* @author yyt
* @version 1.0
* @data 2023/3/31 17:58
* @Project_Name SpringBootDemo
* @Component 定义Spring管理Bean(也就是将标注@Component注解的类交由spring管理)
* @Value() 直接使用注解进行赋值
* @Data lombok方法,快速构造 有参无参构造,get、set方法,tostring()方法
*/
@Component
@Data
public class Dog {
// 狗狗的名字
@Value("小五")
private String name;
// 狗狗的年龄
@Value("3")
private Integer age;
}
4.在 SpringBoot 的测试类下给 Dog 类输出一下。
package com.yyt.springbootdemo;
import com.yyt.springbootdemo.pojo.Dog;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
/**
* @Autowired 将类自动注入进来,自动装配的作用
*/
//单元测试
@SpringBootTest
class SpringBootDemoApplicationTests {
@Autowired
private Dog dog;
@Test
void contextLoads() {
System.out.println(dog);
}
}
5.结果成功输出,@Value注入成功!
6.继续编写一个较复杂的实体类,Person 类
package com.yyt.springbootdemo.pojo;
import lombok.Data;
import org.springframework.stereotype.Component;
import java.util.Date;
import java.util.List;
import java.util.Map;
/**
* @author yyt
* @version 1.0
* @data 2023/3/31 18:01
* @Project_Name SpringBootDemo
*/
@Component
@Data
public class Person {
private String name;
private Integer age;
private Boolean happy;
private Date birth;
private Map<String,Object> maps;
private List<Object> list;
private Dog dog;
}
7.不同于上面的用 @Value 注入值的方法,这次,使用 yaml 配置的方法进行值的注入。
person:
name: yyt
age: 18
happy: true
birth: 2008/01/01
maps: {k1: v1,k2: v2}
list:
- code
- boy
- food
dog:
name: 小高
age: 3
8.通过上面 yaml配置的方式,已将 person 的对象所有值写好,接下来注入到 person 类中,就是添加注解 @ConfigurationProperties
;
package com.yyt.springbootdemo.pojo;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
import java.util.Date;
import java.util.List;
import java.util.Map;
/**
* @author yyt
* @version 1.0
* @data 2023/3/31 18:01
* @Project_Name SpringBootDemo
*
* @ConfigurationProperties 作用:
* 将配置文件中配置的每一个属性的值,映射到这个组件中;
* 告诉SpringBoot 将本类中的所有属性和配置文件中相关的配置进行绑定
* 参数 prefix = "person":将配置文件中的 person 下面的所有属性一一对应。
*/
@Component
@Data
@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> list;
private Dog dog;
}
9.接下来,去测试类中进行测试:
package com.yyt.springbootdemo;
import com.yyt.springbootdemo.pojo.Person;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
//单元测试
@SpringBootTest
class SpringBootDemoApplicationTests {
@Autowired
private Person person;
@Test
void contextLoads() {
System.out.println(person);
}
}
10.查看结果:
② 通过加载指定的配置文件,绑定值
@PropertySource : 加载指定的配置文件;
@ConfigurationProperties : 默认从全局配置文件中获得值;
- 在 resources 目录下新建一个
person.properties
文件
name=yyt
- 然后 person类中指定加载 person.properties 文件,使用
@PropertySource
注解
package com.yyt.springbootdemo.pojo;
import lombok.Data;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.PropertySource;
import org.springframework.stereotype.Component;
import java.util.Date;
import java.util.List;
import java.util.Map;
/**
* @author yyt
* @version 1.0
* @data 2023/3/31 18:01
* @Project_Name SpringBootDemo
* @PropertySource(value = "classpath:person.properties") 加载指定的配置文件
*/
@Component
@Data
@PropertySource(value = "classpath:person.properties")
public class Person {
// 用 EL 表达式取出配置文件
@Value("${name}")
private String name;
private Integer age;
private Boolean happy;
private Date birth;
private Map<String,Object> maps;
private List<Object> list;
private Dog dog;
}
六、JSR303 数据校验
Springboot 中可以用 @Validated 来校验数据,如果数据异常则会统一抛出异常,方便异常中心统一处理。
下面,写个注解,让 person类 中的 name 只能支持 Email 格式;
package com.yyt.springbootdemo.pojo;
import lombok.Data;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.PropertySource;
import org.springframework.stereotype.Component;
import org.springframework.validation.annotation.Validated;
import javax.validation.constraints.Email;
import java.util.Date;
import java.util.List;
import java.util.Map;
/**
* @author yyt
* @version 1.0
* @data 2023/3/31 18:01
* @Project_Name SpringBootDemo
* @Validated:数据校验注解
* @Email()只能支持 Email 格式,参数为错误提示。
* */
@Component
@Data
@ConfigurationProperties(prefix = "person")
@Validated
public class Person {
@Email(message = "name 输入格式不正确,请输入邮箱格式!")
private String name;
private Integer age;
private Boolean happy;
private Date birth;
private Map<String,Object> maps;
private List<Object> list;
private Dog dog;
}
输出:
使用数据校验,可以保证数据的正确性!
常见的参数:
@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 对象是否符合正则表达式的规则
.......等等
除此以外,我们还可以自定义一些数据校验规则
七、多环境配置及配置日志文件
1、多环境配置
① 多环境配置优点:
- ⽅便应⽤程序的管理和维护:在不同环境下,我们可以灵活地配置应⽤程序的参数,例如⽇志级
别、调试模式等,⽅便应⽤程序的管理和维护。
- 提⾼应⽤程序的可移植性:将应⽤程序的配置信息与代码分离,可以提⾼应⽤程序的可移植性,使
其更容易部署到不同的环境中。
- 增强应⽤程序的安全性:通过多环境配置,我们可以将敏感信息(如密码、密钥等)存放在安全的
地⽅,例如配置⽂件中的加密部分或操作系统环境变量中,从⽽增强应⽤程序的安全性。
② 具体配置
在Spring Boot中,可以通过在application.properties
或application.yml
⽂件中定义不同的属性来
配置多个环境。
- 在 src/main/resources目录下,创建
application-{profile}.properties
或application-{profile}.yml
文件,其中 {profile}表示环境名称。例如:application-dev.yml 或 application-prod-yml.
- 在各个环境的配置文件中,可以定义与 application.properties 或 application.yml 中相同的属性,覆盖原有的默认值。
application.yml
(默认配置文件)
server:
port: 8888
application-dev.yml
(开发环境配置文件)
server:
port: 8080
application-pro.yml
(生产环境配置文件)
server:
port: 8081
application-test.yml
(测试环境配置文件)
server:
port: 8082
3.在启动应用程序时,可以通过设置 spring.profiles.active
属性来指定要使用的环境名称。application.yml
server:
port: 8888
spring:
profiles:
active: dev # 启用 application-dev.yml 的配置
如果不指定 spring.profiles.active
属性,则会默认使用 application.properties 或 application.yml中的配置。
4、结果展示:
启用的端口号就为 application-dev.yml
的配置。
2、日志文件配置
① 概念
日志(log)是指在软件或系统运⾏过程中记录下来的事件或状态信息。在软件或系统开发中,日志
是⼀种非常重要的调试⼯具,可以帮助开发者了解程序或系统的运⾏状态,从而快速地定位和解决问
题。
日志可以包含各种信息,例如程序或系统运⾏的时间、事件类型、发⽣时间、相关参数、异常信
息、警告信息等。通过记录这些信息,开发者可以根据需要对⽇志进⾏分析和处理,以了解程序或系统
在运⾏中发⽣的问题和异常情况。
② 功能
- 调试和排错:通过分析⽇志信息,可以快速定位和解决程序或系统的问题,提⾼开发效率。
- 监控和分析:通过对⽇志信息的监控和分析,可以了解程序或系统的运⾏情况,及时发现和解决潜
在问题,提⾼系统的稳定性和可靠性。
- 安全性和审计:通过对⽇志信息的记录和分析,可以跟踪和审计系统中的操作⾏为,提⾼系统的安
全性和防⽌不当操作。
③ 日志的级别
在 Springboot 项目中日志一共有 6 个级别,分别从低到高为 trace
<debug
<info
<warn
<error
<fetal
.
trace
:微量,少许的意思,级别最低。debug
:需要调试的时候关键信息打印。info
:普通的打印信息。warn
:警告,不影响使用,但需要注意的问题。error
:错误信息,级别较高的错误日志信息。fetal
:致命的,因为代码异常导致程序退出执行的事件。
④ Springboot 日志配置
在 springboot 中是内置了日志框架的,所以只要运行 springboot 日志就会被打印在控制台上。但是输出的日志并不是开发者自己定义和打印的。而且,控制台上的日志是不能保存的,所以从以上两个角度出发,可以提出两个问题:
- 开发者自定义打印日志。
- 日志的持久化
日志的配置:在 application.yml 中配置:
logging:
file:
name: server.log # 输出的日志文件的名称(xxx.log)
path: /computer/springboot-web-demo # 输出的日志文件存放的目录(一般于项目目录相同,便于查看)
level:
com.yyt.controller: debug # 日志级别 可以直接全局定义,也可以包名局部定义
logback:
rollingpolicy: # 日志滚动保存
max-file-size: 4KB # 最大文件4KB,但日志文件大于4KB时,再次新建日志文件。
file-name-pattern: server.%d{yyyy-MM-dd}.%i.log # 新建文件的命名格式
通过以上配置,提出的两个问题都可以解决。在yml中,开发者可以自定义打印日志,可以使用logging.file.path
对输出的日志文件进行持久化。
⑤ 日志的使用 (举例)
5.1 获取日志对象,打印日志(使用对象的配置)
- 在需要进行打印日志的操作(controller)下进行对日志对象的获取。在 controller包 下新建 PageController 。
package com.yyt.controller;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
/**
* @author yyt
* @ProjectName springboot-demo
* @createTime 2023/3/28
* @Desc TODO
*/
@Controller
public class PageController {
// 获取日志对象
private static final Logger logger= LoggerFactory.getLogger(PageController.class);
// 每次提交/,index请求的时候生成日志文件。
@GetMapping(value = {"/","/index"})
public String index(){
logger.debug("debug logging~~~");
logger.info("info logging~~~");
logger.warn("warn logging~~~");
logger.error("error logging~~~");
return "login";
}
}
2、启动项目,输入 http://localhost:8080/,控制台输出,并生成日志文件。
5.2 Lombok注解 @slf4j,打印日志
通过在类上增加 @slf4j
注解引入日志框架,将会获得一个全局的 log
对象。效果一样,只不过不用获取对象。
package com.yyt.controller;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
/**
* @author yyt
* @ProjectName springboot-demo
* @createTime 2023/3/28
* @Desc TODO
*/
@Controller
@Slf4j //引入日志框架
public class PageController {
@GetMapping(value = {"/","/index"})
public String index(){
log.debug("debug logging~~~");
log.info("info logging~~~");
log.warn("warn logging~~~");
log.error("error logging~~~");
return "login";
}
}