一、开篇
在使用SpringMvc框架创建工程时,每次都要在xml中添加【<mvc:annotation-driven/>】这一行配置。
到底这行配置起到了什么作用呢?
启用后,Spring会默认帮我们注册处理请求,参数和返回值的类。
主要是实现了以下两个接口:HandlerMapping与HandlerAdapter。
那到底是如何实现的呢,我们在此展开以下。
二、RequestMappingHandlerMapping
首先,由官方帮助文档可以看到,【<mvc:annotation-driven/>】会帮我们注册RequestMappingHandlerMapping的Bean。
RequestMappingHandlerMapping实现了InitializingBean接口,那么在初始化Bean时,会调用afterPropertiesSet()方法。如下
public void afterPropertiesSet() { initHandlerMethods(); }
protected void initHandlerMethods() {
// 遍历候选Bean for (String beanName : getCandidateBeanNames()) { if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
// 执行并注册候选Bean processCandidateBean(beanName); } } handlerMethodsInitialized(getHandlerMethods()); }
protected void processCandidateBean(String beanName) { Class<?> beanType = null; try { beanType = obtainApplicationContext().getType(beanName); } catch (Throwable ex) { // An unresolvable bean type, probably from a lazy bean - let's ignore it. if (logger.isTraceEnabled()) { logger.trace("Could not resolve type for bean '" + beanName + "'", ex); } }
// 判断是不是Handler的Bean if (beanType != null && isHandler(beanType)) { detectHandlerMethods(beanName); } }
protected boolean isHandler(Class<?> beanType) {
// 判断是否被Controller和RequestMapping注解标注 return (AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) || AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class));
}
protected void detectHandlerMethods(Object handler) {
Class<?> handlerType = (handler instanceof String ? obtainApplicationContext().getType((String) handler) : handler.getClass()); if (handlerType != null) { Class<?> userType = ClassUtils.getUserClass(handlerType);
// 选择被@RequestMapping标注的方法 Map<Method, T> methods = MethodIntrospector.selectMethods(userType, (MethodIntrospector.MetadataLookup<T>) method -> { try {
// 根据方法上的@RequestMapping来创建RequestMappingInfo实例 return getMappingForMethod(method, userType); } catch (Throwable ex) { throw new IllegalStateException("Invalid mapping on handler class [" + userType.getName() + "]: " + method, ex); } }); if (logger.isTraceEnabled()) { logger.trace(formatMappings(userType, methods)); } methods.forEach((method, mapping) -> {
Method invocableMethod = AopUtils.selectInvocableMethod(method, userType);
// 注册请求映射 registerHandlerMethod(handler, invocableMethod, mapping); }); } }
protected void registerHandlerMethod(Object handler, Method method, T mapping) { this.mappingRegistry.register(mapping, handler, method); }
public void register(T mapping, Object handler, Method method) { this.readWriteLock.writeLock().lock(); try { HandlerMethod handlerMethod = createHandlerMethod(handler, method); assertUniqueMethodMapping(handlerMethod, mapping);
// 将处理器方法添加到映射表中 this.mappingLookup.put(mapping, handlerMethod); List<String> directUrls = getDirectUrls(mapping); for (String url : directUrls) { this.urlLookup.add(url, mapping); } String name = null; if (getNamingStrategy() != null) { name = getNamingStrategy().getName(handlerMethod, mapping); addMappingName(name, handlerMethod); } CorsConfiguration corsConfig = initCorsConfiguration(handler, method, mapping); if (corsConfig != null) { this.corsLookup.put(handlerMethod, corsConfig); } this.registry.put(mapping, new MappingRegistration<>(mapping, handlerMethod, directUrls, name)); } finally { this.readWriteLock.writeLock().unlock(); } }
由此可以看到,在对RequestMappingHandlerMapping进行Bean的初始化时,就将被@Controller和@RequestMapping注解的处理器方法放入了映射表
三、RequestMappingHandlerAdapter
因为Handler的格式是不固定的,所以处理之前需要HandlerAdapter来做适配,在此给出了其中最重要的handlerInternal方法
protected ModelAndView handleInternal(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod) throws Exception { ModelAndView mav; checkRequest(request); // Execute invokeHandlerMethod in synchronized block if required. if (this.synchronizeOnSession) { HttpSession session = request.getSession(false); if (session != null) { Object mutex = WebUtils.getSessionMutex(session); synchronized (mutex) { mav = invokeHandlerMethod(request, response, handlerMethod); } } else { // No HttpSession available -> no mutex necessary mav = invokeHandlerMethod(request, response, handlerMethod); } } else { // No synchronization on session demanded at all... mav = invokeHandlerMethod(request, response, handlerMethod); } if (!response.containsHeader(HEADER_CACHE_CONTROL)) { if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) { applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers); } else { prepareResponse(response); } } return mav; }
可以看出其中通过invokeHandlerMethod代理执行了方法。
四、HandlerInterceptor
在执行上述三中代理方法时,HandlerIntercepor会对该方法执行前和执行后进行一些准备和善后处理,具体如下
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 { }
preHandle:在handle执行前调用, 通常是做一些环境准备工作,如登录检查
postHandle:在handle执行后,渲染视图前调用,在此处可以修改ModelAndView
afterCompletion:在渲染视图后调用,或是preHandle执行出错后调用,通常是做一些资源回收工作
调用顺序如下:
正常情况下:preHandle(1) -> preHandle(2) -> postHandle(2) -> postHandle(1) -> afterCompletion(2) -> afterCompletion(1)
异常情况下(preHandle(2)处理异常)):preHandle(1) -> preHandle(2) -> afterCompletion(1)