当前位置: 首页 > news >正文

SpringMVC实战(3):拓展

四、RESTFul风格设计和实战

4.1 RESTFul风格概述

4.1.1 RESTFul风格简介

RESTful(Representational State Transfer)是一种软件架构风格,用于设计网络应用程序和服务之间的通信。它是一种基于标准 HTTP 方法的简单和轻量级的通信协议,广泛应用于现代的Web服务开发。

通过遵循 RESTful 架构的设计原则,可以构建出易于理解、可扩展、松耦合和可重用的 Web 服务。RESTful API 的特点是简单、清晰,并且易于使用和理解,它们使用标准的 HTTP 方法和状态码进行通信,不需要额外的协议和中间件。

总而言之,RESTful 是一种基于 HTTP 和标准化的设计原则的软件架构风格,用于设计和实现可靠、可扩展和易于集成的 Web 服务和应用程序!

学习RESTful设计原则可以帮助我们更好去设计HTTP协议的API接口!!

4.1.2 RESTFul风格特点
  1. 每一个URI代表1种资源(URI 是名词);

  2. 客户端使用GET、POST、PUT、DELETE 4个表示操作方式的动词对服务端资源进行操作:GET用来获取资源,POST用来新建资源(也可以用于更新资源),PUT用来更新资源,DELETE用来删除资源;

  3. 资源的表现形式是XML或者JSON

  4. 客户端与服务端之间的交互在请求之间是无状态的,从客户端到服务端的每个请求都必须包含理解请求所必需的信息。

4.1.3 RESTFul风格设计规范
  1. HTTP协议请求方式要求

    REST 风格主张在项目设计、开发过程中,具体的操作符合HTTP协议定义的请求方式的语义

    操作请求方式
    查询操作GET
    保存操作POST
    删除操作DELETE
    更新操作PUT
  2. URL路径风格要求

    REST风格下每个资源都应该有一个唯一的标识符,例如一个 URI(统一资源标识符)或者一个 URL(统一资源定位符)。资源的标识符应该能明确地说明该资源的信息,同时也应该是可被理解和解释的!

    使用URL+请求方式确定具体的动作,他也是一种标准的HTTP协议请求!

    操作传统风格REST 风格
    保存/CRUD/saveEmpURL 地址:/CRUD/emp 请求方式:POST
    删除/CRUD/removeEmp?empId=2URL 地址:/CRUD/emp/2 请求方式:DELETE
    更新/CRUD/updateEmpURL 地址:/CRUD/emp 请求方式:PUT
    查询/CRUD/editEmp?empId=2URL 地址:/CRUD/emp/2 请求方式:GET
  • 总结

    根据接口的具体动作,选择具体的HTTP协议请求方式

    路径设计从原来携带动标识,改成名词,对应资源的唯一标识即可!

4.1.4 RESTFul风格好处
  1. 含蓄,安全

    使用问号键值对的方式给服务器传递数据太明显,容易被人利用来对系统进行破坏。使用 REST 风格携带数据不再需要明显的暴露数据的名称。

  2. 风格统一

    URL 地址整体格式统一,从前到后始终都使用斜杠划分各个单词,用简单一致的格式表达语义。

  3. 无状态

    在调用一个接口(访问、操作资源)的时候,可以不用考虑上下文,不用考虑当前状态,极大的降低了系统设计的复杂度。

  4. 严谨,规范

    严格按照 HTTP1.1 协议中定义的请求方式本身的语义进行操作。

  5. 简洁,优雅

    过去做增删改查操作需要设计4个不同的URL,现在一个就够了。

    操作传统风格REST 风格
    保存/CRUD/saveEmpURL 地址:/CRUD/emp 请求方式:POST
    删除/CRUD/removeEmp?empId=2URL 地址:/CRUD/emp/2 请求方式:DELETE
    更新/CRUD/updateEmpURL 地址:/CRUD/emp 请求方式:PUT
    查询/CRUD/editEmp?empId=2URL 地址:/CRUD/emp/2 请求方式:GET
  6. 丰富的语义

    通过 URL 地址就可以知道资源之间的关系。它能够把一句话中的很多单词用斜杠连起来,反过来说就是可以在 URL 地址中用一句话来充分表达语义。

    http://localhost:8080/shop http://localhost:8080/shop/product http://localhost:8080/shop/product/cellPhone http://localhost:8080/shop/product/cellPhone/iPhone

4.2 RESTFul风格实战

4.2.1 需求分析
  • 数据结构: User {id 唯一标识,name 用户名,age 用户年龄}

  • 功能分析

    • 用户数据分页展示功能(条件:page 页数 默认1,size 每页数量 默认 10)

    • 保存用户功能

    • 根据用户id查询用户详情功能

    • 根据用户id更新用户数据功能

    • 根据用户id删除用户数据功能

    • 多条件模糊查询用户功能(条件:keyword 模糊关键字,page 页数 默认1,size 每页数量 默认 10)

