Web后端实战:(部门管理)

1.准备工作

1.1开发规范

1.1.1前后端分离开发

我们目前基本都是采用的前后台分离开发方式,如下图所示:
[图片]

那么基于前后台分离开发的模式下,我们后台开发者开发一个功能的具体流程如何呢?如下图所示:
[图片]

  1. 需求分析:首先我们需要阅读需求文档,分析需求,理解需求。
  2. 接口定义:查询接口文档中关于需求的接口的定义,包括地址,参数,响应数据类型等等
  3. 前后台并行开发:各自按照接口文档进行开发,实现需求
  4. 测试:前后台开发完了,各自按照接口文档进行测试
  5. 前后段联调测试:前段工程请求后端工程,测试功能

1.1.2 Restful风格

在前后端进行交互的时候,我们需要基于当前主流的REST风格的API接口进行交互。

什么是REST风格呢?

  • REST(Representational State Transfer),表述性状态转换,它是一种软件架构风格。

传统URL风格如下:

  • http://localhost:8080/user/getById?id=1 GET:查询id为1的用户
  • http://localhost:8080/user/saveUser POST:新增用户
  • http://localhost:8080/user/updateUser POST:修改用户
  • http://localhost:8080/user/deleteUser?id=1 GET:删除id为1的用户

我们看到,原始的传统URL呢,定义比较复杂,而且将资源的访问行为对外暴露出来了。而且,对于开发人员来说,每一个开发人员都有自己的命名习惯,就拿根据id查询用户信息来说的,不同的开发人员定义的路径可能是这样的:getById,selectById,queryById,loadById... 。 每一个人都有自己的命名习惯,如果都按照各自的习惯来,一个项目组,几十号或上百号人,那最终开发出来的项目,将会变得难以维护,没有一个统一的标准。

基于REST风格URL如下:

  • http://localhost:8080/users/1 GET:查询id为1的用户
  • http://localhost:8080/users POST:新增用户
  • http://localhost:8080/users PUT:修改用户
  • http://localhost:8080/users/1 DELETE:删除id为1的用户
    其中总结起来,就一句话:通过URL定位要操作的资源,通过HTTP动词(请求方式)来描述具体的操作。

在REST风格的URL中,通过四种请求方式,来操作数据的增删改查。

  • GET : 查询
  • POST :新增
  • PUT : 修改
  • DELETE :删除
    我们看到如果是基于REST风格,定义URL,URL将会更加简洁、更加规范、更加优雅。

注意事项:

  • REST是风格,是约定方式,约定不是规定,可以打破
  • 描述模块的功能通常使用复数,也就是加s的格式来描述,表示此类资源,而非单个资源。如:users、emps、books…

1.1.3 Apifox

因为在浏览器地中所发起的所有的请求,都是GET方式的请求。那大家就需要思考两个问题:

  • 前后端都在并行开发,后端开发完对应的接口之后,如何对接口进行请求测试呢?
  • 前后端都在并行开发,前端开发过程中,如何获取到数据,测试页面的渲染展示呢?
    那这里我们就可以借助一些接口测试工具,比如项:Postman、Apipost、Apifox等。
    [图片]

那这些工具的使用基本类似,只不过Apifox工具的功能更强强大、更加完善,所以在课程中,我们会采用功能更为强大的Apifox工具。

1.2工程搭建

1). 创建SpringBoot工程,并引入web开发起步依赖、mybatis、mysql驱动、lombok。

  • 创建项目
    [图片]

2). 创建数据库及对应的表结构,并在application.yml中配置数据库的基本信息。

  • 创建tlias数据库,并准备dept部门表。
CREATE TABLE dept (id int unsigned PRIMARY KEY AUTO_INCREMENT COMMENT 'ID, 主键',name varchar(10) NOT NULL UNIQUE COMMENT '部门名称',create_time datetime DEFAULT NULL COMMENT '创建时间',update_time datetime DEFAULT NULL COMMENT '修改时间'
) COMMENT '部门表';INSERT INTO dept VALUES (1,'学工部','2023-09-25 09:47:40','2024-07-25 09:47:40'),(2,'教研部','2023-09-25 09:47:40','2024-08-09 15:17:04'),(3,'咨询部','2023-09-25 09:47:40','2024-07-30 21:26:24'),(4,'就业部','2023-09-25 09:47:40','2024-07-25 09:47:40'),(5,'人事部','2023-09-25 09:47:40','2024-07-25 09:47:40'),(6,'行政部','2023-11-30 20:56:37','2024-07-30 20:56:37');
  • 在 application.yml 配置文件中配置数据库的连接信息。
