`
cwqcwq
  • 浏览: 74768 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

Spring2.5源码解读 之 基于annotation的Controller实现原理分析(1)

阅读更多
Spring2.5源码解读 之 基于annotation的Controller实现原理分析(1)

网上已有许多关于Spring源码解读的文章,但对于SpringMVC中基于annotation的Controller这一块,目前还没发现有相关源码解读的文章,这几天,一直在研究SpringMVC,小有所获。这里,对Spring中基于annotation的Controller的实现原理作下简单分析,作为以后学习的参考资料,如果有人也对此感兴趣,也欢迎一起研究,交流心得。

快速开始SpringMVC

1、导入核心JAR,有两种导入方式
   * 导入全部JAR:spring.jar
   * 导入最小JAR:spring-core、spring-beans、spring-context、spring-web、spring-webmvc
   第三方依赖JAR:commons-logging.jar
2、配置核心servlet:

	<servlet>
		<servlet-name>SpringServlet</servlet-name>
		<servlet-class>
			org.springframework.web.servlet.DispatcherServlet
		</servlet-class>
		<load-on-startup>1</load-on-startup>
	</servlet>
	<servlet-mapping>
		<servlet-name>SpringServlet</servlet-name>
		<url-pattern>*.html</url-pattern>
	</servlet-mapping>


* 3、配置包扫描列表
  在[servlet-name]-servlet中配置:

<context:component-scan base-package="com.spring.test" />


将所有基于annotation的handler放在test包下即可。

  相当简洁的配置,体现出了的Sping的强大、灵活,不过估计不会有人这样用Spring,呵呵

源码分析之旅:


SpringMVC的核心是DispatcherServlet,网上已经有对该类的简单分析,见后面参考资料。对于handler扫描、初始化映射关系等,以后有时间再详细解读,这里只是稍微提一下:
DispatcherServlet的初始化:
	protected void initStrategies(ApplicationContext context) {
		//方法入参为ApplicationContext,可证明在DispatcherServlet初始化之前,IoC容器已经开始工作了
		initMultipartResolver(context);
		initLocaleResolver(context);
		initThemeResolver(context);
		initHandlerMappings(context);
		initHandlerAdapters(context);
		initHandlerExceptionResolvers(context);
		initRequestToViewNameTranslator(context);
		initViewResolvers(context);
	}


该初始方法主要完成两件工作:
* 1、将容器中配置(在applicationContext.xml中定义)的HandlerMapping、LocaleResolver等等初始化
* 2、如果容器中未配置,则使用默认策略,该默认策略定义在DispatcherServlet.properties文件中

这其中有几个比较重要的组件(也称管道)需要初始化,包括HandlerMapping、HandlerAdapter、ViewResolver。
HandlerMapping

我们从DispatcherServlet的doService方法入手:

	protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
		...
		//将WebApplicationContext放在了request中
		request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext());
		request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);
		request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);
		request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource());

		try {
			doDispatch(request, response);
		}
		...
	}


可以看出,对于请求的处理实际上是由doDispatch()完成的,这里只对与annotation相关的部分进行分析:

	protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
	
		...		
		try {
			ModelAndView mv = null;
			try {
				//查找匹配的handler
				mappedHandler = getHandler(processedRequest, false);
				if (mappedHandler == null || mappedHandler.getHandler() == null) {
					//如果没有,写入404错误
					noHandlerFound(processedRequest, response);
					return;
				}

				//调用handler的方法
				HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
				mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
		...

	}


再来看查找handler的过程:

	protected HandlerExecutionChain getHandler(HttpServletRequest request, boolean cache) throws Exception {
		...
		//初始时handlerMappings中有两个:
		//1、BeanNameUrlHandlerMapping:根据bean的名字查找匹配的handler,意味我们可以在容器中将bean名以url定义,如"/order/*"
		//2、DefaultAnnotationHandlerMapping:根据annotation定义查找
		//每个handlerMapping中都维持有一个url-handler的HashMap,该列表在生成在初始化时完成
		Iterator it = this.handlerMappings.iterator();
		while (it.hasNext()) {
			HandlerMapping hm = (HandlerMapping) it.next();
			...
			handler = hm.getHandler(request);//实际的匹配过程交由handlerMapping完成
			...
		}
		return null;
	}


实际查找handler的过程由DefaultAnnotationHandlerMapping类完成。从它的继承层次可以看出,匹配的主要工作都由其父类完成了。在父类中定义了算法的骨架,具体的处理交由子类完成,这是Templet设计模式的典型应用。[[BR]]

先看父类AbstractHandlerMapping中定义的算法骨架:

	public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
		Object handler = getHandlerInternal(request);//交由子类实现
		if (handler == null) {
			handler = getDefaultHandler();
		}
		if (handler == null) {
			return null;
		}

		if (handler instanceof String) {	//如果handler是String,即完整类名,在容器中定义
			String handlerName = (String) handler;
			handler = getApplicationContext().getBean(handlerName);//从IoC中直接获取
		}
		return getHandlerExecutionChain(handler, request);
	}


AbstractHandlerMapping的子类AbstractUrlHandlerMapping中getHandlerInternal的定义:
	protected Object getHandlerInternal(HttpServletRequest request) throws Exception {

		Object handler = lookupHandler(lookupPath, request);
		if (handler == null) {
			...
		}
		return handler;
	}

	protected Object lookupHandler(String urlPath, HttpServletRequest request) throws Exception {
		// 直接匹配:
		Object handler = this.handlerMap.get(urlPath);
		if (handler != null) {
			validateHandler(handler, request);//@RequestMapping的其它属性匹配交由子类完成,如method和param的匹配
			return buildPathExposingHandler(handler, urlPath);
		}
		// 正则表达式匹配:
		String bestPathMatch = null;
		for (Iterator it = this.handlerMap.keySet().iterator(); it.hasNext();) {
			String registeredPath = (String) it.next();
			if (getPathMatcher().match(registeredPath, urlPath) &&
					(bestPathMatch == null || bestPathMatch.length() < registeredPath.length())) {
				bestPathMatch = registeredPath;//可以看出,匹配原则是按照url更长则更匹配
			}
		}
		if (bestPathMatch != null) {
			handler = this.handlerMap.get(bestPathMatch);
			validateHandler(handler, request);//@RequestMapping的其它属性匹配交由子类完成,如method和param的匹配
			String pathWithinMapping = getPathMatcher().extractPathWithinPattern(bestPathMatch, urlPath);
			return buildPathExposingHandler(handler, pathWithinMapping);
		}
		// No handler found...
		return null;
	}


子类DefaultAnnotationHandlerMapping中@RequestMapping的匹配过程:

	protected void validateHandler(Object handler, HttpServletRequest request) throws Exception {
		RequestMapping mapping = this.cachedMappings.get(handler.getClass());
		if (mapping == null) {
			mapping = AnnotationUtils.findAnnotation(handler.getClass(), RequestMapping.class);
		}
		if (mapping != null) {
			validateMapping(mapping, request);//具体的匹配在validateMapping中完成
		}
	}

	protected void validateMapping(RequestMapping mapping, HttpServletRequest request) throws Exception {
		RequestMethod[] mappedMethods = mapping.method();

		//请求方法是否匹配?
		if (!ServletAnnotationMappingUtils.checkRequestMethod(mappedMethods, request)) {
			String[] supportedMethods = new String[mappedMethods.length];
			for (int i = 0; i < mappedMethods.length; i++) {
				supportedMethods[i] = mappedMethods[i].name();
			}
			throw new HttpRequestMethodNotSupportedException(request.getMethod(), supportedMethods);//直接就抛异常了?似乎不妥,为什么不尝试下一个比较匹配的那个URL呢?也有可能是父类的算法定义有问题
		}

		//请求参数是否匹配?
		String[] mappedParams = mapping.params();
		if (!ServletAnnotationMappingUtils.checkParameters(mappedParams, request)) {
			throw new ServletException("Parameter conditions {" +
					StringUtils.arrayToDelimitedString(mappedParams, ", ") +
					"} not met for request parameters: " + request.getParameterMap());
		}
	}



请求方法及参数的匹配过程由ServletAnnotationMappingUtils类的静态方法完成,逻辑比较简单:

	public static boolean checkRequestMethod(RequestMethod[] methods, HttpServletRequest request) {
		if (!ObjectUtils.isEmpty(methods)) {
			boolean match = false;
			for (RequestMethod method : methods) {
				if (method.name().equals(request.getMethod())) {
					match = true;
				}
			}
			if (!match) {
				return false;
			}
		}
		return true;
	}

	public static boolean checkParameters(String[] params, HttpServletRequest request) {
		if (!ObjectUtils.isEmpty(params)) {
			for (String param : params) {
				int separator = param.indexOf('=');
				if (separator == -1) {
					if (param.startsWith("!")) {
						if (WebUtils.hasSubmitParameter(request, param.substring(1))) {
							return false;
						}
					}
					else if (!WebUtils.hasSubmitParameter(request, param)) {
						return false;
					}
				}
				else {
					String key = param.substring(0, separator);
					String value = param.substring(separator + 1);
					if (!value.equals(request.getParameter(key))) {
						return false;
					}
				}
			}
		}
		return true;
	}



至此,handler的匹配过程结束,bean的实例作为匹配的handler返回。可以看出,匹配过程并未深入到方法一级,如果类级别和方法级别都定义了url,在这一层次会忽略方法级别的。其实,spring也不推荐在类级别和方法级别同时定义url。[[BR]]
再回到DispatcherServlet中,找到匹配handler后,下一步就要去调用handler,调用的方式有许多,spring抽像出了一个接口HandlerAdapter,接口的定义:

public interface HandlerAdapter {

	boolean supports(Object handler); //是否支持此种类型的handler

	ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;	//具体的处理过程

	long getLastModified(HttpServletRequest request, Object handler);
}


DispatcherServlet中寻找合适的HandlerAdapter的过程:

	protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {

		/*
		spring默认提供四个HandlerAdapter:
			HttpRequestHandlerAdapter:处理HttpRequestHandler接口的实例
			SimpleControllerHandlerAdapter:处理Controller接口的实例
			ThrowawayControllerHandlerAdapter:已经过时
			AnnotationMethodHandlerAdapter:处理annotation定义的实例
		*/
		Iterator it = this.handlerAdapters.iterator();
		while (it.hasNext()) {
			HandlerAdapter ha = (HandlerAdapter) it.next();
			if (logger.isDebugEnabled()) {
				logger.debug("Testing handler adapter [" + ha + "]");
			}
			if (ha.supports(handler)) {
				return ha;
			}
		}
		...
	}


基于annotation的handler则是由AnnotationMethodHandlerAdapter进行处理,来看AnnotationMethodHandlerAdapter中相关处理代码:

	//是否支持此类型的handler?
	public boolean supports(Object handler) {
		return getMethodResolver(handler).hasHandlerMethods();
	}

	private ServletHandlerMethodResolver getMethodResolver(Object handler) {
		Class handlerClass = ClassUtils.getUserClass(handler);
		ServletHandlerMethodResolver resolver = this.methodResolverCache.get(handlerClass);
		if (resolver == null) {
			resolver = new ServletHandlerMethodResolver(handlerClass);//在父类的构造方法中完成handler的解析
			this.methodResolverCache.put(handlerClass, resolver);//缓存起来,方法调用时有用
		}
		return resolver;
	}

	public final boolean hasHandlerMethods() {
		return !this.handlerMethods.isEmpty();//非常简单的判断,如果该类中的方法标记有@RequestMapping就返回true,也意味着它支持此种类型的handler
	}


好了,到这里,handler已确定,由谁去处理handler也已确定,剩下的工作就是如何调用了。来看具体的调用代码:

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

		...
		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);
	}

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

		try {
			ServletHandlerMethodResolver methodResolver = getMethodResolver(handler);//缓存中已有
			Method handlerMethod = methodResolver.resolveHandlerMethod(request);//确定具体该调用哪个方法
			ServletHandlerMethodInvoker methodInvoker = new ServletHandlerMethodInvoker(methodResolver);
			ServletWebRequest webRequest = new ServletWebRequest(request, response);
			ExtendedModelMap implicitModel = new ExtendedModelMap();//方法入参ModelMap的原型

			Object result = methodInvoker.invokeHandlerMethod(handlerMethod, handler, webRequest, implicitModel);//调用方法
			ModelAndView mav =
					methodInvoker.getModelAndView(handlerMethod, handler.getClass(), result, implicitModel, webRequest);//构建ModelAndView
			methodInvoker.updateModelAttributes(
					handler, (mav != null ? mav.getModel() : null), implicitModel, webRequest);//处理ModelMap中的值
			return mav;
		}
		catch (NoSuchRequestHandlingMethodException ex) {
			return handleNoSuchRequestHandlingMethod(ex, request, response);
		}
	}


其中,确定具体调用哪个方法这个过程比较复杂,由ServletHandlerMethodResolver完成。

		public Method resolveHandlerMethod(HttpServletRequest request) throws ServletException {
			...
			for (Method handlerMethod : getHandlerMethods()) {
				RequestMappingInfo mappingInfo = new RequestMappingInfo();
				RequestMapping mapping = AnnotationUtils.findAnnotation(handlerMethod, RequestMapping.class);
				mappingInfo.paths = mapping.value();
				//如果类级别没有定义@RequestMapping,则使用方法级别定义的
				if (!hasTypeLevelMapping() || !Arrays.equals(mapping.method(), getTypeLevelMapping().method())) {
					mappingInfo.methods = mapping.method();
				}
				if (!hasTypeLevelMapping() || !Arrays.equals(mapping.params(), getTypeLevelMapping().params())) {
					mappingInfo.params = mapping.params();
				}
				boolean match = false;
				if (mappingInfo.paths.length > 0) {//先检查url是否匹配
					for (String mappedPath : mappingInfo.paths) {
						if (isPathMatch(mappedPath, lookupPath)) {
							if (checkParameters(mappingInfo, request)) {
								match = true;
								targetPathMatches.put(mappingInfo, mappedPath);
							}
							else {
								break;
							}
						}
					}
				}
				else {
					//如果没有定义url,则只需检查其它项是否匹配,如param、method
					match = checkParameters(mappingInfo, request);
					if (match && mappingInfo.methods.length == 0 && mappingInfo.params.length == 0 &&
							resolvedMethodName != null && !resolvedMethodName.equals(handlerMethod.getName())) {
						match = false;
					}
				}//如果已经有匹配了,还须检查是否有歧义的调用?
				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) {
							...
						}
					}
				}
			}//如果只有一个符合条件的方法
			if (targetHandlerMethods.size() == 1) {
				return targetHandlerMethods.values().iterator().next();
			}//如果有多个,还需确定最合适的方法
			else if (!targetHandlerMethods.isEmpty()) {
				RequestMappingInfo bestMappingMatch = null;
				String bestPathMatch = null;
				for (RequestMappingInfo mapping : targetHandlerMethods.keySet()) {
					String mappedPath = targetPathMatches.get(mapping);
					if (bestMappingMatch == null) {
						bestMappingMatch = mapping;
						bestPathMatch = mappedPath;
					}
					else {
						if (isBetterPathMatch(mappedPath, bestPathMatch, lookupPath) ||
								(!isBetterPathMatch(bestPathMatch, mappedPath, lookupPath) &&
										(isBetterMethodMatch(mapping, bestMappingMatch) ||
										(!isBetterMethodMatch(bestMappingMatch, mapping) &&
												isBetterParamMatch(mapping, bestMappingMatch))))) {
							bestMappingMatch = mapping;
							bestPathMatch = mappedPath;
						}
					}
				}
				return targetHandlerMethods.get(bestMappingMatch);
			}
			else {
				throw new NoSuchRequestHandlingMethodException(lookupPath, request.getMethod(), request.getParameterMap());
			}
		}



完成方法调用,由父类HandlerMethodInvoker完成:

	public final Object invokeHandlerMethod(
			Method handlerMethod, Object handler, NativeWebRequest webRequest, ExtendedModelMap implicitModel)
			throws Exception {
		...
		Object[] args = resolveHandlerArguments(handlerMethod, handler, webRequest, implicitModel);//参数解析

		return doInvokeMethod(handlerMethod, handler, args);
	}


至此,handler的方法调用至此结束。我们的分析也告一段落。但对于spring的处理远还未结束,后面还有许多的收尾工作,至于是什么,以后再说,呵呵

参考资料:

* 关于IOC容器的源码分析:http://www.iteye.com/wiki/Spring-source/1226-Spring%E6%BA%90%E4%BB%A3%E7%A0%81%E8%A7%A3%E6%9E%90(%E4%B8%80%EF%BC%89%EF%BC%9AIOC%E5%AE%B9%E5%99%A8
* 关于Spring MVC基于Controller接口的handler的源码分析:http://www.iteye.com/wiki/Spring-source/1222-Spring%E6%BA%90%E4%BB%A3%E7%A0%81%E8%A7%A3%E6%9E%90(%E5%9B%9B%EF%BC%89%EF%BC%9ASpring%20MVC
分享到:
评论

相关推荐

    Spring注释 注入方式源码示例,Annotation

    花了些时间做了一个实验,彻底弄懂了spring Annotation注入的方式。凡带有@Component,@Controller,@Service,@Repository 标志的等于告诉Spring这类将自动产生对象,而@Resource则等于XML配置中的ref,告诉spring此处...

    spring_MVC源码

    弃用了struts,用spring mvc框架做了几个项目,感觉都不错,而且使用了注解方式,可以省掉一大堆配置文件。本文主要介绍使用注解方式配置的spring mvc,之前写的spring3.0 mvc和rest小例子没有介绍到数据层的内容,...

    Spring MVC 学习笔记 七 controller中其他可用的annotation

    NULL 博文链接:https://starscream.iteye.com/blog/1066712

    spring-annotation:1.Spring 5.X源码分析2.手写框架3.设计模式4.Springcloud2 5.互联网高并发场景6.互联网安全架构

    Conditional,@ Import注解1.1.3 Spring5深度源码分析(三)之AnnotationConfigApplicationContext启动原理分析2.手写框架2.1手写Spring事务框架2.2手写@服务和@资源注解2.3手写SpringMVC框架(手写SpringMVC控制框...

    Spring中文帮助文档

    12.2.4. 不使用回调的基于Spring的DAO实现 12.2.5. 基于Hibernate3的原生API实现DAO 12.2.6. 编程式的事务划分 12.2.7. 声明式的事务划分 12.2.8. 事务管理策略 12.2.9. 容器资源 vs 本地资源 12.2.10. 在应用...

    Spring API

    12.2.4. 不使用回调的基于Spring的DAO实现 12.2.5. 基于Hibernate3的原生API实现DAO 12.2.6. 编程式的事务划分 12.2.7. 声明式的事务划分 12.2.8. 事务管理策略 12.2.9. 容器资源 vs 本地资源 12.2.10. 在应用...

    基于MyEclipse搭建maven+springmvc整合图文教程(含源码0

    本教程是基于MyEclipse环境下基于maven搭建的springmvc步骤(图文教程),文章结尾含有源码下载地址: 新建maven project File-&gt;New-&gt;Project 选择Maven Project 选择项目路径 Use default Workspace location默认...

    史上最全的ios开发源码

    苹果公司最早于2007年1月9日的Macworld大会上公布这个系统,最初是设计给iPhone使用的,后来陆续套用到iPod touch、iPad以及Apple TV等苹果产品上。iOS与苹果的Mac OS X操作系统一样,它也是以Darwin为基础的,因此...

    springmybatis

    mybatis实战教程mybatis in action之五与spring3集成附源码 mybatis实战教程mybatis in action之六与Spring MVC 的集成 mybatis实战教程mybatis in action之七实现mybatis分页源码下载 mybatis实战教程mybatis in ...

    毕业设计论坛源码jsp-myPlatform:我的平台

    毕业设计论坛源码jsp #计算机设计大赛项目作品 SSM,基于RESTful风格的校园论坛平台 ##4月2日 完成Spring,Spring MVC整合,项目开源放在GitHub ##4月3日 Spring MVC在运行时出现 No mapping found for HTTP request ...

Global site tag (gtag.js) - Google Analytics