4.2.2 RESTFul风格接口设计
  1. 接口设计

    功能接口和请求方式请求参数返回值
    分页查询GET /userpage=1&size=10{ 响应数据 }
    用户添加POST /user{ user 数据 }{响应数据}
    用户详情GET /user/1路径参数{响应数据}
    用户更新PUT /user{ user 更新数据}{响应数据}
    用户删除DELETE /user/1路径参数{响应数据}
    条件模糊GET /user/searchpage=1&size=10&keywork=关键字{响应数据}
  2. 问题讨论

    为什么查询用户详情,就使用路径传递参数,多条件模糊查询,就使用请求参数传递?

    误区:restful风格下,不是所有请求参数都是路径传递!可以使用其他方式传递!

    在 RESTful API 的设计中,路径和请求参数和请求体都是用来向服务器传递信息的方式。

    • 对于查询用户详情,使用路径传递参数是因为这是一个单一资源的查询,即查询一条用户记录。使用路径参数可以明确指定所请求的资源,便于服务器定位并返回对应的资源,也符合 RESTful 风格的要求。

    • 而对于多条件模糊查询,使用请求参数传递参数是因为这是一个资源集合的查询,即查询多条用户记录。使用请求参数可以通过组合不同参数来限制查询结果,路径参数的组合和排列可能会很多,不如使用请求参数更加灵活和简洁。 此外,还有一些通用的原则可以遵循:

    • 路径参数应该用于指定资源的唯一标识或者 ID,而请求参数应该用于指定查询条件或者操作参数。

    • 请求参数应该限制在 10 个以内,过多的请求参数可能导致接口难以维护和使用。

    • 对于敏感信息,最好使用 POST 和请求体来传递参数。

4.2.3 后台接口实现

准备用户实体类:

package com.atguigu.pojo;
​
/*** projectName: com.atguigu.pojo* 用户实体类*/
public class User {
​private Integer id;private String name;
​private Integer age;
​public Integer getId() {return id;}
​public void setId(Integer id) {this.id = id;}
​public String getName() {return name;}
​public void setName(String name) {this.name = name;}
​public Integer getAge() {return age;}
​public void setAge(Integer age) {this.age = age;}
​@Overridepublic String toString() {return "User{" +"id=" + id +", name='" + name + '\'' +", age=" + age +'}';}
}
​

准备用户Controller:

/*** projectName: com.atguigu.controller** description: 用户模块的控制器*/
@RequestMapping("user")
@RestController
public class UserController {
​/*** 模拟分页查询业务接口*/@GetMappingpublic Object queryPage(@RequestParam(name = "page",required = false,defaultValue = "1")int page,@RequestParam(name = "size",required = false,defaultValue = "10")int size){System.out.println("page = " + page + ", size = " + size);System.out.println("分页查询业务!");return "{'status':'ok'}";}
​
​/*** 模拟用户保存业务接口*/@PostMappingpublic Object saveUser(@RequestBody User user){System.out.println("user = " + user);System.out.println("用户保存业务!");return "{'status':'ok'}";}
​/*** 模拟用户详情业务接口*/@PostMapping("/{id}")public Object detailUser(@PathVariable Integer id){System.out.println("id = " + id);System.out.println("用户详情业务!");return "{'status':'ok'}";}
​
​/*** 模拟用户更新业务接口*/@PutMappingpublic Object updateUser(@RequestBody User user){System.out.println("user = " + user);System.out.println("用户更新业务!");return "{'status':'ok'}";}
​
​/*** 模拟条件分页查询业务接口*/@GetMapping("search")public Object queryPage(@RequestParam(name = "page",required = false,defaultValue = "1")int page,@RequestParam(name = "size",required = false,defaultValue = "10")int size,@RequestParam(name = "keyword",required= false)String keyword){System.out.println("page = " + page + ", size = " + size + ", keyword = " + keyword);System.out.println("条件分页查询业务!");return "{'status':'ok'}";}
}

五、SpringMVC其他扩展

5.1 全局异常处理机制

5.1.1 异常处理两种方式

开发过程中是不可避免地会出现各种异常情况的,例如网络连接异常、数据格式异常、空指针异常等等。异常的出现可能导致程序的运行出现问题,甚至直接导致程序崩溃。因此,在开发过程中,合理处理异常、避免异常产生、以及对异常进行有效的调试是非常重要的。