spring:application:name: tlias-web-management#mysql连接配置datasource:driver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://localhost:3306/tliasusername: rootpassword: 1234
mybatis:configuration:log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

3). 准备基础包结构,并引入实体类Dept及统一的响应结果封装类Result

  • 准备基础包结构
    [图片]

  • 实体类Dept

package com.itheima.pojo;import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;import java.time.LocalDateTime;@Data
@NoArgsConstructor
@AllArgsConstructor
public class Dept {private Integer id;private String name;private LocalDateTime createTime;private LocalDateTime updateTime;
}
  • 统一响应结果Result
package com.itheima.pojo;import lombok.Data;
import java.io.Serializable;/*** 后端统一返回结果*/
@Data
public class Result {private Integer code; //编码:1成功,0为失败private String msg; //错误信息private Object data; //数据public static Result success() {Result result = new Result();result.code = 1;result.msg = "success";return result;}public static Result success(Object object) {Result result = new Result();result.data = object;result.code = 1;result.msg = "success";return result;}public static Result error(String msg) {Result result = new Result();result.msg = msg;result.code = 0;return result;}}
  • 基础代码结构
    [图片]

    • DeptMapper
package com.itheima.mapper;import org.apache.ibatis.annotations.Mapper;@Mapper
public interface DeptMapper {
}
  • DeptService
package com.itheima.service;public interface DeptService {
}
  • DeptServiceImpl
package com.itheima.service.impl;import com.itheima.service.DeptService;
import org.springframework.stereotype.Service;@Service
public class DeptServiceImpl implements DeptService {
}
  • DeptController
package com.itheima.controller;import org.springframework.web.bind.annotation.RestController;/*** 部门管理控制器*/
@RestController
public class DeptController {
}

2.查询部门

2.1基本实现

2.1.1需求

查询所有的部门数据,查询出来展示在部门管理的页面中。页面原型效果如下:
[图片]

2.1.2接口描述

1 基本信息

请求路径:/depts > > 请求方式:GET > > 接口描述:该接口用于部门列表数据查询

2 请求参数

3 响应数据
参数格式:application/json

参数说明:在这里插入图片描述
响应数据样例:

{"code": 1,"msg": "success","data": [{"id": 1,"name": "学工部","createTime": "2024-09-01T23:06:29","updateTime": "2024-09-01T23:06:29"},{"id": 2,"name": "教研部","createTime": "2022-09-01T23:06:29","updateTime": "2022-09-01T23:06:29"}]
}

2.1.3实现思路

明确了删除部门的需求之后,再来梳理一下实现该功能时,三层架构每一层的职责:
[图片]

  • Controller层,负责接收前端发起的请求,并调用service查询部门数据,然后响应结果。
  • Service层,负责调用Mapper接口方法,查询所有部门数据。
  • Mapper层,执行查询所有部门数据的操作。

2.1.4代码实现

1). Controller层
在 DeptController 中,增加 list 方法,代码如下:

/*** 部门管理控制器*/
@RestController
public class DeptController {@Autowiredprivate DeptService deptService;/*** 查询部门列表*/@RequestMapping("/depts")public Result list(){List<Dept> deptList = deptService.findAll();return Result.success(deptList);}
}

2). Service层
在 DeptService 中,增加 findAll方法,代码如下:

public interface DeptService {/*** 查询所有部门*/public List<Dept> findAll();
}

在 DeptServiceImpl 中,增加 findAll方法,代码如下:

@Service
public class DeptServiceImpl implements DeptService {@Autowiredprivate DeptMapper deptMapper;public List<Dept> findAll() {return deptMapper.findAll();}
}

3). Mapper层
在 DeptMapper 中,增加 findAll方法,代码如下:

@Mapper
public interface DeptMapper {/*** 查询所有部门*/@Select("select * from dept")public List<Dept> findAll();}

2.1.5接口测试

启动项目,然后我们就可以打开Apifox进行测试了。
在这里插入图片描述

我们发现,已经查询出了所有的部门数据,并且响应回来的就是json格式的数据,与接口文档一致。 那接下来,我们再来测试一下,这个查询操作,我们使用post、put、delete方式来请求,是否可以获取到数据。
[图片]

经过测试,我们发现,现在我们其实是可以通过任何方式的请求来访问查询部门的这个接口的。 而在接口文档中,明确要求该接口的请求方式为GET,那么如何限制请求方式呢?

  • 在controller方法上使用,@RequestMapping的衍生注解 @GetMapping。 该注解就是标识当前方法,必须以GET方式请求。
