3. 内容模块管理 - 异常处理与校验

文章目录

  • 内容模块管理
  • 一、自定义异常
    • 1.1 全局异常处理器
    • 1.2 自定义异常
    • 1.3 异常统一响应类
    • 1.4 封装通用异常信息
  • 二、JSR303校验
    • 2.1 Maven坐标
    • 2.2 校验规则
    • 2.3 代码示例
    • 2.4 捕捉校验异常
    • 2.5 分组校验
    • 2.6 备注
  • 三、全局异常处理2
    • 3.1 全局异常处理器
    • 3.2 结果集
    • 3.3 常用注解
      • 3.3.1 @ControllerAdvice
      • 3.3.2 @ExceptionHandle

内容模块管理

一、自定义异常

之前也学过对异常的统一管理,既然现在又看到了,就再学学springboot——全局异常处理器及封装结果集

如果一致对异常进行try…catch…,代码也会很冗余,我们直接throw就行,然后可以对异常进行统一处理

不论是那一层,都会有异常的出现,我们遇到异常都是向上抛出,最后让框架对异常统一处理

假如dao出异常就会抛给service,service有异常就会抛给controller,controller会抛给spring框架

image-20231115212256176

1.1 全局异常处理器

  • @ExceptionHandler

    提供的标识在方法上或类上的注解,用来表明方法的处理异常类型

  • @ControllerAdvice

    控制器增强,在项目中来增强SpringMVC中的Controller。通常和@ExceptionHandler结合使用,来处理SpringMVC的异常信息。

  • @ResponseStatus

    提供的标识在方法上或类上的注解,用状态代码和应返回的原因标记方法或异常类。

    调用处理程序方法时,状态代码将应用于HTTP响应

/**
 * 统一异常处理类
 */
@Slf4j
@ControllerAdvice
@ResponseBody//返回JSON数据
//@RestControllerAdvice = @ControllerAdvice + @ResponseBody
public class GlobalExceptionHandler {

    /**
     * 针对自定义异常进行处理的
     */
    @ResponseBody
    @ExceptionHandler(XueChengPlusException.class)//捕捉这个异常
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)//状态码500
    public RestErrorResponse customException(XueChengPlusException e) {
//      捕捉到异常的信息后解析异常的信息
        log.error("【系统异常】{}", e.getErrMessage(), e);
        return new RestErrorResponse(e.getErrMessage());
    }

    /**
     * 捕捉除了自定义异常以外的其他异常
     */
    @ResponseBody
    @ExceptionHandler(Exception.class)
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)//状态码500
    public RestErrorResponse exception(Exception e) {
        log.error("【系统异常】{}", e.getMessage(), e);
        return new RestErrorResponse(CommonError.UNKOWN_ERROR.getErrMessage());//执行过程异常,请重试

    }

}

1.2 自定义异常

@EqualsAndHashCode(callSuper = true)
@Data
public class XueChengPlusException extends RuntimeException {

   private static final long serialVersionUID = -378480393877627738L;

   private String errMessage;

    public XueChengPlusException() {
        super();
    }

    public XueChengPlusException(String errMessage) {
        super(errMessage);
        this.errMessage = errMessage;
    }


    public static void cast(CommonError commonError) {
        throw new XueChengPlusException(commonError.getErrMessage());
    }

    public static void cast(String errMessage) {
        throw new XueChengPlusException(errMessage);
    }

}

1.3 异常统一响应类

/**
 * 和前端约定返回的异常信息模型
 * 错误响应参数包装
 */
@Data
@AllArgsConstructor
@NoArgsConstructor
public class RestErrorResponse implements Serializable {

    private static final long serialVersionUID = 9026504397012666687L;

    private String errMessage;

}

1.4 封装通用异常信息

/**
 * 通用异常信息
 */
public enum CommonError {

   UNKOWN_ERROR("执行过程异常,请重试。"),
   PARAMS_ERROR("非法参数"),
   OBJECT_NULL("对象为空"),
   QUERY_NULL("查询结果为空"),
   REQUEST_NULL("请求参数为空");

   private String errMessage;

   public String getErrMessage() {
      return errMessage;
   }

   private CommonError( String errMessage) {
      this.errMessage = errMessage;
   }

}

