4.2.2.4 拦截器的实现架构
从前面的章节分析中得知,处理器映射机制支持处理器拦截器功能。处理器拦截器应用一定的功能在满足一定条件的请求上。
处理器拦截器必须实现HandlerInterceptor接口,它定义了三个方法如下,
preHandle()
任何处理器调用之前调用的方法。它返回一个布尔值,如果返回真,则继续调用处理器链的其他处理器或者拦截器,如果返回假,则停止调用处理器链的其他处理器或者拦截器,在这种情况下,它假设拦截器已经处理了HTTP请求,而且写入了HTTP响应。
postHandle()
任何处理器调用之后调用的方法。
afterCompletion()
整个请求处理之后调用的方法。
如下代码注释,
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;
}
在对流程进行分析的时候,我们看到有两个层次的处理器拦截器的实现。在抽象的处理器映射的层次上可以配置处理器拦截器。这些拦截器会应用到所有的处理器上。然后,在抽象URL处理器映射的层次上可以根据URL Pattern配置处理器拦截器,这些拦截器只应用到匹配URL Pattern的处理器上。如下图所示,
图表 4‑37
在抽象处理器映射类中,它声明了一个处理器拦截器的数组,但是并没有提供这个数组的存取方法。这个处理器映射是通过另外一个数组属性适配得到的。如下代码所示,
public abstract class AbstractHandlerMapping extends WebApplicationObjectSupport
implements HandlerMapping, Ordered {
// 通用类型的拦截器对象
// 包含处理器拦截器,它用标准Servlet HTTP请求对象和标准Servlet HTTP响应对象作为方法参数
// 也包含Web Request拦截器, 它使用Spring对请求和响应的封装类型, WebRequest和WebResponse
private final List<Object> interceptors = new ArrayList<Object>();
// 适配后的处理器适配器
private HandlerInterceptor[] adaptedInterceptors;
// 提供方法设置HandlerInterceptor和WebRequestInterceptor
public void setInterceptors(Object[] interceptors) {
this.interceptors.addAll(Arrays.asList(interceptors));
}
// 当应用程序环境初始化的时候,初始化拦截器
protected void initApplicationContext() throws BeansException {
extendInterceptors(this.interceptors);
initInterceptors();
}
// 子类可以改写添加新的拦截器
protected void extendInterceptors(List<Object> interceptors) {
}
protected void initInterceptors() {
if (!this.interceptors.isEmpty()) {
this.adaptedInterceptors = new HandlerInterceptor[this.interceptors.size()];
// 对于每个对象类型的拦截器适配到标准的处理器拦截器
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");
}
this.adaptedInterceptors[i] = adaptInterceptor(interceptor);
}
}
}
protected HandlerInterceptor adaptInterceptor(Object interceptor) {
// 如果已经是处理器拦截器类型,不需要适配
if (interceptor instanceof HandlerInterceptor) {
return (HandlerInterceptor) interceptor;
}
// 如果是Web请求拦截器,需要使用WebRequestHandlerInterceptorAdapter进行适配
else if (interceptor instanceof WebRequestInterceptor) {
return new WebRequestHandlerInterceptorAdapter((WebRequestInterceptor) interceptor);
}
// 不支持其他类型的拦截器
else {
throw new IllegalArgumentException("Interceptor type not supported: " + interceptor.getClass().getName());
}
}
}
我们可以看到,它支持两种类型的拦截器,处理器拦截器和Web请求拦截器。处理器拦截器使用标准的HTTP请求和HTTP响应作为参数,而Web请求拦截器使用了Spring封装类型WebRequest和WebResponse。这是因为除了Servlet规范中的请求和响应以外,它还要支持Portlet中的请求和响应。如下Web请求拦截器代码注释,
public interface WebRequestInterceptor {
// 处理器执行前调用,不支持在拦截器中结束处理器链的操作
void preHandle(WebRequest request) throws Exception;
// 处理器执行后调用
void postHandle(WebRequest request, ModelMap model) throws Exception;
// 请求处理完成后调用
void afterCompletion(WebRequest request, Exception ex) throws Exception;
}
有的时候,我们并不希望实现所有的三个方法,而是希望实现其中某一个,或者某些方法,Spring框架提供了处理器拦截器的适配器类来达到这样的目的。如下类图所示,
图表 4‑38
上图中,处理器拦截器适配器实现了处理器拦截器接口,子类只需要继承自处理器拦截器适配器并且根据业务逻辑需要改写其中的某些方法即可。
Web请求处理器拦截器适配器用来将一个Web请求拦截器适配成为一个标准的处理器拦截器。它应用在抽象处理器拦截器的内部实现上。
下面我们分析抽象URL处理器映射层次的处理器拦截器。这个层次的拦截器根据配置的URL Pattern应用到一部分的处理器上。如下代码所示,
public abstract class AbstractUrlHandlerMapping extends AbstractHandlerMapping {
// 映射拦截器集合,映射拦截器保存从URL Pattern到处理器拦截器的映射信息
private MappedInterceptors mappedInterceptors;
// 可以在外部进行配置映射的拦截器
public void setMappedInterceptors(MappedInterceptor[] mappedInterceptors) {
this.mappedInterceptors = new MappedInterceptors(mappedInterceptors);
}
@Override
// 从Web应用程序环境中查找映射的拦截器
protected void initInterceptors() {
super.initInterceptors();
// 查找Web应用程序环境中所有的映射的拦截器
Map<String, MappedInterceptor> mappedInterceptors = BeanFactoryUtils.beansOfTypeIncludingAncestors(
getApplicationContext(), MappedInterceptor.class, true, false);
if (!mappedInterceptors.isEmpty()) {
this.mappedInterceptors = new MappedInterceptors(mappedInterceptors.values().toArray(
new MappedInterceptor[mappedInterceptors.size()]));
}
}
}
映射的拦截器是一个保存从URL Pattern到处理器拦截器的映射信息。如下代码所示,
public final class MappedInterceptor {
// 匹配的URL Pattern
private final String[] pathPatterns;
// 匹配的URL Pattern应该应用的处理器拦截器
private final HandlerInterceptor interceptor;
//省略了构造器和存取方法
}
有了这些映射信息,处理器映射在构造处理器执行链的时候,就会使用这些信息进行匹配当前的HTTP请求,如果匹配成功,则使用此处理器拦截器。过滤功能是在映射的拦截器集合类中实现的。如下代码注释,
class MappedInterceptors {
// 所有可得的映射的拦截器
private MappedInterceptor[] mappedInterceptors;
public MappedInterceptors(MappedInterceptor[] mappedInterceptors) {
this.mappedInterceptors = mappedInterceptors;
}
// 通过请求的查找路径进行过滤
public Set<HandlerInterceptor> getInterceptors(String lookupPath, PathMatcher pathMatcher) {
Set<HandlerInterceptor> interceptors = new LinkedHashSet<HandlerInterceptor>();
for (MappedInterceptor interceptor : this.mappedInterceptors) {
// 如果映射的拦截器匹配查找路径,则使用此映射的拦截器
if (matches(interceptor, lookupPath, pathMatcher)) {
interceptors.add(interceptor.getInterceptor());
}
}
return interceptors;
}
private boolean matches(MappedInterceptor interceptor, String lookupPath, PathMatcher pathMatcher) {
// 一个映射的拦截器包含有多个URL Pattern, 如果其中一个URL Pattern匹配查找路径,则匹配成功
String[] pathPatterns = interceptor.getPathPatterns();
if (pathPatterns != null) {
for (String pattern : pathPatterns) {
if (pathMatcher.match(pattern, lookupPath)) {
return true;
}
}
return false;
} else {
return true;
}
}
}
Spring框架实现了一些通用的处理器拦截器。其中,每个拦截器的实现都实现了一个独立完整的逻辑功能。如下类图所示,
图表 4‑39
我们已经在前面分析了适配器WebRequestHandlerInterceptorAdapter和HandlerInterceptorAdapter的用途。这里我们将对剩余的类进行功能性的描述,既然,他们的实现并不复杂,这里将不再进行代码注释。
l WebContentInterceptor
它能用于根据配置设置用户的HTTP缓存,并且对HTTP请求进行简单的方法校验。事实上它是在处理器调用之前对WebContentGenerator的实现进行调用。
l ConversionServiceExposingInterceptor
它在处理器调用之前导出配置的转换服务到HTTP请求属性中。
l UserRoleAuthorizationInterceptor
它用于限制一类控制器仅仅可以在一部分的角色中使用。角色校验是在处理器调用之前完成的。
l LocaleChangeInterceptor
它在处理器调用之前根据请求的地域信息配置地域解析器。
l ThemeChangeInterceptor
它在处理器调用之前根据请求的主题信息配置主题解析器。
l PathExposingHandlerInterceptor
处理器内部使用用于导出处理器匹配的路径信息到请求属性中。
l UriTemplateVariablesHandlerInterceptor
处理器内部使用用于导出处理器匹配的路径信息中的模板变量到请求属性中。