@RestController
public class DeptController {@Autowiredprivate DeptService deptService;/*** 查询部门列表*/@GetMapping("/depts")public Result list(){List<Dept> deptList = deptService.findAll();return Result.success(deptList);}
}
  • GET方式:@GetMapping
  • POST方式:@PostMapping
  • PUT方式:@PutMapping
  • DELETE方式:@DeleteMapping

2.1.6数据封装

在上述测试中,我们发现部门的数据中,id、name两个属性是有值的,但是createTime、updateTime两个字段值并未成功封装,而数据库中是有对应的字段值的,这是为什么呢?
[图片]

原因如下:

  • 实体类属性名和数据库表查询返回的字段名一致,mybatis会自动封装。
  • 如果实体类属性名和数据库表查询返回的字段名不一致,不能自动封装。

解决方案:

  • 手动结果映射
  • 起别名
  • 开启驼峰命名

如果字段名与属性名符合驼峰命名规则,mybatis会自动通过驼峰命名规则映射。驼峰命名规则: abc_xyz => abcXyz

  • 表中字段名:abc_xyz
  • 类中属性名:abcXyz
    在application.yml中做如下配置,开启开关。
mybatis:configuration:map-underscore-to-camel-case: true

要使用驼峰命名前提是 实体类的属性 与 数据库表中的字段名严格遵守驼峰命名。

2.2前后端联调

2.2.1联调测试

1.双击 nginx.exe 启动Nginx,一闪而过,就说明nginx已启动完成。
在这里插入图片描述

如果在任务管理器中,能看到上述两个进程,就说明nginx已经启动成功。

2.打开浏览器,访问:http://localhost:90
[图片]

2.2.2请求访问流程

前端工程请求服务器的地址为 http://localhost:90/api/depts,是如何访问到后端的tomcat服务器的?
[图片]

其实这里,是通过前端服务Nginx中提供的反向代理功能实现的。
[图片]

1). 浏览器发起请求,请求的是localhost:90 ,那其实请求的是nginx服务器。
2). 在nginx服务器中呢,并没有对请求直接进行处理,而是将请求转发给了后端的tomcat服务器,最终由tomcat服务器来处理该请求。

这个过程就是通过nginx的反向代理实现的。 那为什么浏览器不直接请求后端的tomcat服务器,而是直接请求nginx服务器呢,主要有以下几点原因:
[图片]

1). 安全:由于后端的tomcat服务器一般都会搭建集群,会有很多的服务器,把所有的tomcat暴露给前端,让前端直接请求tomcat,对于后端服务器是比较危险的。
2). 灵活:基于nginx的反向代理实现,更加灵活,后端想增加、减少服务器,对于前端来说是无感知的,只需要在nginx中配置即可。
3). 负载均衡:基于nginx的反向代理,可以很方便的实现后端tomcat的负载均衡操作。

具体的请求访问流程如下:
[图片]

  1. location:用于定义匹配特定uri请求的规则。
  2. ^~ /api/:表示精确匹配,即只匹配以/api/开头的路径。
  3. rewrite:该指令用于重写匹配到的uri路径。
  4. proxy_pass:该指令用于代理转发,它将匹配到的请求转发给位于后端的指令服务器。

3.删除部门

3.1需求

删除部门数据。在点击 “删除” 按钮,会根据ID删除部门数据。
[图片]

3.2接口描述

1 基本信息

请求路径:/depts > > 请求方式:DELETE > > 接口描述:该接口用于根据ID删除部门数据

2 请求参数
参数说明:
在这里插入图片描述
请求参数样例:

/depts?id=1
/depts?id=2

3 响应数据
参数格式:application/json

参数说明:
在这里插入图片描述
响应数据样例:

{"code":1,"msg":"success","data":null
}

3.3思路分析

明确了删除部门的需求之后,再来梳理一下实现该功能时,三层架构每一层的职责:
[图片]

3.4简单参数接收

  • 如果请求参数名与形参变量名相同,直接定义方法形参即可接收。(省略@RequestParam)
@DeleteMapping("/depts")
public Result delete(Integer id){System.out.println("根据ID删除部门: " + deptId);return Result.success();
}

3.5代码实现

1). Controller层
在 DeptMapper 中,增加 delete 方法,代码实现如下:

/*** 根据id删除部门 - delete http://localhost:8080/depts?id=1*/
@DeleteMapping("/depts")
public Result delete(Integer id){System.out.println("根据id删除部门, id=" + id);deptService.deleteById(id);return Result.success();
}

