Spring Web MVC(详解中)

文章目录

  • Spring MVC(中)
    • RESTFul风格设计
      • RESTFul风格概述
      • RESTFul风格特点
      • RESTFul风格设计规范
      • RESTFul风格好处
      • RESTFul风格实战
        • 需求分析
        • RESTFul风格接口设计
        • 后台接口实现
    • 基于RESTFul风格练习(前后端分离模式)
      • 案例功能和接口分析
        • 功能预览
        • 接口分析
      • 工程项目准备
        • 前端项目搭建
        • 后端项目搭建
      • 后台增删改查实现
        • 项目根路径设计
        • SpringMVC解决跨域问题
        • 业务实现

Spring MVC(中)

RESTFul风格设计

RESTFul风格概述

在这里插入图片描述

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

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

RESTful 架构通常用于构建 Web API,提供数据的传输和操作。它可以用于各种应用场景,包括客户端-服务器应用、单页应用(SPA)、移动应用程序和微服务架构等。

总而言之,RESTful 是一种基于 HTTP 和标准化的设计原则的软件架构风格,用于设计和实现可靠、可扩展和易于集成的 Web 服务和应用程序!
在这里插入图片描述
学习RESTful设计原则可以帮助我们更好去设计HTTP协议的API接口!!

RESTFul风格特点

  • 每一个URI代表1种资源(URI 是名词);
  • 客户端使用GET、POST、PUT、DELETE 4个表示操作方式的动词对服务端资源进行操作:GET用来获取资源,POST用来新建资源(也可以用于更新资源),PUT用来更新资源,DELETE用来删除资源;
  • 资源的表现形式是XML或者JSON
  • 客户端与服务端之间的交互在请求之间是无状态的,从客户端到服务端的每个请求都必须包含理解请求所必需的信息。

RESTFul风格设计规范

HTTP协议请求方式要求

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

操作请求方式
查询操作GET
保存操作POST
删除操作DELETE
更新操作PUT

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协议请求方式

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

RESTFul风格好处

  1. 含蓄,安全

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

  2. 风格统一

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

  3. 无状态

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

  4. 严谨,规范

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

  5. 简洁,优雅

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

  6. 丰富的语义

    通过 URL 地址就可以知道资源之间的关系。它能够把一句话中的很多单词用斜杠连起来,反过来说就是可以在 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

RESTFul风格实战

需求分析
  • 数据结构: User {id 唯一标识,name 用户名,age 用户年龄}
  • 功能分析
    • 用户数据分页展示功能(条件:page 页数 默认1,size 每页数量 默认 10)
    • 保存用户功能
    • 根据用户id查询用户详情功能
    • 根据用户id更新用户数据功能
    • 根据用户id删除用户数据功能
    • 多条件模糊查询用户功能(条件:keyword 模糊关键字,page 页数 默认1,size 每页数量 默认 10)
RESTFul风格接口设计

接口设计

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

问题讨论

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

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

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

  • 对于查询用户详情,使用路径传递参数是因为这是一个单一资源的查询,即查询一条用户记录。使用路径参数可以明确指定所请求的资源,便于服务器定位并返回对应的资源,也符合 RESTful 风格的要求。
  • 而对于多条件模糊查询,使用请求参数传递参数是因为这是一个资源集合的查询,即查询多条用户记录。使用请求参数可以通过组合不同参数来限制查询结果,路径参数的组合和排列可能会很多,不如使用请求参数更加灵活和简洁。
    此外,还有一些通用的原则可以遵循:
  • 路径参数应该用于指定资源的唯一标识或者 ID,而请求参数应该用于指定查询条件或者操作参数。
  • 请求参数应该限制在 10 个以内,过多的请求参数可能导致接口难以维护和使用。
  • 对于敏感信息,最好使用 POST 和请求体来传递参数。
后台接口实现

准备用户实体类:

package com.gj.pojo;/*** projectName: com.gj.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.gj.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'}";}
}

基于RESTFul风格练习(前后端分离模式)

案例功能和接口分析

功能预览

在这里插入图片描述

接口分析

学习计划查询

/* 
需求说明查询全部数据页数据
请求urischedule
请求方式 get   
响应的json{"code":200,"flag":true,"data":[{id:1,title:'学习java',completed:true},{id:2,title:'学习html',completed:true},{id:3,title:'学习css',completed:true},{id:4,title:'学习js',completed:true},{id:5,title:'学习vue',completed:true}]}
*/

