1、自定义拦截器
Spring MVC 也可以使用拦截器对请求进行拦截处理,用户
可以自定义拦截器来实现特定的功能,自定义的拦截器必
须实现HandlerInterceptor接口。
1)、 HandlerInterceptor接口:
public interface HandlerInterceptor { boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception; void postHandle( HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception; void afterCompletion( HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception; }
- preHandle():这个方法在业务处理器处理请求之前被调用,在该方法中对用户请求 request 进行处理。如果程序员决定该拦截器对 请求进行拦截处理后还要调用其他的拦截器,或者是业务处理器去进行处理,则返回true;如果程序员决定不需要再调用其他的组件去处理请求,则返回false。
- postHandle():这个方法在业务处理器处理完请求后,但是DispatcherServlet 向客户端返回响应前被调用,在该方法中对用户请求request进行处理。
- afterCompletion():这个方法在 DispatcherServlet 完全处理完请求后被调用,可以在该方法中进行一些资源清理的操作。
2)自定义拦截器实例
FirstInterceptor :
public class FirstInterceptor implements HandlerInterceptor{
/**
* 该方法在目标方法之前被调用.
* 若返回值为 true, 则继续调用后续的拦截器和目标方法.
* 若返回值为 false, 则不会再调用后续的拦截器和目标方法.
*
* 可以考虑做权限. 日志, 事务等.
*/
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler) throws Exception {
System.out.println("[FirstInterceptor] preHandle");
return 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");
}
}
自定义拦截器实例
SecondInterceptor
:
public class SecondInterceptor implements HandlerInterceptor{
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler) throws Exception {
System.out.println("[SecondInterceptor] preHandle");
return true;
}
@Override
public void postHandle(HttpServletRequest request,
HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
System.out.println("[SecondInterceptor] postHandle");
}
@Override
public void afterCompletion(HttpServletRequest request,
HttpServletResponse response, Object handler, Exception ex)
throws Exception {
System.out.println("[SecondInterceptor] afterCompletion");
}
}
3)自定义拦截器的配置:
<mvc:interceptors>
<!-- 配置自定义的拦截器FirstInterceptor,拦截所有路径 -->
<bean class="com.xl.springmvc.interceptor.FirstInterceptor"></bean>
<!-- 配置拦截器(不)拦截指定的路径 -->
<mvc:interceptor>
<mvc:mapping path="/xlxx"/>
<bean class="com.xl.springmvc.interceptor.SecondInterceptor"></bean>
</mvc:interceptor>
</mvc:interceptors>
4)、拦截器执行顺序
拦截器preHandler返回true情况:
拦截器preHandler返回false情况:
2、拦截器实现
通过
Spring MVC请求处理流程及架构 已经看到,在Spring MVC处理请求的过程中伴随着
拦截器进行拦截处理的流程,在doDispatch()函数中,执行
mappedHandler = getHandler(processedRequest),
通过
getHandler()方法,使用HandlerMapping完成请求到处理器的映射,获取得到的
HandlerExecutionChain 对象中,不仅包含有Handler处理器,还有
HandlerInterceptor拦截器链,那么这个HandlerExecutionChain是如何包装HandlerInterceptor拦截器链的呢,我们知道HandlerExecutionChain对象是通过调用
HandlerMapping 接口方法
HandlerExecutionChain
getHandler
(HttpServletRequest request)得到的,
回到HandlerMapping,以
SimpleUrlHandlerMapping类来看:
SimpleUrlHandlerMapping类继承体系:
看到HandlerMapping继
承自
WebApplicationObjectSupport进而继承了ApplicationObjectSupport,而ApplicationObjectSupport实现了ApplicationContextAware接口,我们知道,在bean的生命周期中如果该bean类实现了ApplicationContextAware那么通过容器获取这个bean时,
void
setApplicationContext
(ApplicationContext context)方法将被调用。
ApplicationObjectSupport类
setApplicationContext()方法:
@Override public final void setApplicationContext(ApplicationContext context) throws BeansException { if (context == null && !isContextRequired()) { // Reset internal context state. this.applicationContext = null; this.messageSourceAccessor = null; } else if (this.applicationContext == null) { // Initialize with passed-in context. if (!requiredContextClass().isInstance(context)) { throw new ApplicationContextException( "Invalid application context: needs to be of type [" + requiredContextClass().getName() + "]"); } this.applicationContext = context; this.messageSourceAccessor = new MessageSourceAccessor(context); //ApplicationObjectSupport中抽象方法initApplicationContext() initApplicationContext(context); } else { // Ignore reinitialization if same context passed in. if (this.applicationContext != context) { throw new ApplicationContextException( "Cannot reinitialize with different application context: current one is [" + this.applicationContext + "], passed-in one is [" + context + "]"); } } }
@Override protected void initApplicationContext() throws BeansException { extendInterceptors(this.interceptors); //探测容器中所有拦截器 detectMappedInterceptors(this.mappedInterceptors); //初始配置这些拦截器 initInterceptors(); }
看到在重写的这个initApplicationContext()方法中,进行了拦截器相关的探测以及初始化设置:
protected void detectMappedInterceptors(List<MappedInterceptor> mappedInterceptors) { mappedInterceptors.addAll( //通过BeanFactoryUtils工具类,获取ioc容器中所有的MappedInterceptor类的bean BeanFactoryUtils.beansOfTypeIncludingAncestors( getApplicationContext(), MappedInterceptor.class, true, false).values()); }
在完成了拦截器对象的获取后,对这些拦截器分别进行初始化配置,最终保存到AbstractHandlerMapping类成员变量mappedInterceptors和adaptedInterceptors中,以便后用
经过以上过程,Spring MVC容器中配置的拦截器都已经初始化设置到HandlerMapping Bean对象中,当调用getHandler方法时:protected void initInterceptors() { if (!this.interceptors.isEmpty()) { for (int i = 0; i < this.interceptors.size(); i++) { Object interceptor = this.interceptors.get(i); if (interceptor == null) { throw new IllegalArgumentException("Entry number " + i + " in interceptors array is null"); } if (interceptor instanceof MappedInterceptor) { this.mappedInterceptors.add((MappedInterceptor) interceptor); } else { this.adaptedInterceptors.add(adaptInterceptor(interceptor)); } } } }
@Override public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception { // 根据request请求映射获取对应的Handler Object handler = getHandlerInternal(request); if (handler == null) { handler = getDefaultHandler(); } if (handler == null) { return null; } // Bean name or resolved handler? if (handler instanceof String) { String handlerName = (String) handler; handler = getApplicationContext().getBean(handlerName); } //获取HandlerExecutionChain return getHandlerExecutionChain(handler, request); }
到此, HandlerExecutionChain 获取完成,拦截器也已经包装到该对象中,可以用于在请求流程中进行拦截处理。protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) { //如果handler 已经是HandlerExecutionChain 实例那么直接使用,否则创建新的HandlerExecutionChain HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ? (HandlerExecutionChain) handler : new HandlerExecutionChain(handler)); chain.addInterceptors(getAdaptedInterceptors()); String lookupPath = this.urlPathHelper.getLookupPathForRequest(request); //逐个遍历mappedInterceptors,将匹配请求路径的Interceptor加入HandlerExecutionChain中 for (MappedInterceptor mappedInterceptor : this.mappedInterceptors) { if (mappedInterceptor.matches(lookupPath, this.pathMatcher)) { chain.addInterceptor(mappedInterceptor.getInterceptor()); } } return chain; }
然而还有一个重要的问题,就是这些拦截器是如何配置到IoC容器中的呢,在自定义拦截器部分我们看到,通过<mvc:interceptors>标签,将我们自定义的拦截器配置到xml文件中。当Ioc容器解析BeanDefinition遇到<mvc:interceptors>这个标签,将使用自定义标签解析机制处理这些标签。
MvcNamespaceHandler类用于加载与Spring MVC模块相关的自定义标签解析器:
public class MvcNamespaceHandler extends NamespaceHandlerSupport { @Override public void init() { registerBeanDefinitionParser("annotation-driven", new AnnotationDrivenBeanDefinitionParser()); //这里注册<mvc:default-servlet-handlerv>标签解析器,该标签主要用与处理静态资源 registerBeanDefinitionParser("default-servlet-handler", new DefaultServletHandlerBeanDefinitionParser()); //这里注册<mvc:interceptors>标签解析器 registerBeanDefinitionParser("interceptors", new InterceptorsBeanDefinitionParser()); registerBeanDefinitionParser("resources", new ResourcesBeanDefinitionParser()); registerBeanDefinitionParser("view-controller", new ViewControllerBeanDefinitionParser()); registerBeanDefinitionParser("redirect-view-controller", new ViewControllerBeanDefinitionParser()); registerBeanDefinitionParser("status-controller", new ViewControllerBeanDefinitionParser()); registerBeanDefinitionParser("view-resolvers", new ViewResolversBeanDefinitionParser()); registerBeanDefinitionParser("tiles-configurer", new TilesConfigurerBeanDefinitionParser()); registerBeanDefinitionParser("freemarker-configurer", new FreeMarkerConfigurerBeanDefinitionParser()); registerBeanDefinitionParser("velocity-configurer", new VelocityConfigurerBeanDefinitionParser()); registerBeanDefinitionParser("groovy-configurer", new GroovyMarkupConfigurerBeanDefinitionParser()); } }
在看DefaultServletHandlerBeanDefinitionParser类,该类核心函数
BeanDefinition
parse
(Element element
,
ParserContext parserContext)如下:
class InterceptorsBeanDefinitionParser implements BeanDefinitionParser { @Override public BeanDefinition parse(Element element, ParserContext parserContext) { CompositeComponentDefinition compDefinition = new CompositeComponentDefinition(element.getTagName(), parserContext.extractSource(element)); parserContext.pushContainingComponent(compDefinition); RuntimeBeanReference pathMatcherRef = null; if (element.hasAttribute("path-matcher")) { pathMatcherRef = new RuntimeBeanReference(element.getAttribute("path-matcher")); } List<Element> interceptors = DomUtils.getChildElementsByTagName(element, "bean", "ref", "interceptor"); for (Element interceptor : interceptors) { RootBeanDefinition mappedInterceptorDef = new RootBeanDefinition(MappedInterceptor.class); mappedInterceptorDef.setSource(parserContext.extractSource(interceptor)); mappedInterceptorDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); ManagedList<String> includePatterns = null; ManagedList<String> excludePatterns = null; Object interceptorBean; if ("interceptor".equals(interceptor.getLocalName())) { includePatterns = getIncludePatterns(interceptor, "mapping"); excludePatterns = getIncludePatterns(interceptor, "exclude-mapping"); Element beanElem = DomUtils.getChildElementsByTagName(interceptor, "bean", "ref").get(0); interceptorBean = parserContext.getDelegate().parsePropertySubElement(beanElem, null); } else { interceptorBean = parserContext.getDelegate().parsePropertySubElement(interceptor, null); } mappedInterceptorDef.getConstructorArgumentValues().addIndexedArgumentValue(0, includePatterns); mappedInterceptorDef.getConstructorArgumentValues().addIndexedArgumentValue(1, excludePatterns); mappedInterceptorDef.getConstructorArgumentValues().addIndexedArgumentValue(2, interceptorBean); if (pathMatcherRef != null) { mappedInterceptorDef.getPropertyValues().add("pathMatcher", pathMatcherRef); } String beanName = parserContext.getReaderContext().registerWithGeneratedName(mappedInterceptorDef); parserContext.registerComponent(new BeanComponentDefinition(mappedInterceptorDef, beanName)); } parserContext.popAndRegisterContainingComponent(); return null; }
通过以上解析函数,解析配置文件中
<mvc:interceptors>范围内所有拦截器配置,并转为BeanDefinition,最终注册得到Ioc容器中。