2). Service层
在 DeptService 中,增加 deleteById 方法,代码实现如下:

/*** 根据id删除部门*/
void deleteById(Integer id);DeptServiceImpl 中,增加 deleteById 方法,代码实现如下:
public void deleteById(Integer id) {deptMapper.deleteById(id);
}

3). Mapper层
在 DeptMapper 中,增加 deleteById 方法,代码实现如下:

/*** 根据id删除部门*/
@Delete("delete from dept where id = #{id}")
void deleteById(Integer id);

如果mapper接口方法形参只有一个普通类型的参数,#{…} 里面的属性名可以随便写,如:#{id}、#{value}。
对于 DML 语句来说,执行完毕,也是有返回值的,返回值代表的是增删改操作,影响的记录数,所以可以将执行 DML 语句的方法返回值设置为 Integer。 但是一般开发时,是不需要这个返回值的,所以也可以设置为void。

代码编写完毕之后,我们就可以启动服务,进行测试了。
[图片]

4.新增部门

4.1需求

点击 “新增部门” 的按钮之后,弹出新增部门表单,填写部门名称之后,点击确定之后,保存部门数据。
[图片]

4.2接口描述

1.3.1 基本信息

请求路径:/depts > > 请求方式:POST > > 接口描述:该接口用于添加部门数据

1.3.2 请求参数
格式:application/json

参数说明:在这里插入图片描述
请求参数样例:

{"name": "教研部"
}

1.3.3 响应数据
参数格式:application/json

参数说明:
在这里插入图片描述
响应数据样例:

{"code":1,"msg":"success","data":null
}

4.3思路分析

明确了新增部门的需求之后,再来梳理一下实现该功能时,三层架构每一层的职责:在这里插入图片描述

4.4 json参数接收

我们看到,在controller中,需要接收前端传递的请求参数。 那接下来,我们就先来看看在服务器端的Controller程序中,如何获取json格式的参数。

  • JSON格式的参数,通常会使用一个实体对象进行接收 。
  • 规则:JSON数据的键名与方法形参对象的属性名相同,并需要使用@RequestBody注解标识。

前端传递的请求参数格式为json,内容如下:{"name":"研发部"}。这里,我们可以通过一个对象来接收,只需要保证对象中有name属性即可。
[图片]

4.5代码实现

1). Controller层
在DeptController中增加方法save,具体代码如下:

/*** 新增部门 - POST http://localhost:8080/depts   请求参数:{"name":"研发部"}*/
@PostMapping("/depts")
public Result save(@RequestBody Dept dept){System.out.println("新增部门, dept=" + dept);deptService.save(dept);return Result.success();
}

2). Service层
在DeptService中增加接口方法save,具体代码如下:

/*** 新增部门*/
void save(Dept dept);DeptServiceImpl中增加save方法,完成添加部门的操作,具体代码如下:
public void save(Dept dept) {//补全基础属性dept.setCreateTime(LocalDateTime.now());dept.setUpdateTime(LocalDateTime.now());//保存部门deptMapper.insert(dept);
}

3). Mapper层

/*** 保存部门*/
@Insert("insert into dept(name,create_time,update_time) values(#{name},#{createTime},#{updateTime})")
void insert(Dept dept);

如果在mapper接口中,需要传递多个参数,可以把多个参数封装到一个对象中。 在SQL语句中获取参数的时候,#{…} 里面写的是对象的属性名**【注意是属性名,不是表的字段名】。**

代码编写完毕之后,我们就可以启动服务,进行测试了。
[图片]

[图片]

5.修改部门

5.1查询回显

5.1.1需求

当我们点击 “编辑” 的时候,需要根据ID查询部门数据,然后用于页面回显展示。
[图片]

5.1.2接口描述

1 基本信息

请求路径:/depts/{id} > > 请求方式:GET > > 接口描述:该接口用于根据ID查询部门数据

2 请求参数
参数格式:路径参数

参数说明:在这里插入图片描述
请求参数样例:

/depts/1
/depts/3

3 响应数据
参数格式:application/json

参数说明:在这里插入图片描述
响应数据样例:

{"code": 1,"msg": "success","data": {"id": 1,"name": "学工部","createTime": "2022-09-01T23:06:29","updateTime": "2022-09-01T23:06:29"}
}

5.1.3思路分析

在这里插入图片描述

5.1.4路径参数接收

/depts/1,/depts/2 这种在url中传递的参数,我们称之为路径参数。 那么如何接收这样的路径参数呢 ?
路径参数:通过请求URL直接传递参数,使用{…}来标识该路径参数,需要使用 @PathVariable获取路径参数。如下所示:
[图片]

