SpringBoot Web开发(SpringMVC)

SpringBoot Web开发(SpringMVC)

MVC 核心组件和调用流程


Spring MVC与许多其他Web框架一样,是围绕前端控制器模式设计的,其中中央 Servlet DispatcherServlet 做整体请求处理调度!
.
除了DispatcherServletSpringMVC还会提供其他特殊的组件协作完成请求处理和响应呈现。

SpringMVC 处理请求流程

在这里插入图片描述

SpringMVC 涉及的组件理解

  • DispatcherServlet : SpringMVC提供,我们需要使用web.xml配置使其生效,它是整个流程处理的核心,所有请求都经过它的处理和分发![ CEO ]

  • HandlerMapping : SpringMVC提供,我们需要进行IoC配置使其加入IoC容器方可生效,它内部缓存handler(controller方法)handler访问路径数据,被DispatcherServlet调用,用于查找路径对应的handler![秘书]

  • HandlerAdapter : SpringMVC提供,我们需要进行IoC配置使其加入IoC容器方可生效,它可以处理请求参数和处理响应数据数据,每次DispatcherServlet都是通过handlerAdapter间接调用handler,他是handlerDispatcherServlet之间的适配器![经理]

  • Handler : handler又称处理器,他是Controller类内部的方法简称,是由我们自己定义,用来接收参数,向后调用业务,最终返回响应结果![打工人]

  • ViewResovler : SpringMVC提供,我们需要进行IoC配置使其加入IoC容器方可生效!视图解析器主要作用简化模版视图页面查找的,但是需要注意,前后端分离项目,后端只返回JSON数据,不返回页面,那就不需要视图解析器!所以,视图解析器,相对其他的组件不是必须的![财务]

简单来讲

在这里插入图片描述

调用 Controller 层的方法,先通过 handlerMapping 里面缓存的 handler访问路径,和 handler(controller方法)识别到需要的请求参数, 通过 HandlerAdapter,提取对应的参数 k 对应的 v。然后才执行 handler,返回数据再经过 handlerAdapter 将 handler 返回的数据封装到 response 中,如果是静态资源返回一个字符。利用视图解析器规定的格式拼接访问静态资源

Web 场景原理

自动配置原理

  • 整合 web 场景依赖
<dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-web</artifactId>
</dependency>
  • 引入了 autoconfigure 功能
  • @EnableAutoConfiguration注解使用@Import(AutoConfigurationImportSelector.class)批量导入组件
  • 加载 META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 文件中配置的所有组件
  • META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports 中按需加载关于 web 自动配置类
org.springframework.boot.autoconfigure.web.client.RestTemplateAutoConfiguration
org.springframework.boot.autoconfigure.web.embedded.EmbeddedWebServerFactoryCustomizerAutoConfiguration
    
====以下是响应式web场景和现在的没关系======
org.springframework.boot.autoconfigure.web.reactive.HttpHandlerAutoConfiguration
org.springframework.boot.autoconfigure.web.reactive.ReactiveMultipartAutoConfiguration
org.springframework.boot.autoconfigure.web.reactive.ReactiveWebServerFactoryAutoConfiguration
org.springframework.boot.autoconfigure.web.reactive.WebFluxAutoConfiguration
org.springframework.boot.autoconfigure.web.reactive.WebSessionIdResolverAutoConfiguration
org.springframework.boot.autoconfigure.web.reactive.error.ErrorWebFluxAutoConfiguration
org.springframework.boot.autoconfigure.web.reactive.function.client.ClientHttpConnectorAutoConfiguration
org.springframework.boot.autoconfigure.web.reactive.function.client.WebClientAutoConfiguration
================以上没关系=================
    
    
org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration
org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration
org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration
org.springframework.boot.autoconfigure.web.servlet.HttpEncodingAutoConfiguration
org.springframework.boot.autoconfigure.web.servlet.MultipartAutoConfiguration
org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration
  • 绑定了配置文件的一堆配置项
    • SpringMVC的所有配置 spring.mvc
    • Web场景通用配置 spring.web
    • 文件上传配置 spring.servlet.multipart
    • 服务器的配置 server: 比如:编码方式

自动配置效果

  • 包含了 ContentNegotiatingViewResolver 和 BeanNameViewResolver 组件,方便视图解析

  • 默认的静态资源处理机制: 静态资源放在 static 文件夹下即可直接访问

  • 自动注册了 Converter,GenericConverter, Formatter组件,适配常见数据类型转换和格式化需求

  • 支持 HttpMessageConverters,可以方便返回 json 等数据类型

  • 注册 MessageCodesResolver,方便国际化及错误消息处理

  • 支持 静态 index.html

  • 自动使用 ConfigurableWebBindingInitializer,实现消息处理、数据绑定、类型转化、数据校验等功能

WebMvcConfigurer 接口!!!

  • External Libraries 中搜索 spring-boot-autoconfigure

  • 然后再进到META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports

  • 找到 WebMvcAutoConfiguration 。它是SpringMvC 自动配置类

  • 再找到 WebMvcAutoConfigurationAdapter

    • 它实现了 WebMvcConfigurer 接口
    • 并且有@EnableConfigurationProperties({WebMvcProperties.class, WebProperties.class})配置前缀
    • 得出这个接口包含了所有 SpringMVC 组件的默认配置

.

通过 WebMvCAutoConfigurationAdapter 搜索以下方法可以找到响应组件的默认配置

在这里插入图片描述

最佳实践

WebMvcConfigurer 接口

当你需要对 Spring MVC 的一些常见功能进行配置和扩展时,会使用 WebMvcConfigurer。比如添加拦截器来实现权限验证、配置视图控制器来简化页面跳转、自定义消息转换器来处理特定格式的数据等。不会影响自动配置

WebMvcConfigurer 接口

当你需要对 Spring MVC 的底层请求处理逻辑进行深度定制时,会使用 WebMvcRegistrations。例如,你想要自定义 RequestMappingHandlerMapping 的请求映射规则,让它根据特定的条件来匹配请求;或者自定义 ExceptionHandlerExceptionResolver 的异常处理逻辑,实现更复杂的异常处理策略。不会影响自动配置

@EnableMVC 注解

当你在配置类上添加 @EnableWebMvc 注解时,它会全面接管 Spring MVC 的配置工作。这意味着 Spring Boot 默认提供的 MVC 自动配置会被完全覆盖,你需要手动配置所有与 Spring MVC 相关的组件,像视图解析器、消息转换器、拦截器、静态资源处理器等。

