Java 过滤器Filter 监听器Listener 拦截器Interceptor 异常处理Exception 详解

过滤器 (Filter)

1. 主要内容

Java 过滤器Filter 监听器Listener 拦截器Interceptor 异常处理Exception 详解

2. 过滤器

2.1 介绍

  • Filter即为过滤,用于在Servlet之外对Request或者Response进行修改。它主要用于对用户请求进行预处理,也可以对HttpServletResponse进行后处理。使用Fiter的完整流程: Filter对用户请求进行预处理,接着将请求交给Servlet进行处理并生成响应,最后Filter再对服务器响应进行后处理。在一个web应用中,可以开发编写多个Filter,这些Filter组合起来称之为一个Fiter链。
    Java 过滤器Filter 监听器Listener 拦截器Interceptor 异常处理Exception 详解
    Java 过滤器Filter 监听器Listener 拦截器Interceptor 异常处理Exception 详解

若是一个过滤器链:先配置先执行(请求时的执行顺序);响应时:以相反的顺序执行。

  • 在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>
上一篇:javax.persistence.Query和javax.persistence.EntityManager的分页查询使用


下一篇:javax.validation分组校验