如果路径参数名与controller方法形参名称一致,@PathVariable注解的value属性是可以省略的。

5.1.5代码实现

1). Controller层
在 DeptController 中增加 getById方法,具体代码如下:

/*** 根据ID查询 - GET http://localhost:8080/depts/1*/
@GetMapping("/depts/{id}")
public Result getById(@PathVariable Integer id){System.out.println("根据ID查询, id=" + id);Dept dept = deptService.getById(id);return Result.success(dept);
}

2). Service层
在 DeptService 中增加 getById方法,具体代码如下:

/*** 根据id查询部门*/
Dept getById(Integer id);DeptServiceImpl 中增加 getById方法,具体代码如下:
public Dept getById(Integer id) {return deptMapper.getById(id);
}

3). Mapper层
在 DeptMapper 中增加 getById 方法,具体代码如下:

/**
* 根据ID查询部门数据
*/
@Select("select id, name, create_time, update_time from dept where id = #{id}")
Dept getById(Integer id);

代码编写完毕之后,我们就可以启动服务,进行测试了。
[图片]

5.2修改数据

5.2.1需求

在这里插入图片描述

5.2.2接口描述

1 基本信息

请求路径:/depts > > 请求方式:PUT > > 接口描述:该接口用于修改部门数据

2 请求参数
格式:application/json

参数说明:

参数名	类型	是否必须	备注
id	number	必须	部门ID
name	string	必须	部门名称
请求参数样例:

{"id": 1,"name": "教研部"
}

3 响应数据
参数格式:application/json

参数说明:在这里插入图片描述
响应数据样例:

{"code":1,"msg":"success","data":null
}

5.2.3思路分析

在这里插入图片描述

5.2.4代码实现

1). Controller层
在 DeptController 中增加 update 方法,具体代码如下:

/*** 修改部门 - PUT http://localhost:8080/depts  请求参数:{"id":1,"name":"研发部"}*/
@PutMapping("/depts")
public Result update(@RequestBody Dept dept){System.out.println("修改部门, dept=" + dept);deptService.update(dept);return Result.success();
}

2). Service层
在 DeptService 中增加 update 方法。

/*** 修改部门*/
void update(Dept dept);DeptServiceImpl 中增加 update 方法。 由于是修改操作,每一次修改数据,都需要更新updateTime。所以,具体代码如下:
public void update(Dept dept) {//补全基础属性dept.setUpdateTime(LocalDateTime.now());//保存部门deptMapper.update(dept);
}

3). Mapper层
在 DeptMapper 中增加 update 方法,具体代码如下:

/*** 更新部门*/
@Update("update dept set name = #{name},update_time = #{updateTime} where id = #{id}")
void update(Dept dept);

代码编写完毕之后,我们就可以启动服务,进行测试了。
[图片]

修改完成之后,我们可以看到最新的数据,如下:
[图片]

5.2.5 @RequestMapping

到此呢,关于基本的部门的增删改查功能,我们已经实现了。 我们会发现,我们在 DeptController 中所定义的方法,所有的请求路径,都是 /depts 开头的,只要操作的是部门数据,请求路径都是 /depts 开头。
那么这个时候,我们其实是可以把这个公共的路径 /depts 抽取到类上的,那在各个方法上,就可以省略了这个 /depts 路径。 代码如下:
[图片]

一个完整的请求路径,应该是类上的 @RequestMapping 的value属性 + 方法上的
@RequestMapping的value属性。

6.日志技术

6.1概述

如果通过 System.out.println(…) 来记录日志,会存在以下几点问题:

  • 硬编码。所有的记录日志的代码,都是硬编码,没有办法做到灵活控制,要想不输出这个日志了,只能删除掉记录日志的代码。
  • 只能输出日志到控制台。
  • 不便于程序的扩展、维护。

所以,在现在的项目开发中,我们一般都会使用专业的日志框架,来解决这些问题。

6.2日志框架

在这里插入图片描述

  • JUL:这是JavaSE平台提供的官方日志框架,也被称为JUL。配置相对简单,但不够灵活,性能较差。
  • Log4j:一个流行的日志框架,提供了灵活的配置选项,支持多种输出目标。
  • Logback:基于Log4j升级而来,提供了更多的功能和配置选项,性能由于Log4j。
  • Slf4j:(Simple Logging Facade for Java)简单日志门面,提供了一套日志操作的标准接口及抽象类,允许应用程序使用不同的底层日志框架。

6.3 Logback入门