方式用法效果
全自动直接编写控制器逻辑全部使用自动配置默认效果
手自一体@Configuration + 配置WebMvcConfigurer+ 配置 WebMvcRegistrations不要标注 @EnableWebMvc保留自动配置效果。手动设置部分功能 ,定义MVC底层组件
全手动@Configuration + 配置WebMvcConfigurer标注 @EnableWebMvc禁用自动配置效果 全手动设置

SpringBoot 访问路径设置


路径注解 @RequestMapping

方法级别

直接在方法上加 @RequestMapping。当多个方法处理同一个路径的不同操作时,可以使用方法级别的 @RequestMapping 注解进行更精细的映射。

@Controller                                             
public class UserController {

	//这里路径就是 /index 访问就会执行这个方法
	@RequestMapping("/index")
    public String index() {

        return null;
    }
}
类级别

在类上加 @RequestMapping,方法上还要再设置一次 @RequestMapping 设置了 @RequestMapping 的参数那就自动加上 类级别 的地址当前缀,如果没有参数就直接用 类地址 地址

@Controller
@RequestMapping("/user")
public class UserController {

	@RequestMapping //这里没有参数就直接用类级别的参数也就是 /user
    public String index() {

        return null;
    }

    @RequestMapping(value = "login") //这里有参数就是自动加上类级别的地址当前缀也就是 /user/login
    public String login() {

        return null;
    }

}
特定请求方式限制 —— 枚举方式

注意:违背请求方式,会出现405异常!!!

HTTP 协议定义了八种请求方式,在 SpringMVC 中封装到了下面这个枚举类:

public enum RequestMethod {
 GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS, TRACE
}
@Controller
public class UserController {

    /**
     * 精准设置访问地址 /user/login
     * method = RequestMethod.POST 可以指定单个或者多个请求方式!
     * 注意:违背请求方式会出现405异常!
     */
    @RequestMapping(value = {"/user/login"} , method = RequestMethod.POST)
    @ResponseBody
    public String login(){
        System.out.println("UserController.login");
        return "login success!!";
    }  
}
特定请求方式限制 —— 注解方式

注意:进阶注解只能添加到 handler 方法上,无法添加到类上!

还有 @RequestMapping 的 HTTP 方法特定快捷方式变体:

  • @GetMapping
  • @PostMapping
  • @PutMapping
  • @DeleteMapping
  • @PatchMapping
@RequestMapping(value="/login",method=RequestMethod.GET)
就等于
@GetMapping(value="/login")

精确路径匹配

SpringMVC@RequestMapping/ 可以省略
.
/ 就表示绝对路径 ,Tomcat 中表示 http://localhost:8080/,设置 Tomcat 上下文路径为 / 缺省就可以直接使用 / + 地址 访问对应的 Servlet

   @RequestMapping(value = "/login", method = RequestMethod.POST)     
   public String login() {return null;    
   }

模糊路径匹配

  • @RequestMapping 注解指定 URL 地址时,通过使用通配符,匹配多个类似的地址。
  • * 表示任意单层:/user/* 这种就是一层可以访问,/user/a/b 这就是两层访问不了
  • **表示任意层/user/** ,比如:/user/a/user/a/b 都可以
@Controller
public class ProductController {

    /**
     *  路径设置为 /product/*  
     *    /* 为单层任意字符串  /product/a  /product/aaa 可以访问此handler  
     *    /product/a/a 不可以
     * 
     *  路径设置为 /product/** 
     *   /** 为任意层任意字符串  /product/a  /product/aaa 可以访问此handler  
     *   /product/a/a 也可以访问
     */
    @RequestMapping("/product/*")
    @ResponseBody
    public String show(){
        System.out.println("ProductController.show");
        return "product show!";
    }
}

SpringBoot 接收参数

param 和 json 参数比较

在 HTTP 请求中,我们可以选择不同的参数类型,如 param 类型和 JSON 类型。下面对这两种参数类型进行区别和对比:

  • paramkey = value & key = value
  • json{ key : value, key : value }
    .
  1. 参数编码:
    param 类型的参数会被编码为 ASCII 码。例如,假设 name=john doe,则会被编码为 name=john%20doe。而 JSON 类型的参数会被编码为 UTF-8。
    .
  2. 参数顺序
    param 类型的参数没有顺序限制。但是,JSON 类型的参数是有序的。JSON 采用键值对的形式进行传递,其中键值对是有序排列的。
    .
  3. 数据类型
    param 类型的参数仅支持字符串类型、数值类型和布尔类型等简单数据类型。而 JSON 类型的参数则支持更复杂的数据类型,如数组、对象等。
  4. 嵌套性
    param 类型的参数不支持嵌套。但是,JSON 类型的参数支持嵌套,可以传递更为复杂的数据结构。
    .
  5. 可读性
    param 类型的参数格式比 JSON 类型的参数更加简单、易读。但是,JSON 格式在传递嵌套数据结构时更加清晰易懂。
    .

总的来说,param 类型的参数适用于单一的数据传递,而 JSON 类型的参数则更适用于更复杂的数据结构传递。根据具体的业务需求,需要选择合适的参数类型。

.
在实际开发中,
常见的做法是:在 GET 请求中采用 param 类型的参数,而在 POST 请求中采用 JSON 类型的参数传递。

返回对象就是 json

简单类型接值

客户端请求

在这里插入图片描述
Handler 接收参数

  • 只要形参参数名和类型与传递的参数相同,即可自动接收!
  • 也可以不传递参数
@Controller
@RequestMapping("param")
public class ParamController {

    /**
     * 前端请求: http://localhost:8080/param/value?name=xx&age=18
     *
     * 可以利用形参列表,直接接收前端传递的param参数!
     *    要求: 参数名 = 形参名
     *          类型相同
     * 出现乱码正常,json接收具体解决!!
     * @return 返回前端数据
     */
    @GetMapping(value="/value")
    @ResponseBody
    public String setupForm(String name,int age){
        System.out.println("name = " + name + ", age = " + age);
        return name + age;
    }
}

简单类型接值【形参和请求参数不一致】

  • 指定绑定的请求参数名:@RequestParam(value="指定请求参数名") 【形参名和请求参数名一致可以省略】
  • 要求请求参数必须传递:required = false【前端是否必须传递此此参数,默认是必须,不传报400异常】
  • 为请求参数提供默认值: defaultValue = "1"【当非必须传递的时候, 可以设置默认值】)

浏览器请求

在这里插入图片描述
handler 接收参数

public class HelloController {


