Day51

JSR-303

简介

JSR全称为 Java Specification Requests,表示 Java 规范提案。JSR-303是 Java 为 Java Bean 数据合法性校验提供的标准框架,它定义了一套可标注在成员变量,属性方法上的校验注解。Hibernate Validatior提供了这套标准的实现。

<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>6.0.1.Final</version>
<!-- 最新7.0.1.Final -->
</dependency>

校验注解

注解解释注解解释
@Null必须为null@NotNull不能为null
@AssertTrue必须为true@AssertFalse必须为false
@Min必须为数字,其值大于或等于指定的最小值@Max必须为数字,其值小于或等于指定的最大值
@DecimalMin必须为数字,其值大于或等于指定的最小值@DecimalMax必须为数字,其值小于或等于指定的最大值
@Size集合的长度@Digits必须为数字,其值必须再可接受的范围内
@Past必须是过去的日期@Future必须是将来的日期
@Pattern必须符合正则表达式@Email必须是邮箱格式
@Length(min=,max=)字符串的大小必须在指定的范围内@NotEmpty不能为null,长度大于0
@Range(min=,max=,message=)元素必须在合适的范围内@NotBlank不能为null,字符串长度大于0(限字符串)

mvc配置

pom.xml:

依赖:

spring-context-support, spring-mvc, fastjson

 <dependencies><dependency><groupId>org.springframework</groupId><artifactId>spring-context-support</artifactId><version>5.3.10</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-webmvc</artifactId><version>5.3.10</version></dependency><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>2.0.42</version></dependency><dependency><groupId>org.hibernate</groupId><artifactId>hibernate-validator</artifactId><version>6.0.1.Final</version></dependency><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>3.8.1</version><scope>test</scope></dependency></dependencies>

web.xml:

配置servlet:DispatcherServlet,其中配置参数:contextConfigLocation,value为自己的xml文件,除了参数外还要配置项目启动即创建:load-on-startup,为1。

配置filter:CharacterEncodingFilter,配置参数:encoding,:value为UTF-8,forceEncoding:value为true。