1). 准备工作:引入logback的依赖(springboot中无需引入,在springboot中已经传递了此依赖)

<dependency><groupId>ch.qos.logback</groupId><artifactId>logback-classic</artifactId><version>1.4.11</version>
</dependency>

2). 引入配置文件 logback.xml

<?xml version="1.0" encoding="UTF-8"?>
<configuration><!-- 控制台输出 --><appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"><encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder"><!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度  %msg:日志消息,%n是换行符 --><pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50}-%msg%n</pattern></encoder></appender><!-- 日志输出级别 --><root level="ALL"><appender-ref ref="STDOUT" /></root>
</configuration>

3). 记录日志:定义日志记录对象Logger,记录日志

public class LogTest {//定义日志记录对象private static final Logger log = LoggerFactory.getLogger(LogTest.class);@Testpublic void testLog(){log.debug("开始计算...");int sum = 0;int[] nums = {1, 5, 3, 2, 1, 4, 5, 4, 6, 7, 4, 34, 2, 23};for (int i = 0; i < nums.length; i++) {sum += nums[i];}log.info("计算结果为: "+sum);log.debug("结束计算...");}}

运行单元测试,可以在控制台中看到输出的日志,如下所示:
[图片]

我们可以看到在输出的日志信息中,不仅输出了日志的信息,还包括:日志的输出时间、线程名、具体在那个类中输出的。

6.4 Logback配置文件

Logback日志框架的配置文件叫 logback.xml 。
该配置文件是对Logback日志框架输出的日志进行控制的,可以来配置输出的格式、位置及日志开关等。
常用的两种输出日志的位置:控制台、系统文件。

1). 如果需要输出日志到控制台。添加如下配置:

<!-- 控制台输出 -->
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"><encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder"><!--格式化输出:%d 表示日期,%thread 表示线程名,%-5level表示级别从左显示5个字符宽度,%msg表示日志消息,%n表示换行符 --><pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50}-%msg%n</pattern></encoder>
</appender>

2). 如果需要输出日志到文件。添加如下配置:

<!-- 按照每天生成日志文件 -->
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"><rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy"><!-- 日志文件输出的文件名, %i表示序号 --><FileNamePattern>D:/tlias-%d{yyyy-MM-dd}-%i.log</FileNamePattern><!-- 最多保留的历史日志文件数量 --><MaxHistory>30</MaxHistory><!-- 最大文件大小,超过这个大小会触发滚动到新文件,默认为 10MB --><maxFileSize>10MB</maxFileSize></rollingPolicy><encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder"><!--格式化输出:%d 表示日期,%thread 表示线程名,%-5level表示级别从左显示5个字符宽度,%msg表示日志消息,%n表示换行符 --><pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50}-%msg%n</pattern></encoder>
</appender>

3). 日志开关配置 (开启日志(ALL),取消日志(OFF))

<!-- 日志输出级别 -->
<root level="ALL"><!--输出到控制台--><appender-ref ref="STDOUT" /><!--输出到文件--><appender-ref ref="FILE" />
</root>

6.5 Logback日志级别

日志级别指的是日志信息的类型,日志都会分级别,常见的日志级别如下(优先级由低到高):
在这里插入图片描述
可以在配置文件logback.xml中,灵活的控制输出那些类型的日志。(大于等于配置的日志级别的日志才会输出)

<!-- 日志输出级别 -->
<root level="info"><!--输出到控制台--><appender-ref ref="STDOUT" /><!--输出到文件--><appender-ref ref="FILE" />
</root>

6.6案例日志记录

/*** 部门管理控制器*/
@Slf4j
@RequestMapping("/depts")
@RestController
public class DeptController {@Autowiredprivate DeptService deptService;/*** 查询部门列表*///@RequestMapping(value = "/depts", method = RequestMethod.GET)@GetMappingpublic Result list(){//System.out.println("查询部门列表");log.info("查询部门列表");List<Dept> deptList = deptService.findAll();return Result.success(deptList);}/*** 根据id删除部门 - delete http://localhost:8080/depts?id=1*/@DeleteMappingpublic Result delete(Integer id){//System.out.println("根据id删除部门, id=" + id);log.info("根据id删除部门, id: {}" , id);deptService.deleteById(id);return Result.success();}/*** 新增部门 - POST http://localhost:8080/depts   请求参数:{"name":"研发部"}*/@PostMappingpublic Result save(@RequestBody Dept dept){//System.out.println("新增部门, dept=" + dept);log.info("新增部门, dept: {}" , dept);deptService.save(dept);return Result.success();}/*** 根据ID查询 - GET http://localhost:8080/depts/1*/@GetMapping("/{id}")public Result getById(@PathVariable Integer id){//System.out.println("根据ID查询, id=" + id);log.info("根据ID查询, id: {}" , id);Dept dept = deptService.getById(id);return Result.success(dept);}/*** 修改部门 - PUT http://localhost:8080/depts  请求参数:{"id":1,"name":"研发部"}*/@PutMappingpublic Result update(@RequestBody Dept dept){//System.out.println("修改部门, dept=" + dept);log.info("修改部门, dept: {}" , dept);deptService.update(dept);return Result.success();}
}