	//绑定请求参数为 myname 和 myage。myage 不必须传递。默认值为 0
    @RequestMapping(value = "/springmvc/hello")
    @ResponseBody
    public String hello
    (@RequestParam(value = "myname") String name, 		
    @RequestParam(value = "myage", required =false, defaultValue = "0") int age) {

        System.out.println("name:" + name + ",age:" + age);

        return name + ":" + age;
    }
}

数组类型接收

paramkey数组参数名 一致就会接收

客户端请求

在这里插入图片描述
handler 参数

@Controller
@RequestMapping("user")
public class testcontroller {


    @ResponseBody
    @RequestMapping
    //注意这里的 数组 参数名要和 param 的 key 一致
    public String test1(@RequestParam String[] names) {
        System.out.println(Arrays.toString(names));
        return Arrays.toString(names);
    }
}

集合类型接收

一个名字对应多个值

  • 多选框,提交的数据的时候一个key对应多个值,我们可以使用集合进行接收!集合用 @RequestParam 声明

.
注意:param 的 key 要和集合名字一样

客户端请求

在这里插入图片描述

handler 接收参数

@Controller
@RequestMapping("user")
public class testcontroller {


    @ResponseBody
    @RequestMapping
    //注意这里的 List 参数名要和 param 的 key 一致
    public String test1(@RequestParam List<String> names) {
        System.out.println(names);
        return names.toString();
    }

}

实体类型接收

要通过对象接收参数值,只需创建一个实体类,为每个属性配备 get 和 set 方法,客户端传递的 param key 要和 实体类的属性名一致。接收参数时,在形参列表中声明实体类对象即可。
.
如果是实体类对象的属性还是个对象那就用 address.provice 这样来做 param 的 key
.
注意点:实体类必须有 get 和 set

POJO

@Data
public class User {
    private int age;
    private String name;
    private Address address;
}
@Data
public class Address {
    private String province;
    private String city;
 }

客户端

在这里插入图片描述
handler

@Controller
@RequestMapping("user")
public class testcontroller {


    @ResponseBody
    @RequestMapping
    public String test1(User user) {
        System.out.println(user);
        return user.toString();
    }

}

日期参数接收

param 的 key 要和 日期类型的参数名一样。并且用 @DateTimeFormat 指定格式

客户端

在这里插入图片描述

handler

@Controller
@RequestMapping("user")
public class testcontroller {


    @ResponseBody
    @RequestMapping
    public String test1(@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") LocalDate updateTime) {
        System.out.println(updateTime);
        return updateTime.toString();
    }

}

路径参数接收

{} 声明的路径原理就是 *

动态路径参数:http://localhost:8080/path/key/root 这里的 key/root 假如是动态路径。那它可以当参数传递给 handler

  • 接收动态路径参数必须要用 @PathVariable 声明
  • 如果 handler 形参名和路径的名字一样。那就直接 @PathVariable 就行。不一样就手动指定 @PathVariable 的 name

客户端

在这里插入图片描述

handler

@Controller
//这里 key 和 password 可以传递给下面的 handler
@RequestMapping("path/{key}/{password}")
public class testcontroller {


    @ResponseBody
    @RequestMapping
    public String 
    //第一个参数因为形参名和路径名不一样所以要设置 name 参数
    test1(@PathVariable(name= "key") String mykey, @PathVariable String password) {
        System.out.println(mykey + ":" + password);
        return mykey + ":" + password;
    }

}

路径匹配默认规则 【路径参数】

Ant 风格路径用法

Ant 风格的路径模式语法具有以下规则:

  • *:表示任意数量的字符。**
  • ?:表示任意一个字符。**
  • **:表示任意数量的目录
  • {}:表示一个命名的模式占位符
  • []:表示字符集合,例如 [a-z] 表示小写字母。

举例

  • *.html 匹配任意名称,扩展名为.html的文件。

  • /folder1/*/*.java 匹配在folder1目录下的任意两级目录下的.java文件。

  • /folder2/**/*.jsp 匹配在folder2目录下任意目录深度的.jsp文件。

  • /{type}/{id}.html 匹配任意文件名为{id}.html,在任意命名的{type}目录下的文件。

注意:Ant 风格的路径模式语法中的特殊字符需要转义,如:

  • 要匹配文件路径中的星号,则需要转义为\\*
  • 要匹配文件路径中的问号,则需要转义为\\?
新规则和旧规则改变

AntPathMatcherPathPatternParser

  • PathPatternParser 在 jmh 基准测试下,有 6~8 倍吞吐量提升,降低 30%~40%空间分配率
  • PathPatternParser 兼容 AntPathMatcher语法,并支持更多类型的路径模式
  • PathPatternParser \** 多段匹配的支持仅允许在模式末尾使用
    @GetMapping("/a*/b?/{p1:[a-f]+}")
    public String hello(HttpServletRequest request, 
                        @PathVariable("p1") String path) {

        log.info("路径变量p1: {}", path);
        //获取请求路径
        String uri = request.getRequestURI();
        return uri;
    }
总结
  • 使用默认的路径匹配规则,是由 PathPatternParser 提供的
  • 如果路径中间需要有 \**,替换成 ant 风格路径
# 改变路径匹配策略:
# ant_path_matcher 老版策略;
# path_pattern_parser 新版策略;
spring.mvc.pathmatch.matching-strategy=ant_path_matcher

JSON 参数接收

前端发送 JSON 数据时,Spring MVC 框架可通过 @RequestBody 注解将其转为 Java 对象。此注解表示方法参数值从请求体获取,Springboot 中直接添加该注解就行,无需额外操作,且不用指定 value 属性,它会自动映射到相应的参数上。

  • 第一步:给前端来的 JSON 存储的 POJO 类
@Data
public class Person {

    private String name;
    private int age;
    private String gender;


}
  • 第二步:接收 JSON
@RequestMapping("/json")
@Controller
@ResponseBody
public class JsonController {

    //data -> 请求体 post {name,age,gender}
    //前端 传了一个 json 报 415
    //原因: java原生的api 只支持路径参数和 param 参数 不支持 json
    // json 本身就是前端的格式
    //解决: 1. 导入 json 处理的依赖 2.handlerAdapter 配置 json 转化器
    @PostMapping("data")
    public String data(@RequestBody Person person) {
        System.out.println("person = " + person);
        return person.toString();
    }
}

Cookie 数据接收

在这里插入图片描述

可以使用 @CookieValue 注解将 HTTP Cookie 的值绑定到的方法参数。

@Controller
@ResponseBody
@RequestMapping("/test")
public class testcontroller {

