过滤器 (Filter)
1. 主要内容
2. 过滤器
2.1 介绍
- Filter即为过滤,用于在Servlet之外对Request或者Response进行修改。它主要用于对用户请求进行预处理,也可以对HttpServletResponse进行后处理。使用Fiter的完整流程:
Filter对用户请求进行预处理,接着将请求交给Servlet进行处理并生成响应,最后Filter再对服务器响应进行后处理。
在一个web应用中,可以开发编写多个Filter,这些Filter组合起来称之为一个Fiter链。
若是一个过滤器链:先配置先执行(请求时的执行顺序);响应时:以相反的顺序执行。
在HttpServletRequest 到达 Servlet之前,拦截客户的HttpServletRequest。根据需要检查 HttpServletRequest,也可以修改HttpServletRequest头和数据。
在HttpServletResponse到达客户端之前,拦截HttpServletResponse,根据需要检查 HttpServletResponse,也可以修改HttpServletResponse头和数据。
2.2 实现
- 可以通过实现一个叫做
javax.servlet.Fileter
的接口来实现一个过滤器,其中定义了三个方法,inito() , doFilter(), destroy()
分别在相应的时机执行。后期观察生命周期。- Filter的实现只需要两步:
- step1:编写java类实现Fiter接口,并实现其
doFiter
方法。- Step2:通过
@WebFilter
注解设置它所能拦截的资源。
- 过滤器1
package com.liu.filter;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;
/**
* @author lms
* @date 2021-09-25 - 15:02
*/
// /* 表示拦截所有的请求
@WebFilter("/*")
public class Filter03 implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
// 业务逻辑的过滤操作
// 选择性放行,否则请求无法到达处理器
filterChain.doFilter(servletRequest, servletResponse);
}
@Override
public void destroy() {
}
}
- 控制器
package com.liu.servlet;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* @author lms
* @date 2021-09-25 - 15:08
*/
//表示 "/ser01" 的请求将会由当前的这个处理器进行处理请求服务
//@WebServlet("/ser01")
public class Servlet01 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("ser01正在被处理请求信息。。。。。。。。。。");
}
}
2.3 用户非法访问拦截
目标:
只有当用户登录之后,才能访问其他的资源信息,非法访问直接跳转至登录页面。
- 过滤器2
package com.liu.filter;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* @author lms
* @date 2021-09-25 - 17:25
*
* 过滤器实现的登录拦截:
*
* 模拟登录拦截器:用户未登录,禁止访问指定的资源
* 非法访问拦截:
* 拦截的资源: 拦截所有的资源 /*
* 需要放行的资源:
* 1.指定页面进行放行(无需登录就可以访问的页面,例如:登录页面,注册页面等)
* 2.静态资源需要放行(比如:images,js,css文件)
* 3.指定操作,放行(无需登录即可执行的操作,例如:登录操作,注册操作)
* 4.如果当前是登录状态,放行(通过判断session中的用户信息是否为空)
*
* 如果上面的状态都不执行,就拦截让其重定向到登录页面
*/
//拦截所有的请求路径
@WebFilter("/*")
public class LoginFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
// 在这里设置需要被放行的路径信息(即不同登录也可以访问的资源/页面)
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
// 基于http请求
HttpServletRequest request = (HttpServletRequest) servletRequest;
HttpServletResponse response = (HttpServletResponse) servletResponse;
// 获取用户访问的路径
String uri = request.getRequestURI();
System.out.println("uri = " + uri);
// 1.指定页面进行放行(无需登录就可以访问的页面,例如:登录页面,注册页面等)
if (uri.contains("/login.jsp")){
filterChain.doFilter(request, response);
return;
}
// 2.静态资源需要放行(比如:images,js,css文件)
if (uri.contains("/js") || uri.contains("/css") || uri.contains("/images")){
filterChain.doFilter(request, response);
return;
}
// 3.指定操作,放行(无需登录即可执行的操作,例如:登录操作,注册操作)
if (uri.contains("/login")){
filterChain.doFilter(request, response);
return;
}
// 4.是登录状态,放行(判断session中的用户信息是否为空)
// 从session中获取保存的域对象信息
String user = (String) request.getSession().getAttribute("user");
// 判断user对象是否为空,不为空,这说明已经是登录状态,则放行
if (user != null){
filterChain.doFilter(request, response);
return;
}
// 当用户未登录时,所有的非法访问拦截请求都会被重定向跳转到登录页面,登录之后才能访问其他资源
response.sendRedirect("login.jsp");
}
@Override
public void destroy() {
}
}
- 控制器
package com.liu.servlet;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* @author lms
* @date 2021-09-25 - 17:30
* 处理用户请求
*/
@WebServlet("/login")
public class LoginServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("用户登录中.............");
String name = req.getParameter("name");
System.out.println("name = " + name);
// 此时在登录页面,如果登录的用户是admin,则表示登录成功,重定向至登录成功的页面,否则登陆失败,请求转发到登录页面
if ("admin".equals(name)){
req.getSession().setAttribute("user", name);
// 重定向到首页
resp.sendRedirect("index.jsp");
}else {
// 转发到登录页面
req.getRequestDispatcher("login.jsp").forward(req, resp);
}
}
}
- index.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>首页</title>
</head>
<body>
<h3>${user}, 登录成功</h3>
</body>
</html>
- login.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>登录</title>
<script type="text/javascript" src="js/jquery-3.4.1.js"></script>
<script type="text/javascript">
// 判断用户名是否为空
function login() {
var name = $("#name").val();
console.log(name);
if (name != null && name.trim() != ''){
// 用户名不为空,则进行表单提交,提交到指定的路径上
$("#formData").submit();
}
}
</script>
</head>
<body>
<form action="login" id="formData">
用户名: <input type="text" name="name" id="name"><br>
<button type="button" onclick="login()">登录</button>
</form>
</body>
</html>
监听器 (Listener)
1. 介绍
- Web 监听器是Servlet中一种的特殊的类,能帮助开发者监听web中的特定事件,比如
ServletContext,HttpSession,ServletRequest
的创建和销毁;变量的创建、销毁和修改等。可以在某些动作前后增加处理,实现监控。例如可以用来统计在线人数等
。
2. 实现
- 常用的监听器(监听生命周期)
ServletRequestListener
HttpSessionListener
ServletContextListener
2.1 解析
- 监听器
package com.liu.listener;
import javax.servlet.annotation.WebListener;
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;
/**
* @author lms
* @date 2021-09-25 - 16:27
* 开启监听器,并设置监听的对象
*/
@WebListener
public class Listener01 implements HttpSessionListener {
@Override
public void sessionCreated(HttpSessionEvent httpSessionEvent) {
System.out.println("监听器被创建了。。。。。。。。。");
}
@Override
public void sessionDestroyed(HttpSessionEvent httpSessionEvent) {
System.out.println("监听器被销毁了。。。。。。。。。");
}
}
- 控制器
package com.liu.servlet;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
/**
* @author lms
* @date 2021-09-25 - 16:29
*/
//创建session
@WebServlet("/ser01")
public class Servlet01 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("servlet01 被调用了......");
// 创建session
HttpSession session = req.getSession();
}
}
package com.liu.servlet;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* @author lms
* @date 2021-09-25 - 16:31
*/
//销毁session
@WebServlet("/ser02")
public class Servlet02 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("servlet02 被调用了......");
// 将session设置为过期
req.getSession().invalidate();
}
}
2.2 在线人数统计
- 监听器
package com.liu.listener;
import javax.servlet.annotation.WebListener;
import javax.servlet.http.HttpSessionEvent;
import javax.servlet.http.HttpSessionListener;
/**
* @author lms
* @date 2021-09-25 - 16:48
* 实现在线人数的统计
*/
@WebListener
public class OnlineListener implements HttpSessionListener {
// 记录在线的人数统计
private Integer onlineNum = 0;
// 通过判断session的创建和销毁来判断人数登录和退出的统计
// 登录
@Override
public void sessionCreated(HttpSessionEvent httpSessionEvent) {
onlineNum++;
// 为了让别的地方能够获取到onlineNum对象,将其设置在session域对象中
// session的作用域比较小,得让所有的浏览器都能看到互相的改变,所以设置在更大的作用域Context中
// HttpSession session = httpSessionEvent.getSession();
// session.setAttribute("onlineNum", onlineNum);
httpSessionEvent.getSession().getServletContext().setAttribute("onlineNum", onlineNum);
}
// 退出
@Override
public void sessionDestroyed(HttpSessionEvent httpSessionEvent) {
onlineNum--;
// 为了让别的地方能够获取到onlineNum对象,将其设置在session域对象中
// HttpSession session = httpSessionEvent.getSession();
// session.setAttribute("onlineNum", onlineNum);
// 理由同上
httpSessionEvent.getSession().getServletContext().setAttribute("onlineNum", onlineNum);
}
}
- 控制器
package com.liu.servlet;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
/**
* @author lms
* @date 2021-09-25 - 16:52
*/
@WebServlet("/online")
public class OnlineServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 设置字符编码格式
resp.setContentType("text/html;charset=UTF-8");
// 通过session获取当前在线的人数
HttpSession session = req.getSession();
// 退出的人数
String key = req.getParameter("key");
if (key != null && "logout".equalsIgnoreCase(key)){
session.invalidate();
return;
}
// 登录
Integer onlineNum = (Integer) session.getServletContext().getAttribute("onlineNum");
// 通过响应流字符流将在线人数输出到页面显示
// 如果online请求携带有参数,则表示退出的操作,没有携带为登录操作
resp.getWriter().write("<h3>当前在线的人数为: " + onlineNum + "</h3><br>" +
"<a href='online?key=logout'>退出</a>");
}
}
拦截器 (Interceptor)
1. 介绍
- SpringMVC中的Interceptor拦截器也是相当重要和相当有用的,它的
主要作用是拦截用户的请求并进行相应的处理
。比如通过它来进行权限验证,或者是来判断用户是否登陆等操作。原理:和过滤器原理相同
- 对于SpringMVC拦截器的定义方式有两种:
实现接口
:org.springframework.web.servlet.HandlerInterceptor
继承适配器
:org.springframework.web.servlet.handler.HandlerInterceptorAdapter
其间接实现了HandlerInterceptor接口
- HandlerInterceptor
public interface HandlerInterceptor {
default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// true: 表示允许目标方法(handler)(类似于过滤器的放行操作)
// false:禁止目标方法的执行
return true;
}
// 目标方法(请求)执行之后,还没有生成视图之前,执行该方法
default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception {
}
// 目标方法(请求)执行之后,生成视图之后,执行该方法
default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception {
}
}
- HandlerInterceptorAdapter(间接实现了HandlerInterceptor接口)
- public abstract class HandlerInterceptorAdapter implements AsyncHandlerInterceptor
- public interface AsyncHandlerInterceptor extends HandlerInterceptor
2. 拦截器的实现
2.1 实现HandlerInterceptor
接口
package com.liu.springmvc.interceptor;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* @author lms
* @date 2021-09-25 - 19:36
* 实现拦截器HandlerInterceptor的接口,并重写3个方法
*/
public class MyInterceptor01 implements HandlerInterceptor {
/**
* 在目标(handler)方法执行前 执行
* @param request
* @param response
* @param handler
* @return true:执行handler(目标)方法,也就是放行
* false:禁止目标方法的执行
* @throws Exception
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("MyInterceptor01 ==> 目标方法之前执行了 ---> preHandle........");
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("目标方法执行之后,视图生成之前,执行了 ---> postHandle........");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("目标方法执行之后,视图生成之后,执行了 ---> afterCompletion........");
}
}
web.xml
- 配置拦截器
- 方式1
<!-- 配置拦截器,方式一:拦截所有的请求,表示哪些路径下的请求会被拦截-->
<mvc:interceptors>
<!--
使用bean标签定义一个Interceptor拦截器
如果直接定义在mvc:interceptors标签中,表示项目下的所有的请求都会被拦截器进行拦截,等价于 /*
-->
<bean class="com.liu.springmvc.interceptor.MyInterceptor01"/>
</mvc:interceptors>
- 方式2
<!-- 配置拦截器:方式二:拦截部分请求,放行部分请求 -->
<mvc:interceptors>
<!--
在标签<mvc:interceptor>中,可以自定义需要被拦截和不被拦截的请求
可以定义多个拦截器
如果有多个拦截器,则会根据配置的先后顺序来执行
-->
<mvc:interceptor>
<!-- 通过<mvc:mapping标签配置需要被拦截的资源,支持通配符,可以配置多个 -->
<!-- path="/**" 表示拦截所有的请求 -->
<mvc:mapping path="/**"/>
<!-- 通过<mvc:exclude-mapping标签配置不需要拦截的资源,支持通配符,可配置多个 -->
<!-- path="/model/*" 表示model下的所有请求 -->
<mvc:exclude-mapping path="/model/*"/>
<bean class="com.liu.springmvc.interceptor.MyInterceptor01"/>
</mvc:interceptor>
</mvc:interceptors>
- 测试类1
package com.liu.springmvc.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
/**
* @author lms
* @date 2021-09-25 - 9:23
*/
@Controller
public class HelloController {
@RequestMapping("/hello")
public ModelAndView hello(){
// 模拟异常(全局异常处理)
// int i = 1 / 0;
// 抛出自定义的异常信息
// if (1 == 1){
// // 参数异常
throw new ParamsException();
// // 业务异常
// throw new BusinessException();
// }
System.out.println("hello请求被拦截了.........");
ModelAndView modelAndView = new ModelAndView();
// 设置数据信息
modelAndView.addObject("name", "zhangsan");
// 设置跳转的视图
modelAndView.setViewName("hello");
return modelAndView;
}
}
- 测试类2
package com.liu.springmvc.controller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import java.util.Map;
/**
* @author lms
* @date 2021-09-25 - 11:40
*
* 请求域对象的设置方式(五种):
*
*/
@Controller
@RequestMapping("model")
public class ModelController {
/**
* ModelAndView设置域对象
* @return
*/
@RequestMapping("/test01")
public ModelAndView test01(){
ModelAndView modelAndView = new ModelAndView();
// 设置请求域对象
modelAndView.addObject("name", "hello model-1");
// 设置视图
modelAndView.setViewName("hello");
return modelAndView;
}
/**
* Model设置域对象
* @return
*/
@RequestMapping("/test02")
public String test02(Model model){
// 设置请求域对象
model.addAttribute("name", "hello model-2");
// 设置视图
return "hello";
}
/**
* ModelMap设置域对象
* @return
*/
@RequestMapping("/test03")
public String test03(ModelMap model){
// 设置请求域对象
model.addAttribute("name", "hello model-3");
// 设置视图
return "hello";
}
/**
* ModelMap设置域对象
* @return
*/
@RequestMapping("/test04")
public String test04(Map model){
// 设置请求域对象
model.put("name", "hello model-4");
// 设置视图
return "hello";
}
/**
* ModelMap设置域对象
* @return
*/
@RequestMapping("/test05")
public String test05(HttpServletRequest request){
// 设置请求域对象
request.setAttribute("name", "hello model-5");
// 设置视图
return "hello";
}
}
2.2 继承HandlerInterceptorAdapter
package com.liu.springmvc.interceptor;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* @author lms
* @date 2021-09-25 - 20:33
*/
public class MyInterceptor02 extends HandlerInterceptorAdapter {
/**
* 在目标方法(handler)执行前 执行
*
* @param request
* @param response
* @param handler
* @return true:执行handler(目标)方法,也就是放行
* false:禁止目标方法的执行
* @throws Exception
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("MyInterceptor02 ==> 目标方法执行之前执行了 ---> preHandle........");
return true;
}
}
-
配置拦截器
-
方式1
<mvc:interceptors>
<mvc:interceptor>
<!-- 被拦截的请求 -->
<mvc:mapping path="/**"/>
<!-- 拦截器不拦截的请求(放行的请求) -->
<mvc:exclude-mapping path="/model/test01"/>
<mvc:exclude-mapping path="/model/test02"/>
<!-- 使用的拦截器 -->
<bean class="com.liu.springmvc.interceptor.MyInterceptor02"/>
</mvc:interceptor>
</mvc:interceptors>
- 方式2
<mvc:interceptors>
<!--
拦截所有的请求,拦截器链(多个拦截器构成)按照先后的顺序执行拦截操作
先配置的拦截器的preHandler()方法先执行
先配置的拦截器的postHandler(),afterCompletion()方法后执行
-->
<mvc:interceptor>
<mvc:mapping path="/**"/>
<bean class="com.liu.springmvc.interceptor.MyInterceptor01"/>
</mvc:interceptor>
<mvc:interceptor>
<mvc:mapping path="/**"/>
<!-- MyInterceptor02 在下面 -->
<bean class="com.liu.springmvc.interceptor.MyInterceptor02"/>
</mvc:interceptor>
</mvc:interceptors>
- 测试类同上
3. 拦截器的应用:非法请求拦截
- 使用拦截器完成用户是否登录的请求验证功能
- 用户控制器
- 注意:当前的模拟登陆是直接通过在地址栏中输入:
http://localhost:8080/项目名/userInfo/login
package com.liu.springmvc.controller;
import com.liu.springmvc.bean.User;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpSession;
/**
* @author lms
* @date 2021-09-25 - 21:26
* 非法请求拦截的模拟实现
* login:登录不拦截(不需要登录即可访问)
* add: 未登录就拦截(访问前需要登录)
* update:未登录就拦截(访问前需要登录)
*/
@Controller
@RequestMapping("userInfo")
public class LoginController {
@RequestMapping("login")
public ModelAndView login(HttpSession session) {
ModelAndView modelAndView = new ModelAndView();
User user = new User();
user.setId(1);
user.setName("admin");
user.setAge(20);
// 将当前登录用户的信息设置在session中
session.setAttribute("user", user);
modelAndView.setViewName("success");
return modelAndView;
}
@RequestMapping("add")
public ModelAndView add() {
ModelAndView modelAndView = new ModelAndView();
modelAndView.setViewName("success");
return modelAndView;
}
@RequestMapping("update")
public ModelAndView update() {
ModelAndView modelAndView = new ModelAndView();
modelAndView.setViewName("success");
return modelAndView;
}
}
- success.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h3>登录成功!</h3>
</body>
</html>
- 登录界面(未登录跳转至当前页面)
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h3>登录页面!</h3>
</body>
</html>
- 登陆拦截器
package com.liu.springmvc.interceptor;
import com.liu.springmvc.bean.User;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* @author lms
* @date 2021-09-25 - 21:32
*/
public class LoginInterceptor extends HandlerInterceptorAdapter {
/**
* 指定目标方法之前执行该方法
*
* @param request
* @param response
* @param handler
* @return
* @throws Exception
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 获取session中的user信息
User user = (User) request.getSession().getAttribute("user");
// 未登录
if (user == null) {
// 重定向到登录页面
response.sendRedirect(request.getContextPath() + "/" + "login.jsp");
// 禁止执行请求
return false;
}
// 否则放行,执行目标方法
return true;
}
}
- 配置拦截器
<!-- 非法访问拦截 拦截器配置 -->
<mvc:interceptors>
<mvc:interceptor>
<!-- 拦截所有的请求 -->
<mvc:mapping path="/**"/>
<!-- 不拦截登录请求 -->
<mvc:exclude-mapping path="/userInfo/login"/>
<bean class="com.liu.springmvc.interceptor.LoginInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
全局异常统一处理(Exception)
1. 介绍
- 在 JavaEE项目的开发中,不管是对底层的数据库操作过程,还是业务层的处理过程,还是控制层的处理过程,都不可避免会遇到各种可预知的、不可预知的异常需要处理。每个过程都单独处理异常,系统的代码耦合度高,工作量大且不好统一,维护的工作量也很大。
- SpringMVC对于异常处理这块提供了支持,通过 SpringMVC提供的全局异常处理机制,能够将所有类型的异常处理从各处理过程解耦出来,既保证了相关处理过程的功能较单一,也实现了异常信息的统一处理和维护。
- 全局异常实现方式 Spring MVC处理异常有3种方式:
使用SpringMVC提供的简单异常处理器SimpleMappingExceptionResolver
- (需要在SpringMVC配置文件中进行配置,处理的是视图的异常,对于返回的是json数据的异常就会有缺陷,所以不推荐使用)实现Spring的异常处理接口 HandlerExceptionResolver自定义自己的异常处理器(推荐使用)
使用@ExceptionHandler注解实现异常处理
2. 全局异常处理实现
2.1 方式一:web.xml中配置SimpleMappingExceptionResolver
<!--
全局异常统一处理(3种)
1. 使用SpringMVC中提供的简单异常处理器 SimpleMappingExceptionResolver
-->
<!-- 方式1: 使用SpringMVC中提供的简单异常处理器 SimpleMappingExceptionResolver-->
<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
<!-- 页面在转发时出现异常,将跳转到error错误页面 -->
<property name="defaultErrorView" value="error"/>
<!-- 属性exceptionAttribute中包含了出现异常的信息,设置别名为ex -->
<property name="exceptionAttribute" value="ex"/>
<!-- 设置自定义异常和该异常对应的处理页面-->
<property name="exceptionMappings">
<props>
<!-- key表示具体的自定义异常的类,标签为出现该自定义异常要跳转到的处理页面 -->
<prop key="com.liu.springmvc.exception.ParamsException">params_error</prop>
<prop key="com.liu.springmvc.exception.BusinessException">business_error</prop>
</props>
</property>
</bean>
- 自定义异常处理类
package com.liu.springmvc.exception;
/**
* @author lms
* @date 2021-09-26 - 10:34
*/
//自定义参数异常类
public class ParamsException extends RuntimeException {
private Integer code = 300;
private String msg = "参数异常";
public ParamsException() {
super("参数异常");
}
public ParamsException(Integer code) {
super("参数异常");
this.code = code;
}
public ParamsException(String msg) {
super(msg);
this.msg = msg;
}
public ParamsException(Integer code, String msg) {
super(msg);
this.code = code;
}
}
package com.liu.springmvc.exception;
/**
* @author lms
* @date 2021-09-26 - 10:39
*/
public class BusinessException extends RuntimeException {
private Integer code = 400;
private String msg = "业务异常";
public BusinessException() {
super("业务异常");
}
public BusinessException(String msg) {
super(msg);
this.msg = msg;
}
public BusinessException(Integer code) {
super("业务异常");
this.code = code;
}
public Integer getCode() {
return code;
}
public void setCode(Integer code) {
this.code = code;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
}
- params_error.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>参数异常</title>
</head>
<body>
<h3>异常信息: ${ex}</h3>
</body>
</html>
- business_error.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>业务异常</title>
</head>
<body>
<h3>异常信息: ${ex}</h3>
</body>
</html>
- 控制器
package com.liu.springmvc.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
/**
* @author lms
* @date 2021-09-25 - 9:23
*/
@Controller
public class HelloController {
@RequestMapping("/hello")
public ModelAndView hello(){
// 模拟异常(全局异常处理)
// int i = 1 / 0;
// 抛出自定义的异常信息
// if (1 == 1){
// // 参数异常
throw new ParamsException();
// // 业务异常
// throw new BusinessException();
// }
System.out.println("hello请求被拦截了.........");
ModelAndView modelAndView = new ModelAndView();
// 设置数据信息
modelAndView.addObject("name", "zhangsan");
// 设置跳转的视图
modelAndView.setViewName("hello");
return modelAndView;
}
}
- 使用
SimpleMappingExceptionResolver
进行异常处理,具有集成简单,有良好的的拓展性,对已有的代码没有入侵性等优点,但该方法仅能获取到异常信息,若出现异常时,对需要获取除了异常之外的数据的情况不适用。
2.2 实现接口 HandlerExceptionResolver
- 配置全局的异常处理类(需要放置在包下)
package com.liu.springmvc;
import com.liu.springmvc.exception.BusinessException;
import com.liu.springmvc.exception.ParamsException;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* @author lms
* @date 2021-09-26 - 11:13
*/
// 为了让当前的全局异常处理类生效,需要将其让IOC容器进行管理
// 方式2: 实现Spring的异常处理接口 HandlerExceptionResolver自定义自己的异常处理器(推荐使用)
// 直接使用注解不生效,不知道为啥,所以需要在SpringMVC的配置文件中进行配置全局异常处理类
//@Component
//@Component("handlerExceptionResolver")
public class GlobalExceptionResolver implements HandlerExceptionResolver {
@Override
public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
// 设置默认的异常处理页面
ModelAndView mv = new ModelAndView("error");
mv.addObject("ex", "默认错误异常信息!");
// 判断是否是自定义异常信息
// 参数异常,跳转至参数异常页面
if (ex instanceof ParamsException) {
mv.setViewName("params_error");
// 获取具体的异常信息
ParamsException e = (ParamsException) ex;
mv.addObject("ex", e.getMessage());
}
// 业务异常,跳转至业务异常页面
if (ex instanceof BusinessException) {
mv.setViewName("business_error");
// 获取具体的异常信息
BusinessException e = (BusinessException) ex;
mv.addObject("ex", e.getMessage());
}
return mv;
}
}
- 配置文件
<!-- 方式2: 实现Spring的异常处理接口 HandlerExceptionResolver自定义自己的异常处理器(推荐使用) -->
<bean id="handlerExceptionResolver" class="com.liu.springmvc.GlobalExceptionResolver"/>
2.3 未捕获异常处理
- 在web.xml中进行配置
<!-- 未捕获的异常信息处理 -->
<error-page>
<exception-type>java.lang.Throwable</exception-type>
<location>/500.jsp</location>
</error-page>
<error-page>
<error-code>500</error-code>
<location>/500.jsp</location>
</error-page>
<error-page>
<error-code>404</error-code>
<location>/404.jsp</location>
</error-page>