Interceptor的基本介绍和使用
简介
拦截器是可以动态的拦截action的调用,它提供了一个接口可以使开发者在一个Action执行的前后添加自己的逻辑。在AOP中,拦截器用于在某个方法或者字段被访问之前,进行拦截然后再之前或者之后加入某些操作。目前,我们需要掌握的主要是Spring的拦截器。
原理
拦截器:它采用了java动态代理和反射的机制来实现。
实现自定义拦截器步骤。
1.创建一个spring Web项目。
2.创建一个实现HandlerInterceptor接口的自定义拦截器实体类。
3.创建一个实现WebMvcConfigurer接口的配置类。将自定义拦截器添加到容器中,并配置拦截的路径。
1 开发工具 :IntelliJ IDEA
当前spring boot备受欢迎的快速开发框架,我这里就采用spring boo框架搭建web项目 。
2 创建自定义拦截器
创建一个实现HandlerInterceptor接口的拦截器类MyInterceptor。
HandlerInterceptor 接口中定义了三个接口,分别是:preHandle(在调取action之前调用),postHandle(在action调用之后,返回视图对象之前调用)和afterCompletion(在action调用完成之后,返回视图之后调用,一般用于清理资源)如下图所示。
在jdk8之前,接口中是没有方法体的,如果实现拦截器接口的话,要同时重写三个方法。很多情况下,我们实际中用不到三个拦截方法。所以jdk8之前我们自定义拦截器一般时继承一个抽象类适配器HandlerInterceptorAdapter。这时候想使用哪个方法,就重写那方法即可。而jdk8之后,接口中可以出现了方法体。所以现在我们可以直接实现接口,使用哪个方法直接重写即可。我这是都重写了。
package com.wyz.study.interceptor;
import org.springframework.lang.Nullable;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.View;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* @author wyz
* @version 1.0
* @ClassName: MyInterceptor
* @Description: 自定义拦截器
* @date 2019/3/12 12:03
**/
@Component
public class MyInterceptor implements HandlerInterceptor {
/**
* 在业务调用action对象之前被调用
* @param request
* @param response
* @param handler
* @return
* @throws Exception
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("preHandle........");
//从session中获取用户信息
String username = (String) request.getSession().getAttribute("userName");
//判断userName是否为null
if(null == username){
System.out.println("用户未登录,请先登录!!!");
// 跳转到登录页面
request.getRequestDispatcher("/WEB-INF/jsp/login.jsp").forward(request, response);
return false;
}
return true;
}
/**
* 在请求完成之后,呈现视图对象之前被调用
* @param request
* @param response
* @param handler
* @param modelAndView
* @throws Exception
*/
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception {
System.out.println("postHandle-----------执行了!!!!!");
String viewName = modelAndView.getViewName();
View view = modelAndView.getView();
System.out.println("viewName==="+viewName);
System.out.println("view==="+view);
}
/**
* 在请求请求结束之后被调用,可以使用该方法清理资源
* @param request
* @param response
* @param handler
* @param ex
* @throws Exception
*/
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception {
System.out.println("afterCompletion#############执行了。。。。。。");
}
}
3.创建配置实体类
创建一个将自定义拦截器加入容器中的配置实体类。该配置类要实现WebMvcConfigurer接口。重写addInterceptors方法。
package com.wyz.study.interceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/**
* @author wyz
* @version 1.0
* @ClassName: MySpringMVCConfig
* @Description: 这是一个配置类,将自定义的拦截器注册到容器中。
* @date 2019/3/13 13:02
**/
@Configuration
public class MySpringMVCConfigurer implements WebMvcConfigurer {
@Autowired
private MyInterceptor myInterceptor;
/**
* 将拦截器注册到容器中
* @param registry
*/
@Override
public void addInterceptors(InterceptorRegistry registry) {
//设置拦截路径
registry.addInterceptor(myInterceptor).addPathPatterns("/userInfo");
}
}
验证自定义拦截器是否成功
1.创建页面
接下来创建几个简单的jsp页面。login.jsp(简单的登录页面),success.jsp(登录成功的页面),userInfo.jsp(用户信息的页面) 。页面用于演示。所以很简陋。
login.jsp
<%--
Created by IntelliJ IDEA.
User: wyz
Date: 2019/3/12
Time: 11:57
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>登录页面</title>
</head>
<body>
<form action="login" >
用户名:<input type="text" name="userName">
密码:<input type="password" name="passWord">
<button >提交</button>
</form>
</body>
</html>
success.jsp
<%--
Created by IntelliJ IDEA.
User: wyz
Date: 2019/3/12
Time: 11:57
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>成功页面</title>
</head>
<body>
<h2>欢迎${userName},您已经登录成功!!!</h2>
<<a href="userInfo">点击获取用户信息</a>
</body>
</html>
success.jsp
<%--
Created by IntelliJ IDEA.
User: wyz
Date: 2019/3/13
Time: 13:15
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>用户页面</title>
</head>
<body>
<h2>用户名:${userName} 用户年龄:${age}(当用户名后面有名字出现,说明拦截器未设置成功,反之说明成功)</h2>
</body>
</html>
2.创建控制层
LoginController.java
package com.wyz.study.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import javax.servlet.http.HttpServletRequest;
/**
* @author wyz
* @version 1.0
* @ClassName: LoginController
* @Description: 用户登录控制层
* @date 2019/3/12 12:04
**/
@Controller
public class LoginController {
/**
* 跳转登录页面
* @return
*/
@RequestMapping("/toLogin")
public String toLogin(){
return "login";
}
/**
* 登录操作
* @param request
* @param userName
* @param passWord
* @return
*/
@RequestMapping("/login")
public String login(HttpServletRequest request,String userName, String passWord){
request.getSession().setAttribute("userName",userName);
request.getSession().setAttribute("age",20);
return "success";
}
}
UserInfoController.java
package com.wyz.study.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
/**
* @author wyz
* @version 1.0
* @ClassName: UserInfoController
* @Description: 这是用户的控制层
* @date 2019/3/13 13:18
**/
@Controller
public class UserInfoController {
@RequestMapping("/userInfo")
public String userInfo(){
System.out.println("返回pay.jsp页面!!!");
return "userInfo";
}
}
3.验证
启动项目,在浏览器直接访问http://127.0.0.1:8080/userInfo,结果返回的是登录页面。
然后让我们看看代码和控制台打印的日志信息:
接口中输出的语句没有输出到控制台,输出的是自定义拦截器的preHandle拦截方法中的信息,由于用户没有登录,所以在调取用户信息接口的时候被拦截掉了。然后重定向到登录页面。这也证明了preHandle方法是在调取action之前执行的。
然后我们就登录一下,在访问连接看看。(登录里的信息随便填写。)
看到下面的页面,说明登录成功了。
,然后在访问userInfo(或者点击超链接)。查看控制台输出。
根据控制台的输入,可以发现执行顺序:preHandle() --> userInfo() --> preHandle() --> afterCompletion()。之前说了,preHandle()是在视图对象呈现前执行,而afterCompletion()是在视图对象呈现之后执行,是不是这样,只能看源码。。。。
从截图中可以得出结论,拦截器中的postHandle方法是在actin方法执行完后,才执行。是不是视图对象呈现前后需要看源码:
进入DispatcherServlet.doDispatch()中。
追进解析的方法中:
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);
}
}
// 判断程序是否返回要呈现的视图
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) {
//这是执行拦截器的afterCompletion方法。自定义的也是在这里执行的
mappedHandler.triggerAfterCompletion(request, response, null);
}
}
render()
triggerAfterCompletion()
然后代码就会进入自定义的AfterCompletion()方法中。所以从源码上可以看出,postHandle()是在返回mv之后呈现视图对象之前执行。而AfterCompletion()实在视图呈现之后执行。
第一次写博客,写的不好请见谅,写的不好的地方请指出。谢谢