    @RequestMapping("/cooie")
    //形参名和 cookie  key 一样就不用给 @CookieValue 指定 name
    public String addPerson(@CookieValue String cookieName){

        System.out.println("value = " + cookieName);
        return cookieName;
    }

    //创建 Cookie
    @GetMapping("save")
    public String save(HttpServletResponse response) {
        Cookie cookie = new Cookie("cookieName", "root");
        response.addCookie(cookie);
        return "ok";
    }

}

请求头信息接收

在这里插入图片描述

  • 第一种:根据参数接收*

@RequestHeader("请求头的 key")

@GetMapping("/demo")
public void handle(
    @RequestHeader("Accept-Encoding") String encoding, 
    @RequestHeader("Keep-Alive") long keepAlive) { 
 	...
}
  • 第二种:根据形参名自动匹配*
//获取 token
//这种是根据根据参数名自动匹配
@GetMapping("/demo")
public void handle(@RequestHeader String token) { 
 
}

SpringBoot 响应数据


模板引擎

模板引擎就是类似 vue 的东西

SpringBoot 包含以下模板引擎的自动配置

  • FreeMarker
  • Groovy
  • Thymeleaf
  • Mustache
整合 Thymeleaf
  • 导入依赖
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
  • templatehtml 文件

在这里插入图片描述

  • controller
@Controller
public class WelcomeController {

    /**
     * 利用模板引擎跳转到指定页面
     * @return
     */
    @GetMapping("/well")
    public String hello(@RequestParam("name") String name, Model model) {

        //模板的逻辑视图名
        //物理视图 = 前缀 + 逻辑视图名 + 后缀
        //真是地址 = classpath:/templates/welcome.html

        //把需要给页面共享的数据放到 model 中
        model.addAttribute("msg", name);
        return "welcome";
    }

}

返回 JSON 数据

基本使用
  • 创建 pojo
@Data
public class User {

    private String name;

    private int age;

}
  • 响应数据
@RequestMapping("json")
@RestController //@Controller + @ResponseBody
public class JsonController {


    @GetMapping("data2")
    public List<User> data1() {
        User user = new User();
        user.setName("two dogs!");
        user.setAge(3);

        List<User> users = new ArrayList<>();
        users.add(user);
        return users;
    }
}
@ResponseBody 注解
  • 方法上使用 @ResponseBody

在前后端分离项目里,@ResponseBody 注解加在方法上,它会把方法返回的对象序列化成 JSON 或 XML 格式的数据,直接发给客户端。这意味着返回值 不会走视图解析器渲染这一步,而是 直接作为数据响应。

@RequestMapping(value = "/user/detail", method = RequestMethod.POST)
@ResponseBody
public User getUser(@RequestBody User userParam) {
    System.out.println("userParam = " + userParam);
    User user = new User();
    user.setAge(18);
    user.setName("John");
    //返回的对象,会使用jackson的序列化工具,转成json返回给前端!
    return user;
}
  • 在类上使用 @ResponseBody

如果类中每个方法上都标记了 @ResponseBody 注解,那么这些注解就可以提取到类上。

@ResponseBody  //responseBody可以添加到类上,代表默认类中的所有方法都生效!
@Controller
@RequestMapping("param")
public class ParamController {
@RestController 注解

类上的 @RestponseBody 注解可以和 @Controller 注解合并为 @RestController 注解。所以使用了 @RestController 注解就相当于给类中的每个方法都加了 @ResponseBody 注解。

内容协商 [ 返回 JSON 原理 ]

@ResponseBody 默认规则

基于请求头内容协商:(默认开启)

  • 客户端向服务端发送请求,携带 HTTP 标准的 Accept 请求头
  • Accept: application/jsontext/xmltext/yaml
  • 服务端根据客户端请求头期望的数据类型进行动态返回

.
.
基于请求参数内容协商:(需要手动开启)

  • 发送请求 GET /projects/spring-boot?format=json
    • format=... 的形式进行内容协商
  • 匹配到 @GetMapping("/projects/spring-boot")
  • 根据参数协商,优先返回 json 类型数据
  • 如果发送请求 GET /projects/spring-boot?format=xml,优先返回 xml 类型数据
# 开启基于请求参数的内容协商
spring.mvc.contentnegotiation.favor-parameter=true  
#自定义参数名,默认为format
spring.mvc.contentnegotiation.parameter-name=myparam 

在这里插入图片描述

@ResponseBody 原理

@ResponseBodyHttpMessageConverter处理

  • HttpMessageConverter 会先进行内容协商

  • 默认MessageConverter有以下

