《SpringBoot项目实战》第五篇—接口发生异常如何统一处理

news2024/11/27 6:19:13

系列文章导航
第一篇—接口参数的一些弯弯绕绕
第二篇—接口用户上下文的设计与实现
第三篇—留下用户调用接口的痕迹
第四篇—接口的权限控制
第五篇—接口发生异常如何统一处理

本文参考项目源码地址:summo-springboot-interface-demo

前言

大家好!我是sum墨,一个一线的底层码农,平时喜欢研究和思考一些技术相关的问题并整理成文,限于本人水平,如果文章和代码有表述不当之处,还请不吝赐教。

作为一名从业已达六年的老码农,我的工作主要是开发后端Java业务系统,包括各种管理后台和小程序等。在这些项目中,我设计过单/多租户体系系统,对接过许多开放平台,也搞过消息中心这类较为复杂的应用,但幸运的是,我至今还没有遇到过线上系统由于代码崩溃导致资损的情况。这其中的原因有三点:一是业务系统本身并不复杂;二是我一直遵循某大厂代码规约,在开发过程中尽可能按规约编写代码;三是经过多年的开发经验积累,我成为了一名熟练工,掌握了一些实用的技巧。

BUG对于程序员来说实在是不陌生,当代码出现BUG时,异常也会随之出现,但BUG并不等于异常,BUG只是导致异常出现的一个原因。导致异常发生的原因非常多,本篇文章我也主要只讲一下接口相关的异常怎么处理。

一、接口异常的分类

在接口设计中,应该尽量避免使用异常来进行控制流程。接口应该尽可能返回明确的错误码和错误信息,而不是直接抛出异常。

1. 业务异常(Business Exception)

这是接口处理过程中可能出现的业务逻辑错误,例如参数校验失败、权限不足等。这些异常通常是预期的,并且可以提供相应的错误码和错误信息给调用方。

2. 系统异常(System Exception)

这是接口处理过程中可能出现的非预期错误,例如数据库异常、网络异常等。这些异常通常是未知的,并且可能导致接口无法正常响应。这种错误不仅需要记录异常信息通知系统管理员处理,还需要封装起来做好提示,不能直接把错误返回给用户。

3. 客户端异常(Client Exception)

这是调用方在使用接口时可能出现的错误,例如请求参数错误、请求超时等。这些异常通常是由于调用方的错误导致的,接口本身没有问题。可以根据具体情况选择是否返回错误信息给调用方。

二、接口异常的常见处理办法

1. 异常捕获和处理

在接口的实现代码中,可以使用try-catch语句捕获异常,并进行相应的处理。可以选择将异常转化为合适的错误码和错误信息,然后返回给调用方。或者根据具体情况选择是否记录异常日志,并通知系统管理员进行处理。

2. 统一异常处理器

可以使用统一的异常处理器来统一处理接口异常。在Spring Boot中,可以使用@ControllerAdvice和@ExceptionHandler注解来定义一个全局的异常处理器。这样可以将所有接口抛出的异常统一处理,例如转化为特定的错误码和错误信息,并返回给调用方。

3. 抛出自定义异常

可以根据业务需求定义一些自定义的异常类,继承RuntimeException或其他合适的异常类,并在接口中抛出这些异常。这样可以在异常发生时,直接抛出异常,由上层调用方进行捕获和处理。

4. 返回错误码和错误信息

可以在接口中定义一套错误码和错误信息的规范,当发生异常时,返回对应的错误码和错误信息给调用方。这样调用方可以根据错误码进行相应的处理,例如展示错误信息给用户或者进行相应的逻辑处理。
例如这样的弹窗提示

5. 跳转到指定错误页

比如遇到401、404、500等错误时,SpringBoot框架会返回自带的错误页,在这里我们其实可以自己重写一些更美观、更友好的错误提示页,最好还能引导用户回到正确的操作上来,例如这样

而不是下面这样

三、接口异常的统一处理

通过前面两段我们可以发现,造成异常的原因很多,出现异常的地方很多,异常的处理手段也很多。基于以上三多的情况,我们需要一个地方来统一接收异常、统一处理异常,上面提到SpringBoot的@ControllerAdvice注解作为一个全局的异常处理器来统一处理异常。但@ControllerAdvice注解不是万能的,它有一个问题:

