目录
一、JSR303
简介
导入所需依赖
pom依赖
后台
实体类
前端界面
实现结果:
JSR303服务端总结
二、拦截器
1、什么是拦截器
2、拦截器与过滤器
2.1、什么是过滤器(Filter)
2.2、拦截器与过滤器的区别
3、应用场景
4.拦截器快速入门
5、拦截器链(多拦截器)
一、JSR303
简介
JSR303是做服务端校验 参数验证
导入所需依赖
pom依赖
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>6.0.7.Final</version>
</dependency>
![](https://img-blog.csdnimg.cn/713ffbafdb45477fbcf925d0641d1ff6.png)
后台
@NotNull :作用于基本数据类型
@NotEmpty 作用于集合
@NotBlank 作用于字符串
实体类
Clazz
@NotNull(message = "cid不能为空") message 提示语句
package com.oyang.ssm.model;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
/**
* @NotNull :作用于基本数据类型
* @NotEmpty 作用于集合
* @NotBlank 作用于字符串
*/
public class Clazz {
@NotNull(message = "cid不能为空")
protected Integer cid;
@NotBlank(message = "班级名称不能为空")
protected String cname;
@NotBlank(message = "教员老师不能为空")
protected String cteacher;
protected String pic;
public Clazz(Integer cid, String cname, String cteacher, String pic) {
this.cid = cid;
this.cname = cname;
this.cteacher = cteacher;
this.pic = pic;
}
public Clazz() {
super();
}
public Integer getCid() {
return cid;
}
public void setCid(Integer cid) {
this.cid = cid;
}
public String getCname() {
return cname;
}
public void setCname(String cname) {
this.cname = cname;
}
public String getCteacher() {
return cteacher;
}
public void setCteacher(String cteacher) {
this.cteacher = cteacher;
}
public String getPic() {
return pic;
}
public void setPic(String pic) {
this.pic = pic;
}
}
ClazzController
/**
* @Valid 是与实体类中的服务端校验 注解配合使用的
* BindingResult 存放了所有违背校验的错误信息
* @param clazz
* @param bindingResult
* @return
*/
@RequestMapping("/valiAdd ")
public String valiAdd(@Valid Clazz clazz, BindingResult bindingResult){
if(bindingResult.hasErrors()){
//违背了规则
//拿到所有错误
List<FieldError> fieldErrors = bindingResult.getFieldErrors();
for (FieldError fieldError : fieldErrors) {
// cid : cid不能为空
System.out.println(fieldError.getField() + ":" + fieldError.getDefaultMessage());
}
}else{//反之没有违背
this.clazzBiz.insertSelective(clazz);
}
return "redirect:/clz/list";
}
前端界面
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title></title>
</head>
<body>
<form action="${pageContext.request.contextPath }/clz/${empty b ? 'valiAdd' : 'edit'}" method="post">
cid:<input type="text" name="cid" value="${b.cid }"><br>
cname:<input type="text" name="cname" value="${b.cname }"><br>
cteacher:<input type="text" name="cteacher" value="${b.cteacher }"><br>
<input type="submit">
</form>
</body>
</html>
我们试一下直接点击提交
![](https://img-blog.csdnimg.cn/a9936617ed0a40798b937d1f427c0ec1.png)
在Debug后台中可以看到,有错误提示
![](https://img-blog.csdnimg.cn/7a7abb92c13742149c577a025fa70a05.png)
当然,我们可不能给客户看我们后台打印的数据,那怎么把错误提示展示到表单后面呢?
![](https://img-blog.csdnimg.cn/4a22a0c56177432d9bc32e666335b015.png)
完善ClazzController类 中valiAdd方法
/**
* @Valid 是与实体类中的服务端校验 注解配合使用的
* BindingResult 存放了所有违背校验的错误信息
* @param clazz
* @param bindingResult
* @return
*/
@RequestMapping("/valiAdd")
public String valiAdd(@Valid Clazz clazz, BindingResult bindingResult,HttpServletRequest request){
if(bindingResult.hasErrors()){//违背了规则
Map msg=new HashMap();
//拿到所有错误
List<FieldError> fieldErrors = bindingResult.getFieldErrors();
for (FieldError fieldError : fieldErrors) {
// cid : cid不能为空
System.out.println(fieldError.getField() + ":" + fieldError.getDefaultMessage());
//相当于 msg.put(cid,cid不能为空);
msg.put(fieldError.getField(),fieldError.getDefaultMessage());
}
request.setAttribute("msg",msg);
//如果出现了错误,应该将提示语显示在表单元素后方
return "clzEdit";
}else{//反之没有违背
this.clazzBiz.insertSelective(clazz);
}
return "redirect:/clz/list";
}
前端代码也需要更新
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title></title>
</head>
<body>
<form action="${pageContext.request.contextPath }/clz/${empty b ? 'valiAdd' : 'edit'}" method="post">
cid:<input type="text" name="cid" value="${b.cid }"><span style="color: red">${msg.cid}</span><br>
cname:<input type="text" name="cname" value="${b.cname }"><span style="color: red">${msg.cname}</span><br>
cteacher:<input type="text" name="cteacher" value="${b.cteacher }"><span style="color: red">${msg.cteacher}</span><br>
<input type="submit">
</form>
</body>
</html>
实现结果:
直接点击提交![](https://img-blog.csdnimg.cn/5f907250d1ef431683d7b339eb84ed94.png)
![](https://img-blog.csdnimg.cn/bd32fb6f77df47d79367bdce03ac59dc.png)
JSR303服务端总结
1.pom依赖导入
2.在待校验的数据库列段对应的实体属性打上校验标签
3.在controller层,方法上添加@valid注解配合前面的校验标签,添加bindingResult,此对象包含了所有校验未通过的错误信息
4.可以将所有的错误信息以map集合的方式保存,并且传递到前台页面展示
二、拦截器
1、什么是拦截器
SpringMVC的处理器拦截器,类似于Servlet开发中的过滤器Filter,用于对处理器进行预处理和后处理(AOP中的环绕通知)。
依赖于web框架,在实现上基于Java的反射机制,属于面向切面编程(AOP)的一种运用。由于拦截器是基于
web框架的调用,因此可以使用Spring的依赖注入(DI)进行一些业务操作,同时一个拦截器实例在一个
controller生命周期之内可以多次调用。
2、拦截器与过滤器
2.1、什么是过滤器(Filter)
依赖于servlet容器。在实现上基于函数回调,可以对几乎所有请求进行过滤,但是缺点是一个过滤器实例只能在容器初始化时调用一次。使用过滤器的目的是用来做一些过滤操作,比如:在过滤器中修改字符编码;
在过滤器中修改HttpServletRequest的一些参数,包括:过滤低俗文字、危险字符等。
2.2、拦截器与过滤器的区别
过滤器(filter):
1) filter属于Servlet技术,只要是web工程都可以使用
2) filter主要对所有请求过滤
3) filter的执行时机早于Interceptor
拦截器(interceptor):
1) interceptor属于SpringMVC技术,必须要有SpringMVC环境才可以使用
2) interceptor通常对处理器Controller进行拦截
3) interceptor只能拦截dispatcherServlet处理的请求
3、应用场景
1)日志记录:记录请求信息的日志,以便进行信息监控、信息统计、计算PV(Page View)等。
2)权限检查:如登录检测,进入处理器检测是否登录,如果没有直接返回到登录页面;
3)性能监控:有时候系统在某段时间莫名其妙的慢,可以通过拦截器在进入处理器之前记录开始时间,在处理完后记录结束时间,从而得到该请求的处理时间(如果有反向代理,如apache可以自动记录);
4)通用行为:读取cookie得到用户信息并将用户对象放入请求,从而方便后续流程使用,还有如提取Locale、Theme信息等,只要是多个Controller中的处理方法都需要的,我们就可以使用拦截器实现
4.拦截器快速入门
1、创建HelloController
2、创建自定义拦截器并实现HandlerInterceptor接口
OneHandlerInterceptor:
package com.oyang.ssm.intercept;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* @author oyang
* @site https://blog.csdn.net
* @qq 1828190940
* @create 2022-08-19 19:04
*/
public class OneHandlerInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//预处理
System.out.println("[OneHandlerInterceptor] . preHandle"+"---预处理");
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
//后处理
System.out.println("[OneHandlerInterceptor] . postHandle"+"---后处理");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
//完成后执行
System.out.println("[OneHandlerInterceptor] . afterCompletion"+"---完成后执行");
}
}
注意,此时preHandle 预处理方法的返回值是true
复制该类全路径进入Spring-servlet.xml进行配置
<!--配置拦截器-->
<mvc:interceptors>
<!--针对于所有的请求进行拦截-->
<bean class="com.oyang.ssm.intercept.OneHandlerInterceptor"></bean>
</mvc:interceptors>
创建HelloController
package com.oyang.ssm.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
/**
* @author oyang
* @site https://blog.csdn.net
* @qq 1828190940
* @create 2022-08-19 19:19
*/
@Controller
public class HelloController {
@RequestMapping("/hello")
public String hello(){
System.out.println("进入业务方法...");
return "index";
}
}
![](https://img-blog.csdnimg.cn/a038b875f4434165a64f00b565ceeec4.png)
![](https://img-blog.csdnimg.cn/8c7e11be732a4675b4d83a5e80f35368.png)
运行测试:
看向控制台,我们可以看到方法的执行顺序 ,先执行预处理->后处理->完成后执行方法
我们可以看到预处理和后处理相当于前置通知和后置通知,所以就有一个结论我们的拦截器
就相当于AOP处理
我们将preHandle 预处理方法的返回值改成false
![](https://img-blog.csdnimg.cn/714c30127af9451da59f272afc9c9f63.png)
运行结果:
![](https://img-blog.csdnimg.cn/ee38b780a9ab4c5f8bcb57cafb2e684c.png)
以看到我们只执行了预处理 ,没有实现业务方法。同时,界面的Hello Word也看不见了
![](https://img-blog.csdnimg.cn/2ca5b47e812b45c695dcc4decf959f8b.png)
5、拦截器链(多拦截器)
TwoHandlerInterceptor
package com.oyang.ssm.intercept;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* @author oyang
* @site https://blog.csdn.net
* @qq 1828190940
* @create 2022-08-19 19:04
*/
public class TwoHandlerInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//预处理
System.out.println("[TwoHandlerInterceptor] . preHandle"+"---预处理");
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
//后处理
System.out.println("[TwoHandlerInterceptor] . postHandle"+"---后处理");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
//完成后执行
System.out.println("[TwoHandlerInterceptor] . afterCompletion"+"---完成后执行");
}
}
复制该类全路径进入Spring-servlet.xml进行配置
<!--配置拦截器链-->
<mvc:interceptors>
<mvc:interceptor>
<!--拦截所有-->
<mvc:mapping path="/**/"/>
<bean class="com.oyang.ssm.intercept.OneHandlerInterceptor"> </bean>
</mvc:interceptor>
<mvc:interceptor>
<mvc:mapping path="/clz/**"/>
<bean class="com.oyang.ssm.intercept.TwoHandlerInterceptor"></bean>
</mvc:interceptor>
</mvc:interceptors>
运行预测结果:
当我们输入http://localhost:8080/hello 会执行OneHandle中的三个方法
当我们输入http://localhost:8080/Clz/list 会执行One和Two两个接口:
首先One--->prehandle
Two------>prehandle
在执行业务方法
One---->posthandle
Two---->posthandle
One----->aftercomplation
Two------>aftercomplation
我们先实现第一种情况,看是否运行了One的三种方法
![](https://img-blog.csdnimg.cn/aa9ee8dbcc3b41eca55bc131b50af976.png)
![](https://img-blog.csdnimg.cn/4eb4682d36b94892baee5dcbb709a39b.png)
运行第二种情况:
![](https://img-blog.csdnimg.cn/a99a1a93382a4f6c91a0ab5a5a80d5f2.png)
![](https://img-blog.csdnimg.cn/75f6db19c34848179d22e39d234ac3d9.png)
6、登录权限控制
首先我们将配置拦截链的一些配置修改一下
![](https://img-blog.csdnimg.cn/2ca3d29c460243d3a6b6c98da977feca.png)
我们想做到的是:以前可以直接访问我们的数据,而先在我们需要登陆才能访问
![](https://img-blog.csdnimg.cn/388cc473965048699878d3a81882d996.png)
LoginController
package com.oyang.ssm.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import javax.servlet.http.HttpServletRequest;
/**
* @author oyang
* @site https://blog.csdn.net
* @qq 1828190940
* @create 2022-08-19 16:35
*/
@Controller
public class LoginController {
//登录
@RequestMapping("/login")
public String login(HttpServletRequest request){
//登录成功 需要保存用户登陆信息
String uname = request.getParameter("uname");
if("oyang".equals(uname)){//数据定死了,如果用户名是oyang就代表登录成功
//登录成功就保存到session里去
request.getSession().setAttribute("uname",uname);
}
return "index";
}
//登出
@RequestMapping("/logout")
public String logout(HttpServletRequest request){
//销毁
request.getSession().invalidate();
return "index";
}
}
OneHandlerInterceptor
判断是否登录
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
//预处理
System.out.println("[OneHandlerInterceptor] . preHandle"+"---预处理");
//如果login/logout 这个请求,就直接放行
String url = request.getRequestURL().toString();
if(url.indexOf("/login")>0 || url.indexOf("/logout")>0){//代表是login/logout 其中的任何一个 截取
//放行
return true;
}
//对于请求业务方法 只有登录过存在session数据 才能访问
else{
Object uname = request.getSession().getAttribute("uname");
if(uname==null||"".equals(uname)){//代表没有登录
response.sendRedirect("/login.jsp");
}
}
return true;
}
![](https://img-blog.csdnimg.cn/48dacedc57184c638d773933914a6772.png)
回车
![](https://img-blog.csdnimg.cn/90d1682504c7406b81857853356f3fda.png)
因为没有登录,就直接跳转到登录界面了
输入用户名
![](https://img-blog.csdnimg.cn/83e934eb2294412db6b0c69a101ebc58.png)
就跳转到了欢迎界面了,同时,我们再查看一下数据(输入地址栏)