    • ByteArrayHttpMessageConverter: 支持字节数据读写
    • StringHttpMessageConverter: 支持字符串读写
    • ResourceHttpMessageConverter:支持资源读写
    • ResourceRegionHttpMessageConverter: 支持分区资源写出
    • AllEncompassingFormHttpMessageConverter:支持表单 xml/json 读写
    • MappingJackson2HttpMessageConverter: 支持请求体响应体 Json 读写
  • 系统提供默认的MessageConverter 功能有限,仅用于json或者普通返回数据。额外增加新的内容协商功能,必须增加新的HttpMessageConverter

静态资源

静态资源路径映射
  • 访问 /** 默认就来以下路径寻找静态资源
classpath:/META-INF/resources/

classpath:/resources/

classpath:/static/

classpath:/public/

注意:

  • 欢迎页 index.html 也是默认在以上路径查找。没有就在 templates下找 index.html项目启动默认访问
  • favicon.ico(浏览器窗口栏那个图标) 也是默认在以上路径查找
静态资源缓存规则

cachePeriod 【大概配置】

  • cachePeriod 是指资源被缓存的时间长度。在此时间范围内,资源会被浏览器或代理服务器缓存,不需要再次从服务器获取。
  • 默认值:如果没有设置 cachePeriod,则默认没有缓存周期,每次请求都会向服务器发送请求以获取最新的资源。
  • 单位:cachePeriod 的单位通常是秒(s)。例如,如果设置为 3600,则表示资源会被缓存1小时。
  • 作用:设置 cachePeriod 可以减少服务器的负载和网络延迟,提高资源的加载速度,特别是对于不经常变化的资源(如图片、CSS、JavaScript文件)。

.
cacheControl 【精确配置】

  • cacheControl 是HTTP协议中的一个头部字段,用于指定在HTTP请求 / 响应链中,资源应该如何被缓存。
  • 默认:没有 cacheControl
  • 详细说明:cacheControl 可以设置多个指令,例如:
    • no-cache:告诉浏览器或代理服务器在重新验证资源之前不能使用缓存资源。
    • no-store:告诉浏览器或代理服务器不存储这次请求或响应的任何部分。
    • max-age=<seconds>:指定资源被缓存的最大时间,单位为秒。
    • public:表明响应可以被任何缓存所存储。
    • private:表明响应只能被单个用户缓存,不能被共享缓存所存储。
  • 作用:通过 cacheControl,可以更精细地控制资源的缓存行为,确保用户能够获取到最新的资源,或者提高资源的加载速度。

.
userLastModified

  • 定义useLastModified 是一个标志,表示是否在HTTP响应中包含 Last-Modified 头部。

  • 默认::false

  • 详细定义:userLastModifiedtrue会带上一个Last-Modified(该资源的最后修改时间)。下次请求改资源会获取 If-Modified-Since。这个 If-Modified-Since 的值就是 Last-Modified(该资源的最后修改时间) 。要是资源从 If-Modified-Since开始就没动过。就直接返回 304 。直接使用缓存中的资源

  • 通过使用 Last-Modified 头部,可以减少不必要的数据传输,因为如果资源没有变化,服务器不需要重新发送资源的内容,只需发送一个状态码即可。

有了精确配置就不用大概配置了

自定义静态资源规则
配置方式

翻阅 WebMvConfigurer 接口笔记。一直往下进到 WebMvcAutoConfigurationAdapter。找到 addResourceHandlers 方法。这里就是静态资源规则源码

在这里插入图片描述

  • 静态资源访问路径默认规则
    • 照着 getStaticPathPattern() 一直往下点
    • 可以知道怎么利用 配置文件 自定义这个规则

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

# 重新设置静态资源访问路径
# 从以上图片可以看出前缀是 spring.mvc 后缀是 staticPathPattern
#会覆盖默认规则
spring.mvc.static-path-patteren=/static/**
  • 访问静态资源默认到哪个目录去找的默认规则
    • 照着 getStaticPathPattern() 一直往下点
    • 可以知道怎么利用 配置文件 自定义这个规则

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

# 重新设置态资源默认到哪个目录去找的规则

# 从以上图片看出 前缀是 spring.web 然后后缀是静态类中的 Resources 中的 staticLocations 属性。然后配置规则是 classpath

#会覆盖默认规则
spring.web.resources.static-locations=classpath:/a/, classpath:/b/
  • 缓存相关
##大概设置
#设置缓存时间
spring.web.resources.cache.period=3600

##详细设置,覆盖period配置:
## 浏览器第一次请求服务器,服务器告诉浏览器此资源缓存7200秒,7200秒以内的所有此资源访问不用发给服务器请求,7200秒以后发请求给服务器
spring.web.resources.cache.cachecontrol.max-age=7200
## 共享缓存。只要是这个客户端。就谁都可以用这个缓存数据
spring.web.resources.cache.cachecontrol.cache-public=true

#使用资源 last-modified 时间,来对比服务器和浏览器的资源是否相同没有变化。相同返回 304
spring.web.resources.cache.use-last-modified=true

总结:

  • spring.mvc 前缀
    • 设置静态资源访问路径
  • spring.web前缀
    • 设置访问静态资源要找哪个路径
    • 设置静态资源缓存策略
代码方式

因为 WebMvcConfigurer接口包含了所有 SpringMVC 组件的默认配置。所以我们可以自己写一个配置了实现 WebMvcConfigurer接口。自己制定规则

  • 第一种方式
@Configuration
public class MyConfig implements WebMvcConfigurer  {
    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        //保留以前的配置
        //没有 @EnableWebMvc 禁用boot 默认配置。就算没这句话默认配置也还在
        WebMvcConfigurer.super.addResourceHandlers(registry);

        //自己加一些配置
        registry.addResourceHandler("/static/**")
                .addResourceLocations("classpath:/a/", "classpath:/b/")
                .setCacheControl(CacheControl.maxAge(1180, TimeUnit.SECONDS))
    }
}
  • 第二种方式

为什么容器中放一个WebMvcConfigurer就能配置底层行为

  • **WebMvcAutoConfiguration 是一个自动配置类,它里面有一个EnableWebMvcConfiguration静态类 **

  • EnableWebMvcConfiguration继承于 DelegatingWebMvcConfiguration,这两个都生效

  • DelegatingWebMvcConfiguration利用 DI 把容器中 所有 WebMvcConfigurer 注入进来

  • 别人调用 DelegatingWebMvcConfiguration 的方法配置底层规则,而它调用所有 WebMvcConfigurer的配置底层方法。

@Configuration //这是一个配置类, 容器中放一个 webMMvcConfigurer组件, 就能自定义底层
public class MyConfig{

    @Bean
    public WebMvcConfigurer webMvcConfigurer() {
        return new WebMvcConfigurer() {
            @Override
            public void addResourceHandlers(ResourceHandlerRegistry registry) {
                registry.addResourceHandler("/static/**")
                        .addResourceLocations("classpath:/static/")
                        .setCacheControl(CacheControl.maxAge(1180, TimeUnit.SECONDS));
            }
        };
    }
}

SpringBoot 异常处理


单独写一个异常类。发生异常就会走此类下的 handler 方法

  • ControllerAdvice: 走字符串拼接网址那一套。前后端不分离的
  • RestControllerAdvice:直接返回数据。不拼接。前后端分离

只要发生异常就进入这里寻找对应的异常处理,并且要注意在配置类扫描这个全局异常类。注意要让主程序扫描到这个类

//全局异常发生会走此类下的 handler 方法
//@ControllerAdvice //可以返回逻辑视图 转发和重定向
@RestControllerAdvice 
public class GlobalExceptionHandler {

    //发生异常 -> 进入 @ControllerAdvice 注解的类型 -> 根据@ExceptionHandler(指定的异常) 去处理
    //指定的异常 可以精准查找 或者查找父异常

    @ExceptionHandler(ArithmeticException.class)
    public Object ArithmeticException(ArithmeticException e) {
        String message = e.getMessage();
        System.out.println("message = " + message);
        return message;
    }

    //如果没有 ArithmeticException 就走 Exception
    @ExceptionHandler(Exception.class)
    public Object Exception(Exception e) {
        String message = e.getMessage();
        System.out.println("message = " + message);
        return message;
    }


}

SpringBoot 登录校验


登录校验会话技术方案

概述

在这里插入图片描述

会话跟踪方案对比

在这里插入图片描述

JWT 令牌技术

概念
  • Token

令牌(Token):在计算机领域,令牌是一种代表某种访问权限或身份认证信息的令牌。它可以是一串随机生成的字符或数字,用于验证用户的身份或授权用户对特定资源的访问。普通的令牌可能以各种形式出现,如访问令牌、身份令牌、刷新令牌等。
.
简单理解 : 每个用户生成的唯一字符串标识,可以进行用户识别和校验

在这里插入图片描述

  • JWT
  • jwt工作流程

    • 用户提供其凭据(通常是用户名和密码)进行身份验证。

    • 服务器对这些凭据进行验证,并在验证成功后创建一个JWT

    • 服务器将JWT发送给客户端,并客户端在后续的请求中将JWT附加在请求头或参数中。

    • 服务器接收到请求后,验证JWT的签名和有效性,并根据JWT中的声明进行身份验证和授权操作

使用语法
  • 导入依赖
<dependency>
    <groupId>io.jsonwebtoken</groupId>
    <artifactId>jjwt</artifactId>
    <version>0.9.1</version>
</dependency>

<!-- JDK9以上需要这个依赖-->
<dependency>
    <groupId>javax.xml.bind</groupId>
    <artifactId>jaxb-api</artifactId>
    <version>2.3.0</version>
</dependency>
  • 生成 JWT 令牌*
    • 签名算法可以去 JWT 官网查找,密钥自己设置
public class JWTTest {

    @Test
    public void jwtTest1() {

        //自己的内容
        Map<String, Object> claims = new HashMap<>();
        claims.put("id", 1);
        claims.put("name", "com");

        String jwt =  Jwts.builder()
                .signWith(SignatureAlgorithm.HS256, "mangfu")//设置签名算法 和 密钥
                .setClaims(claims) //自定义内容(载荷)[自己的内容]
                .setExpiration(new Date(System.currentTimeMillis() + 3600 * 1000)) //设置 jwt 有效期为 1 h (当前时间往后推)
                .compact(); //拿到字符串类型返回值

        System.out.println(jwt);
    }

注意事项

在这里插入图片描述

实战:登录成功,下发令牌

登录成功。令牌发到浏览器。后面浏览器每次访问服务端就携带令牌。到服务端进行登录校验

  • JWT 令牌工具类
    • 下发 JWT 令牌
    • 解析 JWT 令牌
public class JwtUtils {
	//设置密钥
	private static String signKey = "mangfu"
	//设置 JWT 令牌生效时间 12 小时
	private static Long expire = 43200000L;
	
	public static String generateJwt(Map<String, Object> claims) {
		String jwt = JWTs.builder()
				.addClaims(claims)
				.signWith(SignatureAlgoritth.HS256, signKey)
				.setExpiration(new Date(System.currentTimeMillis() + expire))
				.compact()
		return jwx;		
	}
    
    public static Claims parseJWT(String jwt) {
		Claims claims = JWTs.parser()
                .setSigningKey(signKey)
                .parseClaimsJws(jwt)
                .getBody();;

		return claims;		
	}
	
}

Filter 过滤器

概念

在这里插入图片描述

基本使用

在这里插入图片描述

  • 注意是 servlet 包下的 filter
  • initdestory 提供了默认实现可以不重写
详细使用细节
  • 使用流程

在这里插入图片描述

  • 拦截器路径设置

在这里插入图片描述

  • 过滤器链

在这里插入图片描述

实战:登录校验,令牌校验

用户登录成功后,会下发 JWT 令牌,然后在后续的每次请求中,都需要在请求头header中携带到服务端, 请求头名称为 token,值为登录时下发的 JWT 令牌

注意

在这里插入图片描述

校验流程

在这里插入图片描述

@WebServlet(urlPatterns = "/*")
public class DemoFilter implements Filter {

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest req = (HttpServletRequest) servletRequest;
        HttpServletResponse resp = (HttpServletResponse) servletResponse;

        //1. 获取请求 url
        String url = req.getRequestURI().toString();
        System.out.println("请求的 url: " + url);

        //2.判断请求 url 中是否包含 login, 如果包含, 说明是登录操作, 放行
        if (url.contains("login")) {
            System.out.println("登录操作, 放行...");
            filterChain.doFilter(req, resp);
            return;
        }

        //3. 获取请求头中的令牌 token
        String jwt = req.getHeader("token");

        //4. 判断令牌是否存在, 如果不存在, 返回错误结果 (未登录)
        /*
             public static boolean hasLength(@Nullable String str) {
                return str != null && !str.isEmpty();
             }
         */
        if (StringUtils.hasLength(jwt)) {
            System.out.println("请求头 token 为空, 返回未登录的信息");

            /*
                Result error = Result.error("NOT_LOGIN");
                //把错误信息转成 JSON 发给前端
                String notLogin = JSONObject.toJSONString(error)
                resp.getWriter().write(notLogin);
             */

            return;
        }