对于@ControllerAdvice注解来说,它主要用于处理Controller层的异常情况,即在控制器方法中发生的异常。因为它是基于Spring MVC的控制器层的异常处理机制。
而Filter层是位于控制器之前的一层过滤器,它可以用于对请求进行预处理和后处理。当请求进入Filter时,还没有进入到Controller层,所以@ControllerAdvice注解无法直接处理Filter层中的异常。
所以对于Filter中的异常,我们需要单独处理。

1. @ControllerAdvice全局异常处理器的使用

(1)自定义业务异常

由于SpringBoot框架并没有定义业务相关的错误码,所以我们需要自定义业务错误码。该错误码可以根据业务复杂程度进行分类,每个错误码对应一个具体的异常情况。这样前后端统一处理异常时可以根据错误码进行具体的处理逻辑,提高异常处理的准确性和效率。同时,定义错误码还可以方便进行异常监控和日志记录,便于排查和修复问题。

a、定义常见的异常状态码

ResponseCodeEnum.java

package com.summo.demo.model.response;


public enum ResponseCodeEnum {
    /**
     * 请求成功
     */
    SUCCESS("0000", ErrorLevels.DEFAULT, ErrorTypes.SYSTEM, "请求成功"),
    /**
     * 登录相关异常
     */
    LOGIN_USER_INFO_CHECK("LOGIN-0001", ErrorLevels.INFO, ErrorTypes.BIZ, "用户信息错误"),
    /**
     * 权限相关异常
     */
    NO_PERMISSIONS("PERM-0001", ErrorLevels.INFO, ErrorTypes.BIZ, "用户无权限"),
    /**
     * 业务相关异常
     */
    BIZ_CHECK_FAIL("BIZ-0001", ErrorLevels.INFO, ErrorTypes.BIZ, "业务检查异常"),
    BIZ_STATUS_ILLEGAL("BIZ-0002", ErrorLevels.INFO, ErrorTypes.BIZ, "业务状态非法"),
    BIZ_QUERY_EMPTY("BIZ-0003", ErrorLevels.INFO, ErrorTypes.BIZ, "查询信息为空"),
    /**
     * 系统出错
     */
    SYSTEM_EXCEPTION("SYS-0001", ErrorLevels.ERROR, ErrorTypes.SYSTEM, "系统出错啦,请稍后重试"),
    ;

    /**
     * 枚举编码
     */
    private final String code;

    /**
     * 错误级别
     */
    private final String errorLevel;

    /**
     * 错误类型
     */
    private final String errorType;

    /**
     * 描述说明
     */
    private final String description;

    ResponseCodeEnum(String code, String errorLevel, String errorType, String description) {
        this.code = code;
        this.errorLevel = errorLevel;
        this.errorType = errorType;
        this.description = description;
    }

    public String getCode() {
        return code;
    }

    public String getErrorLevel() {
        return errorLevel;
    }

    public String getErrorType() {
        return errorType;
    }

    public String getDescription() {
        return description;
    }


    public static ResponseCodeEnum getByCode(Integer code) {
        for (ResponseCodeEnum value : values()) {
            if (value.getCode().equals(code)) {
                return value;
            }
        }
        return SYSTEM_EXCEPTION;
    }

}

b、自定义业务异常类

BizException.java

package com.summo.demo.exception.biz;

import com.summo.demo.model.response.ResponseCodeEnum;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;

@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class BizException extends RuntimeException {

    /**
     * 错误码
     */
    private ResponseCodeEnum errorCode;

    /**
     * 自定义错误信息
     */
    private String errorMsg;

}

(2) 全局异常处理器

BizGlobalExceptionHandler

package com.summo.demo.exception.handler;

import javax.servlet.http.HttpServletResponse;

import com.summo.demo.exception.biz.BizException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.ModelAndView;

@RestControllerAdvice(basePackages = {"com.summo.demo.controller", "com.summo.demo.service"})
public class BizGlobalExceptionHandler {

    @ExceptionHandler(BizException.class)
    public ModelAndView handler(BizException ex, HttpServletResponse response) {
        ModelAndView modelAndView = new ModelAndView();
        switch (ex.getErrorCode()) {
            case LOGIN_USER_INFO_CHECK:
                // 重定向到登录页
                modelAndView.setViewName("redirect:/login");
                break;
            case NO_PERMISSIONS:
                // 设置错误信息和错误码
                modelAndView.addObject("errorMsg", ex.getErrorMsg());
                modelAndView.addObject("errorCode", ex.getErrorCode().getCode());
                modelAndView.setViewName("403");
                break;
            case BIZ_CHECK_FAIL:
            case BIZ_STATUS_ILLEGAL:
            case BIZ_QUERY_EMPTY:
            case SYSTEM_EXCEPTION:
            default:
                // 设置错误信息和错误码
                modelAndView.addObject("errorMsg", ex.getErrorMsg());
                modelAndView.addObject("errorCode", ex.getErrorCode().getCode());
                modelAndView.setViewName("error");
        }
        return modelAndView;
    }
}

