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) { }
相关推荐
在Spring MVC框架中,应用程序启动时会执行一系列初始化操作,这些操作对于理解Spring MVC的工作原理至关重要。本篇文章将深入探讨Spring MVC启动时初始化的几个常用方法,并解释它们在实际开发中的作用。 首先,...
Spring MVC属于SpringFrameWork的后续产品,已经融合在Spring Web Flow里面。Spring 框架提供了构建 Web 应用程序的全功能 MVC 模块。Spring MVC4是当前zuixin的版本,在众多特性上有了进一步的提升。, 在精通Spring...
Spring MVC的国际化(Internationalization)指的是将应用根据不同地域、语言习惯以及文化差异进行本地化的过程。这涉及了软件应用对多语言的支持,以便不同区域的用户能够以自己的母语使用软件。Spring MVC提供了...
14. **性能优化**:Spring MVC 4.0在内部做了许多性能优化,包括更快的DispatcherServlet初始化、更高效的HTTP方法映射等。 15. **兼容性与兼容性测试**:Spring MVC 4.0确保与各种Web容器的兼容性,如Tomcat、...
Spring MVC 是一款强大的Java Web开发框架,用于构建高效、可维护和模块化的Web应用程序。它作为Spring框架的一部分,提供了一种优雅的方式来处理HTTP请求和响应,使得开发者可以专注于业务逻辑而不是底层实现。在这...
10. **国际化和本地化**:通过消息源和LocaleResolver,Spring MVC可以轻松实现应用的多语言支持。 在实际开发中,我们可以通过Maven或Gradle等构建工具将Spring MVC 4.2.3依赖引入项目。同时,使用IDE如IntelliJ ...
可用于分析spring mvc源码、spring mvc父子容器初始化流程、session和cookie机制、spring session等,也可以用于学习Java Web(servlet、filter、listener等)、spring源码等。 该项目使用servlet3.0规范,无web.xml...
wpblog.sql可能是数据库的初始化脚本,用于创建这些表结构并可能包含一些初始数据。MySQL是常用的开源关系型数据库管理系统,适用于中小型Web应用,具有良好的性能和稳定性。 **5. 文件结构** - `wpblog.sql`: 这个...
除此之外,Spring MVC还支持数据绑定、验证、本地化、主题、异常处理等功能。例如,使用@ModelAttribute注解可以将请求参数绑定到Controller方法的参数上,@Valid用于进行数据验证,Validator接口可以自定义验证逻辑...
Spring MVC 是一个强大的Java Web开发框架,它是Spring框架的一部分,专为构建高度可扩展和模块化的Web应用程序而设计。在2015年的版本中,Spring MVC 4已经相当成熟,提供了许多特性来简化开发流程并提高开发效率。...
DispatcherServlet通过`web.xml`配置文件或Java配置类进行初始化配置。 2. **Controller**: 控制器是实现业务逻辑的类,通常由开发者编写。它们处理HTTP请求,调用业务服务,然后返回一个模型(Model)和视图(View...
Spring MVC 是一个基于 Java 的轻量级 Web 开发框架,它是 Spring 框架的一个重要模块,主要用于构建 Web 应用程序的后端控制层。这个框架提供了模型-视图-控制器(MVC)设计模式的实现,简化了Java Web应用的开发...
Spring MVC是一种基于Java的实现了MVC设计模式的请求驱动类型的轻量级Web框架,使用了IoC容器,支持RESTful风格的应用程序开发。Spring MVC通过分离模型(Model)、视图(View)和控制器(Controller)来简化Web开发...
Spring MVC 和 MyBatis 是两个在Java Web 开发中广泛使用的框架。Spring MVC 作为Spring框架的一部分,负责处理HTTP请求并转发到相应的业务逻辑,而MyBatis则是一个轻量级的持久层框架,用于简化数据库操作。整合这...
DispatcherServlet 是 Spring MVC 的核心组件之一,负责接收请求并分发给合适的控制器进行处理。其启动流程大致如下: 1. **类的继承关系**:`DispatcherServlet` 继承自 `FrameworkServlet`,而 `FrameworkServlet...
Spring MVC是Spring框架的一个核心模块,专用于构建Web应用程序。这个"Spring MVC使用Demo"提供了实践操作,帮助开发者深入理解Spring MVC的开发环境配置、注解的使用以及工作原理。 首先,Spring MVC的设计模式...
Spring MVC提供国际化功能,允许根据用户的语言偏好显示不同语言的内容。 由于这是一个非正式版本,可能存在一些已知问题或未记录的特性。尽管如此,这份中文文档可以帮助开发者理解Spring MVC的基本概念和使用方法...
在本项目中,我们主要探讨的是如何利用Spring MVC和Spring Security框架构建一个基本的无数据库登录系统。Spring MVC是Spring框架的一部分,用于处理Web应用程序的请求-响应模型,而Spring Security则是一个强大的...