二、JSR303校验

SpringBoot提供了JSR-303的支持,它就是spring-boot-starter-validation,它的底层使用Hibernate Validator,Hibernate Validator是Bean Validation 的参考实现。

所以,我们准备在Controller层使用spring-boot-starter-validation完成对请求参数的基本合法性进行校验。

  • 前端请求后端接口传输参数,是在controller中校验还是在Service中校验

    答案是都需要校验,只是分工不同。因为某些Service不仅仅被一个Controller调用,如果Service中不写校验再被其他Controller或其他Service调用时也会发生问题

  • Controller里一般校验什么

    Contoller中校验请求参数的合法性,包括:必填项校验,数据格式校验,比如:是否是符合一定的日期格式,等。

  • Service里一般校验什么

    Service中要校验的是业务规则相关的内容,比如:课程已经审核通过所以提交失败。

    Service中根据业务规则去校验不方便写成通用代码,Controller中则可以将校验的代码写成通用代码

2.1 Maven坐标

首先在Base工程添加spring-boot-starter-validation的依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-validation</artifactId>
</dependency>

在javax.validation.constraints包下有很多这样的校验注解,直接使用注解定义校验规则即可

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

2.2 校验规则

image-20231115224419263

2.3 代码示例

现在准备对内容管理模块添加课程接口进行参数校验,如下接口

定义好校验规则还需要开启校验,在controller方法中添加@Validated注解,否则校验不会生效

    @ApiOperation("新增课程")
    @PostMapping("/course")
    public CourseBaseInfoDto createCourseBase(@Validated @RequestBody AddCourseDto addCourseDto) {
//      将来会集成SpringSecurity框架,用户登录之后就可以获取到用户所属机构的ID
//      先把机构ID写死
        return courseBaseInfoService.createCourseBase(10086L,addCourseDto);
    }

进入AddCourseDto类,在属性上添加校验规则

@NotEmpty表示属性不能为空

@Size表示限制属性内容的长短

/**
 * @description 添加课程dto
 */
@Data
@ApiModel(value = "AddCourseDto", description = "新增课程基本信息")
public class AddCourseDto {

    @NotEmpty(message = "课程名称不能为空")
    @ApiModelProperty(value = "课程名称", required = true)
    private String name;

    @NotEmpty(message = "适用人群不能为空")
    @Size(message = "适用人群内容过少", min = 10)
    @ApiModelProperty(value = "适用人群", required = true)
    private String users;

    @ApiModelProperty(value = "课程标签")
    private String tags;

    @NotEmpty(message = "课程分类不能为空")
    @ApiModelProperty(value = "大分类", required = true)
    private String mt;

    @NotEmpty(message = "课程分类不能为空")
    @ApiModelProperty(value = "小分类", required = true)
    private String st;

    @NotEmpty(message = "课程等级不能为空")
    @ApiModelProperty(value = "课程等级", required = true)
    private String grade;

    @ApiModelProperty(value = "教学模式(普通,录播,直播等)", required = true)
    private String teachmode;

    @ApiModelProperty(value = "课程介绍")
    private String description;

    @ApiModelProperty(value = "课程图片", required = true)
    private String pic;

    @NotEmpty(message = "收费规则不能为空")
    @ApiModelProperty(value = "收费规则,对应数据字典", required = true)
    private String charge;

    @ApiModelProperty(value = "价格")
    private Float price;
    @ApiModelProperty(value = "原价")
    private Float originalPrice;


    @ApiModelProperty(value = "qq")
    private String qq;

    @ApiModelProperty(value = "微信")
    private String wechat;
    @ApiModelProperty(value = "电话")
    private String phone;

    @ApiModelProperty(value = "有效期")
    private Integer validDays;
}

2.4 捕捉校验异常

该异常表示在方法参数验证失败时抛出,其中验证失败的详细信息可以通过 BindingResult 对象获取