        //5. 如果令牌存在, 解析 token, 如果解析失败, 返回错误结果 (未登录)
        //解析不报错说明成功, 报错说明失败
        try {
            JwtUtils.parseJWT(jwt);
        } catch (Exception e) {
            e.printStackTrace();
            System.out.println("解析令牌失败, 返回未登录错误信息");

             /*
                Result error = Result.error("NOT_LOGIN");
                //把错误信息转成 JSON 发给前端
                String notLogin = JSONObject.toJSONString(error)
                resp.getWriter().write(notLogin);
             */
        }

        //6. 放行
        System.out.println("令牌合法, 放行");
        filterChain.doFilter(req, resp);

    }
}

Interceptor 拦截器

概念

在这里插入图片描述
在这里插入图片描述

拦截器使用

在这里插入图片描述

就是自定义一个拦截器类实现 HandlerInterceptor ,然后配置类实现 WebMvcConfigurer 扫描拦截器类。配置拦截路径

  • 实现 HandlerInterceptor 实现其所有方法
    在这里插入图片描述
  • 配置类实现 WebMvcConfigurer 添加拦截器
@EnableWebMvc  //json数据处理,必须使用此注解,因为他会加入json处理器
@Configuration
@ComponentScan(basePackages = {"com.atguigu.controller","com.atguigu.exceptionhandler"}) 
public class SpringMvcConfig implements WebMvcConfigurer {

  
     //添加拦截器
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        //配置方案1 拦截全部请求
        registry.addInterceptor(new MyInterceptor());

        //配置方案2 指定地址拦截
        // * 任意一层字符串 ** 任意多层字符串
        registry.addInterceptor(new MyInterceptor())
                .addPathPatterns("/user/data");

