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

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

 
阅读更多

上一节我们看到  当***-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接口及其子类。

 

 

 

 

 

 

 

 

 

 

 

0
1
分享到:
评论

相关推荐

    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类 。

    Spring MVC运行流程

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

    java spring mvc

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

    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源代码分析与实践

    第2章 常见协议和标准17 2.1 DNS协议17 2.2 TCP/IP协议与Socket18 2.3 HTTP协议20 2.4 Servlet与Java Web开发22 第3章 DNS的设置23 3.1 DNS解析23 3.2 Windows 7设置DNS服务器24 3.3 Windows设置本机域名和...

    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工作原理 及注解说明1

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

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

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

    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