getBindingResult() 方法是 MethodArgumentNotValidException 类的一个方法,用于获取包含验证错误详细信息的 BindingResult 对象。BindingResult 包含了验证结果,包括错误对象、错误代码、默认错误消息等。

    @ExceptionHandler(MethodArgumentNotValidException.class)
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    public RestErrorResponse methodArgumentNotValidException(MethodArgumentNotValidException e) {
//      这个异常对象有一个方法getBindingResult
        BindingResult bindingResult = e.getBindingResult();
        List<String> msgList = new ArrayList<>();
        //将错误信息放在msgList
        //因为校验的时候可能多个字段都会出现问题,所以使用了一个list集合存放有问题的消息,我们后面会将他们一块拼接起来的
        bindingResult.getFieldErrors().stream().forEach(item -> msgList.add(item.getDefaultMessage()));
        //拼接错误信息
        String msg = StringUtils.join(msgList, ",");
        log.error("【系统异常】{}", msg);
        return new RestErrorResponse(msg);
    }

比如下面的这一次请求中就是两个字段不合格,都会放到List集合中,最后由StringUtils.join将其拼接起来

image-20231115231654132

2.5 分组校验

假如修改课程的Dto和增加课程的Dto是一个样子的,但是校验规则不一样,怎么办呢

方法1:定义两个Dto来分别校验

方法2:分组校验

分组校验:将Dto中的校验规则分组,新增课程使用一个检验规则组,修改课程使用一个校验规则组,但是都还是同一个Dto

定义分组

/**
 * 用于分组校验,定义一些常用的组
 */
public class ValidationGroups {

 public interface Insert{};
 public interface Update{};
 public interface Delete{};

}

划分组别

//    @NotEmpty(message = "课程名称不能为空")
//  这个地方就说明了下面@NotEmpty校验属于ValidationGroups.Insert.class组别
    @NotEmpty(message = "新增课程名称不能为空",groups = {ValidationGroups.Insert.class})
//  这个地方就说明了下面@NotEmpty校验属于ValidationGroups.Update.class组别
    @NotEmpty(message = "修改课程名称不能为空",groups = {ValidationGroups.Update.class})
    @ApiModelProperty(value = "课程名称", required = true)
    private String name;

此时Controller中也需要修改

    @ApiOperation("新增课程")
    @PostMapping("/course")
    public CourseBaseInfoDto createCourseBase(@Validated(ValidationGroups.Insert.class) @RequestBody AddCourseDto addCourseDto) {
//      将来会集成SpringSecurity框架,用户登录之后就可以获取到用户所属机构的ID
//      先把机构ID写死
        return courseBaseInfoService.createCourseBase(10086L,addCourseDto);
    }

image-20231115232957202

假如说是修改的话,如下所示

 public CourseBaseInfoDto  updateCourseBase(@Validated(ValidationGroups.Update.class) @RequestBody AddCourseDto addCourseDto) {
    ...
}

2.6 备注

如果javax.validation.constraints包下的校验规则满足不了需求怎么办?

1、手写校验代码 。

2、自定义校验规则注解。

三、全局异常处理2

上面的异常处理是在看学成在线的时候做的笔记,但是怎么看怎么别扭,特别是封装的响应结果集,很别扭,然后就再整理一份

系统如何处理异常

  1. 我们自定义一个统一的异常处理器去捕获并处理异常

  2. 使用控制器增加注解@ControllerAdvice和异常处理注解@ExceptionHandler来实现

  3. 处理自定义异常

    程序在编写代码时根据校验结果主动抛出自定义异常类对象,抛出异常时指定详细的异常信息,异常处理器捕获异常信息记录异常日志并响应给用户

  4. 处理自定义异常

    接口执行过程中的一些运行时异常也会由异常处理器统一捕获,记录异常日志,统一响应给用户500错误(状态码由前后端协定)

  5. 在异常处理中还可以针对某个异常类型进行单独处理

3.1 全局异常处理器

/**
 * 全局异常处理
 * 底层是通过代理,代理controller,通过AOP把我们的一些方法拦截到,如果有异常,就在这个类统一进行处理
 * 下面就是只要带有RestController.class, Controller.class,Service.class的注解的类或方法出现异常,我们都会进行统一处理
 */