lombok中提供的@Slf4j注解,可以简化定义日志记录器这步操作。添加了该注解,就相当于在类中定义了日志记录器,就下面这句代码:

private static Logger log = LoggerFactory. getLogger(Xxx. class);

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/pingmian/87940.shtml
繁体地址,请注明出处:http://hk.pswp.cn/pingmian/87940.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

字节寻址(Byte Addressing) 与 Verilog中的寄存器索引

字节寻址&#xff08;Byte Addressing&#xff09; 与 Verilog中的寄存器索引 之间的关系。 您的疑问非常正确&#xff0c;直接看 3h1 很容易让人以为地址就是 0x01。 但答案是&#xff1a;是的&#xff0c;3h1 在这里对应的字节地址&#xff08;Byte Address&#xff09;确实是…

Ubuntu远程桌面

方法1: 检查并使用已安装的VNC或远程桌面组件 请在终端中执行以下命令检查系统中已安装的相关组件: bash# 检查系统中已安装的VNC和远程桌面相关软件 dpkg -l | grep -E "vnc|vino|remote|rdp"# 检查常见远程桌面服务 which vino-server tigervncserver x11vnc xr…

WEB攻防-文件包含LFIRFI伪协议编码算法无文件利用黑白盒

知识点&#xff1a; 1、文件包含-原理&分类&危害-LFI&RFI 2、文件包含-利用-黑白盒&无文件&伪协议 一、演示案例-文件包含-原理&分类&利用 1、原理 程序开发人员通常会把可重复使用的函数写到单个文件中&#xff0c;在使用某些函数时&#xff…

LabVIEW的GPIB仪器校准

基于LabVIEW开发平台与 GPIB 总线技术&#xff0c;采用是德科技、泰克等硬件设备&#xff0c;构建示波器与频谱分析仪自动校准系统。通过图形化编程实现校准流程自动化&#xff0c;涵盖设备连接、参数配置、数据采集、误差分析及报告生成&#xff0c;显著提升校准效率与精度&am…

Zotero中进行文献翻译【Windows11】

zotero官网&#xff1a;https://www.zotero.org/ 1 在Zotero软件中安装插件 进入Zotero百科全书&#xff0c;依次点击&#xff1a;插件→翻译插件→插件介绍→Zotero 中文社区插件商店 进去后搜索pdf2zh&#xff0c;然后下载后放入空白文件夹zotero-pdf2zh 打开Zotero软件后…

用U盘启动制作centos系统最常见报错,系统卡住无法继续问题(手把手)

一、按照操作系统centos7报错如下&#xff08;U盘&#xff09; 按照系统报错如下&#xff1a; ERROR: could not insert ‘floppy’; ERROR: could not insert ‘edd’ : No这种报错很常见&#xff0c;基本上就是u盘启动路径找不到导致&#xff0c;遇到次数比较多所以也比较好解…

C#中的BindingList有什么作用?

在C#中&#xff0c;BindingList<T> 是一个非常重要的集合类&#xff0c;位于 System.ComponentModel 命名空间&#xff0c;主要用于实现数据绑定&#xff08;Data Binding&#xff09;场景。1. 核心作用BindingList<T> 是 List<T> 的增强版&#xff0c;主要提…

Python爬取知乎评论:多线程与异步爬虫的性能优化

1. 知乎评论爬取的技术挑战 知乎的评论数据通常采用动态加载&#xff08;Ajax&#xff09;&#xff0c;这意味着直接使用**<font style"color:rgb(64, 64, 64);background-color:rgb(236, 236, 236);">requests</font>****<font style"color:rg…

软件系统测试的基本流程

软件系统测试流程是确保软件质量的规范化过程&#xff0c;涵盖从测试准备到最终上线评估的全周期&#xff0c;通常分为以下6个核心阶段&#xff0c;各阶段紧密衔接、形成闭环&#xff1a; 一、测试启动与规划阶段 核心目标&#xff1a;明确“测什么、谁来测、怎么测”&#xff…