(3) 测试效果

@RestControllerAdvice和@ExceptionHandler使用起来很简单,下面我们来测试一下(由于不写界面截图是在太丑,我麻烦ChatGPT帮我写了一套简单的界面)。

a、普通业务异常捕获
第一步、打开登录页

访问链接:http://localhost:8080/login
输入账号、密码,点击登录进入首页

第二步、登录进入首页

第三步、调用一个会报错的接口

再服务启动之前我写了一个根据用户名查询用户的方法,如果查询不到用户的话我会抛出一个异常,代码如下:

public ResponseEntity<String> query(String userName) {
  //根据名称查询用户
  List<UserDO> list = userRepository.list(
  new QueryWrapper<UserDO>().lambda().like(UserDO::getUserName, userName));
  if (CollectionUtils.isEmpty(list)) {
    throw new BizException(ResponseCodeEnum.BIZ_QUERY_EMPTY, "根据用户名称查询用户为空!");
  }
  //返回数据
  return ResponseEntity.ok(JSONObject.toJSONString(list));
}

这时,我们查询一个不存在的用户
访问接口:http://localhost:8080/user/query?userName=sss
因为数据库中没有用户名为sss的这个用户,会抛出一个异常

b、403权限不足异常捕获
第一步、打开登录页

访问链接:http://localhost:8080/login
登录界面使用小B的账号登录

第二步、登录进入首页

第三步、调用删除用户的接口

调用接口:http://localhost:8080/user/delete?userId=2
由于小B的账号只有查询权限,没有删除权限,所以返回403错误页

注意👉🏻:在调试之前需要在application.yml或application.properties配置文件中增加一个配置:server.error.whitelabel.enabled=false
这个配置的意思是是否启用默认的错误页面,这里我们自己写了一套错误页,所以不需要框架自带的配置了。

2. 自定义Filter中异常的处理

由于@ControllerAdvice注解无法捕获自定义Filter中抛出的异常,这里我们就需要使用另外一种方法进行处理:ErrorController接口。

(1) 原理解释

Spring Boot的ErrorController是一个接口,用于定义处理应用程序中发生的错误的自定义逻辑。它允许开发人员以更灵活的方式处理和响应异常,而不是依赖于默认的错误处理机制。:

  • 定制错误页面:通过实现ErrorController接口,可以自定义应用程序的错误页面,以提供更好的用户体验。可以根据不同的异常类型和HTTP状态码提供不同的错误页面或错误信息。
  • 记录错误日志:ErrorController可以用于捕获和记录应用程序中的异常,并将其记录到日志中。这对于问题追踪和排查非常有帮助,可以了解应用程序中发生的错误和异常的详细信息。
  • 重定向或转发请求:通过ErrorController,可以根据错误的类型或其他条件,将请求重定向到不同的URL或转发到其他控制器方法。这对于根据错误情况做出不同的处理非常有用,例如重定向到自定义的错误页面或执行特定的错误处理逻辑。

(2) 使用方法

使用方法直接看看我的代码就知道了。
CustomErrorController.java

package com.summo.demo.controller;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang3.StringUtils;
import org.springframework.boot.web.servlet.error.ErrorController;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;

@Controller
public class CustomErrorController implements ErrorController {