学习计划删除

/* 
需求说明根据id删除日程
请求urischedule/{id}
请求方式 delete
响应的json{"code":200,"flag":true,"data":null}
*/

学习计划保存

/* 
需求说明增加日程
请求urischedule
请求方式 post
请求体中的JSON{title: '',completed: false}
响应的json{"code":200,"flag":true,"data":null}
*/

学习计划修改

/* 
需求说明根据id修改数据
请求urischedule
请求方式 put
请求体中的JSON{id: 1,title: '',completed: false}
响应的json{"code":200,"flag":true,"data":null}
*/

工程项目准备

前端项目搭建

安装node和npm

  • node和npm简介

    Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行时环境,可以使 JavaScript 运行在服务器端。

    NPM全称Node Package Manager,是Node.js包管理工具,是全球最大的模块生态系统,里面所有的模块都是开源免费的;也是Node.js的包管理工具!

  • node和npm安装

    安装Nodejs,自动安装npm包管理工具!

    1. 打开官网https://nodejs.org/en下载对应操作系统的 LTS 版本。(资料中已有node安装包)

    2. 双击安装包进行安装,安装过程中遵循默认选项即可(或者参照https://www.runoob.com/nodejs/nodejs-install-setup.html )。安装完成后,可以在命令行终端输入 node -vnpm -v 查看 Node.js 和 npm 的版本号。
      在这里插入图片描述

  • npm镜像和版本升级

    • 配置阿里镜像
    npm config set registry https://registry.npmmirror.com
    
    • 查看镜像配置结果
    npm config get registry
    
    • 升级npm版本
    npm install -g npm@9.6.6
    
  • 使用npm安装项目依赖

    使用cmd黑窗口,进入前端工程项目文件夹下(切记:进入到package.json同层文件夹下,运行一下命令)

    npm i
    
  • 启动前端程序

    npm run dev
    
后端项目搭建

数据库怎么办?使用HashMap模拟,所以不涉及和MyBatis、Spring的整合!

搭建后台项目

pom.xml

<properties><spring.version>6.0.6</spring.version><maven.compiler.source>17</maven.compiler.source><maven.compiler.target>17</maven.compiler.target><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties><dependencies><!-- springioc相关依赖  --><dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>${spring.version}</version></dependency><!-- web相关依赖  --><!-- 在 pom.xml 中引入 Jakarta EE Web API 的依赖 --><!--在 Spring Web MVC 6 中,Servlet API 迁移到了 Jakarta EE API,因此在配置 DispatcherServlet 时需要使用Jakarta EE 提供的相应类库和命名空间。错误信息 “‘org.springframework.web.servlet.DispatcherServlet’is not assignable to ‘javax.servlet.Servlet,jakarta.servlet.Servlet’” 表明你使用了旧版本的Servlet API,没有更新到 Jakarta EE 规范。--><dependency><groupId>jakarta.platform</groupId><artifactId>jakarta.jakartaee-web-api</artifactId><version>9.1.0</version><scope>provided</scope></dependency><dependency><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-databind</artifactId><version>2.15.0</version></dependency><!-- springwebmvc相关依赖  --><dependency><groupId>org.springframework</groupId><artifactId>spring-webmvc</artifactId><version>${spring.version}</version></dependency></dependencies>

准备实体类

/*** projectName: com.gj.pojo** description: 任务实体类*/
public class Schedule {private Integer id;private String title;private Boolean completed;public Schedule() {}public Schedule(Integer id, String title, Boolean completed) {this.id = id;this.title = title;this.completed = completed;}public Integer getId() {return id;}public void setId(Integer id) {this.id = id;}public String getTitle() {return title;}public void setTitle(String title) {this.title = title;}public Boolean getCompleted() {return completed;}public void setCompleted(Boolean completed) {this.completed = completed;}@Overridepublic String toString() {return "Schedule{" +"id=" + id +", title='" + title + '\'' +", completed=" + completed +'}';}
}

准备R结果包装类

/*** projectName: com.gj.utils** description: 返回结果类*/
public class R {private int code = 200; //200成功状态码private boolean flag = true; //返回状态private Object data;  //返回具体数据public  static R ok(Object data){R r = new R();r.data = data;return r;}public static R  fail(Object data){R r = new R();r.code = 500; //错误码r.flag = false; //错误状态r.data = data;return r;}public int getCode() {return code;}public void setCode(int code) {this.code = code;}public boolean isFlag() {return flag;}public void setFlag(boolean flag) {this.flag = flag;}public Object getData() {return data;}public void setData(Object data) {this.data = data;}
}

准备业务类

业务接口

/*** projectName: com.gj.service** description: schedule业务接口*/public interface ScheduleService {/*** 返回全部学习计划* @return*/List<Schedule> getAll();/*** 保存学习计划* @param schedule*/void saveSchedule(Schedule schedule);/*** 更新学习计划* @param schedule*/void updateSchedule(Schedule schedule);/*** 移除学习计划* @param*/void removeById(Integer id);}

业务实现

/*** projectName: com.gj.service.impl** description:*/
@Service
public class ScheduleServiceImpl  implements ScheduleService {//准备假数据private static Map<Integer,Schedule> scheduleMap;private static  int maxId = 5;static {scheduleMap = new HashMap<>();Schedule schedule = null;schedule = new Schedule(1, "学习Java", false);scheduleMap.put(1, schedule);schedule = new Schedule(2, "学习H5", true);scheduleMap.put(2, schedule);schedule = new Schedule(3, "学习Css", false);scheduleMap.put(3, schedule);schedule = new Schedule(4, "学习JavaScript", false);scheduleMap.put(4, schedule);schedule = new Schedule(5, "学习Spring", true);scheduleMap.put(5, schedule);}    /*** 返回全部学习计划** @return*/@Overridepublic List<Schedule> getAll() {return new ArrayList<>(scheduleMap.values());}/*** 保存学习计划** @param schedule*/@Overridepublic void saveSchedule(Schedule schedule) {maxId++;schedule.setId(maxId);scheduleMap.put(maxId,schedule);}/*** 更新学习计划** @param schedule*/@Overridepublic void updateSchedule(Schedule schedule) {scheduleMap.put(schedule.getId(),schedule);}/*** 移除学习计划** @param id*/@Overridepublic void removeById(Integer id) {scheduleMap.remove(id);}}

准备spring-mvc.配置文件

位置:resources/spring-mvc.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:context="http://www.springframework.org/schema/context"xmlns:mvc="http://www.springframework.org/schema/mvc"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd"><!-- 扫描controller对应的包,将handler加入到ioc--><context:component-scan base-package="com.gj.controller,com.gj.service" /><!--注意: 导入mvc命名空间!mvc:annotation-driven 是一个整合标签他会导入handlerMapping和handlerAdapter他会导入json数据格式转化器等等!--><mvc:annotation-driven /><!-- viewResolver 不需要配置,因为我们不需要查找逻辑视图!!! --><!-- 加入这个配置,SpringMVC 就会在遇到没有 @RequestMapping 的请求时放它过去 --><!-- 所谓放它过去就是让这个请求去找它原本要访问的资源 --><mvc:default-servlet-handler/>
</beans>

准备 web.xml配置文件

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"version="4.0"><!-- 配置SpringMVC中负责处理请求的核心Servlet,也被称为SpringMVC的前端控制器 --><servlet><servlet-name>DispatcherServlet</servlet-name><!-- DispatcherServlet的全类名 --><servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class><!-- 通过初始化参数指定SpringMVC配置文件位置 --><init-param><!-- 如果不记得contextConfigLocation配置项的名称,可以到DispatcherServlet的父类FrameworkServlet中查找 --><param-name>contextConfigLocation</param-name><!-- 使用classpath:说明这个路径从类路径的根目录开始才查找 --><param-value>classpath:spring-mvc.xml</param-value></init-param><!-- 作为框架的核心组件,在启动过程中有大量的初始化操作要做,这些操作放在第一次请求时才执行非常不恰当 --><!-- 我们应该将DispatcherServlet设置为随Web应用一起启动 --><load-on-startup>1</load-on-startup></servlet><servlet-mapping><servlet-name>DispatcherServlet</servlet-name><!-- 对DispatcherServlet来说,url-pattern有两种方式配置 --><!-- 方式一:配置“/”,表示匹配整个Web应用范围内所有请求。这里有一个硬性规定:不能写成“/*”。只有这一个地方有这个特殊要求,以后我们再配置Filter还是可以正常写“/*”。 --><!-- 方式二:配置“*.扩展名”,表示匹配整个Web应用范围内部分请求 --><url-pattern>/</url-pattern></servlet-mapping></web-app>

后台增删改查实现

项目根路径设计

因为前端项目设置了后台访问的项目根路径为 /rest

我们后台项目也对应的设置:
在这里插入图片描述

SpringMVC解决跨域问题

假设我们有一个网站 http://example.com,现在需要跨域请求另外一个网站 http://api.example.com 中的数据。浏览器就会因为安全问题,拒绝客户端访问请求!

跨域问题是指在浏览器中发起跨域请求被浏览器拦截的问题。在同一个源域(同一协议、主机、端口),浏览器允许 JavaScript 发起跨域请求;在不同的源域下,浏览器对发起的异域请求会做出不同的限制。

常见的跨域问题的场景有:

  • 访问不同的子域名;
  • 访问不同的端口号;
  • 访问不同的协议(http、https);
  • 访问不同的域名;

基于CORS方式,解决跨域思路:

CORS(Cross-Origin Resource Sharing)是 W3C 制定的一种跨域解决方案,它给出了跨域请求和响应的标准。服务器端代码需要在响应头中设置 Access-Control-Allow-Origin,并指定访问来源域名名或 * 通配符,表示允许的跨域请求。浏览器可以根据响应头信息,判断是否允许该请求。

SpringMVC基于CORS思路解决跨域方案:

  • @CrossOrigin注解

    @CrossOrigin 注释在带注释的【控制器方法】 / 【控制器类】上启用跨源请求

    @RestController
    @RequestMapping("/account")
    public class AccountController {@CrossOrigin@GetMapping("/{id}")public Account retrieve(@PathVariable Long id) {// ...}@DeleteMapping("/{id}")public void remove(@PathVariable Long id) {// ...}
    }
    

    默认情况下, @CrossOrigin 允许:

    • All origins.
    • All headers.
    • All HTTP methods to which the controller method is mapped.
      注解核心设置属性讲解:
    @Target({ElementType.TYPE, ElementType.METHOD})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface CrossOrigin {/*** 设置哪些客户端地址可以跨域访问! 格式为: 协议://主机地址:端口号* @return*/@AliasFor("origins")String[] value() default {};@AliasFor("value")String[] origins() default {};/*** 设置哪些客户端的[自定义请求头]可以跨域访问!*/String[] allowedHeaders() default {};/*** 设置哪些服务端的自定义响应头,可以被客户端读取!*/String[] exposedHeaders() default {};/***设置哪些请求方法,可以跨域方式! */RequestMethod[] methods() default {};/*** 值为 true 或者 false* 客户端是否可以携带cookie!*/String allowCredentials() default "";
    }
    
  • xml全局跨域配置

    <mvc:cors><mvc:mapping path="/**"allowed-origins="*"allowed-methods="GET, PUT"allowed-headers="header1, header2, header3"exposed-headers="header1, header2" allow-credentials="true"/><mvc:mapping path="/**"allowed-origins="https://domain1.com" /></mvc:cors>
    
业务实现

查询业务

/*** projectName: com.gj.controller** description: 学习计划controller*/
@CrossOrigin
/*@CrossOrigin 注释在带注释的控制器方法上启用跨源请求默认情况下, @CrossOrigin 允许:All origins  任何请求主机地址All headers  任何请求头All HTTP methods to which the controller method is mapped.  任何请求方式!可以设置:@CrossOrigin(origins = "https://domain2.com") 指定允许跨域请求的主机地址*/
@RequestMapping("schedule")
@RestController
public class ScheduleController
{@Autowiredprivate ScheduleService scheduleService;@GetMappingpublic R showList(){List<Schedule> list = scheduleService.getAll();return  R.ok(list);}
}

修改业务

@PutMapping
public R changeSchedule(@RequestBody Schedule schedule){scheduleService.updateSchedule(schedule);return R.ok(null);
}

删除业务

@DeleteMapping("/{id}")
public R removeSchedule(@PathVariable Integer id){scheduleService.removeById(id);return R.ok(null);
}

保存业务

@PostMapping
public R saveSchedule(@RequestBody Schedule schedule){scheduleService.saveSchedule(schedule);return R.ok(null);
}

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

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

相关文章

输入json 达到预览效果

下载 npm i vue-json-pretty2.4.0 <template><div class"newBranchesDialog"><t-base-dialogv-if"addDialogShow"title"Json数据配置"closeDialog"closeDialog":dialogVisible"addDialogShow":center"…

STL算法之基本算法<stl_algobase.h>

STL标准规格中没哟区分基本算法或复杂算法&#xff0c;然后SGI却把常用的一些算法定义于<stl_algobase.h>之中&#xff0c;其他算法定义于<stl_algo.h>之中。以下一一列举这些基本算法。 目录 运用实例 equal,fill,fill_n,iter_swap, lexicographical_compare,m…

dns 服务器简单介绍

dns 服务器分类&#xff1a; 根域名服务器顶级域名服务器权威域名服务器本地域名服务器 dns 的查询过程 国内优秀公共域名 腾讯&#xff1a;DNSPod-免费智能DNS解析服务商-电信_网通_教育网,智能DNS-烟台帝思普网络科技有限公司 119.29.29.29 和 182.254.118.118 阿里&#xf…

AI智算-正式上架GPU资源监控概览 Grafana Dashboard

下载链接 https://grafana.com/grafana/dashboards/22424-ai-gpu-20241127/

CAN详解

CAN简介 • CAN 总线&#xff08; Controller Area Network Bus &#xff09;控制器局域网总线 • CAN 总线是由 BOSCH 公司开发的一种简洁易用、传输速度快、易扩展、可靠性高的串行通信总线&#xff0c;广泛应用于汽车、嵌入式、工业控制等领域 • CAN 总线特征&#xff1a; …

透视投影(Perspective projection)与等距圆柱投影(Equirectangular projection)

一、透视投影 1.方法概述 Perspective projection&#xff08;透视投影&#xff09;是一种模拟人眼观察三维空间物体时的视觉效果的投影方法。它通过模拟观察者从一个特定视点观察三维场景的方式来创建二维图像。在透视投影中&#xff0c;远处的物体看起来比近处的物体小&…

(四)Spring Boot学习——整合修改使用druid连接池

我的是使用springboot3的&#xff0c;对应的有整合的druid-spring-boot-3-starter的jar实现对springboot3的兼容。 <!--******************数据库相关配置************************--> <!-- 1.配置数据库相关的jar包,连接池使用druids上&#xff0c;并引入整合spring…

think php处理 异步 url 请求 记录

1、需求 某网站 需要 AI生成音乐&#xff0c;生成mp3文件的时候需要等待&#xff0c;需要程序中实时监听mp3文件是否生成 2、用的开发框架 为php 3、文件结构 配置路由设置 Route::group(/music, function () {Route::post(/musicLyrics, AiMusic/musicLyrics);//Ai生成歌词流式…

Linux八股积累与笔记

1、iptables 是一个用于配置Linux内核防火墙规则的工具。四表五链&#xff1a;在iptables中&#xff0c;有四个表&#xff08;tables&#xff09;和五个链&#xff08;chains&#xff09;&#xff0c;用于管理不同类型的数据包过滤规则。如下&#xff1a; 表&#xff08;Tabl…

乐鑫发布 esp-iot-solution v2.0 版本

今天&#xff0c;乐鑫很高兴地宣布&#xff0c;esp-iot-solution v2.0 版本已经发布&#xff0c;release/v2.0 分支下的正式版本组件将为用户提供为期两年的 Bugfix 维护&#xff08;直到 2027.01.25 ESP-IDF v5.3 EOL&#xff09;。该版本将物联网开发中常用的功能进行了分类整…

【爬虫框架:feapder,管理系统 feaplat】

github&#xff1a;https://github.com/Boris-code/feapder 爬虫管理系统 feaplat&#xff1a;http://feapder.com/#/feapder_platform/feaplat 爬虫在线工具库 &#xff1a;http://www.spidertools.cn &#xff1a;https://www.kgtools.cn/1、feapder 简介 对于学习 Python…

uni-app 蓝牙开发

一. 前言 Uni-App 是一个使用 Vue.js 开发&#xff08;所有&#xff09;前端应用的框架&#xff0c;能够编译到 iOS、Android、快应用以及各种小程序等多个平台。因此&#xff0c;如果你需要快速开发一款跨平台的应用&#xff0c;比如在 H5、小程序、iOS、Android 等多个平台上…

C语言——海龟作图(对之前所有内容复习)

一.问题描述 海龟作图 设想有一只机械海龟&#xff0c;他在C程序控制下在屋里四处爬行。海龟拿了一只笔&#xff0c;这支笔或者朝上&#xff0c;或者朝下。当笔朝下时&#xff0c;海龟用笔画下自己的移动轨迹&#xff1b;当笔朝上时&#xff0c;海龟在移动过程中什么也不画。 …

【Maven】继承和聚合

5. Maven的继承和聚合 5.1 什么是继承 Maven 的依赖传递机制可以一定程度上简化 POM 的配置&#xff0c;但这仅限于存在依赖关系的项目或模块中。当一个项目的多个模块都依赖于相同 jar 包的相同版本&#xff0c;且这些模块之间不存在依赖关系&#xff0c;这就导致同一个依赖…

Android 性能优化:内存优化(理论篇)

内存作为App程序运行最重要的资源之一&#xff0c;需要运行过程中做到合理的资源分配与回收&#xff0c;不合理的内存占用轻则使得用户应用程序运行卡顿、ANR、黑屏&#xff0c;重则导致用户应用程序发生 OOM&#xff08;out of memory&#xff09;崩溃。喜马直播随着近些年的业…

技能之发布自己的依赖到npm上

目录 开始 解决 步骤一&#xff1a; 步骤二&#xff1a; 步骤三&#xff1a; 运用 一直以为自己的项目在github上有了&#xff08;之传了github&#xff09;就可以进行npm install下载&#xff0c;有没有和我一样萌萌的同学。没事&#xff0c;萌萌乎乎的不犯罪。 偶然的机…

【选择排序和交换排序】直接选择排序、堆排序、冒泡排序、快速排序

【选择排序和交换排序】直接选择排序、堆排序、冒泡排序、快速排序 1. 选择排序1.1 直接选择排序1.1.1详细过程1.1.2 代码实现1.1.3 复杂度和稳定性 1.2 堆排序 2. 交换排序2.1 冒泡排序2.1.1 代码实现2.1.2 复杂度和稳定性 2.2 快速排序——挖坑法2.2.1详细过程2.2.2 代码实现…

DI依赖注入详解

DI依赖注入 声明了一个成员变量&#xff08;对象&#xff09;之后&#xff0c;在该对象上面加上注解AutoWired注解&#xff0c;那么在程序运行时&#xff0c;该对象自动在IOC容器中寻找对应的bean对象&#xff0c;并且将其赋值给成员变量&#xff0c;完成依赖注入。 AutoWire…

51c大模型~合集79

我自己的原文哦~ https://blog.51cto.com/whaosoft/12661268 #还是回谷歌好 创业一年半&#xff0c;胖了30斤&#xff0c;AI大佬感叹 回到大厂&#xff0c;和老领导重聚。 「由于工作强度和不健康的生活方式&#xff0c;我已胖了 15 公斤。」 本周一&#xff0c;知名 AI 学…

工业AI质检 AI质检智能系统 尤劲恩(上海)信息科技有限公司

来的现代化工厂&#xff0c;将逐步被无人化车间取代&#xff0c;无人工厂除了产线自动化&#xff0c;其无人质检将是绕不开的话题。尤劲恩致力于帮助工业制造领域上下游工厂减员增效、提高品质效率&#xff0c;真正实现无人质检IQC/IPQC/OQC的在线质检系统。分析生产环节真实品…