对于异常的处理,一般分为两种方式:

  • 编程式异常处理:是指在代码中显式地编写处理异常的逻辑。它通常涉及到对异常类型的检测及其处理,例如使用 try-catch 块来捕获异常,然后在 catch 块中编写特定的处理代码,或者在 finally 块中执行一些清理操作。在编程式异常处理中,开发人员需要显式地进行异常处理,异常处理代码混杂在业务代码中,导致代码可读性较差。

  • 声明式异常处理:则是将异常处理的逻辑从具体的业务逻辑中分离出来,通过配置等方式进行统一的管理和处理。在声明式异常处理中,开发人员只需要为方法或类标注相应的注解(如 @Throws@ExceptionHandler),就可以处理特定类型的异常。相较于编程式异常处理,声明式异常处理可以使代码更加简洁、易于维护和扩展。

站在宏观角度来看待声明式事务处理:

整个项目从架构这个层面设计的异常处理的统一机制和规范。

一个项目中会包含很多个模块,各个模块需要分工完成。如果张三负责的模块按照 A 方案处理异常,李四负责的模块按照 B 方案处理异常……各个模块处理异常的思路、代码、命名细节都不一样,那么就会让整个项目非常混乱。

使用声明式异常处理,可以统一项目处理异常思路,项目更加清晰明了!

5.1.2 基于注解异常声明异常处理
  1. 声明异常处理控制器类

    异常处理控制类,统一定义异常处理handler方法!

    /*** projectName: com.atguigu.execptionhandler* * description: 全局异常处理器,内部可以定义异常处理Handler!*/
    ​
    /*** @RestControllerAdvice = @ControllerAdvice + @ResponseBody* @ControllerAdvice 代表当前类的异常处理controller! */
    @RestControllerAdvice
    public class GlobalExceptionHandler {
    ​}
  2. 声明异常处理hander方法

    异常处理handler方法和普通的handler方法参数接收和响应都一致!

    只不过异常处理handler方法要映射异常,发生对应的异常会调用!

    普通的handler方法要使用@RequestMapping注解映射路径,发生对应的路径调用!

    /*** 异常处理handler * @ExceptionHandler(HttpMessageNotReadableException.class) * 该注解标记异常处理Handler,并且指定发生异常调用该方法!* * * @param e 获取异常对象!* @return 返回handler处理结果!*/
    @ExceptionHandler(HttpMessageNotReadableException.class)
    public Object handlerJsonDateException(HttpMessageNotReadableException e){return null;
    }
    ​
    /*** 当发生空指针异常会触发此方法!* @param e* @return*/
    @ExceptionHandler(NullPointerException.class)
    public Object handlerNullException(NullPointerException e){
    ​return null;
    }
    ​
    /*** 所有异常都会触发此方法!但是如果有具体的异常处理Handler! * 具体异常处理Handler优先级更高!* 例如: 发生NullPointerException异常!*       会触发handlerNullException方法,不会触发handlerException方法!* @param e* @return*/
    @ExceptionHandler(Exception.class)
    public Object handlerException(Exception e){
    ​return null;
    }
  3. 配置文件扫描控制器类配置

    确保异常处理控制类被扫描

     <!-- 扫描controller对应的包,将handler加入到ioc-->@ComponentScan(basePackages = {"com.atguigu.controller","com.atguigu.exceptionhandler"})

5.2 拦截器使用

5.2.1 拦截器概念

拦截器和过滤器解决问题

  • 生活中

    为了提高乘车效率,在乘客进入站台前统一检票

  • 程序中

    在程序中,使用拦截器在请求到达具体 handler 方法前,统一执行检测

拦截器 Springmvc VS 过滤器 javaWeb:

  • 相似点

    • 拦截:必须先把请求拦住,才能执行后续操作

    • 过滤:拦截器或过滤器存在的意义就是对请求进行统一处理

    • 放行:对请求执行了必要操作后,放请求过去,让它访问原本想要访问的资源

  • 不同点

    • 工作平台不同

      • 过滤器工作在 Servlet 容器中

      • 拦截器工作在 SpringMVC 的基础上

    • 拦截的范围

      • 过滤器:能够拦截到的最大范围是整个 Web 应用

      • 拦截器:能够拦截到的最大范围是整个 SpringMVC 负责的请求

    • IOC 容器支持

      • 过滤器:想得到 IOC 容器需要调用专门的工具方法,是间接的

      • 拦截器:它自己就在 IOC 容器中,所以可以直接从 IOC 容器中装配组件,也就是可以直接得到 IOC 容器的支持

选择:

功能需要如果用 SpringMVC 的拦截器能够实现,就不使用过滤器。