    @RequestMapping("/error")
    public ModelAndView handleError(HttpServletRequest request, HttpServletResponse response) {
        //获取当前响应返回的状态码
        int statusCode = response.getStatus();
        //如果响应头中存在statusCode,则默认使用这个statusCode
        if (StringUtils.isNotBlank(response.getHeader("statusCode"))) {
            statusCode = Integer.valueOf(response.getHeader("statusCode"));
        }
        if (statusCode == HttpServletResponse.SC_FOUND) {
            // 获取Location响应头的值,进行重定向
            String redirectLocation = response.getHeader("Location");
            return new ModelAndView("redirect:" + redirectLocation);
        } else if (statusCode == HttpServletResponse.SC_UNAUTHORIZED) {
            // 重定向到登录页
            return new ModelAndView("redirect:/login");
        } else if (statusCode == HttpServletResponse.SC_FORBIDDEN) {
            // 返回403页面
            return new ModelAndView("403");
        } else if (statusCode == HttpServletResponse.SC_NOT_FOUND) {
            // 返回404页面
            return new ModelAndView("404");
        } else if (statusCode == HttpServletResponse.SC_INTERNAL_SERVER_ERROR) {
            // 返回500页面,并传递errorMsg和errorCode到模板
            ModelAndView modelAndView = new ModelAndView("500");
            modelAndView.addObject("errorMsg", response.getHeader("errorMsg"));
            modelAndView.addObject("errorCode", response.getHeader("errorCode"));
            return modelAndView;
        } else {
            // 返回其他错误页面
            return new ModelAndView("error");
        }
    }

}

细心的读者可能会看到,statusCode来自于两个地方,第一个是response.getStatus();第二个是response.getHeader(“statusCode”)。这两者的区别是第一个是框架自动设置的,第二个则是我根据业务逻辑设置的。
原因是在WebFilter中一旦抛出了异常,response.getStatus()一定会是500,即使这个异常是因为用户身份失效导致的。但异常又不得不抛出,所以我通过自定义response的header的方式设置了错误码,传递到/error接口。

(3) 测试效果

a、404错误页,接口找不到
第一步、打开登录页

访问链接:http://localhost:8080/login
输入账号、密码,点击登录进入首页

第二步、登录进入首页

第三步、访问一个不存在的页面

访问链接:http://localhost:8080/xxxx
由于xxxx接口没有被定义过,界面会返回404

b、401错误,用户身份标识为空或无效

这里我做的处理是,如果用户身份标识为空或无效那么我会默认跳转到登录页。
测试方法是打开一个无痕界面,随便输入一个链接:http://localhost:8080/user/query
由于Cookie中token不存在,所以我不管访问的是哪个链接,直接将状态码改为401,而CustomErrorController遇到401的错误,会默认重定向到登录页。

四、优化无痕窗口下的重新登录体验

Filter异常的全局处理除了ErrorController之外,还可以通过自定义拦截器的方式实现,这两个东西会一个就行了。这里我再说一个高级一点的东西,举个例子:
我在一个无痕窗口调用接口:http://localhost:8080/user/query?userName=小B
因为当前窗口的Cookie中是没有token的,按照401错误的处理方式,我会重定向到登录页去。
但这个有一个问题:重新登录之后,进入的是首页,不是调用user/query接口,我还得重新去找这个接口,重新输入参数。而且这要是一个分享页那就尴尬了,登陆完不知道对方分享了啥,用户体验会很差,那么有办法优化这个问题吗?答案是有,如何做,继续看。

1. 在WebFilter中获取当前请求的全路径

所谓全路径就是“http://localhost:8080/user/query?userName=小B” ,如何获取,可以用我这个方法

/**
   * 获取完整的路径URL,包括参数
   *
   * @param httpServletRequest
   * @return 路径URL
*/
private String getRequestURL(HttpServletRequest httpServletRequest) {
  String url = httpServletRequest.getRequestURL().toString();
  String query = httpServletRequest.getQueryString();
  if (query != null) {
    url += "?" + query;
  }
  return url;
}

2. 在WebFilter抛出401错误的地方设置httpServletResponse的header

如下

httpServletResponse.setHeader("redirectURL",URLEncoder.encode(getRequestURL(httpServletRequest), "utf-8"));

因为参数有可能是中文,这里需要用URLEncoder转下义。

3. 在CustomErrorController中获取到这个跳转链接

// 重定向到登录页或指定页面
 if (StringUtils.isNotBlank(response.getHeader("redirectURL"))) {
  return new ModelAndView("redirect:/login?redirectURL=" + response.getHeader("redirectURL"));
 }

效果如下

可以看到我们在login后面携带了一个redirectURL参数

4. 登录提交时将redirectURL参数一并提交

 @PostMapping("/login")
public void userLogin(@RequestParam(required = true) String userName,
        @RequestParam(required = true) String password,
        @RequestParam(required = false) String redirectURL,
        HttpServletRequest httpServletRequest,
        HttpServletResponse httpServletResponse) {
  userService.login(userName, password, redirectURL, httpServletRequest, httpServletResponse);
}