<servlet><servlet-name>dispatcherServlet</servlet-name><servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class><init-param><param-name>contextConfigLocation</param-name><param-value>classpath:spring-mvc.xml</param-value></init-param><load-on-startup>1</load-on-startup></servlet><servlet-mapping><servlet-name>dispatcherServlet</servlet-name><url-pattern>/</url-pattern></servlet-mapping><filter><filter-name>encodingFilter</filter-name><filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class><init-param><param-name>encoding</param-name><param-value>UTF-8</param-value></init-param><init-param><param-name>forceEncoding</param-name><param-value>true</param-value></init-param></filter><filter-mapping><filter-name>encodingFilter</filter-name><url-pattern>/*</url-pattern></filter-mapping>

spring-mvc.xml:

配置视图解析器:InternalResourceViewResolver。

配置处理字符串的消息转换器:StringHttpMessageConverter。

配置fastjson转换器:FastJsonHttpMessageConverter。

配置启动注解支持。

配置扫描包。

<?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:p="http://www.springframework.org/schema/p"xmlns:mvc="http://www.springframework.org/schema/mvc"xmlns:context="http://www.springframework.org/schema/context"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd"><!--视图解析器--><bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" p:prefix="/" p:suffix=".jsp"/><!--处理字符串的消息转换器--><bean id="httpMessageConverter" class="org.springframework.http.converter.StringHttpMessageConverter"/><bean id="fastJsonHttpMessageConverter" class="com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter"><property name="supportedMediaTypes"><list><value>text/html;charset=UTF-8</value><value>application/json;charset=UTF-8</value></list></property></bean><mvc:annotation-driven><mvc:message-converters><ref bean="httpMessageConverter"/><ref bean="fastJsonHttpMessageConverter"/></mvc:message-converters></mvc:annotation-driven><context:component-scan base-package="com.qf.controller"/></beans>

使用:

package com.qf.controller;
import org.hibernate.validator.constraints.Length;
import org.hibernate.validator.constraints.Range;
import javax.validation.constraints.NotNull;public class UserController {@NotNull(message = "账号不能为空")@Length(min = 8, max = 15, message = "账号长度必须为8~15位")private String username;@NotNull(message = "密码不能为空")@Length(min = 8, max = 20, message = "密码长度必须为8~20位")private String password;@Range(min = 0, max = 120, message = "年龄只能在0~120岁之间")private int age;public String getUsername() {return username;}public void setUsername(String username) {this.username = username;}public String getPassword() {return password;}public void setPassword(String password) {this.password = password;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}
}import org.springframework.stereotype.Controller;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;import javax.validation.Valid;@Controller
@RequestMapping("/user")
public class UserController {@RequestMapping("/add")@ResponseBodypublic Object saveUser(@Valid User user, BindingResult result){if(result.hasErrors()) return result.getAllErrors();return 1;}
}


Spring MVC中DispatcherServlet与核心组件底层

DispatcherServlet找到配置类,通过配置类信息拿到包信息,扫描包(扫描包的目的是找到配置类和controller类,并创建对象保存起来)。

扫描包的思路为:利用当前线程获取上下文的类加载器,将包名变成文件的URL地址,再从URL地址中获取文件的路径。对于路径中的每一个文件进行查找,找到.class文件后创建对象并将对象保存起来。

创建对象和保存的思路为:查看类是否是配置类,即检查类上是否有configuration注解,如果有则创建配置类的实例,获取配置类中定义的公开方法,对于每个方法获取方法上的bean注解,获取bean的名字,然后通过method.invoke(配置类实例)无参方法执行得到bean对象,并将bean对象存储在IOC容器中。

查看类是否是处理器类,即检查类上是否有controller注解,如果有则获取注解的值,即bean名字,如果为空那么就使用类名作为bean的名字,创建bean对象存储到IOC容器中。

IOC容器:拥有一个静态的hashmap属性,映射的键值分别为bean对象的名字和bean对象。

扫描完包后,从IOC容器中拿到所有的bean对象,对bean对象进行判断,如果bean对象属于处理器映射器类,则用自己的处理器映射器容器属性添加此bean对象;如果是处理器适配器类则用自己的处理器适配器容器属性添加此bean对象;如果是视图解析器类,则将自己的视图解析器属性赋值为此bean对象。

处理器映射器类都实现HandlerMapping接口,此接口只有一个getHandler方法用于获得一个处理器链,而处理器链有一个处理器属性。相当于就是返回一个处理器。

HandlerMapping接口下有若干处理器映射器实现类,如(这里举例说明两个处理器映射器类)

BeanNameUrlHandlerMapping,用bean的名字与url进行匹配,其重写的getHandler方法的思路就是从请求地址中拼接出url,然后将此url作为bean对象名去Ioc容器中获取bean对象并返回。

DefautAnnotationHandlerMapping,根据注解来获取对应的bean对象,思路为先获取所有的Ioc容器中的bean对象,利用stream流筛选含有Controller注解的控制器类,先通过RequestMapping注解获得父级url,即类上的url,然后获取控制器类中的所有公开方法,判断方法属于什么请求方式,即判断方法的注解为GetMapping、PostMapping、PutMapping、DeleteMapping中的哪一个,并获取对应注解内容中的子级url,拼接成url。然后将请求中的url和注解拼接的url进行匹配,注解中的url可能有{},因此匹配逻辑如果有花括号则不管,其余部分都必须一样。如果匹配成功并且请求方式和方法的请求方式一致则返回方法级别的处理器对象,即返回控制器对象、方法、url。

方法级别的处理器对象构建:一个表示方法级别的处理器类,只针对使用Controller或者RestController注解标识的控制器,有三个属性,分别为控制器对象、方法对象、返回值对象。

处理器适配器类都实现了HandlerAdapter接口,这个接口拥有两个方法,一个是表示当前处理器适配的处理器类型,返回布尔值,另外一个方法是返回ModelAndView类型的handle方法。ModelAndView类拥有视图名和map两个属性。只有当第一个方法满足之后才会执行handle方法。

HandlerAdapter接口下有若干处理器适配器类,如(列举两个处理器适配器类):

ControllerHandlerAdapter,这个适配器只支持实现Controller接口定义的控制器,Controller接口中只有一个handle方法,用户的controller类可以实现这个接口方法,而ControllerHandlerAdapter适配器就是首先判断当前处理器是否是Controller接口的实例,如果是则调用接口的方法,即调用用户写的方法。

HandlerMethodAdapter,这个适配器支持方法级别的处理器,首先判断处理器是否是HandlerMethod类的实例,即是否是方法级别的处理器,如果是则获取方法对象,获取参数列表并转换为参数数组,然后用method.invoke()调用方法,判断返回值类型,如果方法有ResponseBody注解则将返回值作为对象添加到modelandview对象中,如果没有则表明返回的是视图名,则作为视图名添加到modelandview对象中。

其中将参数列表转换为参数数组需要对于参数列表进行遍历,先获取参数的类型,然后根据不同情况分别进行对应的参数获取,具体为先判断参数的注解类型:

如果是RequestParam注解类,则从请求中获取给定参数名(注解内容表示参数名)的参数值并进行相应的类型解析转换。

如果是RequestBody注解,则表明参数为JSON格式,需要先获取请求体内容的字符流进行写入,然后转换成参数类型的对象。

如果是PathVariable注解,则表明是从路径中获得参数,需要拼接出url,然后寻找花括号{}里面的值,拿到后进行相应的解析转换。

如果是RequestHeader注解,则直接从请求头中获取参数值,并解析转换。

如果是CookieValue注解,则从cookie中获取参数值并解析转换。

然后判断参数类型是否是特殊类型,如请求类型、响应类型、session类型,如果是这三种类型则直接获取。

如果是其他类型则利用请求的getParameterMap()方法获取所有的参数,然后遍历判断类型是字符串还是字符串数组,利用JSON格式(JSON本身是一个map)获取并封装为对象返回。

视图解析器类实现ViewResolver接口,该接口有两个方法,getPrefix和getSuffix,这里以InternalResourceViewResolver实现类为例:

InternalResourceViewResolver类有两个属性,就是prefix和suffix,重写的方法就是两个属性的get方法,此外还有set方法。

DispatcherServlet的init方法到此结束,总结就是通过扫描包创建了处理器映射器的list,处理器适配器的list以及设置了视图解析器。之后要重写service方法,首先根据用户的请求地址和请求方式拿到处理器链,如果为空就会向前端发送一个404,如果不为空就说明有处理器可以处理,但是不能确定处理器的类型,所以需要处理器适配器进行适配,在处理器适配器中实现方法的调用,最后对返回值进行处理,如果返回的是数据则利用字符流进行输出,如果是视图则根据视图名和前后缀找到视图文件,用流读取文件的内容进行渲染。

其中,根据用户而定请求地址和请求方式拿到处理器链的方式为遍历处理器映射器,拿到其中的处理器链(处理器),如果不为空就返回,因此会找到第一个处理器链。

而处理器适配器进行适配的逻辑则为遍历处理器适配器,调用该处理器适配器的匹配类型的方法,如果匹配成功就返回,因此也是返回第一个处理器适配器。

整个DispatcherServlet的代码:

package com.qf.mvc;import com.alibaba.fastjson.JSON;
import com.qf.anno.ComponentScan;
import com.qf.container.IocContainer;
import com.qf.data.ModelAndView;
import com.qf.handler.HandlerAdapter;
import com.qf.handler.HandlerExecutionChain;
import com.qf.mapping.HandlerMapping;
import com.qf.utils.PackageScanner;import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;public class DispatcherServlet extends HttpServlet {//当前用户配置的处理器映射器private List<HandlerMapping> mappings = new ArrayList<>();private List<HandlerAdapter> adapters = new ArrayList<>();private ViewResolver viewResolver;@Overridepublic void init(ServletConfig config) throws ServletException {//找到配置类String configClass = config.getInitParameter("appConfig");try {Class<?> clazz = Class.forName(configClass);//获取配置类的字节码信息ComponentScan scan = clazz.getAnnotation(ComponentScan.class);//获取含有扫描包信息的注解if(scan!=null){String[] packages = scan.value();//获取要扫描的包if(packages.length ==0){//如果没有配置扫描的包,那么就扫描这个配置类所在的包packages = new String[]{clazz.getPackage().getName()};}for (String pk : packages) {PackageScanner.scan(pk);//循环扫描每一个配置的包}}Collection<Object> beans = IocContainer.getAllBeans();for (Object bean : beans) {if(bean instanceof HandlerMapping){mappings.add((HandlerMapping) bean);} else if (bean instanceof HandlerAdapter) {adapters.add((HandlerAdapter) bean);}else if(bean instanceof ViewResolver){viewResolver = (ViewResolver) bean;}}} catch (ClassNotFoundException e) {throw new RuntimeException(e);} catch (Exception e) {throw new RuntimeException(e);}}@Overrideprotected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {//当拿到请求的时候,我们需要根据请求的地址和请求的方式去找到匹配的处理器//如何来查找处理器呢?就需要根据用户的配置来决定try {HandlerExecutionChain handlerExecutionChain = getHandlerExecutionChain(req);if(handlerExecutionChain == null || handlerExecutionChain.getHandler() == null){//没有找到匹配的处理器执行链,那么此时就会向前端发送一个404resp.setContentType("text/html;charset=UTF-8");PrintWriter writer = resp.getWriter();writer.write("404  Not Found");writer.flush();writer.close();return;}//只要代码执行到这里,说明处理器也不为空,但是此时不能确定处理器的类型,因此需要处理器适配器HandlerAdapter adapter = getHandlerAdapter(handlerExecutionChain.getHandler());if(adapter == null){//如果说处理器适配器为空,说明存在配置问题return;}ModelAndView view = adapter.handle(req, resp, handlerExecutionChain.getHandler());if(view.hasData()){Object data = view.getData("data");resp.setContentType("application/json;charset=UTF-8");PrintWriter writer = resp.getWriter();writer.write(JSON.toJSONString(data));writer.flush();writer.close();} else {String viewName = view.getViewName();String location = viewResolver.getPrefix() + viewName + viewResolver.getSuffix();//根据位置用流读取文件的内容InputStream in = DispatcherServlet.class.getResourceAsStream(location);InputStreamReader isr = new InputStreamReader(in);BufferedReader reader = new BufferedReader(isr);PrintWriter writer = resp.getWriter();String line;while ((line = reader.readLine()) != null){writer.write(line);}writer.flush();writer.close();}} catch (Exception e) {e.printStackTrace();}}private HandlerExecutionChain getHandlerExecutionChain(HttpServletRequest req) throws Exception {for (HandlerMapping mapping : mappings) {HandlerExecutionChain handlerExecutionChain = mapping.getHandler(req);if(handlerExecutionChain != null){return handlerExecutionChain;}}return null;}private HandlerAdapter getHandlerAdapter(Object handler){for (HandlerAdapter adapter : adapters) {if(adapter.supports(handler)) return adapter;}return null;}
}

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

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

相关文章

Git使用[推送大于100M的文件后解救办法]

推送大于100M的文件后解救办法 本文摘录于&#xff1a;https://blog.csdn.net/u012150602/article/details/122687435只是做学习备份之用&#xff0c;绝无抄袭之意&#xff0c;有疑惑请联系本人&#xff01; 当有文件大于100M的时候在提交的时候没有问题,但是在push的似乎就不行…

番外篇 | 手把手教你如何去更换YOLOv5的检测头为ASFF_Detect

前言:Hello大家好,我是小哥谈。自适应空间特征融合(ASFF)的主要原理旨在解决单次检测器中不同尺度特征的不一致性问题。具体来说,ASFF通过动态调整来自不同尺度特征金字塔层的特征贡献,确保每个检测对象的特征表示是一致且最优的。本文所做出的改进是将YOLOv5的检测头更换…

程序算法设计分析

动态规划和分治、贪心相比有什么区别&#xff1f;各自的优缺点&#xff1f; 分治算法特征&#xff1a; 1&#xff09;规模如果很小&#xff0c;则很容易解决。//一般问题都能满足 2&#xff09;大问题可以分为若干规模小的相同问题。//前提 3&#xff09;利用子问题的解&#x…

O2OA(翱途)开发平台 V9.1 即将发布,更安全、更高效、更开放

尊敬的O2OA(翱途)平台合作伙伴、用户以及亲爱的开发小伙伴们&#xff0c;O2OA(翱途)平台 V9.1将于7月3日正式发布&#xff0c;届时欢迎大家到O2OA官网部署下载及体验最新版本。新版本我们在如下方面做了更大的努力&#xff1a; 1.扩展数据库兼容性和功能范围&#xff1a;在O2OA…

基于Web技术的教育辅助系统设计与实现(SpringBoot MySQL)+文档

&#x1f497;博主介绍&#x1f497;&#xff1a;✌在职Java研发工程师、专注于程序设计、源码分享、技术交流、专注于Java技术领域和毕业设计✌ 温馨提示&#xff1a;文末有 CSDN 平台官方提供的老师 Wechat / QQ 名片 :) Java精品实战案例《700套》 2025最新毕业设计选题推荐…

汽车电子工程师入门系列——CAN 规范系列通读

我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 屏蔽力是信息过载时代一个人的特殊竞争力,任何消耗你的人和事,多看一眼都是你的不对。非必要不费力证明自己,无利益不试图说服别人,是精神上的节…

使用 rye 管理 python 依赖

rye 是使用 rust 实现的一个项目和包管理解决方案。 配置 pip 源 vim ~/.pip/pip.conf [global] index-url http://mirrors.cloud.tencent.com/pypi/simple/ trusted-host mirrors.cloud.tencent.com安装 rye cargo install --git https://github.com/mitsuhiko/rye rye配…

laravel Dcat Admin 入门应用(五)文件上传之OSS自定义上传

laravel Dcat Admin 入门应用&#xff08;五&#xff09;文件上传之OSS自定义上传 Dcat Admin 是一个基于 Laravel-admin 二次开发而成的后台构建工具&#xff0c;只需很少的代码即可构建出一个功能完善的高颜值后台系统。支持页面一键生成 CURD 代码&#xff0c;内置丰富的后台…

使用ps给gif动图抠图

目录 导入gif图片 打开时间轴 选择图片 魔棒抠图-初步抠图 套索抠图-精准抠图 导入gif图片 打开时间轴 因为gif动图实际上多张图片实现的效果&#xff0c;所以如果要给gif抠图&#xff0c;就得挨个给每个时间线的图片抠图 点击窗口->时间轴 选择图片 在时间轴上选择要…

以 Vue 3 项目为例,多个请求下如何全局封装 Loading 的展示与关闭?其中大有学问!

大家好,我是CodeQi! 项目开发中,Loading 的展示与关闭是非常关键的用户体验设计。 当我们的应用需要发起多个异步请求时,如何有效地管理全局 Loading 状态,保证用户在等待数据加载时能有明确的反馈,这是一个值得深入探讨的问题。 本文将以 Vue 3 项目为例,详细讲解如…

eventloop 事件循环机制 (猜答案)

// eventloop 事件循环机制// console.log(555);setTimeout(() > {console.log(666);})let p new Promise((resolve,reject)>{// 同步执行console.log(111);resolve();});// promise 的回调函数是异步的微任务p.then(v > {console.log(222);}, r > {console.log(r…

STMF4学习笔记(天空星)

前言&#xff1a;本篇笔记参考嘉立创文档&#xff0c;连接放在最后 #RTC相关概念定义 Real-Time Clock 缩写 RTC 翻译 实时时钟&#xff0c;是单片机片内外设的一种&#xff0c;作用于提供准确的时间还有日期&#xff0c;这个外设有独立的电源&#xff0c;当单片机停止供电…

Java同步包装器

通过 Collections.synchronizedList() 方法将一个普通的 ArrayList 包装成了线程安全的 List&#xff1a; import java.util.*;public class SynchronizedWrapperExample {public static void main(String[] args) {// 创建一个非线程安全的 ArrayListList<String> list…

AzureDataFactory Dataverse connector自动处理了分页问题(单次查询上限5000条的限制)

众所周知&#xff0c;在用fetch执行D365的查询时&#xff0c;单次的查询是5000条&#xff0c;如果超过5000条则需要自己处理分页&#xff0c;添加额外的处理逻辑&#xff0c;但在ADF中&#xff0c;Dataverse connector已经自动处理了分页&#xff0c;我们可以很简单的做个POC. …

计算机网络——数据链路层(点对点协议PPP)

点对点协议PPP的概述 对于点对点的链路&#xff0c;目前使用得最广泛的数据链路层协议是点对点协议 PPP (Point-to-Point Protocol)。 它主要应用于两个场景&#xff1a; 用户计算机与ISP之间的链路层协议就是点对点协议 PPP&#xff0c;1999年公布了回以在以太网上运行的PPP协…

【教程】lighttpd配置端口反向代理

转载请注明出处&#xff1a;小锋学长生活大爆炸[xfxuezhagn.cn] 如果本文帮助到了你&#xff0c;欢迎[点赞、收藏、关注]哦~ 1、修改配置文件&#xff1a; sudo vim /etc/lighttpd/lighttpd.conf2、先添加mod_proxy&#xff1a; 3、然后添加端口映射&#xff1a; 4、保存&…

耗材分类功能解析:智慧校园的必备利器

在智慧校园的资产管理架构中&#xff0c;耗材分类功能是确保日常运营物资有效管理的关键组成部分&#xff0c;它致力于提高耗材使用的效率和经济性。此功能通过智能化、精细化的管理手段&#xff0c;对校园内各种易耗品进行科学分类与跟踪。 耗材分类功能首先建立在对校园日常运…

C++ 实现QT信号槽

https://github.com/libsigcplusplus/libsigcplusplus #include <iostream>/* 在sigslot.h的420,将&#xff1a; //typedef sender_set::const_iterator const_iterator; 改为&#xff1a; //typedef typename sender_set::const_iterator const_iterator;#include <…

Softing助力工业4.0 | 通过OPC UA和MQTT访问SINUMERIK 840D CNC控制器数据

Softing uaGate 840D是用于采集西门子SINUMERIK 840D SL/PL CNC控制器数据的物联网网关&#xff0c;支持OPC UA服务器和MQTT发布功能。该网关提供对SINUMERIK 840D CNC控制器机床数据的访问&#xff0c;支持读取、处理重要的主轴和从轴数据&#xff0c;例如扭矩和功耗&#xff…

C++——stack和queue类用法指南

一、stack的介绍和使用 1.1 stack的介绍 1、stack是一种容器适配器&#xff0c;专门用在具有后进先出操作的上下文环境中&#xff0c;其删除只能从容器的一端进行插入与提取操作 2、stack是作为容器适配器被实现的&#xff0c;容器适配器即是对特定类封装作为其底层的容器&am…