使用Linux操作MySQL数据库分批导出数据为.SQL文件

当数据库某张数据量非常大的表进行其他操作&#xff0c;需要先进行导出时&#xff1b; 先用linux进入操作环境&#xff0c; 1.添加一个export_mysql_batches.sh脚本文件&#xff0c; #!/bin/bash# 数据库连接配置 DB_HOST"36.33.0.138:3306" DB_USER"devuser&qu…

LeetCode 算法题解:链表与二叉树相关问题 打打卡

LeetCode 算法题解&#xff1a;链表与二叉树相关问题 在算法学习和实践中&#xff0c;LeetCode 是一个非常好的平台&#xff0c;它包含了各种各样的算法题目&#xff0c;有助于我们提升编程能力和解决问题的能力。本文将详细讲解在 leetcoding.cpp 文件中实现的一些链表和二叉树…

故宫票价监控接口分析(一)

故宫票价监控接口分析(一) 对爬虫、逆向感兴趣的同学可以查看文章,一对一小班教学(系统理论和实战教程)、提供接单兼职渠道:https://blog.csdn.net/weixin_35770067/article/details/142514698 本文内容仅供学习和参考之用,不得用于商业目的。作者对文中内容的准确性、完整…

AWS OpenSearch Dev Tools使用

# 创建通用索引模版 PUT _template/aws-waf_logs_template {"index_patterns": ["aws-waf-logs-*"],"mappings": {"properties": {"timestamp": {"type": "date"}}} }# 设置单个索引格式 PUT /aws-waf-…

git-安装 Gerrit Hook 自动生成changeid

要在 Git 中安装 Gerrit Hook 以自动生成 Change-ID&#xff0c;可以按照以下步骤操作&#xff1a; 全局钩子配置&#xff08;推荐&#xff09; 创建全局钩子目录并下载 Gerrit 提供的 commit-msg 钩子脚本&#xff0c;确保所有仓库共享该配置&#xff1a; mkdir -p ~/.githook…

Excel 的多线程特性

Excel 本身并不是完全多线程的应用程序&#xff0c;但它在某些操作和功能上支持多线程处理。以下是对 Excel 是否多线程的详细解答&#xff0c;结合你之前提到的 VBA/COM 自动化代码和受保护视图问题&#xff0c;提供清晰且准确的分析。 Excel 的多线程特性计算引擎的多线程支持…

【嵌入式ARM汇编】-操作系统基础(一)

操作系统基础(一) 文章目录 操作系统基础(一)1、操作系统架构概述2、用户模式与内核模式3、进程4、系统调用5、对象和句柄我们想要逆向的程序几乎从来不会在真空中执行。相反,程序通常在正在运行的操作系统(例如 Linux、Windows 或 macOS)的上下文中运行。因此,了解这些…

[创业之路-474]:企业经营层 - 小米与华为多维对比分析(2025年视角),以后不要把这两家公司放在同一个维度上 进行比较了

一、行业定位与市场角色不同华为&#xff1a;用技术手段解决行业的难题&#xff0c;顺便赚钱技术驱动型硬科技企业&#xff1a;以通信设备起家&#xff0c;延伸至智能手机、芯片、操作系统&#xff08;鸿蒙&#xff09;、云计算、智能汽车等领域&#xff0c;构建“云-管-端”全…

C#基础篇(06)抽象类与接口详解区别

抽象类是 C# 面向对象编程中的一个重要概念&#xff0c;它介于普通类和接口之间&#xff0c;提供了一种定义部分实现并要求派生类完成其余部分的机制。一、C# 中的抽象类抽象类是 C# 面向对象编程中的一个重要概念&#xff0c;它介于普通类和接口之间&#xff0c;提供了一种定义…

使用Python将PDF转换成word、PPT

在现代企业环境中,文档格式的转换是一项普遍且关键的需求。PDF(Portable Document Format)作为一种最终的、通常不可编辑的“打印”状态格式,被广泛用于分发和归档。然而,内容的创建、协作和修改主要在Microsoft Office套件中进行,特别是Word(DOCX)和PowerPoint(PPTX)…

香港风水(原生)林地的逻辑分类器

风水林是香港的原生林地&#xff0c;由于地处偏远、地形崎岖以及当地居民的信仰&#xff0c;这些林地得以保存完好。在香港&#xff0c;我们可以追溯到1924年的航拍图像记录&#xff0c;这些黑白航拍图像已经帮助我们划分和区分了林地、草地和灌木丛。然后&#xff0c;通过二战…