`
sha0k
  • 浏览: 83858 次
  • 性别: Icon_minigender_1
  • 来自: 西安
社区版块
存档分类
最新评论

Spring MVC 源码学习札记(四)HandlerMapping和HandlerAdapter(4)

阅读更多

每天敦促自己阅读spring源码 绝不松懈

下面是当没有注册HandlerAdapter时,spring提供的默认HandlerAdapter的实现类

org.springframework.web.servlet.HandlerAdapter=org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,\
    org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,\
    org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter
上一节看完了DefaultAnnoationHandlerMapping类,HandlerMapping对request的处理就差不多了解完了,接下来再看HandlerAdapter的概念,该接口有两个重要方法(方法所做的事情就不解释了,注释已经很清楚了):

/**
	 * Given a handler instance, return whether or not this HandlerAdapter can
	 * support it. Typical HandlerAdapters will base the decision on the handler
	 * type. HandlerAdapters will usually only support one handler type each.
	 * <p>A typical implementation:
	 * <p><code>
	 * return (handler instanceof MyHandler);
	 * </code>
	 * @param handler handler object to check
	 * @return whether or not this object can use the given handler
	 */	
boolean supports(Object handler); 
 
/**
	 * Use the given handler to handle this request.
	 * The workflow that is required may vary widely.
	 * @param request current HTTP request
	 * @param response current HTTP response
	 * @param handler handler to use. This object must have previously been passed
	 * to the <code>supports</code> method of this interface, which must have
	 * returned <code>true</code>.
	 * @throws Exception in case of errors
	 * @return ModelAndView object with the name of the view and the required
	 * model data, or <code>null</code> if the request has been handled directly
	 */
	ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;

 先来看SimpleControllerHandlerAdapter对这两个方法的实现,一句话的事情,因为太简单了:

public boolean supports(Object handler) {
		return (handler instanceof Controller);
	}

public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
			throws Exception {

		((Controller) handler).handleRequest(request, response);
		return null;
	}

  Controller接口只是定义了handleRequest方法,所以这里的handle处理,就交给了我们实现了Controller的接口的处理类了。到这里,思路就理的很清楚了,但是我忽然有问题,这里适配支持了Controller的实现类,处理调用的是handleRequest,那method对request的处理是怎么回事呢,我们知道在detectHandler时,所有的@Request的value都被对准了一个类。先把我自己的疑惑带着吧,后面我看怎么破解我自己的疑惑。

  下面就来看看AnnotationMethodHandlerAdapter类吧,这个类可比SimpleControllerHandlerAdapter复杂多了,先来看看supports方法:

public boolean supports(Object handler) {
		return getMethodResolver(handler).hasHandlerMethods();
	}

 接着去看getMethodResolver方法吧:

	/**
	 * Build a HandlerMethodResolver for the given handler type.
	 */
	private ServletHandlerMethodResolver getMethodResolver(Object handler) {
		Class handlerClass = ClassUtils.getUserClass(handler);
		ServletHandlerMethodResolver resolver = this.methodResolverCache.get(handlerClass);
		if (resolver == null) {
			resolver = new ServletHandlerMethodResolver(handlerClass);
			this.methodResolverCache.put(handlerClass, resolver);
		}
		return resolver;
	}
 

 首先我查看了ClassUtils的源码,为了查找getUserClass的涵义:

/**
	 * Return the user-defined class for the given instance: usually simply
	 * the class of the given instance, but the original class in case of a
	 * CGLIB-generated subclass.
	 * @param instance the instance to check
	 * @return the user-defined class
	 */	
        public static Class<?> getUserClass(Object instance) {
		Assert.notNull(instance, "Instance must not be null");
		return getUserClass(instance.getClass());
	}
 
/**
	 * Return the user-defined class for the given class: usually simply the given
	 * class, but the original class in case of a CGLIB-generated subclass.
	 * @param clazz the class to check
	 * @return the user-defined class
	 */
	public static Class<?> getUserClass(Class<?> clazz) {
		return (clazz != null && clazz.getName().contains(CGLIB_CLASS_SEPARATOR) ?
				clazz.getSuperclass() : clazz);
	}

  这里得到的结果无非是handler的父类或者自身,再回到getMethodResolver方法,下面是从缓存中取出handlerClass对应的ServletHandlerMethodResolver对象,如果缓存中没有,那么就重新构造。从看了后面的代码,我头大了,真心的,你们是不知道后面有多麻烦,先来说明ServletHandlerMethodResolver它的意思,可以理解为解析能作为serlvet使用的方法,这个内部类有点儿大,慢慢来看,先来看它的构造函数。

private ServletHandlerMethodResolver(Class<?> handlerType) {
			init(handlerType);
		}

 这里的init是它的父类HandlerMethodResovler的初始化方法:

/**
	 * Initialize a new HandlerMethodResolver for the specified handler type.
	 * @param handlerType the handler class to introspect
	 */
	public void init(Class<?> handlerType) {
		Class<?>[] handlerTypes =
				Proxy.isProxyClass(handlerType) ? handlerType.getInterfaces() : new Class<?>[] {handlerType};
		for (final Class<?> currentHandlerType : handlerTypes) {
			ReflectionUtils.doWithMethods(currentHandlerType, new ReflectionUtils.MethodCallback() {
				public void doWith(Method method) {
					Method specificMethod = ClassUtils.getMostSpecificMethod(method, currentHandlerType);
					if (isHandlerMethod(method)) {
						handlerMethods.add(specificMethod);
					}
					else if (method.isAnnotationPresent(InitBinder.class)) {
						initBinderMethods.add(specificMethod);
					}
					else if (method.isAnnotationPresent(ModelAttribute.class)) {
						modelAttributeMethods.add(specificMethod);
					}
				}
			}, ReflectionUtils.NON_BRIDGED_METHODS);
		}
		this.typeLevelMapping = AnnotationUtils.findAnnotation(handlerType, RequestMapping.class);
		SessionAttributes sessionAttributes = handlerType.getAnnotation(SessionAttributes.class);
		this.sessionAttributesFound = (sessionAttributes != null);
		if (this.sessionAttributesFound) {
			this.sessionAttributeNames.addAll(Arrays.asList(sessionAttributes.value()));
			this.sessionAttributeTypes.addAll(Arrays.asList(sessionAttributes.types()));
		}
	}

 这里又出现了RelectionUtils的doWithMethods方法,我在学习札记(五)里,已经详细介绍了这个方法,在回调处理函数中,我得一层层的来剥if判断,首先是isHandlerMethod

	protected boolean isHandlerMethod(Method method) {
		return AnnotationUtils.findAnnotation(method, RequestMapping.class) != null;
	}

 这个很好理解了,就是查找该方法的注解,看看有没有@RequestMapping如果有,那证明它就是请求处理方法。将它加入handlerMethods集合中,下面的判断是Method类的方法,isAnnotationPresent是其父类AccessibleObject的方法,内部实现则是getAnnotation方法,这一方法在AccessibleObject中是没有具体实现的,但并非抽象,方法内部很有意思。

public boolean isAnnotationPresent(
        Class<? extends Annotation> annotationClass) {
        return getAnnotation(annotationClass) != null;
    }
 
public <T extends Annotation> T getAnnotation(Class<T> annotationClass) {
        throw new AssertionError("All subclasses should override this method");
    }

 Method类中:

public <T extends Annotation> T getAnnotation(Class<T> annotationClass) {
        if (annotationClass == null)
            throw new NullPointerException();

        return (T) declaredAnnotations().get(annotationClass);
    }
 
private transient Map<Class, Annotation> declaredAnnotations;

    private synchronized  Map<Class, Annotation> declaredAnnotations() {
        if (declaredAnnotations == null) {
            declaredAnnotations = AnnotationParser.parseAnnotations(
                annotations, sun.misc.SharedSecrets.getJavaLangAccess().
                getConstantPool(getDeclaringClass()),
                getDeclaringClass());
        }
        return declaredAnnotations;
    }

 注意刚才的doWithMethods方法,它是有3个参数的,最有一个参数是一个过滤器:

/**
	 * Pre-built MethodFilter that matches all non-bridge methods.
	 */
	public static MethodFilter NON_BRIDGED_METHODS = new MethodFilter() {

		public boolean matches(Method method) {
			return !method.isBridge();
		}
	};

 什么是bridge方法,参见http://freish.iteye.com/blog/1158008

再回到AnnotationMethodHandlerAdapter类的support方法中,我们可以看到hasHandlerMethods方法,该方法是HandlerMethodResolver类中方法:

public final boolean hasHandlerMethods() {
		return !this.handlerMethods.isEmpty();
	}

 只要这个handler中有@RequestMapping注解的方法,那么这个handler必然不为空。

下面我们再进入AnnotationMethodHandlerAdapter类的handle方法,看的好累 不过总算收获不小:

public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
			throws Exception {

		if (AnnotationUtils.findAnnotation(handler.getClass(), SessionAttributes.class) != null) {
			// Always prevent caching in case of session attribute management.
			checkAndPrepare(request, response, this.cacheSecondsForSessionAttributeHandlers, true);
			// Prepare cached set of session attributes names.
		}
		else {
			// Uses configured default cacheSeconds setting.
			checkAndPrepare(request, response, true);
		}

		// 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) {
					return invokeHandlerMethod(request, response, handler);
				}
			}
		}

		return invokeHandlerMethod(request, response, handler);
	}

 首先判断该handler是否有@SessionAttributes的注解,如果有调用WebContentGenerator的checkAndPrepare。重点不在这里,重点在于invokeHandlerMethod方法:

protected ModelAndView invokeHandlerMethod(HttpServletRequest request, HttpServletResponse response, Object handler)
			throws Exception {

		ServletHandlerMethodResolver methodResolver = getMethodResolver(handler);
		Method handlerMethod = methodResolver.resolveHandlerMethod(request);
		ServletHandlerMethodInvoker methodInvoker = new ServletHandlerMethodInvoker(methodResolver);
		ServletWebRequest webRequest = new ServletWebRequest(request, response);
		ExtendedModelMap implicitModel = new BindingAwareModelMap();

		Object result = methodInvoker.invokeHandlerMethod(handlerMethod, handler, webRequest, implicitModel);
		ModelAndView mav =
				methodInvoker.getModelAndView(handlerMethod, handler.getClass(), result, implicitModel, webRequest);
		methodInvoker.updateModelAttributes(handler, (mav != null ? mav.getModel() : null), implicitModel, webRequest);
		return mav;
	}

 getMethodResolver方法,我们已经看过了,因为已经创建了一次ServletHandlerMethodResolver对象为该handler,所以这次执行直接从缓存Map中读取了,下面就是为request找到适合的Method,使用resolveHandlerMethod方法,这个代码太长了,我贴上来你们别嫌烦。

public Method resolveHandlerMethod(HttpServletRequest request) throws ServletException {
			String lookupPath = urlPathHelper.getLookupPathForRequest(request);
			Comparator<String> pathComparator = pathMatcher.getPatternComparator(lookupPath);
			Map<RequestMappingInfo, Method> targetHandlerMethods = new LinkedHashMap<RequestMappingInfo, Method>();
			Set<String> allowedMethods = new LinkedHashSet<String>(7);
			String resolvedMethodName = null;
			for (Method handlerMethod : getHandlerMethods()) {
				RequestMappingInfo mappingInfo = new RequestMappingInfo();
				RequestMapping mapping = AnnotationUtils.findAnnotation(handlerMethod, RequestMapping.class);
				mappingInfo.paths = mapping.value();
				if (!hasTypeLevelMapping() || !Arrays.equals(mapping.method(), getTypeLevelMapping().method())) {
					mappingInfo.methods = mapping.method();
				}
				if (!hasTypeLevelMapping() || !Arrays.equals(mapping.params(), getTypeLevelMapping().params())) {
					mappingInfo.params = mapping.params();
				}
				if (!hasTypeLevelMapping() || !Arrays.equals(mapping.headers(), getTypeLevelMapping().headers())) {
					mappingInfo.headers = mapping.headers();
				}
				boolean match = false;
				if (mappingInfo.paths.length > 0) {
					List<String> matchedPaths = new ArrayList<String>(mappingInfo.paths.length);
					for (String methodLevelPattern : mappingInfo.paths) {
						String matchedPattern = getMatchedPattern(methodLevelPattern, lookupPath, request);
						if (matchedPattern != null) {
							if (mappingInfo.matches(request)) {
								match = true;
								matchedPaths.add(matchedPattern);
							}
							else {
								for (RequestMethod requestMethod : mappingInfo.methods) {
									allowedMethods.add(requestMethod.toString());
								}
								break;
							}
						}
					}
					Collections.sort(matchedPaths, pathComparator);
					mappingInfo.matchedPaths = matchedPaths;
				}
				else {
					// No paths specified: parameter match sufficient.
					match = mappingInfo.matches(request);
					if (match && mappingInfo.methods.length == 0 && mappingInfo.params.length == 0 &&
							resolvedMethodName != null && !resolvedMethodName.equals(handlerMethod.getName())) {
						match = false;
					}
					else {
						for (RequestMethod requestMethod : mappingInfo.methods) {
							allowedMethods.add(requestMethod.toString());
						}
					}
				}
				if (match) {
					Method oldMappedMethod = targetHandlerMethods.put(mappingInfo, handlerMethod);
					if (oldMappedMethod != null && oldMappedMethod != handlerMethod) {
						if (methodNameResolver != null && mappingInfo.paths.length == 0) {
							if (!oldMappedMethod.getName().equals(handlerMethod.getName())) {
								if (resolvedMethodName == null) {
									resolvedMethodName = methodNameResolver.getHandlerMethodName(request);
								}
								if (!resolvedMethodName.equals(oldMappedMethod.getName())) {
									oldMappedMethod = null;
								}
								if (!resolvedMethodName.equals(handlerMethod.getName())) {
									if (oldMappedMethod != null) {
										targetHandlerMethods.put(mappingInfo, oldMappedMethod);
										oldMappedMethod = null;
									}
									else {
										targetHandlerMethods.remove(mappingInfo);
									}
								}
							}
						}
						if (oldMappedMethod != null) {
							throw new IllegalStateException(
									"Ambiguous handler methods mapped for HTTP path '" + lookupPath + "': {" +
											oldMappedMethod + ", " + handlerMethod +
											"}. If you intend to handle the same path in multiple methods, then factor " +
											"them out into a dedicated handler class with that path mapped at the type level!");
						}
					}
				}
			}
			if (!targetHandlerMethods.isEmpty()) {
				List<RequestMappingInfo> matches = new ArrayList<RequestMappingInfo>(targetHandlerMethods.keySet());
				RequestMappingInfoComparator requestMappingInfoComparator =
						new RequestMappingInfoComparator(pathComparator);
				Collections.sort(matches, requestMappingInfoComparator);
				RequestMappingInfo bestMappingMatch = matches.get(0);
				String bestMatchedPath = bestMappingMatch.bestMatchedPath();
				if (bestMatchedPath != null) {
					extractHandlerMethodUriTemplates(bestMatchedPath, lookupPath, request);
				}
				return targetHandlerMethods.get(bestMappingMatch);
			}
			else {
				if (!allowedMethods.isEmpty()) {
					throw new HttpRequestMethodNotSupportedException(request.getMethod(),
							StringUtils.toStringArray(allowedMethods));
				}
				else {
					throw new NoSuchRequestHandlingMethodException(lookupPath, request.getMethod(),
							request.getParameterMap());
				}
			}
		}

 写不下去了,累死了,忙一天了,明天再弄吧,睡觉去了sorry~~~

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

1
0
分享到:
评论
1 楼 yjq8116 2014-09-11  
不错。很有收获

相关推荐

    SpringMVC源码总结(一)HandlerMapping和HandlerAdapter入门

    NULL 博文链接:https://yihuawuye1.iteye.com/blog/2104547

    org.apache.cxf.spring.remoting.Jsr181HandlerMapping.jar

    org.apache.cxf.spring.remoting.Jsr181HandlerMapping.jar

    全面掌握Spring MVC:从基础到高级的实践指南

    本文通过分析Spring MVC的核心组件和执行流程,提供了一个全面的学习指南。 Spring MVC基于Model-View-Controller(MVC)架构模式,优化了Web应用程序的设计和开发。在Spring MVC中,DispatcherServlet作为前端控制...

    HandlerMapping HandlerAdapter View ViewResolver类图 矢量图

    HandlerMapping HandlerAdapter View ViewResolver类图 矢量图文件 https://blog.csdn.net/qq_39609993/article/details/105435850

    spring3mvc入门资料

    HandlerMapping接口 HandlerAdapter接口 Controller接口 HandlerInterceptor 接口 View接口 LocalResolver接口HandlerExceptionResolver接口 ModelAndView类 。

    java spring mvc

    5)Spring MVC处理流程 a.首先客户端发出spring mvc请求,请求到达DispatcherServlet主控制器处理(前端控制器) b.主控制器调用HandlerMapping组件,根据请求不同调用Controller处理器 c.主控制器调用Controller方法...

    Spring MVC运行流程

    Spring MVC运行流程,分七个步骤,1.DispatcherServlet 2.HandlerMapping

    Spring MVC 学习笔记

    2、 DispatcherServlet把请求转交给HandlerMapping, HandlerMapping将会把请求映射为HandlerExecutionChain对象(包含一个Handler处理器对象、多个HandlerInterceptor拦截器)对象.(后面会学习到拦截器) 3、 ...

    民宿网站管理系统java+spring.7z,架构是SSM

    Spring MVC 内置了 Requestmapping、HandlerMapping 和 ViewResolver 等组件,可以简化开发流程。MyBatis 作为持久层框架,负责处理数据库操作。 在安全性方面,该网站采用了 SSL 证书进行加密传输,并实现了用户...

    Spring MVC 员工管理系统

    和众多其它Web框架一样,它基于MVC设计理念,此外,由于它采用了松散耦合可插拔组件结构,具有比其它MVC框架更多的扩展性和灵活性。 Spring MVC框架围绕DispatcherServlet这个核心展开,DispatcherServlet的作用是...

    基于spring+java的教务管理系统.7z,SSM框架

    Spring MVC 内置了 Requestmapping、HandlerMapping 和 ViewResolver 等组件,可以简化开发流程。MyBatis 作为持久层框架,负责处理数据库操作。 在安全性方面,该教务管理系统采用了 SSL 证书进行加密传输,并实现...

    spring mvc 思维导图

    Spring的模型-视图-控制器(MVC)框架是围绕一个DispatcherServlet来设计的,这个Servlet会把请求分发给各个处理器,并支持可配置的处理器映射、视图渲染、本地化、时区与主题渲染等,甚至还能支持文件上传。...

    看透springMvc源代码分析与实践

    8.2.2 创建Spring MVC的xml配置文件85 8.2.3 创建Controller和view86 8.3 关联spring源代码87 8.4 小结89 第9章 创建Spring MVC之器90 9.1 整体结构介绍90 9.2 HttpServletBean93 9.3 FrameworkServlet95 ...

    springMVC技术概述

    springMVC相关技术配置使用注解的HandlerMapping和HandlerAdapter使用&lt;mvc:annotation-driver&gt; 不过springBoot已经省略了这些配置 配置使用注解的Handler和Service等等使用&lt;context:component-scan&gt; 不过springBoot...

    SSM框架原理 spring-mvc执行流程

    SSM框架是spring MVC ,spring和mybatis框架的整合,是标准的MVC模式,将整个系统划分为controller层,service层,mapper层三层,通常称为三层架构 使用spring MVC负责请求的转发和视图管理 spring实现业务对象管理...

    入门案例-SpringMVC技术架构图

    Spring MVC以DispatcherServlet为核心,众多组件如HandlerMapping为辅助,为用户封装了请求映射等底层逻辑,让用户可以更专注与业务逻辑的处理。本文会对Spring MVC整体结构做简单介绍。 Spring MVC结构图 Spring ...

    Spring MVC之DispatcherServlet详解_动力节点Java学院整理

    DispatcherServlet是前端控制器设计模式的实现,提供Spring Web MVC的集中访问点,而且负责职责的分派,而且与Spring IoC容器无缝集成,从而可以获得Spring的所有好处。 具体请参考第二章的图2-1。  ...

    Spring MVC工作原理 及注解说明1

    1. 客户端请求提交到DispatcherServlet 2. 由DispatcherServlet控制器查询个或多个HandlerMapping,找到处理 3

    spring mvc

    只有对应的HandlerMapping (为了实现类型级别的注解)和/ 或HandlerAdapter (为了实现方法级别的注解)出现在 dispatcher中时, @RequestMapping 才会被处理。 这在DispatcherServlet 和DispatcherPortlet 中都是...

    Spring MVC之DispatcherServlet_动力节点Java学院整理

    Spring MVC之DispatcherServlet 使用Spring MVC,配置DispatcherServlet是第一步。 DispatcherServlet是一个Servlet,所以可以配置多个DispatcherServlet。 DispatcherServlet是前置控制器,配置在web.xml文件中的...

Global site tag (gtag.js) - Google Analytics