- 浏览: 84890 次
- 性别:
- 来自: 西安
最新评论
-
w67856101:
sssss
在Spring中使用jcaptcha实现图片验证 -
yjq8116:
不错。很有收获
Spring MVC 源码学习札记(四)HandlerMapping和HandlerAdapter(4) -
rolenz:
mongodb越来越火,但是网上的资料不是很全,希望深入的写些 ...
MongoDB学习(二)索引的用法 -
sha0k:
rolenz 写道能不能说说replSet到底如何进行认证我才 ...
MongoDB学习(一)安装和基本使用 -
rolenz:
能不能说说replSet到底如何进行认证
MongoDB学习(一)安装和基本使用
上一节我们看到 当***-serlvet.xml中未声明HandlerMapping的实体bean时,默认构造
org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping和
org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping
的实例。
所以本节就从这两个类入手研究,HandlerMapping中最重要的方法是 getHandler 用于获取HandlerExcutionChain的对象。
先来看BeanNameUrlHandlerMapping类,它的内部没有实现getHandler方法,而它继承自AbstractDetectingUrlHandlerMapping,所以自底向上我们去查看AbstractDetectingUrlHandlerMapping类的实现。
在这个类中,我看到了一个方法
/** * Register all handlers found in the current ApplicationContext. * <p>The actual URL determination for a handler is up to the concrete * {@link #determineUrlsForHandler(String)} implementation. A bean for * which no such URLs could be determined is simply not considered a handler. * @throws org.springframework.beans.BeansException if the handler couldn't be registered * @see #determineUrlsForHandler(String) */ protected void detectHandlers() throws BeansException { if (logger.isDebugEnabled()) { logger.debug("Looking for URL mappings in application context: " + getApplicationContext()); } String[] beanNames = (this.detectHandlersInAncestorContexts ? BeanFactoryUtils.beanNamesForTypeIncludingAncestors(getApplicationContext(), Object.class) : getApplicationContext().getBeanNamesForType(Object.class)); // Take any bean name that we can determine URLs for. for (String beanName : beanNames) { String[] urls = determineUrlsForHandler(beanName); if (!ObjectUtils.isEmpty(urls)) { // URL paths found: Let's consider it a handler. registerHandler(urls, beanName); } else { if (logger.isDebugEnabled()) { logger.debug("Rejected bean name '" + beanName + "': no URL paths identified"); } } } }
whether to detect handler beans in ancestor ApplicationContexts.
private boolean detectHandlersInAncestorContexts = false;
该方法首先查询了上下文中所有的bean,因为查询的Type是Object.class,得到了所有的bean名称,之后解析bean名称,查看是否存是urlName,determineUrlsForHandler方法在BeanNameUrlHandlerMapping类中具体实现:
/** * Checks name and aliases of the given bean for URLs, starting with "/". */ @Override protected String[] determineUrlsForHandler(String beanName) { List<String> urls = new ArrayList<String>(); if (beanName.startsWith("/")) { urls.add(beanName); } String[] aliases = getApplicationContext().getAliases(beanName); for (int i = 0; i < aliases.length; i++) { if (aliases[i].startsWith("/")) { urls.add(aliases[i]); } } return StringUtils.toStringArray(urls); }
可以看出它查找了所有beanName以“/”开头的bean,将它们的name作为url返回。
接下来就对那些作为处理url请求的bean进行handler的注册,registerHandler方法在AbstractDetectingUrlHandlerMapping的父类AbstractUrlHandlerMapping类中:
/** * Register the specified handler for the given URL paths. * @param urlPaths the URLs that the bean should be mapped to * @param beanName the name of the handler bean * @throws BeansException if the handler couldn't be registered * @throws IllegalStateException if there is a conflicting handler registered */ protected void registerHandler(String[] urlPaths, String beanName) throws BeansException, IllegalStateException { Assert.notNull(urlPaths, "URL path array must not be null"); for (String urlPath : urlPaths) { registerHandler(urlPath, beanName); } }
/** * Register the specified handler for the given URL path. * @param urlPath the URL the bean should be mapped to * @param handler the handler instance or handler bean name String * (a bean name will automatically be resolved into the corresponding handler bean) * @throws BeansException if the handler couldn't be registered * @throws IllegalStateException if there is a conflicting handler registered */ protected void registerHandler(String urlPath, Object handler) throws BeansException, IllegalStateException { Assert.notNull(urlPath, "URL path must not be null"); Assert.notNull(handler, "Handler object must not be null"); Object resolvedHandler = handler; // Eagerly resolve handler if referencing singleton via name. if (!this.lazyInitHandlers && handler instanceof String) { String handlerName = (String) handler; if (getApplicationContext().isSingleton(handlerName)) { resolvedHandler = getApplicationContext().getBean(handlerName); } } Object mappedHandler = this.handlerMap.get(urlPath); if (mappedHandler != null) { if (mappedHandler != resolvedHandler) { throw new IllegalStateException( "Cannot map handler [" + handler + "] to URL path [" + urlPath + "]: There is already handler [" + resolvedHandler + "] mapped."); } } else { if (urlPath.equals("/")) { if (logger.isInfoEnabled()) { logger.info("Root mapping to handler [" + resolvedHandler + "]"); } setRootHandler(resolvedHandler); } else if (urlPath.equals("/*")) { if (logger.isInfoEnabled()) { logger.info("Default mapping to handler [" + resolvedHandler + "]"); } setDefaultHandler(resolvedHandler); } else { this.handlerMap.put(urlPath, resolvedHandler); if (logger.isInfoEnabled()) { logger.info("Mapped URL path [" + urlPath + "] onto handler [" + resolvedHandler + "]"); } } } }
handlerMap就是一个K-V保存url对应的handler,初始时我们传入的Object handler参数是String对象beanNames,lazyInit表示是延迟初始化handler,如果设为true那么在注册handler的时候我们存入的handler还是String,如果是false,该方法中被用来取得bean实例,所以handlerMap中存放的V就是bean的对象,使用Object泛型是因为bean的不一致。
private final Map<String, Object> handlerMap = new LinkedHashMap<String, Object>();
注意上面的setDefaultHandler方法,它是AbstractUrlHandlerMapping类的父类AbstractHandlerMapping中的方法,在AbstractHandlerMapping中有一个Object对象属性:
private Object defaultHandler;
从字面意思可以理解为默认的处理器,因为它对应的映射是"/*",即处理所有符合该表达式的url请求。
阅读完BeanNameUrlHandlerMapping和AbstractDetectingUrlHandlerMapping两个类后,我们发现它们中的方法主要进行了handler的set,而handler的get却没有在它们中体现,所以再向上走,我们查看最高的父类AbstractHandlerMapping类,它直接实现了HandlerMapping接口,所以剖析它的getHandler方法更容易让我们理解,handler获取的流程。
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception { Object handler = getHandlerInternal(request); if (handler == null) { handler = getDefaultHandler(); } if (handler == null) { return null; } // Bean name or resolved handler? if (handler instanceof String) { String handlerName = (String) handler; handler = getApplicationContext().getBean(handlerName); } return getHandlerExecutionChain(handler, request); }
该方法首先调用了getHandlerInternal处理request,getHandlerInternal是其子类AbstractUrlHandlerMapping实现的方法,用来查询handlerMap查找request的url对应的handler。
@Override protected Object getHandlerInternal(HttpServletRequest request) throws Exception { String lookupPath = this.urlPathHelper.getLookupPathForRequest(request); Object handler = lookupHandler(lookupPath, request); if (handler == null) { // We need to care for the default handler directly, since we need to // expose the PATH_WITHIN_HANDLER_MAPPING_ATTRIBUTE for it as well. Object rawHandler = null; if ("/".equals(lookupPath)) { rawHandler = getRootHandler(); } if (rawHandler == null) { rawHandler = getDefaultHandler(); } if (rawHandler != null) { // Bean name or resolved handler? if (rawHandler instanceof String) { String handlerName = (String) rawHandler; rawHandler = getApplicationContext().getBean(handlerName); } validateHandler(rawHandler, request); handler = buildPathExposingHandler(rawHandler, lookupPath, lookupPath, null); } } if (handler != null && this.mappedInterceptors != null) { Set<HandlerInterceptor> mappedInterceptors = this.mappedInterceptors.getInterceptors(lookupPath, this.pathMatcher); if (!mappedInterceptors.isEmpty()) { HandlerExecutionChain chain; if (handler instanceof HandlerExecutionChain) { chain = (HandlerExecutionChain) handler; } else { chain = new HandlerExecutionChain(handler); } chain.addInterceptors(mappedInterceptors.toArray(new HandlerInterceptor[mappedInterceptors.size()])); } } if (handler != null && logger.isDebugEnabled()) { logger.debug("Mapping [" + lookupPath + "] to handler '" + handler + "'"); } else if (handler == null && logger.isTraceEnabled()) { logger.trace("No handler mapping found for [" + lookupPath + "]"); } return handler; }
下面来分析getHandlerInternal方法内部,第一句使用类中UrlPathHelper对象urlPathHelper调用getLookupPathForRequest方法解析request,从而得到请求的urlPath。
第二句调用lookupHandler方法,用于查找url path对应的handler,从上面的regist我们可以很快理解,这里肯定是在handlerMap中进行查找,它是AbstractUrlHandlerMapping实现的方法:
/** *Look up a handler instance for the given URL path. */ protected Object lookupHandler(String urlPath, HttpServletRequest request) throws Exception { // Direct match? Object handler = this.handlerMap.get(urlPath); if (handler != null) { // Bean name or resolved handler? if (handler instanceof String) { String handlerName = (String) handler; handler = getApplicationContext().getBean(handlerName); } validateHandler(handler, request); return buildPathExposingHandler(handler, urlPath, urlPath, null); } // Pattern match? List<String> matchingPatterns = new ArrayList<String>(); for (String registeredPattern : this.handlerMap.keySet()) { if (getPathMatcher().match(registeredPattern, urlPath)) { matchingPatterns.add(registeredPattern); } } String bestPatternMatch = null; if (!matchingPatterns.isEmpty()) { Collections.sort(matchingPatterns, getPathMatcher().getPatternComparator(urlPath)); if (logger.isDebugEnabled()) { logger.debug("Matching patterns for request [" + urlPath + "] are " + matchingPatterns); } bestPatternMatch = matchingPatterns.get(0); } if (bestPatternMatch != null) { handler = this.handlerMap.get(bestPatternMatch); // Bean name or resolved handler? if (handler instanceof String) { String handlerName = (String) handler; handler = getApplicationContext().getBean(handlerName); } validateHandler(handler, request); String pathWithinMapping = getPathMatcher().extractPathWithinPattern(bestPatternMatch, urlPath); Map<String, String> uriTemplateVariables = getPathMatcher().extractUriTemplateVariables(bestPatternMatch, urlPath); return buildPathExposingHandler(handler, bestPatternMatch, pathWithinMapping, uriTemplateVariables); } // No handler found... return null; }
果不其然,从上面的注释//直接匹配 我们可以看到该方法查找handlerMap中key为urlPath的handler value。
当查找到后,判断我们取出的handler是否是String对象,因为前面我们已经说明了lazyInit,所以这里不难理解。如果是String那么就构造bean,如果不是代码就继续向下走,请允许我先跳过buildPathExposingHandler方法,因为后面还会用到,在这里不是至关重要的,等下再说明它。
当直接匹配没有成功的时候,就进行//模式匹配,从handlerMap的keySet中查找到最符合urlPath模式的key,从而得到handler,如果没有找到,那就返回null。
回到getHandlerInternal方法中,当我们没有查找到urlPath对应的handler时,也就是返回handler==null,则进入第一个if条件中,首先要检查两种情况,因为我们在注册时将"/"set到了rootHandler中,而将"/*"set到了defaultHandler中,当从这两种情况中的一种得到handler后,也是判断是否为String,如果是则构造bean,然后调用buildPathExposingHandler方法。
细心的朋友会注意到getDefaultHandler方法在AbstractHandlerMapping的getHandler方法也被调用了一次,这是当getHandlerInternal返回null时调用的。
到这里我们就来介绍一下buildPathExposingHandler方法:
protected Object buildPathExposingHandler(Object rawHandler, String bestMatchingPattern, String pathWithinMapping, Map<String, String> uriTemplateVariables) { HandlerExecutionChain chain = new HandlerExecutionChain(rawHandler); chain.addInterceptor(new PathExposingHandlerInterceptor(bestMatchingPattern, pathWithinMapping)); if (!CollectionUtils.isEmpty(uriTemplateVariables)) { chain.addInterceptor(new UriTemplateVariablesHandlerInterceptor(uriTemplateVariables)); } return chain; }
该方法中,使用handler作为参数构造了一个HandlerExecutionChain的对象,不熟悉HandlerExecutionChain的朋友可以打开该类的源码,可以看到它保存了两个最重要的对象,一个handler和一组interceptor
private final Object handler; private HandlerInterceptor[] interceptors; private List<HandlerInterceptor> interceptorList;
然后调用addInterceptor方法,为chain添加了一个HandlerInterceptor----PathExposingHandlerInterceptor
public void addInterceptor(HandlerInterceptor interceptor) { initInterceptorList(); this.interceptorList.add(interceptor); }
private void initInterceptorList() { if (this.interceptorList == null) { this.interceptorList = new ArrayList<HandlerInterceptor>(); } if (this.interceptors != null) { this.interceptorList.addAll(Arrays.asList(this.interceptors)); this.interceptors = null; } }
当uriTemplateVariables(这是模式匹配时传入buildPathExposingHandler的Map)不为空时,又添加一个HandlerInterceptor-----UriTemplateVariablesHandlerInterceptor
上述的两个interceptor都是AbstractUrlHandlerMapping类的内部类,这里我们先不讨论拦截器,等后面的文章会进行详细阅读。
后面直到getHandler方法完结,均是对handler和handlerinceptor的拼装,就先不介绍了。后面会着重阅读HandlerInceptor接口及其子类。
发表评论
-
SpringMVC源码学习札记(六)DispatcherServlet类在3.2中的变化
2013-08-07 22:48 2257protected void doDispatch(Htt ... -
Spring MVC 源码学习札记(五)说说ReflectionUtils
2012-01-30 21:33 7814这里主要是为了解决我前面遇到的问题,因为在默认注解Defaul ... -
Spring MVC 源码学习札记(四)HandlerMapping和HandlerAdapter(4)
2012-01-30 22:55 8176每天敦促自己阅读spring源码 绝不松懈 下面是当没有注册 ... -
Spring MVC 源码学习札记(四)HandlerMapping和HandlerAdapter(3)
2012-01-29 13:44 3882上一节主要阅读了BeanNameUrlHandlerMappi ... -
Spring MVC 源码学习札记(四)HandlerMapping和HandlerAdapter(1)
2012-01-28 13:35 7630在分析HandlerMapping和HandlerAdapte ... -
Spring MVC 源码学习札记(三)看看View怎么做到视图转发的
2012-01-21 00:16 3237上一节看完了ViewResolver解析视图名产生View的过 ... -
Spring MVC 源码学习札记(二)ViewResolver处理viewname
2012-01-19 17:53 6638第一节我们看到了ViewResolver对ModelAndVi ... -
Spring MVC 源码学习札记(一)DispatcherServlet阅读
2012-01-19 15:06 15007马上过年了~闲来无事,阅读Spring源码提升自己,做事要有个 ...
相关推荐
在本篇博客“SpringMVC源码总结(一)HandlerMapping和HandlerAdapter入门”中,我们将探讨Spring MVC框架的核心组件——HandlerMapping和HandlerAdapter。这两个组件是Spring MVC处理请求的关键,它们负责映射HTTP...
Spring MVC是Java EE开发中广泛使用的一个Web框架,它是Spring框架的一部分,主要负责Model-View-Controller(MVC)架构模式的...通过学习和实践,开发者可以更有效地利用Spring MVC框架来构建高性能的Web应用程序。
标题中的"开发Spring MVC应用程序补充—程序源码下载.rar_spring_spring mvc_spring mvc 源码_sp"表明这是一个关于Spring MVC框架的开发教程,其中包含了源代码供学习者参考。Spring MVC是Spring框架的一个核心组件...
通过深入学习这个 Spring MVC 项目源码,你可以掌握 MVC 设计模式的运用,了解 Spring 框架的组件协同工作方式,以及在实际项目中的最佳实践。这将有助于提升你的 Java Web 开发技能,更好地理解和使用 Spring MVC。
- 学习Spring MVC和MyBatis的整合,可以参考官方文档、教程和开源项目的源码。 - 通过调试源码,理解其实现原理,有助于提升实战技能。 总结,这个压缩包提供了一个完整的Spring MVC和MyBatis整合的示例,包含了...
而`spring-framework-2.5.6-with-docs.zip`可能包含了Spring 2.5.6的源码和文档,帮助开发者了解Spring MVC的内部实现和最佳实践。 总之,这个压缩包提供了开发基于Spring MVC和Hibernate的Java Web应用所需要的...
【Spring5MVC源码分析】 Spring MVC 是一个基于Java的、用于构建Web应用程序的、高度可插拔的MVC框架,它是Spring Framework的重要组成部分。Spring MVC的核心目标是简化前端控制器的开发,使得开发者可以专注于...
本书首先介绍了Spring MVC的基础知识,包括如何搭建Spring MVC环境,以及它的核心组件如DispatcherServlet、ModelAndView、HandlerMapping和HandlerAdapter等的工作原理。这些组件在Web请求处理过程中扮演着重要角色...
在 Spring MVC 中,核心类和接口是 DispatcherServlet、HandlerMapping、HandlerAdapter、Controller、ViewResolver、HandlerInterceptor、View 等。 DispatcherServlet 是 Spring MVC 的前置控制器,它负责处理...
这个"Spring MVC 基础实例源码01"的资源很可能是为了帮助初学者理解Spring MVC的核心概念和基本用法。下面我们将详细探讨Spring MVC的一些关键知识点。 1. **MVC模式**:MVC(Model-View-Controller)是一种设计...
### Spring MVC核心组件之HandlerMapping详解 #### 一、引言 在Java Web开发领域,Spring MVC框架因其灵活且强大的特性而备受青睐。...理解和掌握`HandlerMapping`的工作机制对于深入学习Spring MVC框架至关重要。
通过深入研究源码,我们可以更好地理解Spring MVC的运行机制,如何配置和使用其特性,以及如何优化和调试Spring MVC应用。对于开发人员来说,掌握Spring MVC的源码分析有助于提升Web应用开发的效率和质量。
- 创建Spring MVC的配置文件,例如`servlet-context.xml`,配置HandlerMapping、HandlerAdapter、ViewResolver等组件。 - 在配置文件中声明Controller Bean,通常使用注解@Controller。 3. **注解驱动开发**: -...
在这个学习记录总结中,我们将深入理解Spring MVC的核心概念、主要组件以及其工作流程。 1. Spring MVC 概述 Spring MVC 是Spring框架的一部分,它基于Spring IoC(Inversion of Control,控制反转)容器,简化了...
2. **配置 Spring**:在 `applicationContext.xml` 或者 Spring Boot 的配置文件中,配置 Spring MVC 的 DispatcherServlet、HandlerMapping 和 HandlerAdapter。同时,配置 Spring 的 DataSource,这是连接数据库的...
spring-webmvc-5.0.9.RELEASE-sources.jar则包含了源码,可以深入研究Spring Web MVC的实现细节,对于学习和调试都非常有帮助。 九、依赖管理 在实际项目中,Spring Web MVC往往与其他Spring模块如Core、AOP、Data...
这份“Spring.MVC学习指南.pdf”很可能是为了帮助开发者深入理解和掌握Spring MVC的核心概念、工作原理以及实践技巧。下面将详细阐述Spring MVC的相关知识点。 1. **核心组件**: - **DispatcherServlet**:Spring...
3. **创建Spring MVC配置**:创建一个XML文件(如`servlet-context.xml`),配置HandlerMapping、HandlerAdapter、视图解析器等。 4. **编写Controller**:创建一个名为`GreetingController`的Java类,使用`@...
8. **配置文件**:理解Spring MVC的配置文件,如`servlet-context.xml`,它定义了Spring MVC的组件,如视图解析器、HandlerMapping和HandlerAdapter等。 9. **Maven或者Gradle构建**:项目可能使用Maven或Gradle...
此外,该项目还展示了如何配置 Spring MVC,例如在 web.xml 中配置 DispatcherServlet,以及在 Spring 配置文件中定义 HandlerMapping 和 HandlerAdapter 等。这有助于理解 Spring MVC 的工作流程。 "spring-mvc-...