        //配置方案3 排除拦截 
        // addPathPatterns 需要拦截的路径
        // excludePathPatterns 需要拦截的路径中有哪些不拦截
        registry.addInterceptor(new MyInterceptor())
                .addPathPatterns("/user/**").excludePathPatterns("/user/data1");
    
    }
}




多个拦截器情况
  • 如果有多个拦截器,执行顺序
    • preHandle() 方法:SpringMVC 会把所有拦截器收集到一起,然后按照配置顺序调用各个 preHandle() 方法。
    • postHandle() 方法:SpringMVC 会把所有拦截器收集到一起,然后按照配置相反的顺序调用各个 postHandle() 方法。
    • afterCompletion() 方法:SpringMVC 会把所有拦截器收集到一起,然后按照配置相反的顺序调用各个 afterCompletion() 方法。
实战:登录校验,令牌校验

在这里插入图片描述

@Component
public class DemoInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

        //1. 获取请求 url
        String url = request.getRequestURI().toString();
        System.out.println("请求的 url: " + url);

        //2.判断请求 url 中是否包含 login, 如果包含, 说明是登录操作, 放行
        if (url.contains("login")) {
            System.out.println("登录操作, 放行...");
            return true;
        }

        //3. 获取请求头中的令牌 token
        String jwt = request.getHeader("token");

        //4. 判断令牌是否存在, 如果不存在, 返回错误结果 (未登录)
        /*
             public static boolean hasLength(@Nullable String str) {
                return str != null && !str.isEmpty();
             }
         */
        if (StringUtils.hasLength(jwt)) {
            System.out.println("请求头 token 为空, 返回未登录的信息");

            /*
                Result error = Result.error("NOT_LOGIN");
                //把错误信息转成 JSON 发给前端
                String notLogin = JSONObject.toJSONString(error)
                resp.getWriter().write(notLogin);
             */

            return false;
        }

        //5. 如果令牌存在, 解析 token, 如果解析失败, 返回错误结果 (未登录)
        //解析不报错说明成功, 报错说明失败
        try {
            JwtUtils.parseJWT(jwt);
        } catch (Exception e) {
            e.printStackTrace();
            System.out.println("解析令牌失败, 返回未登录错误信息");

             /*
                Result error = Result.error("NOT_LOGIN");
                //把错误信息转成 JSON 发给前端
                String notLogin = JSONObject.toJSONString(error)
                resp.getWriter().write(notLogin);
             */
        }

        //6. 放行
        System.out.println("令牌合法, 放行");
        return true;

    }
}

拦截器和过滤器的区别

在这里插入图片描述

RESTFul 风格设计规范

HTTP 协议请求方式要求

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

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

URL 风格要求

RESTful 风格的 API 设计里,URL 路径一般用名词命名,用来表示资源。资源可以是用户、订单这类实体,也能是搜索、计算等服务。设计 URL 路径时,强调用名词标识资源,而非用动词描述操作。比如,把/editEmp(动词)改成/Emp(名词) 。
.
例如:

  • GET /users:检索用户列表
  • POST /users:创建新用户
  • PUT /users/123:更新ID为123的用户
  • DELETE /users/123:删除ID为123的用户
操作传统风格REST 风格
保存/CRUD/saveEmp URL 地址:/CRUD/emp 请求方式:POST
删除/CRUD/removeEmp?empId=2 URL 地址:/CRUD/emp/2 请求方式:DELETE
更新/CRUD/updateEmp URL 地址:/CRUD/emp 请求方式:PUT
查询/CRUD/editEmp?empId=2URL 地址:/CRUD/emp/2 请求方式:GET

传递参数设计要求

  • 获取数据 GET ,删除数据 DELTETE

    • 参数是 id 标识。使用路径 方式 /url/id
    • 参数是 是 范围参数。使用 param 方式 /url?page=1&size=10
  • 保存数据 POST,修改数据 PUT

    • 全部使用请求体传递 JSON 方式

其他原则

  • 请求参数应该限制在 10 个以内,过多的请求参数可能导致接口难以维护和使用。
  • 对于敏感信息,最好使用 POST 采用请求体来传递参数。
  • 如果地址冲突 (请求方式 和 路径都一样) :那就在后面加个动词区分一下,比如 GET /user GET /user/search

实战举例

接口设计

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

/**
 * projectName: com.atguigu.pojo
 * 用户实体类
 */
@Data
public class User {

    private Integer id;
    private String name;

    private Integer age;
}

  • controller
/**
 * projectName: com.atguigu.controller
 *
 * description: 用户模块的控制器
 */
@RequestMapping("user")
@RestController
public class UserController {

    /**
     * 模拟分页查询业务接口
     */
    @GetMapping
    public 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'}";
    }


    /**
     * 模拟用户保存业务接口
     */
    @PostMapping
    public 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'}";
    }


    /**
     * 模拟用户更新业务接口
     */
    @PutMapping
    public 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'}";
    }
}

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

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

相关文章

Linux《基础指令》

在之前的Linux《Linux简介与环境的搭建》当中我们已经初步了解了Linux的由来和如何搭建Linux环境&#xff0c;那么接下来在本篇当中我们就要来学习Linux的基础指令。在此我们的学习是包括两个部分&#xff0c;即指令和关于Linux的基础知识&#xff1b;因此本篇指令和基础知识的…

我的求职面经:(1)C++里指针和数组的区别

经典问题&#xff1a; char s1[]"hello"; char *s2"hello"; 1、s1的值是放在栈上的&#xff0c;值是可以修改的&#xff0c;而hello是一个字符串常量放在静态存储区是不能修改的。 2、内存大小不一样 #include<stdio.h>int main(){char s1[]&quo…

react中如何获取dom元素

实现代码 const inputRef useRef(null) inputRef.current.focus()

【LLM】Deepseek本地部署学习

文章目录 1. 访问ollama官网安装平台2. 选择配置3. 下载和运行 1. 访问ollama官网安装平台 https://ollama.com/ 2. 选择配置 参考以下配置要求 3. 下载和运行 ollama run deepseek-r1:7b

deepseek R1 14b显存占用

RTX2080ti 11G显卡&#xff0c;模型7b速度挺快&#xff0c;试试14B也不错。 7B显存使用5.6G&#xff0c;14B显存刚好够&#xff0c;出文字速度差不多。 打算自己写个移动宽带的IPTV播放器&#xff0c;不知道怎么下手&#xff0c;就先问他了。

