携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第17天,点击查看活动详情
1. HandlerInterceptor
1.1. 拦截器介绍
HandlerInterceptor可以为某些处理器注册任意数量已有或自定义的拦截器,用于添加预处理行为,而无需更改具体处理器实现。
HandlerInterceptor的源码分为两个过程:
- 拦截器查找过程
- 拦截器应用过程
1.2. 拦截器源码
HandlerInterceptor就是一个接口,里面的方法就三个方法需要实现。
public interface HandlerInterceptor {
default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
return true;
}
default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
@Nullable ModelAndView modelAndView) throws Exception {
}
default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,
@Nullable Exception ex) throws Exception {
}
}
何时创建
我们在前面的学习中知道了initHandlerMappings的作用,其中就有初始化DispatcherServlet.properties里面的类(bean),以SimpleUrlHandlerMapping为例:
我们前面的这张图不知道大家记不记得
在initializeBean后面我们上面进入的是invokeInitMethods方,但这个方法上面有applyBeanPostProcessorsBeforeInitialization这个方法
protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) {
...
if (mbd == null || !mbd.isSynthetic()) {
wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
}
...
}
进入里面
其实就是在初始化之前应用 Bean 后处理器
@Override
public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName)
throws BeansException {
Object result = existingBean;
for (BeanPostProcessor processor : getBeanPostProcessors()) {
Object current = processor.postProcessBeforeInitialization(result, beanName); // <- 这里
if (current == null) {
return result;
}
result = current;
}
return result;
}
再进入postProcessBeforeInitialization方法,初始化前的后处理
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if (!(bean instanceof EnvironmentAware || bean instanceof EmbeddedValueResolverAware ||
bean instanceof ResourceLoaderAware || bean instanceof ApplicationEventPublisherAware ||
bean instanceof MessageSourceAware || bean instanceof ApplicationContextAware ||
bean instanceof ApplicationStartupAware)) {
return bean;
}
AccessControlContext acc = null;
if (System.getSecurityManager() != null) {
acc = this.applicationContext.getBeanFactory().getAccessControlContext();
}
if (acc != null) {
AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
invokeAwareInterfaces(bean); // <-- 这里
return null;
}, acc);
}
else {
invokeAwareInterfaces(bean); // <-- 这里
}
return bean;
}
里面有初始化一些接口,其他的我们不看,就看最后一条接口setApplicationContext
private void invokeAwareInterfaces(Object bean) {
....
if (bean instanceof ApplicationContextAware) {
((ApplicationContextAware) bean).setApplicationContext(this.applicationContext);
}
}
ApplicationObjectSupport实现了这个接口,首先调用setApplicationContext()方法,其中的initApplicationContext()方法由子类覆盖和实现。
protected void initApplicationContext(ApplicationContext context) throws BeansException {
initApplicationContext();
}
通过覆盖initApplicationContext()方法实现初始化的一些工作。
在子类SimpleUrlHandlerMapping中的initApplicationContext()方法中,先初始化Spring MVC容器,然后再对Handler进行注册。
public void initApplicationContext() throws BeansException {
super.initApplicationContext(); // <- 这里
registerHandlers(this.urlMap);
}
调用父类的初始化程序
protected void initApplicationContext() throws BeansException {
extendInterceptors(this.interceptors); // 里面是空方法 给定已配置的拦截器,子类可以覆盖以注册其他拦截器的扩展挂钩
detectMappedInterceptors(this.adaptedInterceptors);
initInterceptors();
}
我们看到了这个父方法就是用来初始化拦截器的,英文注释写的也很清楚Initializes the interceptors.
detectMappingInterceptors()方法探测ApplicationContext中已经解析过的MappedInterceptor,initInterceptors()方法来初始化拦截器。
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");
}
this.adaptedInterceptors.add(adaptInterceptor(interceptor));
}
}
}
调用initInterceptors()方法将SimpleUrlHandlerMapping中定义的interceptors包装成HandlerInterceptor对象保存在adaptedInterceptors数组中。
再回到initApplicationContext()方法中调用的registerHandlers()方法,这边主要是主要是对urlMap中的key值进行了一些处理,要是没有“/”的就加上"/",去掉空格等处理。这里的urlMap就是在配置文件中SimpleUrlHandlerMapping的通过mappings属性注入的的内容。key是url的某个字段,value是bean的id。j
protected void registerHandlers(Map<String, Object> urlMap) throws BeansException {
if (urlMap.isEmpty()) {
logger.trace("No patterns in " + formatMappingName());
}
else {
urlMap.forEach((url, handler) -> {
// Prepend with slash if not already present.
if (!url.startsWith("/")) {
url = "/" + url;
}
// Remove whitespace from handler bean name.
if (handler instanceof String) {
handler = ((String) handler).trim();
}
registerHandler(url, handler);
});
logMappings();
}
}
从这边就可以清楚的看到,这里根据SimpleUrlHandlerMapping中的urlMap中的value值通过getBean()方法得到bean对象(通过id查找)。同时将url的某个字段作为key值,bean作为value重新存入到AbstractUrlHandlerMapping的urlMap属性中去,这样就达到url的某个字段对应到具体的controller了的目的,当遇到有请求访问服务器的时候,就可以根据url找到具体的controller去执行这个请求了。
调用顺序
我们写个测试用例来了解一下执行顺序
@Component
public class MyInterceptor implements HandlerInterceptor {
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("===========preHandle===========");
return true;
}
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("===========postHandle===========");
}
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("===========afterCompletion===========");
}
}
controller
@GetMapping("/hello")
public String hello() {
System.out.println("controller");
return "ok";
}
我们执行一下/hello的请求,看看结果
一共4个断点,每个方法结束就会输出对应的语句,可以看到,其实我们的handle就已经是去执行了我们controller的方法了
进入第一个断点的applyPreHandle下
boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
for (int i = 0; i < this.interceptorList.size(); i++) {
HandlerInterceptor interceptor = this.interceptorList.get(i);
if (!interceptor.preHandle(request, response, this.handler)) { // <== 这里进行判断
triggerAfterCompletion(request, response, null);
return false;
}
this.interceptorIndex = i;
}
return true;
}
遍历我们定义的拦截器,只要最前面的拦截了就返回false,因为this.interceptorList是个List类型的,所以存放的顺序是定义的顺序,先定义的就先进行判断。
再来到applyPostHandle方法下
void applyPostHandle(HttpServletRequest request, HttpServletResponse response, @Nullable ModelAndView mv)
throws Exception {
for (int i = this.interceptorList.size() - 1; i >= 0; i--) {
HandlerInterceptor interceptor = this.interceptorList.get(i);
interceptor.postHandle(request, response, this.handler, mv);
}
}
可以很清楚的看到for循环是从后往前遍历的,所以他的执行顺序是定义的逆序,并且不需要返回的!! 。
再来到processDispatchResult方法,前面在这里不重要,我们省略
private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
@Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv,
@Nullable Exception exception) throws Exception {
...
if (mappedHandler != null) {
// Exception (if any) is already handled..
mappedHandler.triggerAfterCompletion(request, response, null);
}
}
在triggerAfterCompletion方法里面
void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, @Nullable Exception ex) {
for (int i = this.interceptorIndex; i >= 0; i--) {
HandlerInterceptor interceptor = this.interceptorList.get(i);
try {
interceptor.afterCompletion(request, response, this.handler, ex);
}
catch (Throwable ex2) {
logger.error("HandlerInterceptor.afterCompletion threw exception", ex2);
}
}
}
同样是逆序判断,不需要返回
总结
| preHandle | postHandle | afterCompletion | |
|---|---|---|---|
| 调用时间 | Controller方法处理之前 | Controller方法处理完之后,DispatcherServlet进行视图的渲染之前,也就是说在这个方法中你可以对ModelAndView进行操作 | DispatcherServlet进行视图的渲染之后 |
| 执行顺序 | 链式Intercepter情况下,Intercepter按照声明的顺序一个接一个执行,若返回false,则中断执行 | 链式Intercepter情况下,Intercepter按照声明的顺序倒着执行。 | 逆序执行,可以用来处理异常,清理资源 |