一、JSR303
1.1 JSR303是什么
JSR 303是Java规范请求(Java Specification Request)的编号,它定义了Java Bean验证的标准规范。JSR 303的全称是Bean Validation,它提供了一种在Java应用程序中进行数据验证的机制。
Bean Validation是一种基于注解的验证框架,它允许开发人员在Java Bean的属性上添加验证规则,以确保数据的合法性和完整性。通过使用Bean Validation,开发人员可以在数据输入阶段就捕获错误,避免后续可能出现的潜在问题。
1.2 JSR 303的好处包括
- 简化验证逻辑:通过使用注解,开发人员可以在Java Bean的属性上直接添加验证规则,而不需要编写繁琐的验证代码。
- 统一验证规范:JSR303定义了一组常用的验证注解,使得不同的开发人员在验证过程中可以使用相同的规范,提高了代码的可读性和可维护性。
- 提高代码质量:通过在Java Bean中添加验证规则,可以在数据输入阶段就捕获错误,避免了后续可能出现的潜在问题。
- 可扩展性:JSR303提供了自定义注解的能力,开发人员可以根据自己的需求定义新的验证注解。
1.3 常用注解
- @NotNull:用于验证属性值不能为null。
- @Size:用于验证字符串、集合或数组的大小是否在指定范围内。
- @Min:用于验证数字属性的最小值。
- @Max:用于验证数字属性的最大值。
- @Pattern:用于验证字符串属性是否符合指定的正则表达式。
- @Email:用于验证字符串属性是否符合电子邮件格式。
- @NotBlank:用于验证字符串属性是否非空且长度大于0。
- @NotEmpty:用于验证字符串、集合或数组属性是否非空。
- @Range:用于验证数字属性是否在指定范围内。
- @Valid:用于嵌套验证,验证关联对象。
1.4 实例
1.4.1 导入JSR303依赖
<!-- JSR303 --> <hibernate.validator.version>6.0.7.Final</hibernate.validator.version> <!-- JSR303 --> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-validator</artifactId> <version>${hibernate.validator.version}</version> </dependency>
1.4.2 规则配置
package com.xqx.model; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import org.hibernate.validator.constraints.Length; import javax.validation.constraints.NotBlank; import javax.validation.constraints.NotNull; @Data//相当于set get toString方法 @AllArgsConstructor //有参构造器 @NoArgsConstructor //无参构造器 public class Book{ @NotBlank(message = “书本信息不能为空!") private String bookName; @NotNull(message = “书本价格不能为空!") private Float bookPrice: private String bookType; }
1.4.3 编写校验方法
/** * 书本新增 * 请求路径 SSM/book/editBook * @param book * @return */ @RequestMapping("/addBook") public String addBook(@Validated Book book, BindingResult bindingResult){ System.out.println("进入新增方法"); //判断是否验证成功 if(bindingResult.hasErrors()){ System.out.println("验证失败"); //验证失败 return "book/addBook"; }else { //验证成功 bookService.insert(book); return "redirect:queryBookPage"; } } /** * @ModelAttribute: 所标记的方法为非请求处理方法,在所有请求方法之前被调用 * @odelAttribute 在所有的@RequestMapping的方法之前被调用! * * 作用:数据预加载 * 特点:无返回值 */ @ModelAttribute public void init(Model model){ System.out.println("非请求处理方法"); //必须操作 model.addAttribute("book",new Book()); }
1.4.4 编写前端
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <%@ taglib prefix="fmt" uri="http://www.springframework.org/tags/form" %> <html> <head> <%@include file="/common/head.jsp"%> <title>Title</title> </head> <style> .cl{ color: red; } </style> <body> <h1>书本新增</h1> <fmt:form modelAttribute="book" action="${ctx}/book/addBook" method="post"> <label>书本名称:</label><input type="text" name="bookName"><fmt:errors cssClass="cl" path="*"/><br/> <label>书本价格:</label><input type="text" name="bookPrice"><%--<fmt:errors cssClass="cl" path="bookPrice"/>--%><br/> <label>书本类型:</label> <select name="bookType"> <option value="神话">神话</option> <option value="教育">教育</option> <option value="文学">文学</option> <option value="玄幻">玄幻</option> </select> <br/> <input type="submit" value="添加"> </fmt:form> </body> </html>
二、拦截器
2.1 拦截器是什么
SpringMVC的处理器拦截器,类似于Servlet开发中的过滤器Filter,用于对处理器进行预处理和后处理。依赖于web框架,在实现上基于Java的反射机制,属于面向切面编程(AOP)的一种运用。由于拦截器是基于web框架的调用,因此可以使用Spring的依赖注入(DI)进行一些业务操作,同时一个拦截器实例在一个controller生命周期之内可以多次调用。
2.2 拦截器与过滤器的区别
拦截器(Interceptor)通常是指一种拦截和处理请求的组件或模块。它可以在请求到达目标之前或之后进行拦截,并对请求进行修改、记录或处理。拦截器通常用于实现横切关注点(cross-cutting concerns),如日志记录、安全认证、性能监控等。拦截器可以在应用程序的不同层级中使用,例如在网络层、业务逻辑层或数据访问层。
过滤器(Filter)则是一种用于过滤和处理数据的组件或模块。它可以在数据流经过时对数据进行过滤、转换或处理。过滤器通常用于对数据进行预处理或后处理,以满足特定的需求或要求。常见的应用包括数据清洗、数据转换、数据压缩等。过滤器可以在不同的数据处理环节中使用,例如在输入输出流、数据库查询、图像处理等。
2.3.应用场景
- 日志记录:记录请求信息的日志,以便进行信息监控、信息统计、计算PV(Page View)等。
- 权限检查:如登录检测,进入处理器检测是否登录,如果没有直接返回到登录页面;
- 性能监控:有时候系统在某段时间莫名其妙的慢,可以通过拦截器在进入处理器之前记录开始时间,在处理完后记录结束时间,从而得到该请求的处理时间(如果有反向代理,如apache可以自动记录);
- 通用行为:读取cookie得到用户信息并将用户对象放入请求,从而方便后续流程使用,还有如提取Locale、Theme信息等,只要是多个Controller中的处理方法都需要的,我们就可以使用拦截器实现。
2.4 快速入门
创建拦截器
package com.xqx.interceptor; import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.ModelAndView; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; /* 1. 定义拦截器类,实现HandlerInterceptor接口 2. 注意当前类必须受Spring容器控制 */ public class OneInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { System.out.println("【OneInterceptor】:preHandle..."); return true; } @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { System.out.println("【OneInterceptor】:postHandle..."); } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { System.out.println("【OneInterceptor】:afterCompletion..."); } }
配置拦截器
在spring-mvc.xml中配置:
<!--配置拦截器--> <mvc:interceptors> <bean class="com.xqx.interceptor.OneInterceptor"></bean> </mvc:interceptors>
2.5.拦截器链
如果多个拦截器能够对相同的请求进行拦截,则多个拦截器会形成一个拦截器链,主要理解拦截器链中各个拦截器的执行顺序。拦截器链中多个拦截器的执行顺序,根拦截器的配置顺序有关,先配置的先执行。
在spring-mvc.xml中配置多个拦截器
<!--2) 多拦截器(拦截器链)--> <mvc:interceptors> <mvc:interceptor> <!--拦截所有--> <mvc:mapping path="/**"/> <bean class="com.xqx.interceptor.OneInterceptor"/> </mvc:interceptor> <mvc:interceptor> <!--拦截users下的controller--> <mvc:mapping path="/book/**"/> <bean class="com.xqx.interceptor.TwoInterceptor"/> </mvc:interceptor> </mvc:interceptors>
使用了拦截器链的效果
2.6 登录拦截权限案例
2.6.1 LoginController.java
package com.xqx.controller; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.RequestMapping; import javax.servlet.http.HttpSession; @Controller public class LoginController { /** * 跳转到登录页面 * @return */ @RequestMapping("tologin") public String toLogin(){ return "login"; } /** * 登录方法 * @param username 账号 * @param password 密码 * @return */ @RequestMapping("/userLogin") public String userLogin(String username, String password, HttpSession session, Model model){ if("admin".equals(username)|| password.equals("123")){ session.setAttribute("username",username); //这里的"/"是跳转的@RequestMapping配置的值 return "redirect:/"; } model.addAttribute("msg","账号或者密码错误"); return "login"; } /** * 安全退出 * @param session * @return */ @RequestMapping("/userLogout") public String userLogout(HttpSession session){ //清空session session.invalidate(); return "redirect:tologin"; } }
2.6.2 配置拦截器
<mvc:interceptors> <bean class="com.xqx.interceptor.LoginInterceptor"></bean> </mvc:interceptors>
2.6.3 创建拦截器
package com.xqx.interceptor; import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.ModelAndView; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; public class LoginInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { //获取请求路径 String url = request.getRequestURI(); System.out.println(url); //判断是否是跳转登录页面的请求 放行 if(url.indexOf("/tologin")>0) return true; //判断是否是用户登录 放行 if(url.indexOf("/userLogin")>0) return true; //获取session HttpSession session = request.getSession(); //获取session中的用户对象 String username = (String) session.getAttribute("username"); //判断session中的用户对象是否存在,存在放行,不存在跳转登录页面 if(username!=null) return true; request.setAttribute("msg","您还没有登录,请登录!"); request.getRequestDispatcher("/WEB-INF/jsp/login.jsp").forward(request,response); return false; } @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { } }
2.6.4 测试
账号密码错误的情况下
在未登录的情况下访问首页