5.2.2 拦截器使用
  1. 创建拦截器类

    public class Process01Interceptor implements HandlerInterceptor {
    ​// if( ! preHandler()){return;}// 在处理请求的目标 handler 方法前执行@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {System.out.println("request = " + request + ", response = " + response + ", handler = " + handler);System.out.println("Process01Interceptor.preHandle");// 返回true:放行// 返回false:不放行return true;}// 在目标 handler 方法之后,handler报错不执行!@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {System.out.println("request = " + request + ", response = " + response + ", handler = " + handler + ", modelAndView = " + modelAndView);System.out.println("Process01Interceptor.postHandle");}// 渲染视图之后执行(最后),一定执行!@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {System.out.println("request = " + request + ", response = " + response + ", handler = " + handler + ", ex = " + ex);System.out.println("Process01Interceptor.afterCompletion");}
    }
    
  2. 修改配置类添加拦截器

    @EnableWebMvc  //json数据处理,必须使用此注解,因为他会加入json处理器
    @Configuration
    @ComponentScan(basePackages = {"com.atguigu.controller","com.atguigu.exceptionhandler"}) //TODO: 进行controller扫描
    //WebMvcConfigurer springMvc进行组件配置的规范,配置组件,提供各种方法! 前期可以实现
    public class SpringMvcConfig implements WebMvcConfigurer {
    ​//配置jsp对应的视图解析器@Overridepublic void configureViewResolvers(ViewResolverRegistry registry) {//快速配置jsp模板语言对应的registry.jsp("/WEB-INF/views/",".jsp");}
    ​//开启静态资源处理 <mvc:default-servlet-handler/>@Overridepublic void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {configurer.enable();}
    ​//添加拦截器@Overridepublic void addInterceptors(InterceptorRegistry registry) { //将拦截器添加到Springmvc环境,默认拦截所有Springmvc分发的请求registry.addInterceptor(new Process01Interceptor());}
    }
