`
zhangwei_david
  • 浏览: 476113 次
  • 性别: 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启动时初始化的几个常用方法

    在Spring MVC框架中,应用程序启动时会执行一系列初始化操作,这些操作对于理解Spring MVC的工作原理至关重要。本篇文章将深入探讨Spring MVC启动时初始化的几个常用方法,并解释它们在实际开发中的作用。 首先,...

    精通Spring MVC 4

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

    Spring mvc 国际化

    Spring MVC的国际化(Internationalization)指的是将应用根据不同地域、语言习惯以及文化差异进行本地化的过程。这涉及了软件应用对多语言的支持,以便不同区域的用户能够以自己的母语使用软件。Spring MVC提供了...

    spring mvc 4.0

    14. **性能优化**:Spring MVC 4.0在内部做了许多性能优化,包括更快的DispatcherServlet初始化、更高效的HTTP方法映射等。 15. **兼容性与兼容性测试**:Spring MVC 4.0确保与各种Web容器的兼容性,如Tomcat、...

    最全最经典spring-mvc教程

    Spring MVC 是一款强大的Java Web开发框架,用于构建高效、可维护和模块化的Web应用程序。它作为Spring框架的一部分,提供了一种优雅的方式来处理HTTP请求和响应,使得开发者可以专注于业务逻辑而不是底层实现。在这...

    Spring MVC 4.2.3

    10. **国际化和本地化**:通过消息源和LocaleResolver,Spring MVC可以轻松实现应用的多语言支持。 在实际开发中,我们可以通过Maven或Gradle等构建工具将Spring MVC 4.2.3依赖引入项目。同时,使用IDE如IntelliJ ...

    spring mvc项目

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

    Spring MVC + Mybatis+Spring实现的个人博客系统

    wpblog.sql可能是数据库的初始化脚本,用于创建这些表结构并可能包含一些初始数据。MySQL是常用的开源关系型数据库管理系统,适用于中小型Web应用,具有良好的性能和稳定性。 **5. 文件结构** - `wpblog.sql`: 这个...

    Spring.MVC-A.Tutorial-Spring.MVC学习指南 高清可复制版PDF

    除此之外,Spring MVC还支持数据绑定、验证、本地化、主题、异常处理等功能。例如,使用@ModelAttribute注解可以将请求参数绑定到Controller方法的参数上,@Valid用于进行数据验证,Validator接口可以自定义验证逻辑...

    Mastering Spring MVC 4(2015.09)源码

    Spring MVC 是一个强大的Java Web开发框架,它是Spring框架的一部分,专为构建高度可扩展和模块化的Web应用程序而设计。在2015年的版本中,Spring MVC 4已经相当成熟,提供了许多特性来简化开发流程并提高开发效率。...

    spring MVC .docx

    DispatcherServlet通过`web.xml`配置文件或Java配置类进行初始化配置。 2. **Controller**: 控制器是实现业务逻辑的类,通常由开发者编写。它们处理HTTP请求,调用业务服务,然后返回一个模型(Model)和视图(View...

    Spring MVC所需jar包

    Spring MVC 是一个基于 Java 的轻量级 Web 开发框架,它是 Spring 框架的一个重要模块,主要用于构建 Web 应用程序的后端控制层。这个框架提供了模型-视图-控制器(MVC)设计模式的实现,简化了Java Web应用的开发...

    Spring MVC 教程快速入门 深入分析

    Spring MVC是一种基于Java的实现了MVC设计模式的请求驱动类型的轻量级Web框架,使用了IoC容器,支持RESTful风格的应用程序开发。Spring MVC通过分离模型(Model)、视图(View)和控制器(Controller)来简化Web开发...

    spring mvc mybatis 整合源码,带数据库脚本,带详细注释

    Spring MVC 和 MyBatis 是两个在Java Web 开发中广泛使用的框架。Spring MVC 作为Spring框架的一部分,负责处理HTTP请求并转发到相应的业务逻辑,而MyBatis则是一个轻量级的持久层框架,用于简化数据库操作。整合这...

    Spring MVC框架简介和使用

    DispatcherServlet 是 Spring MVC 的核心组件之一,负责接收请求并分发给合适的控制器进行处理。其启动流程大致如下: 1. **类的继承关系**:`DispatcherServlet` 继承自 `FrameworkServlet`,而 `FrameworkServlet...

    Spring MVC使用Demo

    Spring MVC是Spring框架的一个核心模块,专用于构建Web应用程序。这个"Spring MVC使用Demo"提供了实践操作,帮助开发者深入理解Spring MVC的开发环境配置、注解的使用以及工作原理。 首先,Spring MVC的设计模式...

    Spring MVC 4.2.4.RELEASE 中文文档

    Spring MVC提供国际化功能,允许根据用户的语言偏好显示不同语言的内容。 由于这是一个非正式版本,可能存在一些已知问题或未记录的特性。尽管如此,这份中文文档可以帮助开发者理解Spring MVC的基本概念和使用方法...

    基本的spring mvc + spring security实现的登录(无数据库)

    在本项目中,我们主要探讨的是如何利用Spring MVC和Spring Security框架构建一个基本的无数据库登录系统。Spring MVC是Spring框架的一部分,用于处理Web应用程序的请求-响应模型,而Spring Security则是一个强大的...

Global site tag (gtag.js) - Google Analytics