@ControllerAdvice(annotations = {RestController.class, Controller.class, Service.class})  // 通知
@ResponseBody  //我们需要返回JSON数据
@Slf4j
public class GlobalExceptionHandler {
 
//  表示处理SQL异常
    @ExceptionHandler(SQLIntegrityConstraintViolationException.class)
    public R<String> exceptionHandler(SQLIntegrityConstraintViolationException ex) {
//      打印日志信息
        log.error(ex.getMessage());//Duplicate entry 'zhangjingqi' for key 'employee.idx_username'
 
//      在这里也可以判断异常的具体信息,比如:
        if(ex.getMessage().contains("Duplicate entry")){
            String[] s = ex.getMessage().split(" ");
            String username = s[2];
            String msg =s[2]+"已经存在";
           return   R.error(msg);
        }
//      其他情况下可以直接输出
        return R.error("未知错误:"+ex.getMessage());
    }
}
 

3.2 结果集

不论是成功响应还是异常响应,都使用下面这个结果集

/**
 * 通用返回结果类,服务端响应的数据都会封装成此对象
 * @param <T>  这个类会接受多种类型,可能是普通对象,可能是数组、集合等等等等,所以我们要将这个加个泛型<T>,表示可以接收任何参数
 */
//  为什么不用Object,而用<T>?  如果用object需要强转类型 而T不用
@Data
public class R<T> {
 
    private Integer code; //编码:1成功,0和其它数字为失败
 
    private String msg; //错误信息
 
    private T data; //数据
 
    private Map map = new HashMap(); //动态数据
 
//  方法的返回值及参数中的T属于泛型
    public static <T> R<T> success(T object) {
        R<T> r = new R<T>();
        r.data = object;
        r.code = 1;   //成功
        return r;
    }
 
    public static <T> R<T> error(String msg) {
        R r = new R();
        r.msg = msg;
        r.code = 0;   //失败
        return r;
    }
 
    public R<T> add(String key, Object value) {
        this.map.put(key, value);
        return this;
    }
 
}

3.3 常用注解

3.3.1 @ControllerAdvice

SpringMVC 中 @ControllerAdvice 注解的三种使用场景! - 江南一点雨 - 博客园 (cnblogs.com)

  • basePackages/basePackageClasses属性

    指定扫描哪个包

@ControllerAdvice(basePackages = "com.zhangjingqi.controller")
public class GlobalExceptionHandler {
    // ...
}

  • annotations属性

    指定注解类型,限定只有被特定注解标记的控制器才会受到 @ControllerAdvice 类的影响

@ControllerAdvice(annotations = {RestController.class, Controller.class, Service.class})  // 通知
@ResponseBody  //我们需要返回JSON数据
@Slf4j
public class GlobalExceptionHandler {
    .......
}
  • assignableTypes属性

    通过指定类类型,限定只有继承自特定类的控制器才会受到 @ControllerAdvice 类的影响

@ControllerAdvice(assignableTypes = MyController.class)
public class GlobalExceptionHandler {
    // ...
}

  • value

    basePackages 属性类似,用于指定扫描的包。在很多情况下,value 属性可以替代 basePackages

@ControllerAdvice(value = "com.zhangjingqi.controllers")
public class GlobalExceptionHandler {
    // ...
}

  • useDefaultResponseAdvice

    默认为 true。当设置为 false 时,禁用默认的 ResponseEntityExceptionHandler,这样你就可以完全掌控异常处理的行为

@ControllerAdvice(useDefaultResponseAdvice = false)
public class CustomExceptionHandler {
    // ...
}

3.3.2 @ExceptionHandle

可以捕获到controller中抛出的一些自定义异常,统一进行处理,一般用于进行一些特定的异常处理

可以根据需要定义多个 @ExceptionHandler 方法,每个方法处理一种特定类型的异常

//  表示处理SQL异常
    @ExceptionHandler(SQLIntegrityConstraintViolationException.class)
    public R<String> exceptionHandler(SQLIntegrityConstraintViolationException ex) {
        ......
    }
  • value

    指定要处理的异常类型。可以是单个异常类或一组异常类

@ExceptionHandler(value = {NullPointerException.class, IllegalArgumentException.class})
public String handleSpecificExceptions(Exception e) {
    // 处理特定类型的异常
    return "处理特定类型的异常";
}

  • exceptions

    value 属性类似,用于指定要处理的异常类型