5. 验证通过后重定向到redirectURL

 try {
  //如果跳转路径不为空,则直接重定向到跳转路径
  if (StringUtils.isNotBlank(redirectURL)) {
    httpServletResponse.sendRedirect(redirectURL);
    return;
  }
 //跳转到登录页
  httpServletResponse.sendRedirect("/index");
  } catch (IOException e) {
  log.error("重定向发生异常", e);
}

以上就是这个问题的解决方案了,具体代码大家可以看我的demo:summo-springboot-interface-demo

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

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

相关文章

开源六轴协作机械臂MechArm 拖动示教模型案例

介绍 今天&#xff0c;我将向大家展示一个我独立设计并实现的机械臂模型。这个模型的核心功能是实现实时的手势追踪——只需用手轻轻拖拽&#xff0c;机械臂就能立即跟随你的动作进行移动。 我之所以想要创造这样一个模型&#xff0c;是因为在一些危险环境中&#xff0c;我们可…

搭建二维码系统,轻松实现固定资产的一物一码管理

固定资产管理中普遍存在盘点难、家底不清、账实不一致、权责不清晰等问题&#xff0c;可以在草料上搭建固定资产管理系统&#xff0c;通过组合功能模块实现资产信息展示、领用登记、出入库管理、故障报修等功能&#xff0c;对固定资产进行一物一码规范化管理。 比如张掖公路事业…

C++学习之map和set

目录 一&#xff0c;什么是map和set 二&#xff0c;set的使用 插入 键值对 删除&#xff08;erase&#xff09;与查找 lowerbound与upperbound equal_range multiset 三&#xff0c;map的使用 insert 查找 删除 重载[ ] ​编辑 一&#xff0c;什么是map和set C中…

云闪付app拉新好做吗?地推和网推百搭拉新项目申请渠道

云闪付拉新可通过”聚量推客“申请 云闪付是银联出的支付平台&#xff0c;每年在拉新市场的预算较高&#xff0c;比较适合地推和网推做项目搭配&#xff0c;只需要完成一次动账即可结算 我们上了两个版本的云闪付项目&#xff0c;下图展示 也有更多的其它地推和网推拉新项目可…

【通关选择】upload-labs通关攻略(大全)

前提条件&#xff1a; 1.文件能够成功上传到服务器 2.攻击者能够知道文件的上传路径 upload-labs靶场 Pass-01&#xff08; 前端验证&#xff09; 三种方法解决思路 1.禁用客户端JavaScript弹窗 2.通过burp抓包&#xff0c;修改后缀名 3.f12删除return filecheck&#xff0…

树专题 —— 二叉搜索树和中序遍历

大家好&#xff0c;我是 方圆。我准备把树写成一个专题&#xff0c;包括二叉搜索树、前序、中序、后序遍历以及红黑树&#xff0c;我也想试试能不能将红黑树写好。 本篇是关于二叉搜索树&#xff0c;也是所有后续学习的基础&#xff0c;其中会涉及前序、中序、后序遍历&#x…

外卖系统的数据管理和隐私保护应该如何进行?

1. 数据管理 外卖系统处理大量用户数据&#xff0c;包括个人信息、订单记录、支付信息等。以下是一些数据管理的最佳实践&#xff1a; 合规性与透明度&#xff1a;确保你的数据收集、存储和处理符合相关法规&#xff0c;例如GDPR&#xff08;通用数据保护条例&#xff09;。同…

网络安全防御体系构建思路

前言 在某一天的深夜&#xff0c;作为安全从业人员&#xff0c;穿着大裤衩子&#xff0c;坐在门前&#xff0c;点燃一根烟&#xff08;画面自己想象&#xff09;开始思考企业如何打造自己的安全体系&#xff0c;虽然这不是作为月薪3k该考虑的问题&#xff0c;但是毕竟当初笔者…

element的表单校验正常手机号码以及输入框填写“不详”的情况

element的表单校验正常手机号码以及输入框填写“不详”的情况 <el-col :span"6"><el-form-item label"手机号码" prop"phoneNumber" class"grid-content bg-purple"><el-input v-model"testForm.phoneNumber&quo…

Linux环境下的SVN服务器搭建并结合内网穿透实现远程连接

