JSR 303全解析:如何在Java项目中实施高效数据校验

1. JSR 303是什么?

JSR 303(Java Specification Request 303),也称为Bean Validation,是Java中的一个规范,用于定义Java对象的校验规则。

1.1 JSR 303的主要功能

  • 注解驱动:通过注解直接在Java类上定义校验规则。
  • 内置约束:如@NotNull、@Size、@Min、@Max等。
  • 自定义约束:可以定义自定义的校验注解和逻辑。
  • 分组校验:支持对不同场景(如创建和更新)进行分组校验。

1.2 常用注解

  • @NotNull:验证注解的元素值不是null。
  • @Size:验证注解的元素的大小在指定范围内。
  • @Min和@Max:验证注解的元素值在指定范围内。
  • @Email:验证注解的元素是一个合法的电子邮件地址。

2. 使用步骤

JSR 303 是一个规范,所以需要具体的实现。Hibernat Bean Validator 就是Bean Validator的实现

2.1.引入库

        <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-validation</artifactId></dependency><dependency><groupId>javax.validation</groupId><artifactId>validation-api</artifactId><version>2.0.1.Final</version></dependency><dependency><groupId>org.hibernate</groupId><artifactId>hibernate-validator</artifactId><version>8.0.1.Final</version></dependency>

2.2 实体类编写校验规则

代码如下(示例):

@Data
public class BrandEntity implements Serializable {/*** 品牌ID,用于标识品牌。* * 此字段通过注解进行了不同的验证逻辑配置,以适应不同的业务场景。* 在更新操作(UpdateGroup)中,要求此字段不为空,确保了更新操作有明确的目标品牌ID。* 在新增操作(AddGroup)中,要求此字段为空,因为新增品牌时不应该预先指定ID。* 这种通过注解进行验证的方式,提高了代码的灵活性和可维护性,避免了在业务逻辑中硬编码验证逻辑。*/@NotNull(message = "修改必须指定品牌id",groups = {UpdateGroup.class})@Null(message = "新增不能指定id",groups = {AddGroup.class})private Long brandId;/*** 品牌名称字段。* * 该字段是必填的,不允许为空字符串,这在添加和更新品牌信息时都必须遵守。* 使用@NotBlank注解来强制验证品牌名的非空性,如果为空,则会触发验证失败,* 返回相应的错误消息。*/@NotBlank(message = "品牌名必须提交",groups = {AddGroup.class,UpdateGroup.class})private String name;/*** 品牌logo地址* * 此字段在添加(AddGroup)和更新(UpdateGroup)时都必须是一个合法的URL地址,* 以确保公司徽标的链接是有效和可访问的。使用@URL注解进行验证,* 如果不符合URL格式,则会提示指定的错误信息。* * 使用@NotBlank注解确保在添加时该字段不为空,为空则认为是无效的输入。* 这是因为在更新时,如果用户没有提供新的徽标URL,可以保留旧的URL,* 所以在更新组(UpdateGroup)中,@NotBlank约束被移除,允许为空。*/@NotBlank(groups = {AddGroup.class})@URL(message = "logo必须是一个合法的url地址",groups={AddGroup.class,UpdateGroup.class})private String logo;/*** 展示状态字段,用于标记对象的展示状态。* 显示状态[0-不显示;1-显示]* * 此字段受到两个验证组(AddGroup, UpdateStatusGroup)的约束。* 在这两个组中,该字段不能为空(@NotNull)且其值必须在预定义的列表中(@ListValue)。* 这样的设计确保了在添加对象和更新状态操作时,展示状态的值是有效且受控的。* * @NotNull(groups = {AddGroup.class, UpdateStatusGroup.class})* 表明在AddGroup和UpdateStatusGroup验证组中,此字段不能为空。* @ListValue(vals={0,1},groups = {AddGroup.class, UpdateStatusGroup.class})* 表明在AddGroup和UpdateStatusGroup验证组中,此字段的值必须是0或1。*/@NotNull(groups = {AddGroup.class, UpdateStatusGroup.class})@ListValue(vals={0,1},groups = {AddGroup.class, UpdateStatusGroup.class})private Integer showStatus;/*** 字段firstLetter用于存储实体的首字母。* 该字段的验证有以下规则:* 1. 在添加(AddGroup)时,不能为空,确保数据完整性。* 2. 在添加(AddGroup)和更新(UpdateGroup)时,必须是一个字母,确保数据的格式符合预期。* 这些验证规则通过注解的方式进行声明,以在运行时对数据进行校验。*/@NotEmpty(groups={AddGroup.class})@Pattern(regexp="^[a-zA-Z]$",message = "检索首字母必须是一个字母",groups={AddGroup.class,UpdateGroup.class})private String firstLetter;/*** 排序字段,用于控制元素的显示顺序。* * @NotNull 标注指示该字段在添加(AddGroup)时不能为空,确保了排序值的有效性。* @Min 标注指定了排序值必须大于等于0,适用于添加(AddGroup)和更新(UpdateGroup)操作,保证了排序的逻辑正确性。*/@NotNull(groups={AddGroup.class})@Min(value = 0,message = "排序必须大于等于0",groups={AddGroup.class,UpdateGroup.class})private Integer sort;}

