`
zhangwei_david
  • 浏览: 468801 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

Spring MVC之HandlerMap 初始化

阅读更多

 

     DispatcherServlet请求处理请求的过程中,会发现getHandler实际上是调用AbstractUrlHandlerMapping.getHandlerInternal()。 通过对该段代码进行走读后发现,是通过handlermap.get(urlPath)获取匹配的handler的,那么该handlerMap是在什么时候进行初始化的呢?

   通过DefaultAnnotationHandlerMapping的继承关系可以去分析初始化的过程。

 



 

通过继承关系可以知道DefaultAnnotationHandlerMapping实现了ApplicationContextAware接口,这个接口的主要功能就是在Application初始化完成后会条用该接扩的setApplicationContext()方法。

 



 

 

AbstractDetectingUrlHandlerMapping是HandlerMapping初始化的关键入口,在初始化方法中首先调用父类的初始化方法,然后查找处理器。这个抽象类使用了模板方法模式,将具体的determineUrlsForHanlder方法延迟到子类中实现。
/**
 * Abstract implementation of the {@link org.springframework.web.servlet.HandlerMapping}
 * interface, detecting URL mappings for handler beans through introspection of all
 * defined beans in the application context.
 *
 * @author Juergen Hoeller
 * @since 2.5
 * @see #determineUrlsForHandler
 */
public abstract class AbstractDetectingUrlHandlerMapping extends AbstractUrlHandlerMapping {

	private boolean detectHandlersInAncestorContexts = false;


	/**
	 * Set whether to detect handler beans in ancestor ApplicationContexts.
	 * <p>Default is "false": Only handler beans in the current ApplicationContext
	 * will be detected, i.e. only in the context that this HandlerMapping itself
	 * is defined in (typically the current DispatcherServlet's context).
	 * <p>Switch this flag on to detect handler beans in ancestor contexts
	 * (typically the Spring root WebApplicationContext) as well.
	 */
	public void setDetectHandlersInAncestorContexts(boolean detectHandlersInAncestorContexts) {
		this.detectHandlersInAncestorContexts = detectHandlersInAncestorContexts;
	}


	/**
	 *在ApplicatonObjectSupport的setApplicationContext()方法中调用该方法
	 * Calls the {@link #detectHandlers()} method in addition to the
	 * superclass's initialization.
	 */
	@Override
	public void initApplicationContext() throws ApplicationContextException {
		// 调用父类的initApplicationContext()方法
		super.initApplicationContext();
		//查找Handler
		detectHandlers();
	}

	/**
	 * 注册所有能够从应用上下文中找到的handler
	 * 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());
		}
		// 从上下文中获取所有的beanNames
		String[] beanNames = (this.detectHandlersInAncestorContexts ?
				BeanFactoryUtils.beanNamesForTypeIncludingAncestors(getApplicationContext(), Object.class) :
				getApplicationContext().getBeanNamesForType(Object.class));

		// 遍历所有BeanNames
		for (String beanName : beanNames) {
			//调用子类实现方法,判定这个bean 处理的url
			String[] urls = determineUrlsForHandler(beanName);
			// 如果这个bean可以处理url则将调用父类的registerHandler方法完成 url和handler映射关系的注册
			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");
				}
			}
		}
	}


	/**
	 * Determine the URLs for the given handler bean.
	 * @param beanName the name of the candidate bean
	 * @return the URLs determined for the bean,
	 * or <code>null</code> or an empty array if none
	 */
	protected abstract String[] determineUrlsForHandler(String beanName);

}
 DefaultAnnotationHandlerMapping继承了AbstractDetectingUrlHandlerMapping,实现了父类的抽象方法,
首先查找这个类是否有RequestMapping的注解以及这个Bean中所有方法是否有这个注解
public class DefaultAnnotationHandlerMapping extends AbstractDetectingUrlHandlerMapping {

	static final String USE_DEFAULT_SUFFIX_PATTERN = DefaultAnnotationHandlerMapping.class.getName() + ".useDefaultSuffixPattern";

	private boolean useDefaultSuffixPattern = true;

	private final Map<Class<?>, RequestMapping> cachedMappings = new HashMap<Class<?>, RequestMapping>();


	/**
	 * Set whether to register paths using the default suffix pattern as well:
	 * i.e. whether "/users" should be registered as "/users.*" and "/users/" too.
	 * <p>Default is "true". Turn this convention off if you intend to interpret
	 * your <code>@RequestMapping</code> paths strictly.
	 * <p>Note that paths which include a ".xxx" suffix or end with "/" already will not be
	 * transformed using the default suffix pattern in any case.
	 */
	public void setUseDefaultSuffixPattern(boolean useDefaultSuffixPattern) {
		this.useDefaultSuffixPattern = useDefaultSuffixPattern;
	}


	/**
	 * Checks for presence of the {@link org.springframework.web.bind.annotation.RequestMapping}
	 * annotation on the handler class and on any of its methods.
	 */
	@Override
	protected String[] determineUrlsForHandler(String beanName) {
		ApplicationContext context = getApplicationContext();
		Class<?> handlerType = context.getType(beanName);
		RequestMapping mapping = context.findAnnotationOnBean(beanName, RequestMapping.class);
		if (mapping != null) {
			// @RequestMapping found at type level
			this.cachedMappings.put(handlerType, mapping);
			Set<String> urls = new LinkedHashSet<String>();
			String[] typeLevelPatterns = mapping.value();
			if (typeLevelPatterns.length > 0) {
				// @RequestMapping specifies paths at type level
				String[] methodLevelPatterns = determineUrlsForHandlerMethods(handlerType, true);
				for (String typeLevelPattern : typeLevelPatterns) {
					if (!typeLevelPattern.startsWith("/")) {
						typeLevelPattern = "/" + typeLevelPattern;
					}
					boolean hasEmptyMethodLevelMappings = false;
					for (String methodLevelPattern : methodLevelPatterns) {
						if (methodLevelPattern == null) {
							hasEmptyMethodLevelMappings = true;
						}
						else {
							String combinedPattern = getPathMatcher().combine(typeLevelPattern, methodLevelPattern);
							addUrlsForPath(urls, combinedPattern);
						}
					}
					if (hasEmptyMethodLevelMappings ||
							org.springframework.web.servlet.mvc.Controller.class.isAssignableFrom(handlerType)) {
						addUrlsForPath(urls, typeLevelPattern);
					}
				}
				return StringUtils.toStringArray(urls);
			}
			else {
				// actual paths specified by @RequestMapping at method level
				return determineUrlsForHandlerMethods(handlerType, false);
			}
		}
		else if (AnnotationUtils.findAnnotation(handlerType, Controller.class) != null) {
			// @RequestMapping to be introspected at method level
			return determineUrlsForHandlerMethods(handlerType, false);
		}
		else {
			return null;
		}
	}

	/**
	 * Derive URL mappings from the handler's method-level mappings.
	 * @param handlerType the handler type to introspect
	 * @param hasTypeLevelMapping whether the method-level mappings are nested
	 * within a type-level mapping
	 * @return the array of mapped URLs
	 */
	protected String[] determineUrlsForHandlerMethods(Class<?> handlerType, final boolean hasTypeLevelMapping) {
		//在该类中返回值一直是null
		String[] subclassResult = determineUrlsForHandlerMethods(handlerType);
		if (subclassResult != null) {
			return subclassResult;
		}
		// 创建一个set,之所以使用set就是set中不会有重复数据
		final Set<String> urls = new LinkedHashSet<String>();
		Set<Class<?>> handlerTypes = new LinkedHashSet<Class<?>>();
		//添加类以及所有接口
		handlerTypes.add(handlerType);
		handlerTypes.addAll(Arrays.asList(handlerType.getInterfaces()));
		// 遍历handlerTypes
		for (Class<?> currentHandlerType : handlerTypes) {
		// 反射处理
			ReflectionUtils.doWithMethods(currentHandlerType, new ReflectionUtils.MethodCallback() {
				public void doWith(Method method) {
				   // 获取所有该方法上的RequestMapping注解
					RequestMapping mapping = AnnotationUtils.findAnnotation(method, RequestMapping.class);
					// 如果存在RequestMapping注解
					if (mapping != null) {
						// 获取注解 value属性值
						String[] mappedPatterns = mapping.value();
						// 如果value的值不为空
						if (mappedPatterns.length > 0) {
							// 遍历mappedPatterns
							for (String mappedPattern : mappedPatterns) {
							    //如果hasTypeLevelMapping=false,同时mappedPattern不是以"/"开头则在其前面加上"/"
								if (!hasTypeLevelMapping && !mappedPattern.startsWith("/")) {
									mappedPattern = "/" + mappedPattern;
								}
								//将url添加在set中
								addUrlsForPath(urls, mappedPattern);
							}
						}
						else if (hasTypeLevelMapping) {
							// empty method-level RequestMapping
							urls.add(null);
						}
					}
				}
			}, ReflectionUtils.USER_DECLARED_METHODS);
		}
		return StringUtils.toStringArray(urls);
	}

	/**
	 * Derive URL mappings from the handler's method-level mappings.
	 * @param handlerType the handler type to introspect
	 * @return the array of mapped URLs
	 */
	protected String[] determineUrlsForHandlerMethods(Class<?> handlerType) {
		return null;
	}

	/**
	 * Add URLs and/or URL patterns for the given path.
	 * @param urls the Set of URLs for the current bean
	 * @param path the currently introspected path
	 */
	protected void addUrlsForPath(Set<String> urls, String path) {
		urls.add(path);
		if (this.useDefaultSuffixPattern && path.indexOf('.') == -1 && !path.endsWith("/")) {
			urls.add(path + ".*");
			urls.add(path + "/");
		}
	}


	/**
	 * Validate the given annotated handler against the current request.
	 * @see #validateMapping
	 */
	@Override
	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);
		}
		request.setAttribute(USE_DEFAULT_SUFFIX_PATTERN, this.useDefaultSuffixPattern);
	}

	/**
	 * Validate the given type-level mapping metadata against the current request,
	 * checking HTTP request method and parameter conditions.
	 * @param mapping the mapping metadata to validate
	 * @param request current HTTP request
	 * @throws Exception if validation failed
	 */
	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);
		}

		String[] mappedParams = mapping.params();
		if (!ServletAnnotationMappingUtils.checkParameters(mappedParams, request)) {
			throw new UnsatisfiedServletRequestParameterException(mappedParams, request.getParameterMap());
		}

		String[] mappedHeaders = mapping.headers();
		if (!ServletAnnotationMappingUtils.checkHeaders(mappedHeaders, request)) {
			throw new ServletRequestBindingException("Header conditions \"" +
					StringUtils.arrayToDelimitedString(mappedHeaders, ", ") +
					"\" not met for actual request");
		}
	}

	@Override
	protected boolean supportsTypeLevelMappings() {
		return true;
	}
}
 

 

 

public abstract class WebApplicationObjectSupport extends ApplicationObjectSupport
		implements ServletContextAware {

	private ServletContext servletContext;

	//由于该类实现了ServletContextAware接口,在ServletContext初始化完成后,调用该方法
	public final void setServletContext(ServletContext servletContext) {
		if (servletContext != this.servletContext) {
			this.servletContext = servletContext;
			if (servletContext != null) {
				initServletContext(servletContext);
			}
		}
	}

	@Override
	protected boolean isContextRequired() {
		return true;
	}

	/**
	 * 在ApplicationContext初始完成后调用该方法,如果已用上下文是WebApplicationContext则调用initServeltContext
	 */
	@Override
	protected void initApplicationContext(ApplicationContext context) {
		// 调用父类initApplicationContext,也就是调用ApplicationObjectSupport.initApplictionContext()方法
		super.initApplicationContext(context);
		if (this.servletContext == null && context instanceof WebApplicationContext) {
			this.servletContext = ((WebApplicationContext) context).getServletContext();
			if (this.servletContext != null) {
				// 模板方法模式,具体实现延迟到子类中进行
				initServletContext(this.servletContext);
			}
		}
	}

	/**
	 * Subclasses may override this for custom initialization based
	 * on the ServletContext that this application object runs in.
	 * <p>The default implementation is empty. Called by
	 * {@link #initApplicationContext(org.springframework.context.ApplicationContext)}
	 * as well as {@link #setServletContext(javax.servlet.ServletContext)}.
	 * @param servletContext the ServletContext that this application object runs in
	 * (never <code>null</code>)
	 */
	protected void initServletContext(ServletContext servletContext) {
	}

 


 

 

  • 大小: 51.8 KB
  • 大小: 61.3 KB
1
0
分享到:
评论

相关推荐

    精通Spring MVC 4

    Spring MVC属于SpringFrameWork的后续产品,已经融合在Spring Web Flow里面。Spring 框架提供了构建 Web 应用程序的全功能 MVC 模块。Spring MVC4是当前zuixin的版本,在众多特性上有了进一步的提升。, 在精通Spring...

    Spring MVC启动时初始化的几个常用方法

    NULL 博文链接:https://bijian1013.iteye.com/blog/2296743

    spring mvc项目

    可用于分析spring mvc源码、spring mvc父子容器初始化流程、session和cookie机制、spring session等,也可以用于学习Java Web(servlet、filter、listener等)、spring源码等。 该项目使用servlet3.0规范,无web.xml...

    spring mvc 官方文档

    本文详细介绍spring MVC的原理和开发心得体会。

    精通Spring MVC 4 中文

    精通Spring MVC 4 中文

    Spring MVC 基于注解实例

    Spring MVC 基于注解实例Spring MVC 基于注解实例Spring MVC 基于注解实例Spring MVC 基于注解实例Spring MVC 基于注解实例Spring MVC 基于注解实例Spring MVC 基于注解实例Spring MVC 基于注解实例Spring MVC 基于...

    Spring mvc 国际化

    Spring MVC 国际化的配置方法及使用,希望能对大家的学习、工作有帮助。

    [免费]Spring MVC学习指南(高清)

    全书共计12章,分别从Spring框架、模型2和MVC模式、Spring MVC介绍、控制器、数据绑定和表单标签库、传唤器和格式化、验证器、表达式语言、JSTL、国际化、上传文件、下载文件多个角度介绍了Spring MVC。除此之外,...

    Spring MVC所需jar包

    Spring MVC所需jar包,包含java开发中 Spring MVC架构中最常用的jar包

    Spring MVC+MyBatis开发从入门到项目实战

    《Spring MVC+MyBatis开发从入门到项目实战》分为4篇。第1篇是Java开发环境的搭建,包括JDK的下载与安装、环境变量的配置、MyEclipse的下载与基本配置。第2篇是MyBatis技术入门,包括剖析JDBC的弊端、MyBatis的背景...

    大优惠 Spring MVC学习指南(第2版)2017.pdf

    全书共计12章,分别从Spring框架、模型2和MVC模式、Spring MVC介绍、控制器、数据绑定和表单标签库、传唤器和格式化、验证器、表达式语言、JSTL、国际化、上传文件、下载文件多个角度介绍了Spring MVC。除此之外,...

    Spring MVC jar包

    关于构建Sping MVC的Jar包,包括Sping2.5.6和Hibernate3.6.8

    Spring MVC 入门实例

    首先, 我需要在你心里建立起 Spring MVC 的基本概念. 基于 Spring 的 Web 应用程序接收到 http://localhost:8080/hello.do(事实上请求路径是 /hello.do) 的请求后, Spring 将这个请求交给一个名为 helloController ...

    spring MVC数据绑定大全

    spring MVC数据绑定 含例子 转载自疯芒毕露的专栏 刚开始用spring mvc 做web开发时 经常会不知道如何合适绑定页面数据 用惯struts2的朋友更认为spring mvc 绑定数据不如struts2方便 本人最开始也是这么认为 经过一段...

    spring mvc源代码

    spring mvc4.1.4 源代码 spring mvc4.1.4 源代码spring mvc4.1.4 源代码spring mvc4.1.4 源代码spring mvc4.1.4 源代码

    Spring MVC 4.2.3

    Spring mvc jar包

    Servlet JSP和Spring MVC初学指南

    Servlet JSP和Spring MVC初学指南

    Spring Bean创建初始化流程.docx

    Spring Bean创建初始化流程

Global site tag (gtag.js) - Google Analytics