文章目录 前言1. Ubuntu安装SVN服务2. 修改配置文件2.1 修改svnserve.conf文件2.2 修改passwd文件2.3 修改authz文件 3. 启动svn服务4. 内网穿透4.1 安装cpolar内网穿透4.2 创建隧道映射本地端口 5. 测试公网访问6. 配置固定公网TCP端口地址6.1 保留一个固定的公网TCP端口地址6…

第四章 Web服务器(1)

1.www简介 Web网络服务也叫WWW(World Wide Web 全球信息广播)万维网服务&#xff0c;一般是指能够让用户通过浏览器访问到互联网中文档等资源的服务 Web 网络服务是一种被动访问的服务程序&#xff0c;即只有接收到互联网中其他主机发出的请求后才会响应&#xff0c;最…

LINQ to SQL系列三 使用DeferredLoadingEnabled,DataLoadOption指定加载选项

介绍linq to sql 的 DataContext类DeferredLoadingEnabled属性使用,以及DataLoadOptions限定加载相关表数据的LoadWith和AssociateWith方法。 本文中举例用到的数据模型如下: Student和Class之间是多对一关系,Student和Course之间是多对多关系。 DataContext的DeferredLo…

使用oracle虚拟机添加新硬盘

1、关闭运行的虚拟机后配置 单击选择要配置的oracle虚拟机&#xff0c;单击设置–>存储—>控制器&#xff0c;单击添加虚拟硬盘图标。 2、配置硬盘 单击“创建”&#xff0c;单击“下一步”&#xff0c;选择需要创建的虚拟硬盘大小&#xff0c;完成创建。 完成创建后…

基于SpringBoot+Redis的前后端分离外卖项目-苍穹外卖(一)

熟悉项目环境 1. 苍穹外卖项目介绍1.1 项目介绍1.2 技术选型 2. 开发环境搭建2.1 前端环境2.2 后端环境搭建2.3 Git版本控制2.4 nginx反向代理和负载均衡 3.登录功能4. Swagger4.1 介绍4.2 使用步骤4.3 常用注解 1. 苍穹外卖项目介绍 1.1 项目介绍 苍穹外卖是专门为餐饮企业&…

SRC | CORS跨资源共享漏洞

CORS跨资源共享 跨源资源共享 (CORS) 是一种浏览器机制&#xff0c;允许网页使用来自其他页面或域的资产和数据。 大多数站点需要使用资源和图像来运行它们的脚本。这些嵌入式资产存在安全风险&#xff0c;因为这些资产可能包含病毒或允许服务器访问黑客。 CORS响应头 CORS通…

C-DS二叉树_另一棵树的子树

Description 给你两棵二叉树tree1和tree2,检验tree1中是否包含和tree2具有相同结构和结点值的子树。如果存在,输出true;否则,输出false。 Input 第一行输入t,表示有t个测试样例。 第二行首先输入n1,接着输入n1个整数,表示二叉树tree1。 第三行首先输入n2,接着输入n…

Keras人工智能神经网络 Classifier 分类 神经网络搭建

前期我们分享tensorflow以及pytorch时&#xff0c;分享过tensorflow以及pytorch的分类神经网络的搭建步骤&#xff0c;在哪里我们使用的训练集是mnist&#xff0c;同样Keras分类神经网络的搭建&#xff0c;我们同样使用mnist数据集来进行分类神经网络的搭建&#xff08;有关mni…

【NI-DAQmx入门】NI-DAQmx之Python

NI-DAQmx Python GitHub资源&#xff1a; NI-DAQmx Python 文档说明&#xff1a;NI-DAQmx Python Documentation — NI-DAQmx Python API 0.9 documentation nidaqmx支持 CPython 3.7和 PyPy3&#xff0c;需要注意的是多支持USB DAQ和PCI DAQ&#xff0c;cDAQ需要指定…

改进的yolov5

The networkstructure of these models is constant, but the modules and con-volution kernels are scaled, which alters the complexity and sizeof each model.&#xff08;这些模型的网络结构是恒定的&#xff0c;但模块和卷积核被缩放&#xff0c;这改变了每个模型的复杂…

自动驾驶算法(五):基于遗传算法的路径规划(上)

目录 1 遗传算法介绍 2 遗传算法代码详解--绘制地图与种群初始化代码讲解 1 遗传算法介绍 模拟生物进化过程&#xff0c;物竞天择&#xff0c;适者生存。 我们先为栅格地图进行编码&#xff1a;从起点0出发到终点24这个栅格。我们首先有一条路径&#xff08;0&#xff0c;6&a…