2.3 自定义约束

通过上面的代码可以看出,如果要指定注解作用的范围,就要自己添加分组。

AddGroup

public interface AddGroup {
}

UpdateGroup

public interface UpdateGroup {
}

UpdateStatusGroup

public interface UpdateStatusGroup {
}

上述代码中为了对状态取值进行验证,我们采用了自定义验证器的方式

ListValue

@Documented
@Constraint(validatedBy = { ListValueConstraintValidator.class })
@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE })
@Retention(RUNTIME)
public @interface ListValue {String message() default "{com.xunqi.common.valid.ListValue.message}";Class<?>[] groups() default { };Class<? extends Payload>[] payload() default { };int[] vals() default { };
}

ListValueConstraintValidator

/*** 实现了ListValue注解的验证器,用于验证一个整数是否在预定义的整数列表中。* 该验证器在验证阶段检查给定的整数是否存在于初始化时指定的整数集合中。*/
public class ListValueConstraintValidator implements ConstraintValidator<ListValue, Integer> {/*** 用于存储验证器初始化时指定的整数列表的集合。*/private Set<Integer> set = new HashSet<>();/*** 初始化验证器,加载注解中指定的整数列表到集合中。** @param constraintAnnotation ListValue注解实例,其中包含需要验证的整数列表。*/@Overridepublic void initialize(ListValue constraintAnnotation) {int[] value = constraintAnnotation.vals();for (int val : value) {set.add(val);}}/*** 验证给定的整数是否在集合中。** @param value 待验证的整数。* @param context 验证上下文,提供关于验证操作的上下文信息。* @return 如果给定的整数存在于集合中,则返回true;否则返回false。*/@Overridepublic boolean isValid(Integer value, ConstraintValidatorContext context) {return set.contains(value);}
}

3. 业务层使用


