在Java的网络的应用程序中通常使用过滤器(即过滤器)来捕获HTTP请求。但它们仅为web应用保留.Spring引入了一种新的方法来实现,更通用,称为处理程序拦截器。
本文将分3部分。第一部分来讲春季处理程序拦截器的理论概念。第二部分,说一说默认的春天拦截器。最后一部分老规矩,应用实战,我们将写我们自己的处理程序拦截器。
什么是春天中的处理程序拦截器?
要了解弹簧拦截器的作用,我们需要先解释一下HTTP请求的执行链.DispatcherServlet捕获每个请求。调度员做的第一件事就是将接收到的URL和相应的控制器进行映射(控制器必须恰到好处地处理当前的请求)。但是,在到达对应的控制器之前,请求可以被拦截器处理。这些拦截器就像过滤器。只有当URL找到对应于它们的映射时才调用它们。在通过拦截器(拦截器预处理,其实也可以说前置处理)进行前置处理后,请求最终到达控制器。之后,发送请求生成视图。但是在这之前,拦截器还是有可能来再次处理它(拦截器后置处理)。只有在最后一次操作之后,视图解析器才能捕获数据并输出视图。
处理程序映射拦截器基于org.springframework.web.servlet.HandlerInterceptor接口。和之前简要描述的那样,它们可以在将其发送到控制器(方法前使用preHandle)之前或之后(方法后使用的postHandle)拦截请求.preHandle方法返回一个布尔值,如果返回错误,则可以在执行链中执行中断请求处理。此接口中还有一个方法afterCompletion执行,只有在preHandler方法发送为真时才会在渲染视图后调用它(完成请求处理后的回调,即渲染视图后)。
拦截器也可以在新线程中启动。在这种情况下,拦截器必须实现org.springframework.web.servlet.AsyncHandlerInterceptor接口。它继承的HandlerInterceptor并提供一个方法afterConcurrentHandlingStarted。每次处理程序得到正确执行时,都会调用此方法而不是调用postHandler()和afterCompletion执()。它也可以对发送请求进行异步处理。通过弹簧源码此方法注释可以知道,这个方法的典型的应用是可以用来清理本地线程变量。
拦截器和过滤器之间的区别
拦截器看起来很像的servlet过滤器,为什么春节不采用默认的Java的解决方案?这其中主要区别就是两者的作用域的问题。过滤器只能在servlet的容器下使用。而我们的春天容器不一定运行在网络环境中,在这种情况下过滤器就不好使了,而拦截器依然可以在春天容器中调用。
春天通过拦截器为请求提供了一个更细粒度的控制。就像我们之前看到的那样,它们可以在控制器对请求处理之前或之后被调用,也可以在将渲染视图呈现给用户之后被调用。如果是过滤器的话,只能在将响应返回给最终用户之前使用它们。
下一个不同之处在于中断链执行的难易程度。拦截器可以通过在preHandler()方法内返回假来简单实现。而在过滤器的情况下,它就变得复杂了,因为它必须处理请求和响应对象来引发中断,需要一些额外的动作,比如如将用户重定向到错误页面。
什么是默认的春天拦截器?
春季主要将拦截器用于切换操作。比如我们最常用的功能之一是区域设置更改(也就是本地化更改)。请查看org.springframework.web.servlet.i18n.LocaleChangeInterceptor类中源码,可以通过我们所定义的语言环境解析器来对HTTP请求进行分析来实现。所有区域设置解析器都会分析请求元素(头,饼干),以确定向用户提供哪种本地化语言设置。
另一个本地拦截器是org.springframework.web.servlet.theme.ThemeChangeInterceptor,它允许更改视图的主题(见此类的注释)。它还使用主题解析器更精确地来知道要使用的主题(参照下面preHandle方法)。它的解析器也基于请求分析(饼干,会话或参数)。
/** * Interceptor that allows for changing the current theme on every request, * via a configurable request parameter (default parameter name: "theme"). * * @author Juergen Hoeller * @since 20.06.2003 * @see org.springframework.web.servlet.ThemeResolver */public class ThemeChangeInterceptor extends HandlerInterceptorAdapter { /** * Default name of the theme specification parameter: "theme". */ public static final String DEFAULT_PARAM_NAME = "theme"; private String paramName = DEFAULT_PARAM_NAME; /** * Set the name of the parameter that contains a theme specification * in a theme change request. Default is "theme". */ public void setParamName(String paramName) { this.paramName = paramName; } /** * Return the name of the parameter that contains a theme specification * in a theme change request. */ public String getParamName() { return this.paramName; } @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws ServletException { String newTheme = request.getParameter(this.paramName); if (newTheme != null) { ThemeResolver themeResolver = RequestContextUtils.getThemeResolver(request); if (themeResolver == null) { throw new IllegalStateException("No ThemeResolver found: not in a DispatcherServlet request?"); } themeResolver.setThemeName(request, response, newTheme); } // Proceed in any case. return true; } }
在春天中自定义处理程序拦截器
我们写一个例子来简单实现HandlerInterceptor。一个乐透彩票的场景,这个自定义的拦截器将分析每个请求,并决定是否是彩票的“彩票赢家”。为了简化代码逻辑,只有用于生成一个随机数并通过取模判断是否返回0的请求。
public class LotteryInterceptor implements HandlerInterceptor { public static final String ATTR_NAME = "lottery_winner"; private static final Logger LOGGER = LoggerFactory.getLogger(LotteryInterceptor.class); @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception exception) throws Exception { LOGGER.debug("[LotteryInterceptor] afterCompletion"); } @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView view) throws Exception { LOGGER.debug("[LotteryInterceptor] postHandle"); } @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { LOGGER.debug("[LotteryInterceptor] preHandle"); if (request.getSession().getAttribute(ATTR_NAME) == null) { Random random = new Random(); int i = random.nextInt(10); request.getSession().setAttribute(ATTR_NAME, i%2 == 0); } return true; } }
关于相应控制器中要展示的信息:
@Controllerpublic class TestController { private static final Logger LOGGER = LoggerFactory.getLogger(TestController.class); @RequestMapping(value = "/test", method = RequestMethod.GET) public String test(HttpServletRequest request) { LOGGER.debug("Controller asks, are you a lottery winner ? "+request.getSession().getAttribute(LotteryInterceptor.ATTR_NAME)); return "test"; } }
如果我们尝试访问/测试,我们将看不到拦截器的日志,因为它没有在配置中定义。如果我们是使用注解来配置的web应用程序。我们需要将下面这个配置添加到应用程序的上下文文件中( Springboot配置个相应的豆就可):
<mvc:interceptors> <bean class="com.waitingforcode.interceptors.LotteryInterceptor" /></mvc:interceptors>
现在我们可以访问/测试页面并检查日志:
[LotteryInterceptor] preHandleController asks, are you a lottery winner ? false[LotteryInterceptor] postHandle[LotteryInterceptor] afterCompletio
登录 | 立即注册