【漫话机器学习系列】064.梯度下降小口诀(Gradient Descent rule of thume)

梯度下降小口诀 为了帮助记忆梯度下降的核心原理和关键注意事项&#xff0c;可以用以下简单口诀来总结&#xff1a; 1. 基本原理 损失递减&#xff0c;梯度为引&#xff1a;目标是让损失函数减少&#xff0c;依靠梯度指引方向。负梯度&#xff0c;反向最短&#xff1a;沿着负…

让万物「听说」:AI 对话式智能硬件方案和发展洞察

本文整理自声网 SDK 新业务探索组技术负责人&#xff0c;IoT 行业专家 吴方方 1 月 18 日在 RTE 开发者社区「Voice Agent 硬件分享会」上的分享。本次主要介绍了 AI 对话式智能硬件的发展历程&#xff0c;新一波 AI 浪潮所带来的创新机遇、技术挑战以及未来的展望。 在语音交…

SpringBoot 日志

目录 一. 日志概述 二. 日志的使用 1. 打印日志 (1) 获取日志对象 (2) 输出要打印的内容 2. 日志框架简介 (1) 门面模式简介 (2) SLF4J 框架简介 3. 日志的格式 4. 日志的级别 5. 日志配置 (1) 配置日志级别 (2) 日志持久化存储 ① 配置日志文件名 ② 配置日志的…

Python 梯度下降法(一):Gradient Descent

文章目录 Python 梯度下降法&#xff08;一&#xff09;&#xff1a;Gradient Descent一、原理1.1 多元函数1.2 梯度下降法 二、常见的梯度公式2.1 标量对向量的梯度2.2 向量对向量的梯度2.3 向量对标量的梯度2.4 标量对矩阵的梯度 三、常见梯度算法3.1 Batch Gradient Descent…

从AD的原理图自动提取引脚网络的小工具

这里跟大家分享一个我自己写的小软件&#xff0c;实现从AD的原理图里自动找出网络名称和引脚的对应。存成文本方便后续做表格或是使用简单行列编辑生成引脚约束文件&#xff08;如.XDC .UCF .TCL等&#xff09;。 我们在FPGA设计中需要引脚锁定文件&#xff0c;就是指示TOP层…

【2025年最新版】Java JDK安装、环境配置教程 (图文非常详细)

文章目录 【2025年最新版】Java JDK安装、环境配置教程 &#xff08;图文非常详细&#xff09;1. JDK介绍2. 下载 JDK3. 安装 JDK4. 配置环境变量5. 验证安装6. 创建并测试简单的 Java 程序6.1 创建 Java 程序&#xff1a;6.2 编译和运行程序&#xff1a;6.3 在显示或更改文件的…

WGCLOUD服务器资源监控软件使用笔记 - Token is error是什么错误

[wgcloud-agent]2025/01/30 10:41:30 WgcloudAgent.go:90: 主机监控信息上报server开始 [wgcloud-agent]2025/01/30 10:41:30 WgcloudAgent.go:99: 主机监控信息上报server返回信息: {"result":"Token is error"} 这个错误是因为agent配置的wgToken和serv…

MySQL(表空间)

​开始前先打开此图配合食用 MySQL表空间| ProcessOn免费在线作图,在线流程图,在线思维导图 InnoDB 空间文件中的页面管理 后面也会持续更新&#xff0c;学到新东西会在其中补充。 建议按顺序食用&#xff0c;欢迎批评或者交流&#xff01; 缺什么东西欢迎评论&#xff01;我都…

白嫖DeepSeek:一分钟完成本地部署AI

1. 必备软件 LM-Studio 大模型客户端DeepSeek-R1 模型文件 LM-Studio 是一个支持众多流行模型的AI客户端&#xff0c;DeepSeek是最新流行的堪比GPT-o1的开源AI大模型。 2. 下载软件和模型文件 2.1 下载LM-Studio 官方网址&#xff1a;https://lmstudio.ai 打开官网&#x…

知识管理平台在数字经济时代推动企业智慧决策与知识赋能的路径分析

内容概要 在数字经济时代&#xff0c;知识管理平台被视为企业智慧决策与知识赋能的关键工具。其核心作用在于通过高效地整合、存储和分发企业内部的知识资源&#xff0c;促进信息的透明化与便捷化&#xff0c;使得决策者能够在瞬息万变的市场环境中迅速获取所需信息。这不仅提…

关于MySQL InnoDB存储引擎的一些认识

文章目录 一、存储引擎1.MySQL中执行一条SQL语句的过程是怎样的&#xff1f;1.1 MySQL的存储引擎有哪些&#xff1f;1.2 MyIsam和InnoDB有什么区别&#xff1f; 2.MySQL表的结构是什么&#xff1f;2.1 行结构是什么样呢&#xff1f;2.1.1 NULL列表&#xff1f;2.1.2 char和varc…

【开源免费】基于SpringBoot+Vue.JS公交线路查询系统(JAVA毕业设计)

本文项目编号 T 164 &#xff0c;文末自助获取源码 \color{red}{T164&#xff0c;文末自助获取源码} T164&#xff0c;文末自助获取源码 目录 一、系统介绍二、数据库设计三、配套教程3.1 启动教程3.2 讲解视频3.3 二次开发教程 四、功能截图五、文案资料5.1 选题背景5.2 国内…

【Unity3D】实现横版2D游戏角色二段跳、蹬墙跳、扶墙下滑

目录 一、二段跳、蹬墙跳 二、扶墙下滑 一、二段跳、蹬墙跳 GitHub - prime31/CharacterController2D 下载工程后直接打开demo场景&#xff1a;DemoScene&#xff08;Unity 2019.4.0f1项目环境&#xff09; Player物体上的CharacterController2D&#xff0c;Mask添加Wall层…

讯飞智作 AI 配音技术浅析(二):深度学习与神经网络

讯飞智作 AI 配音技术依赖于深度学习与神经网络&#xff0c;特别是 Tacotron、WaveNet 和 Transformer-TTS 模型。这些模型通过复杂的神经网络架构和数学公式&#xff0c;实现了从文本到自然语音的高效转换。 一、Tacotron 模型 Tacotron 是一种端到端的语音合成模型&#xff…

初始化mysql报错cannot open shared object file: No such file or directory

报错展示 我在初始化msyql的时候报错&#xff1a;mysqld: error while loading shared libraries: libaio.so.1: cannot open shared object file: No such file or directory 解读&#xff1a; libaio包的作用是为了支持同步I/O。对于数据库之类的系统特别重要&#xff0c;因此…