```
  1. 配置详解

    1. 默认拦截全部

      @Override
      public void addInterceptors(InterceptorRegistry registry) {//将拦截器添加到Springmvc环境,默认拦截所有Springmvc分发的请求registry.addInterceptor(new Process01Interceptor());
      }
      ​
    2. 精准配置

      @Override
      public void addInterceptors(InterceptorRegistry registry) {//将拦截器添加到Springmvc环境,默认拦截所有Springmvc分发的请求registry.addInterceptor(new Process01Interceptor());//精准匹配,设置拦截器处理指定请求 路径可以设置一个或者多个,为项目下路径即可//addPathPatterns("/common/request/one") 添加拦截路径//也支持 /* 和 /** 模糊路径。 * 任意一层字符串 ** 任意层 任意字符串registry.addInterceptor(new Process01Interceptor()).addPathPatterns("/common/request/one","/common/request/tow");
      }
      ​
    3. 排除配置

      //添加拦截器
      @Override
      public void addInterceptors(InterceptorRegistry registry) {//将拦截器添加到Springmvc环境,默认拦截所有Springmvc分发的请求registry.addInterceptor(new Process01Interceptor());//精准匹配,设置拦截器处理指定请求 路径可以设置一个或者多个,为项目下路径即可//addPathPatterns("/common/request/one") 添加拦截路径registry.addInterceptor(new Process01Interceptor()).addPathPatterns("/common/request/one","/common/request/tow");//排除匹配,排除应该在匹配的范围内排除//addPathPatterns("/common/request/one") 添加拦截路径//excludePathPatterns("/common/request/tow"); 排除路径,排除应该在拦截的范围内registry.addInterceptor(new Process01Interceptor()).addPathPatterns("/common/request/one","/common/request/tow").excludePathPatterns("/common/request/tow");
      }
  2. 多个拦截器执行顺序

    1. preHandle() 方法:SpringMVC 会把所有拦截器收集到一起,然后按照配置顺序调用各个 preHandle() 方法。

    2. postHandle() 方法:SpringMVC 会把所有拦截器收集到一起,然后按照配置相反的顺序调用各个 postHandle() 方法。

    3. afterCompletion() 方法:SpringMVC 会把所有拦截器收集到一起,然后按照配置相反的顺序调用各个 afterCompletion() 方法。

5.3 参数校验

在 Web 应用三层架构体系中,表述层负责接收浏览器提交的数据,业务逻辑层负责数据的处理。为了能够让业务逻辑层基于正确的数据进行处理,我们需要在表述层对数据进行检查,将错误的数据隔绝在业务逻辑层之外。

  1. 校验概述

    JSR 303 是 Java 为 Bean 数据合法性校验提供的标准框架,它已经包含在 JavaEE 6.0 标准中。JSR 303 通过在 Bean 属性上标注类似于 @NotNull、@Max 等标准的注解指定校验规则,并通过标准的验证接口对Bean进行验证。

    注解规则
    @Null标注值必须为 null
    @NotNull标注值不可为 null
    @AssertTrue标注值必须为 true
    @AssertFalse标注值必须为 false
    @Min(value)标注值必须大于或等于 value
    @Max(value)标注值必须小于或等于 value
    @DecimalMin(value)标注值必须大于或等于 value
    @DecimalMax(value)标注值必须小于或等于 value
    @Size(max,min)标注值大小必须在 max 和 min 限定的范围内
    @Digits(integer,fratction)标注值值必须是一个数字,且必须在可接受的范围内
    @Past标注值只能用于日期型,且必须是过去的日期
    @Future标注值只能用于日期型,且必须是将来的日期
    @Pattern(value)标注值必须符合指定的正则表达式
    JSR 303 只是一套标准,需要提供其实现才可以使用。Hibernate Validator 是 JSR 303 的一个参考实现,除支持所有标准的校验注解外,它还支持以下的扩展注解:
    注解规则
    ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
    @Email标注值必须是格式正确的 Email 地址
    @Length标注值字符串大小必须在指定的范围内
    @NotEmpty标注值字符串不能是空字符串
    @Range标注值必须在指定的范围内
    Spring 4.0 版本已经拥有自己独立的数据校验框架,同时支持 JSR 303 标准的校验框架。Spring 在进行数据绑定时,可同时调用校验框架完成数据校验工作。在SpringMVC 中,可直接通过注解驱动 @EnableWebMvc 的方式进行数据校验。Spring 的 LocalValidatorFactoryBean 既实现了 Spring 的 Validator 接口,也实现了 JSR 303 的 Validator 接口。只要在Spring容器中定义了一个LocalValidatorFactoryBean,即可将其注入到需要数据校验的 Bean中。Spring本身并没有提供JSR 303的实现,所以必须将JSR 303的实现者的jar包放到类路径下。
    配置 @EnableWebMvc后,SpringMVC 会默认装配好一个 LocalValidatorFactoryBean,通过在处理方法的入参上标注 @Validated 注解即可让 SpringMVC 在完成数据绑定后执行数据校验的工作。
  2. 操作演示

    • 导入依赖

      <!-- 校验注解 -->
      <dependency><groupId>jakarta.platform</groupId><artifactId>jakarta.jakartaee-web-api</artifactId><version>9.1.0</version><scope>provided</scope>
      </dependency><!-- 校验注解实现-->        
      <!-- https://mvnrepository.com/artifact/org.hibernate.validator/hibernate-validator -->
      <dependency><groupId>org.hibernate.validator</groupId><artifactId>hibernate-validator</artifactId><version>8.0.0.Final</version>
      </dependency>
      <!-- https://mvnrepository.com/artifact/org.hibernate.validator/hibernate-validator-annotation-processor -->
      <dependency><groupId>org.hibernate.validator</groupId><artifactId>hibernate-validator-annotation-processor</artifactId><version>8.0.0.Final</version>
      </dependency>
    • 应用校验注解

      import jakarta.validation.constraints.Email;
      import jakarta.validation.constraints.Min;
      import org.hibernate.validator.constraints.Length;
      ​
      /*** projectName: com.atguigu.pojo*/
      public class User {//age   1 <=  age < = 150@Min(10)private int age;
      ​//name 3 <= name.length <= 6@Length(min = 3,max = 10)private String name;
      ​//email 邮箱格式@Emailprivate String email;
      ​public int getAge() {return age;}
      ​public void setAge(int age) {this.age = age;}
      ​public String getName() {return name;}
      ​public void setName(String name) {this.name = name;}
      ​public String getEmail() {return email;}
      ​public void setEmail(String email) {this.email = email;}
      }
      ​
    • handler标记和绑定错误收集

      @RestController
      @RequestMapping("user")
      public class UserController {
      ​/*** @Validated 代表应用校验注解! 必须添加!*/@PostMapping("save")public Object save(@Validated @RequestBody User user,//在实体类参数和 BindingResult 之间不能有任何其他参数, BindingResult可以接受错误信息,避免信息抛出!BindingResult result){//判断是否有信息绑定错误! 有可以自行处理!if (result.hasErrors()){System.out.println("错误");String errorMsg = result.getFieldError().toString();return errorMsg;}//没有,正常处理业务即可System.out.println("正常");return user;}
      }
  3. 易混总结

    @NotNull、@NotEmpty、@NotBlank 都是用于在数据校验中检查字段值是否为空的注解,但是它们的用法和校验规则有所不同。

    1. @NotNull (包装类型不为null)

      @NotNull 注解是 JSR 303 规范中定义的注解,当被标注的字段值为 null 时,会认为校验失败而抛出异常。该注解不能用于字符串类型的校验,若要对字符串进行校验,应该使用 @NotBlank 或 @NotEmpty 注解。

    2. @NotEmpty (集合类型长度大于0)

      @NotEmpty 注解同样是 JSR 303 规范中定义的注解,对于 CharSequence、Collection、Map 或者数组对象类型的属性进行校验,校验时会检查该属性是否为 Null 或者 size()==0,如果是的话就会校验失败。但是对于其他类型的属性,该注解无效。需要注意的是只校验空格前后的字符串,如果该字符串中间只有空格,不会被认为是空字符串,校验不会失败。

    3. @NotBlank (字符串,不为null,切不为" "字符串)

      @NotBlank 注解是 Hibernate Validator 附加的注解,对于字符串类型的属性进行校验,校验时会检查该属性是否为 Null 或 “” 或者只包含空格,如果是的话就会校验失败。需要注意的是,@NotBlank 注解只能用于字符串类型的校验。 总之,这三种注解都是用于校验字段值是否为空的注解,但是其校验规则和用法有所不同。在进行数据校验时,需要根据具体情况选择合适的注解进行校验。

六、SpringMVC总结

核心点掌握目标
springmvc框架主要作用、核心组件、调用流程
简化参数接收路径设计、参数接收、请求头接收、cookie接收
简化数据响应模板页面、转发和重定向、JSON数据、静态资源
restful风格设计主要作用、具体规范、请求方式和请求参数选择
功能扩展全局异常处理、拦截器、参数校验注解

相关文章:

SpringMVC实战(3):拓展

四、RESTFul风格设计和实战 4.1 RESTFul风格概述 4.1.1 RESTFul风格简介 RESTful&#xff08;Representational State Transfer&#xff09;是一种软件架构风格&#xff0c;用于设计网络应用程序和服务之间的通信。它是一种基于标准 HTTP 方法的简单和轻量级的通信协议&…...

Vue应用中使用xlsx库实现Excel文件导出的完整指南

Vue应用中使用xlsx库实现Excel文件导出的完整指南 在现代Web开发中&#xff0c;经常需要将数据导出为Excel文件&#xff0c;以便于用户进行离线分析或记录。Vue.js作为一个轻量级且高效的前端框架&#xff0c;结合xlsx库可以轻松实现这一功能。本文将详细介绍如何在Vue应用中使…...

【数据分析】Power BI的使用教程

目录 1 Power BI架构1.1 Power BI Desktop1.2 Power BI服务1.3 Power BI移动版 2 Power Query2.1 Power Query编辑器2.2 Power Query的优点2.3 获取数据2.4 数据清洗的常用操作2.4.1 提升标题2.4.2 更改数据类型2.4.3 删除错误/空值2.4.4 删除重复项2.4.5 填充2.4.6 合并列2.4.…...

融合ASPICE与敏捷开发:探索汽车软件开发的最佳实践

ASPICE&#xff08;Automotive SPICE&#xff0c;即汽车软件过程改进和能力dEtermination&#xff09;与敏捷开发在软件开发领域各自具有独特的价值和特点&#xff0c;它们之间的关系可以归纳为既相互区别又相互补充。 一、ASPICE的特点 ASPICE是汽车行业对软件开发流程的一个评…...

后台管理系统的通用权限解决方案(三)SpringBoot整合Knife4j生成接口文档

1 Knife4j介绍 knife4j是为Java MVC框架集成Swagger生成Api文档的增强解决方案&#xff0c;前身是swagger-bootstrap-ui&#xff0c;取名knife4j是希望它能像一把匕首一样小巧&#xff0c;轻量&#xff0c;并且功能强悍&#xff01; 其底层是对Springfox的封装&#xff0c;使…...

保研考研机试攻略:python笔记(1)

&#x1f428;&#x1f428;&#x1f428;宝子们好呀 ~ 我来更新欠大家的python笔记了&#xff0c;从这一篇开始我们来学下python&#xff0c;当然&#xff0c;如果只是想应对机试并且应试语言以C和C为主&#xff0c;那么大家对python了解一点就好&#xff0c;重点可以看高分篇…...

在浏览器中运行 Puppeteer:解锁新能力

Puppeteer&#xff0c;这个强大的浏览器自动化工具&#xff0c;通常在Node.js环境中运行。但你有没有想过&#xff0c;在浏览器本身中运行Puppeteer会是什么样子&#xff1f;这不仅能让我们利用Puppeteer的功能完成更多任务&#xff0c;还能避开Node.js特定的限制。 支持的功…...

Kafka消费者故障,出现活锁问题如何解决?

大家好&#xff0c;我是锋哥。今天分享关于【Kafka消费者故障&#xff0c;出现活锁问题如何解决&#xff1f;】面试题&#xff1f;希望对大家有帮助&#xff1b; Kafka消费者故障&#xff0c;出现活锁问题如何解决&#xff1f; 1000道 互联网大厂Java工程师 精选面试题-Java资…...

pytorch 交叉熵损失函数 BCELoss

BCE Loss 交叉熵损失函数计算公式&#xff1a; BCE Loss - 1/n*(y_actual * log(y_pred) (1 - y_actual) * log(1 - y_pred)) t[i]为标签值&#xff1a;0或者1 o[i]是经过sigmoid后的概率值 BCEWithLogitsLoss 这个损失将Sigmoid层和BCELoss合并在一个类中。 BCEWithLog…...

【进阶】面向对象之接口(多学三招)

文章目录 IDK8开始接口中新增的方法1.允许在接口中定义默认方法,需要使用关键字default修饰2.接口中的默认方法的定义格式3.接口中默认方法的注意事项总结 IDK8开始接口中新增的方法 JDK7以前:接口中只能定义抽象方法。JDK8的新特性:接口中可以定义有方法体的方法。(默认、静态…...

linux上trace code的几种方法

我们在看代码时&#xff0c;总是会遇到下面问题&#xff1a; 1.查看某个场景下的代码执行流 2.查看某个函数被执行时的routine 但是&#xff0c;如果直接查看源码&#xff0c;源码可能代码量大&#xff0c;且分支多&#xff0c;不容易理清。就需要让相关程序运行起来查看。 …...

文件操作(1) —— 文件基础知识

目录 1. 为什么使用文件&#xff1f; 2. 文件种类【按功能分】 3. 文件名 4. 数据文件种类【按存储方式细分】 5. 文件的打开和关闭 5.1 流和标准流 5.2 文件指针 5.3 文件的打开和关闭函数 6. 文件缓冲区 1. 为什么使用文件&#xff1f; 如果没有⽂件&#xff0c;我…...

4K双模显示器7款评测报告

4K双模显示器7款评测报告 HKC G27H7Pro 4K双模显示器 ROG华硕 XG27UCG 4K双模显示器 雷神 ZU27F160L 4K双模显示器 泰坦军团 P275MV PLUS 4K双模显示器 外星人&#xff08;Alienware&#xff09;AW2725QF 4K双模显示器 SANC盛色 D73uPro 4K双模显示器 ANTGAMER蚂蚁电竞 …...

2024.10.24华为(留学生)笔试题解

第一题集装箱堆叠 看注释即可 // 看题目,是最长连续序列的变种。底应该选大的,然后往上堆叠选择次大的(越接近底越好?) // 后续想一下,像是动态规划? // 再一想,好像排序后很容易处理#include <bits/stdc++.h> #include <functional> using namespace st…...

基于neo4j的医疗问诊系统

当你身体不适时&#xff0c;想要找到准确的答案却经常遇到模棱两可的答复&#xff0c;糟心吗&#xff1f;现在&#xff0c;基于neo4j的智能医疗问诊系统为你带来全新体验&#xff01;我们设计了一个具备自动化问答功能的医疗系统&#xff0c;帮助用户快速获取专业的健康知识答案…...

java :String 类

在我们之前的讲解中我们已经了解了很多的Java知识&#xff0c;这节我们讲Java中字符如何定义以及关于String如何使用还有常见的string函数。 【本节目标】 1. 认识 String 类 2. 了解 String 类的基本用法 3. 熟练掌握 String 类的常见操作 4. 认识字符串常量池 5. 认识 …...

关于非中文或者url文本不换行的问题

我在一个写一个简单的url展示的时候&#xff0c;发现url一直溢出不换行&#xff0c;查了各种方法不管用&#xff0c;我请教了我大哥&#xff0c;他直接甩给我两个css放进去就好了 word-break:break-all; 按字符截断换行 /* 支持IE和chrome&#xff0c;FF不支持*/ word-w…...

LeetCode 热题 100之矩阵

1.矩阵置0 思路分析&#xff1a;使用标记数组 记录需要置为 0 的行和列&#xff1a;使用两个布尔数组 zeroRows 和 zeroCols 来记录需要置为 0 的行和列两次遍历 第一遍遍历整个矩阵&#xff0c;找到所有为0的元素&#xff0c;并更新zeroRows和zeroCols&#xff1b;第二遍遍历…...

YOlO系列——yolo v3

文章目录 一、算法原理二、网络结构三、正负样本匹配规则四、损失函数五、边框预测六、性能特点七、应用场景 YOLO-v3&#xff08;You Only Look Once version 3&#xff09;是一种先进的目标检测算法&#xff0c;属于YOLO系列算法的第三代版本。以下是对YOLO-v3的详细介绍&…...

基于Datawhale开源量化投资学习指南(11):LightGBM在量化选股中的优化与实战

1. 概述 在前几篇文章中&#xff0c;我们初步探讨了如何通过LightGBM模型进行量化选股&#xff0c;并进行了一些简单的特征工程和模型训练。在这一篇文章中&#xff0c;我们将进一步深入&#xff0c;通过优化超参数和实现交叉验证来提高模型的效果&#xff0c;并最终通过回测分…...

Python4

4. 更多控制流工具 除了刚介绍的 while 语句&#xff0c;Python 还用了一些别的。我们将在本章中遇到它们。 4.1. if 语句 if elif else if x<0: x 0 print(Negative changed to zero) elif x0: print( zero) else: print(More) 4.2. for 语句 Pyth…...

springboot系列--web相关知识探索六

一、前言 web相关知识探索五中研究了请求中所带的参数是如何映射到接口参数中的&#xff0c;也即请求参数如何与接口参数绑定。主要有四种、分别是注解方式、Servlet API方式、复杂参数、以及自定义对象参数。web相关知识探索五中主要研究自定义对象参数数据绑定底层原理。本次…...

FreeSWITCH 简单图形化界面30 - 使用MYODBC时可能遇到的错误

FreeSWITCH 简单图形化界面30 - 使用MYODBC时可能遇到的错误 测试环境1、 MYODBC 3.51.18 or higher2、分析和解决2.1 解决1&#xff0c;降级MySQL ODBC2.2 解决2&#xff0c;修改FreeSWITCH代码 测试环境 http://myfs.f3322.net:8020/ 用户名&#xff1a;admin&#xff0c;密…...

阿里云物联网的通信方式

阿里云物联网通信的两种方式&#xff0c;一个是物模型&#xff08;分为服务&#xff0c;事件&#xff0c;属性&#xff09;&#xff0c;一个是自定义topic&#xff08;要另外设置数据流转&#xff09; 1.使用产品内的功能定义&#xff0c;&#xff08;其实也就是Topic中定义好的…...

自由职业者的一天:作为小游戏开发者的真实工作日记

大家好&#xff0c;我是小蜗牛。 在这个快节奏的数字时代&#xff0c;自由职业者的生活往往充满了挑战与机遇。作为一名微信小游戏开发者&#xff0c;我的日常工作并不像人们想象中的那样充满光鲜亮丽的画面&#xff0c;而是由无数的编码、调试和创意碰撞组成的。今天&#xf…...

【RL Latest Tech】分层强化学习:Option-Critic架构算法

&#x1f4e2;本篇文章是博主强化学习RL领域学习时&#xff0c;用于个人学习、研究或者欣赏使用&#xff0c;并基于博主对相关等领域的一些理解而记录的学习摘录和笔记&#xff0c;若有不当和侵权之处&#xff0c;指出后将会立即改正&#xff0c;还望谅解。文章分类在&#x1f…...

分布式数据库

前言 分布式数据库系统&#xff08;‌DDBS&#xff09;包含分布式数据库管理系统&#xff08;‌DDBMS&#xff09;和分布式数据库&#xff08;DDB&#xff09;。在分布式数据库系统中&#xff0c;一个应用程序可以对数据库进行透明操作&#xff0c;数据库中的数据分别在不同的…...

MySQL(2)【库的操作】

阅读导航 引言一、创建数据库1. 基本语法2. 创建数据库案例&#x1f4cc;创建名为db1的数据库&#x1f4cc;创建一个使用utf8字符集的db2数据库&#x1f4cc;创建一个使用utf8字符集&#xff0c;并带校对规则的db3数据库 二、字符集和校验规则1. 查看系统默认字符集以及校验规则…...

python pip更换(切换)国内镜像源

国内镜像源列表(个人推荐清华大学的源) ​ 清华大学&#xff1a; https://pypi.tuna.tsinghua.edu.cn/simple阿里云&#xff1a; http://mirrors.aliyun.com/pypi/simple豆瓣&#xff1a; http://pypi.douban.com/simple中国科技大学&#xff1a; https://pypi.mirrors.ustc.e…...

阿里云镜像源无法访问?使用 DaoCloud 镜像源加速 Docker 下载(Linux 和 Windows 配置指南)

&#x1f680; 作者主页&#xff1a; 有来技术 &#x1f525; 开源项目&#xff1a; youlai-mall &#x1f343; vue3-element-admin &#x1f343; youlai-boot &#x1f343; vue-uniapp-template &#x1f33a; 仓库主页&#xff1a; GitCode&#x1f4ab; Gitee &#x1f…...