/*** <p>* 商品品牌 前端控制器* </p>** @author shiqi* @version 1.0.0* @createTime 2024-06-26*/
@RestController
@RequestMapping("product/brand")
public class BrandController {/*** 保存品牌信息。* <p>* 该方法通过@RequestMapping注解映射了"/save"的HTTP请求,用于保存BrandEntity对象。* 使用@Validated注解对brandEntity参数进行验证,确保添加或修改品牌时数据的合法性。* BindingResult参数用于接收验证后的错误信息,可以进一步处理和反馈给前端。* <p>* 方法返回一个R对象,通常表示操作的成功或失败状态。** @param brandEntity 品牌实体对象,包含待保存的品牌信息。* @param bindingResult 验证结果对象,用于存储brandEntity验证过程中产生的错误信息。* @return 返回一个表示操作结果的对象,通常是一个包含成功状态和相关消息的R对象。*/@RequestMapping("/save")public R save(@Validated({AddGroup.class}) @RequestBody BrandEntity brandEntity, BindingResult bindingResult) {// 方法体中应包含保存品牌信息的具体逻辑,此处省略。return R.ok();}}

4. 封装统一异常

4.1 业务异常状态枚举类

/*** <p>* 描述:业务异常枚举类* </p>** @author shiqi* @version 1.0.0* @createTime 2024-06-26*/
public enum BizCodeEnum {UNKNOWN_EXCEPTION(10000,"系统未知异常"),VALID_EXCEPTION(10001,"参数格式校验失败"),TO_MANY_REQUEST(10002,"请求流量过大,请稍后再试"),SMS_CODE_EXCEPTION(10002,"验证码获取频率太高,请稍后再试"),PRODUCT_UP_EXCEPTION(11000,"商品上架异常"),USER_EXIST_EXCEPTION(15001,"存在相同的用户"),PHONE_EXIST_EXCEPTION(15002,"存在相同的手机号"),NO_STOCK_EXCEPTION(21000,"商品库存不足"),LOGIN_ACCOUNT_PASSWORD_EXCEPTION(15003,"账号或密码错误");private int code;private String message;BizCodeEnum(int code, String message) {this.code = code;this.message = message;}public int getCode() {return code;}public String getMessage() {return message;}
}

4.2 封装统一返回结果


/*** <p>*  统一返回结果* </p>** @author shiqi* @version 1.0.0* @createTime 2024-06-26*/public class R extends HashMap<String, Object> {private static final long serialVersionUID = 1L;public R setData(Object data) {put("data",data);return this;}//利用fastjson进行反序列化public <T> T getData(TypeReference<T> typeReference) {Object data = get("data");	//默认是mapString jsonString = JSON.toJSONString(data);T t = JSON.parseObject(jsonString, typeReference);return t;}//利用fastjson进行反序列化public <T> T getData(String key,TypeReference<T> typeReference) {Object data = get(key);	//默认是mapString jsonString = JSON.toJSONString(data);T t = JSON.parseObject(jsonString, typeReference);return t;}public R() {put("code", 0);put("msg", "success");}public static R error() {return error(HttpStatus.SC_INTERNAL_SERVER_ERROR, "未知异常,请联系管理员");}public static R error(String msg) {return error(HttpStatus.SC_INTERNAL_SERVER_ERROR, msg);}public static R error(int code, String msg) {R r = new R();r.put("code", code);r.put("msg", msg);return r;}public static R ok(String msg) {R r = new R();r.put("msg", msg);return r;}public static R ok(Map<String, Object> map) {R r = new R();r.putAll(map);return r;}public static R ok() {return new R();}public R put(String key, Object value) {super.put(key, value);return this;}public Integer getCode() {return (Integer) this.get("code");}}

自定义校验异常处理器

/*** <p>*  集中处理所有异常* </p>** @author shiqi* @version 1.0.0* @createTime 2024-06-26*/@Slf4j
@RestControllerAdvice(basePackages = {"com.shiqi.jsr303demo"})
public class CustomExceptionControllerAdvice {/*** 处理方法参数不合法异常。* 当方法参数不满足验证条件时,Spring MVC会抛出MethodArgumentNotValidException异常。* 该异常处理器专门捕获此类异常,以统一的方式处理参数验证失败的情况。** @param e MethodArgumentNotValidException异常实例,包含验证失败的详细信息。* @return 返回一个包含错误信息的响应对象。*/@ExceptionHandler(value = MethodArgumentNotValidException.class)public R handleMethodArgumentNotValidException(MethodArgumentNotValidException e){// 获取验证结果对象,其中包含了具体的验证错误信息。BindingResult bindingResult = e.getBindingResult();// 初始化一个映射,用于存储字段名和对应的错误信息。HashMap<String,String> errMap=new HashMap<String,String>();// 检查是否有验证错误,如果有,则遍历所有字段错误,并将字段名和错误信息添加到errMap中。if (bindingResult.hasErrors()){bindingResult.getFieldErrors().forEach((item)->{errMap.put(item.getField(),item.getDefaultMessage());});}// 返回一个包含错误代码、错误消息和具体错误详情的响应对象。// 错误代码为400,表示客户端请求错误,错误消息为"参数校验不合法"。// errMap作为数据部分的一部分,包含了所有验证失败的字段和对应的错误信息。return R.error(400,"参数校验不合法").put("data",errMap);}/*** 处理所有异常的控制器异常处理器。* <p>* 该方法旨在捕获控制器层抛出的任何异常,无论是预期的业务异常还是未预期的运行时异常。* 它的目的是统一异常的处理方式,向客户端返回一个标准的响应体,而不是直接暴露服务器内部错误信息。** @param throwable 抛出的异常对象,无论异常类型为何。* @return 返回一个表示错误响应的R对象。这个响应体可以帮助客户端识别请求处理过程中发生了什么错误。*/@ExceptionHandler(value = Throwable.class)private R handleValidException(Throwable throwable) {// 记录异常信息到日志系统,以便后续的问题排查和分析。log.error("出现异常{},异常类型{}", throwable.getMessage(), throwable.getClass());// 返回一个通用的错误响应体,通知客户端请求处理过程中发生了错误。return R.error();}}

5. 实际业务中的应用

  1. 表单校验:确保用户输入的数据合法,如用户注册、登录、表单提交等。
  2. 数据传输对象(DTO)校验:在进行数据传输时,确保传输的数据符合预期,如API请求和响应。
  3. 领域对象校验:确保业务逻辑中的对象状态合法,如订单处理、支付处理等。

使用JSR 303可以有效减少手动校验代码,简化代码结构,提高代码可读性和维护性。在实际应用中,常结合Spring框架和Hibernate Validator一起使用

6. 扩展知识

6.1 PO(Persistence Object)

定义:持久化对象,通常对应数据库中的一张表,每个实例对应表中的一行。
应用场景:用于数据持久化层,与数据库的表结构一一对应,通过ORM(如Hibernate)进行数据操作。
使用时机:当需要将数据持久化到数据库,或者从数据库中读取数据时使用。
示例:

public class UserPO {private Long id;private String username;private String password;// Getters and Setters
}

具体使用场景

  • 在需要进行数据库操作(CRUD)时使用。
  • 与DAO一起使用,以实现数据持久化逻辑。

简单来说就是Java Bean 对应的是数据库中的一张表

6.2. BO(Business Object)

定义:业务对象,封装业务逻辑的Java对象,通常包含业务操作方法。
应用场景:在业务层使用,用于处理业务逻辑,可能会调用多个DAO或与多个PO交互。
使用时机:当需要处理复杂的业务逻辑或操作多个数据对象时使用。
示例:

public class UserBO {private Long id;private String username;private String password;public void changePassword(String newPassword) {// 业务逻辑:更新密码this.password = newPassword;}// Getters and Setters
}

具体使用场景

  • 在业务层需要进行复杂业务逻辑处理时。
  • 需要将多个数据对象(PO)进行组合或处理时。

6.3. VO(Value Object)

定义:值对象,用于在应用层传递数据,通常是只读的。
应用场景:在视图层(如MVC中的View)使用,用于展示数据,不包含业务逻辑。
使用时机:当需要在视图层展示数据,而不需要修改数据时使用。
示例:

public class UserVO {private Long id;private String username;// 只包含展示所需的字段// Getters and Setters
}

具体使用场景

  • 在需要将数据传递给前端进行展示时。
  • 在只读数据场景下使用,如显示用户信息。

简单来说就是对PO进行阉割或者强化,比如我们需要查询用户列表的时候,需要对敏感信息进行加密,不太关心的数据我们也不必返回给前端,造成带宽的浪费。

6.4 DTO(Data Transfer Object)

定义:数据传输对象,用于在不同层之间传输数据,通常是无状态的。
应用场景:在数据传输层使用,如在服务接口之间传递数据,减少远程调用次数。
使用时机:当需要在不同系统或不同层之间传递数据时使用。
示例:

public class UserDTO {private Long id;private String username;private String email;// Getters and Setters
}

具体使用场景

  • 在服务之间的远程调用中传输数据。
  • 在Controller与Service之间传递数据。

这个怎么理解了,就是我们进行表单提交的时候,或者更新查询操作时,需要前端传递很多参数,如果每个参数都写在Controller层的参数列表中就很不优雅,而且如果需新增参数的话就需要改动很多地方,会导致容易遗漏,这个时候我们就把这些请求参数封装成一个请求对象,这就是DTO。

6.5. POJO(Plain Old Java Object)

定义:简单的Java对象,不依赖任何特定框架或库。
应用场景:用于定义普通的Java对象,通常作为PO、BO、VO、DTO的基础。
使用时机:在需要创建简单的数据容器而不依赖任何特定框架时使用。
示例:

public class User {private Long id;private String username;private String password;// Getters and Setters
}

具体使用场景

  • 创建简单的Java对象用于数据封装。
  • 作为其他对象(PO、BO、VO、DTO)的基础类。

也就是我们自己编写的业务类,无需与数据库打交道的。上述的统一返回结果类,我们就可以理解为POJO

6.6. DAO(Data Access Object)

定义:数据访问对象,提供对数据库的抽象和封装,包含CRUD操作。
应用场景:在数据访问层使用,负责与数据库进行交互。
使用时机:当需要对数据库进行操作(如查询、插入、更新、删除)时使用。
示例:

    public void save(UserPO user) {// 保存用户到数据库}public UserPO findById(Long id) {// 根据ID查询用户return new UserPO();}// 其他CRUD操作
}

具体使用场景

  • 在需要与数据库进行直接交互时。
  • 在业务逻辑中需要持久化或检索数据时。

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

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

相关文章

多家国产大模型提供OpenAI API服务替代方案,谷歌将推出明星网红AI聊天机器人

ChatGPT狂飙160天&#xff0c;世界已经不是之前的样子。 更多资源欢迎关注 1、OpenAI终止对中国提供服务 6月25日凌晨&#xff0c;多个用户收到OpenAI的推送邮件&#xff0c;信中称&#xff0c;自今年7月9日起&#xff0c;将开始阻止来自非支持国家和地区的API&#xff08;应…

3.任务的创建与删除

1.什么是任务&#xff1f; 任务可以理解为进程/线程&#xff0c;创建一个任务&#xff0c;就会在内存开辟一个空间。 任务通常都含有while(1)死循环 2.任务创建与删除相关的函数 3.CUBEMAX相关配置 编辑一个led1闪烁的任务

小程序使用echarts和echarts配置项总结(全网最简单详细)

文章目录 概要小程序中使用echarts1. ec-canvas2. 下载项目3. 去echarts官网定制&#xff1a;4.点击下载5.引入使用 echarts的option配置知识点归纳整理&#xff08;还在更新&#xff09;&#xff1a;小结 概要 小程序中使用echarts&#xff08;简单详细&#xff09; 小程序中…

解密城市酷选为何异军突起!打造消费新潮流的排队免单模式

一、城市酷选平台简介 在数字化浪潮席卷全球的今天&#xff0c;城市酷选作为一个前沿的消费平台&#xff0c;凭借其独特的排队免单模式&#xff0c;成功吸引了众多消费者和商家的目光。该平台不仅整合了线上线下的资源&#xff0c;更通过数字化手段&#xff0c;为消费者提供了…

云计算 | 期末梳理(中)

1. 经典虚拟机的特点 多态(Polymorphism):支持多种类型的OS。重用(Manifolding):虚拟机的镜像可以被反复复制和使用。复用(Multiplexing):虚拟机能够对物理资源时分复用。2. 系统接口 最基本的接口是微处理器指令集架构(ISA)。应用程序二进制接口(ABI)给程序提供使用硬件资源…

C3P0数据库连接池

目录 一&#xff1a;连接池介绍 1.1连接池解决的问题 2.常用的数据库连接池 二&#xff1a;c3p0介绍 2.1C3P0介绍&#xff1a; 2.2C3P0快速入门 1.常用参数说明 2.API介绍 3.使用步骤 1.导入jar包c3p0-0.9.1.2.jar 2.编写c3p0-config.xml配置文件&#xff0c;配置对…

Python 中字符串修饰符

1. 原始字符串 (Raw String) - r 或 R 使用 r 或 R 前缀&#xff0c;可以告诉 Python 字符串中的所有反斜杠都是普通字符&#xff0c;而不是转义字符。这在处理文件路径、正则表达式等情况下非常有用。 path rC:\new_folder\test.txt # 原始字符串2. 格式化字符串 (Formatt…

第十九条:要么为继承而设计并提供文档说明,要么就禁止继承

在前面一条中&#xff0c;我们已经知道了David写了A类被Tom拿去继承了&#xff0c;导致了A类的封装性遭到了破坏&#xff0c;那么有没有可能做点事情避免此事发生呢&#xff1f;第十九条孕育而生&#xff01;David在创建A类的时候写上文档说明&#xff0c;说Al类不允许任何类来…

node 实现导出, 在导出excel中包含图片(附件)

如果想查看 node mySql 实现数据的导入导出&#xff0c;以及导入批量插入的sql语句&#xff0c;连接如下 node mySql 实现数据的导入导出&#xff0c;以及导入批量插入的sql语句-CSDN博客https://blog.csdn.net/snows_l/article/details/139998373 一、效果如图&#xff1a; 二…

中介子方程三十四

XXFXXuXXWXXuXXdXXrXXαXXuXpXXKXηXiXXαXXiXηXKXXpXuXXαXXrXXdXXuXWXπXXWXeXyXeXbXπXpXXNXXqXeXXrXXαXXuXpXXKXηXiXXαXXiXηXKXXpXuXXαXXrXXeXqXXNXXpXπXbXeXyXeXWXXπXWXuXXdXXrXXαXXuXpXXKXηXiXXαXXiXηXKXXpXuXXαXXrXXdXXuXXWXXuXXFXXEXXyXXEXXrXXαXXuXpXXK…

paraview跨节点并行渲染

参考&#xff1a; https://cloud.tencent.com/developer/ask/sof/101483588 ParaView 支持使用其内置的网络拓扑来进行跨节点的并行渲染。以下是一个简单的步骤来设置和运行跨节点的并行渲染&#xff1a; 确保你的计算环境支持多节点计算&#xff0c;比如通过SSH、MPI或其他集…

阿里云扩容

官网&#xff1a;https://help.aliyun.com/zh/ecs/user-guide/extend-the-partitions-and-file-systems-of-disks-on-a-linux-instance?spm5176.ecs-console-storage_disk.help.dexternal.72d24df5QOL4ss 博客&#xff1a;http://t.csdnimg.cn/cUykr

Android APP通过View修改鼠标样式

app view上修改鼠标样式比较简单&#xff0c;使用如下方法修改为自定义图片&#xff1a; getWindow().getDecorView().setPointerIcon(PointerIcon.load(getResources(), R.drawable.pointer_spot_touch_icon)); 设置鼠标样式setPointerIcon的调用栈 frameworks/base/core/jav…

C语言:流量控制

前言 流量控制可以让发送端根据接收端的实际接受能力控制发送的数据量。它的具体操作是&#xff0c;接收端主机向发送端主机通知自己可以接收数据的大小&#xff0c;于是发送端会发送不会超过该大小的数据&#xff0c;该限制大小即为窗口大小&#xff0c;即窗口大小由接收端主…

【Linux详解】进程的状态 | 运行 阻塞 挂起 | 僵尸和孤儿状态

目录 操作系统中 运行状态 阻塞状态 进程状态转换 Linux系统中 查看进程状态 深度睡眠状态 T 暂停状态 Z 僵尸状态 孤儿状态 文章手稿 xmind: 引言 介绍系统中的进程状态及其管理方式。将通过结合操作系统原理和实际代码示例&#xff0c;详细说明进程的各种状态、转换…

鸿蒙开发Ability Kit(程序框架服务):【FA模型切换Stage模型指导】 app和deviceConfig的切换

app和deviceConfig的切换 为了便于开发者维护应用级别的属性配置&#xff0c;Stage模型将config.json中的app和deviceConfig标签提取到了app.json5中进行配置&#xff0c;并对部分标签名称进行了修改&#xff0c;具体差异见下表。 表1 配置文件app标签差异对比 配置项FA模型…

Excel中的“点选输入”——次级下拉列表创建

在Excel中&#xff0c;用“数据验证”功能可以设置下拉列表&#xff0c;二级下拉列表需要设置公式。 (笔记模板由python脚本于2024年06月16日 18:36:37创建&#xff0c;本篇笔记适合经常使用Excel处理数据的coder翻阅) 【学习的细节是欢悦的历程】 Python 官网&#xff1a;http…

基于 Spring AOP 实现安全检查

在现代应用程序中&#xff0c;安全性是一个至关重要的方面。通过对系统中的关键操作进行安全检查&#xff0c;可以有效防止未授权的访问和操作。Spring AOP&#xff08;面向切面编程&#xff09;提供了一种优雅的方式来实现安全检查&#xff0c;而无需修改业务逻辑代码。本文将…

后端之路第三站(Mybatis)——入门配置

一、Mybatis是啥&#xff1f; 就是一个用java来操控数据库的框架语言 之前学的datagrip或者navicat这些软件里我们操作数据库&#xff0c;原理是我们编写完的操作语句发送到服务器传送到数据库系统&#xff0c;然后数据库执行完之后再发送给服务器返回给datagrip或者navicat显…

【linux/shell案例实战】shell界面命令快捷键

快捷键及含义&#xff1a; Ctrl&#xff0b;u剪切光标之前的内容。Ctul&#xff0b;k剪切光标之后的内容。Ctrl&#xff0b;e让光标移动到命令最前&#xff0c;Ctrl&#xff0b;a让光标移动到命令最后Ctrl&#xff0b;y 粘贴刚才所删除的内容。Ctrl&#xff0b;d 删除光标所在…