@ExceptionHandler(exceptions = NullPointerException.class)
public String handleCustomException(Exception e) {
    // 处理自定义异常
    return "处理自定义异常";
}

  • basePackages/basePackageClasses

    限定 @ExceptionHandler 方法的扫描范围,类似于 @ControllerAdvice 的属性

@ExceptionHandler(basePackages = "com.zhangjingqi.controllers")
public String handleExceptionsInControllerPackage(Exception e) {
    // 处理指定包中的异常
    return "packageError";
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

拷贝的艺术:深拷贝与浅拷贝的区别与应用(上)

&#x1f90d; 前端开发工程师&#xff08;主业&#xff09;、技术博主&#xff08;副业&#xff09;、已过CET6 &#x1f368; 阿珊和她的猫_CSDN个人主页 &#x1f560; 牛客高级专题作者、在牛客打造高质量专栏《前端面试必备》 &#x1f35a; 蓝桥云课签约作者、已在蓝桥云…

虚拟电厂 能源物联新方向

今年有多热&#xff1f;据上海市气象局官微消息&#xff0c;5月29日13时09分&#xff0c;徐家汇站气温达36.1℃&#xff0c;打破了百年来的当地5月份气温*高纪录。不仅如此&#xff0c;北京、四川、江西、湖南、广东、广西等地也频频发布高温预警。 伴随着居民用电急剧攀升&am…

LED 底层原理 和 GPIO引脚、寄存器操作

目录 LED 原理 LED 的驱动方式 普适的 GPIO 引脚操作方法 GPIO 寄存器操作 LED 原理 当我们学习 C 语言的时候&#xff0c;我们会写个 Hello 程序。 那当我们写 ARM 程序&#xff0c;也该有一个简单的程序引领我们入门&#xff0c;这个程序就是点亮 LED。 我们怎样去点亮…

Java 中 IO 流

目录 前言 1. 字节流&#xff08;Byte Streams&#xff09;&#xff1a; 1.1 输入字节流&#xff1a; 1.2 输出字节流&#xff1a; 2. 字符流&#xff08;Character Streams&#xff09;&#xff1a; 2.1 输入字符流&#xff1a; 2.2 输出字符流&#xff1a; 3. 转换流&…

如何一键生成多个文件二维码?批量文件二维码制作技巧

文件能批量生成二维码吗&#xff1f;现在的二维码用途范围越来越广&#xff0c;比如常见的有图文、文件、问卷、音频或者视频等内容生成二维码图片&#xff0c;扫码查看内容。那么当需要将很多的文件每个都单独生成一个二维码时&#xff0c;有没有比较简单快捷的操作方法吗&…

SELinux介绍

本章主要介绍在RHEL8中如何使用 SELinux。 了解什么是 SELinux了解 SELinux 的上下文配置端口上下文了解SELinux的布尔值了解SELinux的模式 在 Windows系统中安装了一些安全软件后&#xff0c;当执行某个命令时&#xff0c;如果安全软件认为这个命令对系统是一种危害&#…

二维差分详解

前言 上一期我们分享了一维差分的使用方法&#xff0c;这一期我们将接着上期的内容带大家了解二位差分的使用方法&#xff0c;话不多说&#xff0c;LET’S GO!&#xff08;上一期链接&#xff09; 二维差分 二维差分我们可以用于对矩阵区间进行多次操作的题。 二维差分我们还…

使用国内镜像源安装opencv

在控制台输入命令&#xff1a; pip install opencv-python -i https://pypi.tuna.tsinghua.edu.cn/simple 验证安装&#xff1a; step 1&#xff1a; 打开终端&#xff1b;step 2&#xff1a; 输入python&#xff0c;进入Python编译环境&#xff1b;step 3&#xff1a; 粘贴…

LT8711HE方案《任天堂Switch底座方案》

LT8711HE Type-c转HDMI方案 LT8711HE是高性能的Type-C/DP1.2转HDMI2.0转换器&#xff0c;设计用于连接 USB Type-C 源或 DP1.2 源到 HDMI2.0 接收器。该LT8711HE集成了符合 DP1.2 标准的接收器和符合 HDMI2.0 标准的发射器。此外&#xff0c;两个 CC 控制器是包括用于 CC 通信以…

20.Java程序设计-基于SSM框架的安卓掌上校园生活系统的设计与实现

摘要&#xff1a; 随着移动互联网技术的快速发展&#xff0c;校园生活信息化成为提高学校管理效率、方便学生生活的关键。本研究以基于SSM&#xff08;Spring Spring MVC MyBatis&#xff09;框架的技术体系为基础&#xff0c;致力于设计与实现一款功能强大、高效稳定的安卓…

手动添加Git Bash Here到右键菜单(超详细)

通过WindowsR快捷键可以打开“运行窗口”&#xff0c;在“窗口”中输入“regedit”&#xff0c;点击“确定”打开注册表。 依次进入HKEY_CLASSES_ROOT —-》 Directory —-》Background —-》 shell 路径为Computer\HKEY_CLASSES_ROOT\Directory\Background\shell 3.在“s…

来聊聊Spring的循环依赖

文章目录 首先了解一下什么是循环依赖简述解决循环依赖全过程通过debug了解Spring解决循环依赖全过程Aservice的创建递归来到Bservice的创建然后BService递归回到了getAservice的doGetBean中故事再次回到Aservice填充BService的步骤 总结成流程图为什么二级就能解决循环依赖问题…

2023年底总结丨5大好用的局域网监控软件

不知不觉间2023年又到结尾了&#xff0c;今年我们服务过很多想要电脑监控软件的客服&#xff0c;也服务了很多想要加密软件的客户。 这一年&#xff0c;我们走得不疾不徐&#xff0c;走得稳而坚定&#xff1b;这一年&#xff0c;我们累积服务超过万计客户&#xff1b;这一年&a…

面试官:这些大学生都会

大家好&#xff0c;我是 JavaPub。 最近有些同学在后台问我&#xff0c;面试总是会遇到被问 Linux 命令的问题&#xff0c;自己就面试个后端开发岗位&#xff0c;怎么这么难呢&#xff1f; 其实 Linux 命令&#xff0c;对于一个后端开发来说&#xff0c;并不是很难&#xff0c…

springcloudalibaba01

整合springcloud 和 springcloudalibaba&#xff0c;&#xff0c;&#xff0c; 版本对应关系 <dependencyManagement><dependencies><!--每个springcloud的工具都有一个版本每个springcloud alibaba的工具都有一个版本统一版本--> <!-- 整合…

spring boot 实现直播聊天室

spring boot 实现直播聊天室 技术方案: spring bootwebsocketrabbitmq 使用 rabbitmq 提高系统吞吐量 引入依赖 <dependencies><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>2.0.42&…

广州华锐互动:汽车电子线束加工VR仿真培训与实际生产场景相结合,提高培训效果

随着科技的不断发展&#xff0c;虚拟现实&#xff08;VR&#xff09;技术已经逐渐渗透到各个领域&#xff0c;为企业和个人带来了前所未有的便利。在汽车制造行业中&#xff0c;线束加工作为一项关键的生产工艺&#xff0c;其质量直接影响到汽车的性能和安全。因此&#xff0c;…

【UE】在蓝图中修改材质实例的参数的两种方式

目录 方式一、通过“在材质上设置标量/向量参数值”节点实现 方式二、通过“设置标量/向量参数值”节点实现 方式一、通过“在材质上设置标量/向量参数值”节点实现 1. 在材质中设置了两个参数 2. 创建材质实例 3. 创建一个蓝图&#xff0c;对静态网格体赋予材质实例 在事件…

基于虚拟机下的win7系统安装简记

文章目录 安装系统激活win7提示系统保留分区未分配驱动器问题使用win7 Active激活系统根据dns分配的ip地址将网络改为固定ip&#xff0c;然后关闭防火墙&#xff0c;即可完成虚拟机与宿主机互通 安装系统 在虚拟机中找到自己下载win7镜像文件&#xff0c;配置完成后一路next即…

MySQL数据库卸载-Windows

目录 1. 停止MySQL服务 2. 卸载MySQL相关组件 3. 删除MySQL安装目录 4. 删除MySQL数据目录 5. 再次打开服务&#xff0c;查看是否有MySQL卸载残留 1. 停止MySQL服务 winR 打开运行&#xff0c;输入 services.msc 点击 "确定" 调出系统服务。 2. 卸载MySQL相关组…