当前位置:   article > 正文

11、拦截器_拦截器返回数据

拦截器返回数据


【尚硅谷】SSM框架全套教程-讲师:杨博超

保持热爱、奔赴山河

11、拦截器

11.1、创建拦截器

SpringMVC中的拦截器用于拦截控制器方法的执行

SpringMVC中的拦截器需要实现HandlerInterceptor

SpringMVC的拦截器必须在SpringMVC的配置文件中进行配置

public class FirstIntercetor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("FirstInterceptor--->preHandle:控制器方法执行之前执行");
        return true; // 返回值作用:false(拦截)true(放行)
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("FirstInterceptor--->postHandle:控制器方法执行之后执行");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("FirstInterceptor--->afterCompletion:渲染视图之后执行的");
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

11.2、拦截器的配置

1 方式一

<!--配置拦截器-->
<mvc:interceptors>
    <!-- 拦截器引用 -->
    <bean class="pers.tianyu.interceptor.FirstInterceptor"></bean>
</mvc:interceptors>
  • 1
  • 2
  • 3
  • 4
  • 5

2 方式二

<!-- 自定义拦截器 -->
<bean id="firstInterceptor" class="pers.tianyu.interceptor.FirstInterceptor"></bean>
<!--配置拦截器-->
<mvc:interceptors>
    <!-- 拦截器引用 -->
    <ref bean="firstInterceptor"></ref>
</mvc:interceptors>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

3 方式三

<!-- 更精确的自定义拦截器 -->
<bean id="firstInterceptor" class="pers.tianyu.interceptor.FirstInterceptor"></bean>
<!--配置拦截器-->
<mvc:interceptors>
    <!-- 以上两种配置方式都是对DispatcherServlet处理的所有的请求进行拦截 -->
    <mvc:interceptor>
        <!-- 配置需要拦截的请求路径,/**:拦截所有,/*:拦截一层目录结构请求 -->
        <mvc:mapping path="/**"/>
        <!-- 配置不需要拦截的请求路径 -->
        <mvc:exclude-mapping path="/testRequestEntity"/>
        <!-- 拦截器引用 -->
        <ref bean="firstInterceptor"></ref>
    </mvc:interceptor>
</mvc:interceptors>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

11.3、拦截器的执行顺序

1 拦截器的三个方法执行顺序

preHandle();

控制器方法执行之前执行preHandle(),其boolean类型的返回值表示拦截(false)或放行(true)。

postHandle();

控制器方法执行之后执行postHandle()。

afterCompletion();

在控制器方法执行之后,且渲染视图完毕之后执行afterCompletion()。

2 在源码中的执行顺序

类位置:org.springframework.web.servlet.DispatcherServlet

public class DispatcherServlet extends FrameworkServlet {
	protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
		HttpServletRequest processedRequest = request;
		HandlerExecutionChain mappedHandler = null;
		boolean multipartRequestParsed = false;
	
		WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
	
		try {
			ModelAndView mv = null;
			Exception dispatchException = null;
	
			try {
				processedRequest = checkMultipart(request);
				multipartRequestParsed = (processedRequest != request);
	
				// Determine handler for the current request.
				mappedHandler = getHandler(processedRequest);
				if (mappedHandler == null) {
					noHandlerFound(processedRequest, response);
					return;
				}
	
				// Determine handler adapter for the current request.
				HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
	
				// Process last-modified header, if supported by the handler.
				String method = request.getMethod();
				boolean isGet = HttpMethod.GET.matches(method);
				if (isGet || HttpMethod.HEAD.matches(method)) {
					long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
					if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
						return;
					}
				}
				// mappedHandler 处理器执行链
				// 调用拦截器中 preHandle 方法
				if (!mappedHandler.applyPreHandle(processedRequest, response)) {
					return;
				}
	
				// Actually invoke the handler.(调用控制器方法)
				mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
	
				if (asyncManager.isConcurrentHandlingStarted()) {
					return;
				}
	
				applyDefaultViewName(processedRequest, mv);
				// 调用拦截器中 postHandle 方法
				mappedHandler.applyPostHandle(processedRequest, response, mv);
			}
			catch (Exception ex) {
				dispatchException = ex;
			}
			catch (Throwable err) {
				// As of 4.3, we're processing Errors thrown from handler methods as well,
				// making them available for @ExceptionHandler methods and other scenarios.
				dispatchException = new NestedServletException("Handler dispatch failed", err);
			}
			// 处理转发结果
			processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
		}
		catch (Exception ex) {
			triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
		}
		catch (Throwable err) {
			triggerAfterCompletion(processedRequest, response, mappedHandler,
					new NestedServletException("Handler processing failed", err));
		}
		finally {
			if (asyncManager.isConcurrentHandlingStarted()) {
				// Instead of postHandle and afterCompletion
				if (mappedHandler != null) {
					mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
				}
			}
			else {
				// Clean up any resources used by a multipart request.
				if (multipartRequestParsed) {
					cleanupMultipart(processedRequest);
				}
			}
		}
	}

	private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
			@Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv,
			@Nullable Exception exception) throws Exception {

		boolean errorView = false;

		if (exception != null) {
			if (exception instanceof ModelAndViewDefiningException) {
				logger.debug("ModelAndViewDefiningException encountered", exception);
				mv = ((ModelAndViewDefiningException) exception).getModelAndView();
			}
			else {
				Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
				mv = processHandlerException(request, response, handler, exception);
				errorView = (mv != null);
			}
		}

		// Did the handler return a view to render?
		if (mv != null && !mv.wasCleared()) {
			// 渲染视图
			render(mv, request, response);
			if (errorView) {
				WebUtils.clearErrorRequestAttributes(request);
			}
		}
		else {
			if (logger.isTraceEnabled()) {
				logger.trace("No view rendering, null ModelAndView returned.");
			}
		}

		if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
			// Concurrent handling started during a forward
			return;
		}

		if (mappedHandler != null) {
			// Exception (if any) is already handled..
			// mappedHandler 处理器执行链 (渲染视图完毕后,调用拦截器afterCompletion方法)
			mappedHandler.triggerAfterCompletion(request, response, null);
		}
	}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130

11.4、多个拦截器的执行顺序

1 若每个拦截器的preHandle()都返回true

与SpringMVC配置文件的配置顺序有关。

preHandle方法按照配置顺序执行。

postHandle方法按照配置反序执行。

afterCompletion方法在postHandle方法之后按照配置反序执行。

2 源码解析

类路径:org.springframework.web.servlet.HandlerExecutionChain

mappedHandler.applyPreHandle(processedRequest, response);方法

/**
 * Apply preHandle methods of registered interceptors.
 * @return {@code true} if the execution chain should proceed with the
 * next interceptor or the handler itself. Else, DispatcherServlet assumes
 * that this interceptor has already dealt with the response itself.
 */
boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
	// 对当前类中成员变量interceptorList进行正序循环
	for (int i = 0; i < this.interceptorList.size(); i++) {
		// 获取集合中每一个拦截器
		HandlerInterceptor interceptor = this.interceptorList.get(i);
		// 调用拦截器 preHandle 方法,返回true不执行,返回false执行
		if (!interceptor.preHandle(request, response, this.handler)) {
			// 执行拦截器 afterCompletion 方法(如果 preHandle 方法返回false,不会执行 postHandle 方法,而会执行 afterCompletion 方法)
			triggerAfterCompletion(request, response, null);
			return false;
		}
		this.interceptorIndex = i;
	}
	return true;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

类路径:org.springframework.web.servlet.HandlerExecutionChain

mappedHandler.applyPostHandle(processedRequest, response, mv);方法

/**
 * Apply postHandle methods of registered interceptors.
 */
void applyPostHandle(HttpServletRequest request, HttpServletResponse response, @Nullable ModelAndView mv)
		throws Exception {
	// 对当前类中成员变量interceptorList进行反序循环
	for (int i = this.interceptorList.size() - 1; i >= 0; i--) {
		// 获取拦截器集合中的每一个拦截器
		HandlerInterceptor interceptor = this.interceptorList.get(i);
		// 调用 postHandle 方法
		interceptor.postHandle(request, response, this.handler, mv);
	}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

类路径:org.springframework.web.servlet.HandlerExecutionChain

mappedHandler.triggerAfterCompletion(request, response, null);方法

/**
 * Trigger afterCompletion callbacks on the mapped HandlerInterceptors.
 * Will just invoke afterCompletion for all interceptors whose preHandle invocation
 * has successfully completed and returned true.
 */
void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, @Nullable Exception ex) {
	// 对当前类中成员变量interceptorIndex进行反序循环
	// interceptorIndex:preHandle 返回false之前拦截器的索引
	for (int i = this.interceptorIndex; i >= 0; i--) {
		// 获取拦截器集合中的每一个拦截器
		HandlerInterceptor interceptor = this.interceptorList.get(i);
		try {
			// 调用 afterCompletion 方法
			interceptor.afterCompletion(request, response, this.handler, ex);
		}
		catch (Throwable ex2) {
			logger.error("HandlerInterceptor.afterCompletion threw exception", ex2);
		}
	}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

3 若某个拦截器的preHandle()返回了false

若拦截器中,有某个拦截器的 preHandle() 方法返回false,拦截器的 preHandle() 返回false和它之前的拦截器的 preHandle() 都会执行。

preHandle() 返回false之后的拦截器的postHandle()都不执行。

拦截器的 preHandle() 返回 false 之前的拦截器的 afterCompletion() 会执行,当前拦截器的 afterCompletion() 不会执行。

除了自己配制的拦截器,SpringMVC还有一个自己默认的拦截器。

11.5、验证用户是否登录 (认证用户)

1 实现思路

  1. 一个登陆页面,需要写一个controller访问页面。
  2. 登陆页面有一提交表单的动作。需要在controller中处理。判断用户名密码是否正确。如果正确,向session中写入用户信息。返回登陆成功。
  3. 拦截用户请求,判断用户是否登陆。如果用户已经登陆。放行, 如果用户未登陆,跳转到登陆页面。

2 实现代码

  1. 编写一个登陆页面 login.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
	<head>
	    <title>登录</title>
	</head>
	<body>
	<%--在web-inf下面的所有页面或者资源,只能通过controller或者Servlet进行访问--%>
	<h1>登录页面</h1>
		<form action="${pageContext.request.contextPath}/user/login" method="post">
		    用户名:<input type="text" name="username">
		    密码:<input type="text" name="password">
		    <input type="submit" value="提交">
		</form>
	</body>
</html>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  1. 编写一个Controller处理请求
@Controller
@RequestMapping("/user")
public class LoginController {
   @RequestMapping("/main")
   public String main() {
       return "main";
   }
   @RequestMapping("/goLogin")
   public String login(){
       return "login";
   }
   @RequestMapping("/login")
   public String login(HttpSession session, String username, String password){
       if(null != username && null != password){
           if("admin".equals(username) && "pword".equals(password)){
               // 把用户的信息存在session中
               session.setAttribute("userLoginInfo",username);
           }
       }
       return "redirect:/user/main";
   }
   @RequestMapping("/goOut")
   public void goOut(HttpSession session, HttpServletResponse response) throws IOException {
       session.removeAttribute("userLoginInfo");
       response.sendRedirect("/index.jsp");
   }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  1. 编写一个登陆成功的页面 main.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
	<head>
	    <title>首页</title>
	</head>
	<body>
		<h1>首页</h1>
		<span>${password}</span>
		<span>${username}</span>
		<p>
		    <a href="${pageContext.request.contextPath}/user/goOut">注销</a>
		</p>
	</body>
</html>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  1. 在 index 页面上测试跳转!启动Tomcat 测试,未登录也可以进入主页!
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
	<head>
	  <title>index</title>
	</head>
	<body>
	<h1><a href="${pageContext.request.contextPath}/user/goLogin">登录页面</a></h1>
	<h1><a href="${pageContext.request.contextPath}/user/main">首页</a></h1>
	</body>
</html>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  1. 编写用户登录拦截器
package pers.tianyu.config;

import org.springframework.web.servlet.HandlerInterceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

public class LoginInterceptor implements HandlerInterceptor {
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        HttpSession session = request.getSession();
        //放行:判断什么情况下登录

        //登录页面也会放行
        if (request.getRequestURI().contains("goLogin")) {
            return true;
        }
        if (request.getRequestURI().contains("login")) {
            return true;
        }
        if (session.getAttribute("userLoginInfo") != null) {
            return true;
        }
        //判断什么情况下没有登录
        request.getRequestDispatcher("/WEB-INF/jsp/login.jsp").forward(request,response);
        return false;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  1. 在Springmvc的配置文件中注册拦截器
<!--关于拦截器的配置-->
<mvc:interceptors>
<mvc:interceptor>
            <mvc:mapping path="/**"/>
            <bean id="loginInterceptor" class="pers.tianyu.config.LoginInterceptor"/>
        </mvc:interceptor>
</mvc:interceptors>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  1. 再次重启Tomcat测试!

OK,测试登录拦截功能无误

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/繁依Fanyi0/article/detail/282236
推荐阅读
相关标签
  

闽ICP备14008679号