`

Spring MVC如何初始化HandlerMapping接口

 
阅读更多
      Spring MVC框架中使用HandlerMapping来进行请求到处理器的映射处理。
     那么就会有一个问题:那么框架到底是如何从Url获取到一个处理器的呢? 
     而回到这个问题,一般要思考两个问题:
     1、框架如何初始化HandlerMapping接口.
                  2、 框架如何使用HandlerMapping接口进行请求到处理器的映射处理。
    现在,先来看看第一个问题。我们以@RequestMapping为例,是如何处理的。
     其中SpringMVC容器提供了RequestMappingHandlerMapping实现类来处理这个注解.
  debug源码,发现是在初始化容器的时候会注册RequestMappingHandlerMappingBean:
   
    步骤如下:

    1:在初始化容器的时候,有一个步骤: 

      AbstractAutowireCapableBeanFactory.initializeBeaninvoke
     在这方法中有一个invokeInitMethods, 在这个代码中会执行bean.afterPropertiesSet()方法
    
     2:afterPropertiesSet()方法
     该方法是InitializingBean的一个接口,所有实现了这个接口,都要实现  afterPropertiesSet()方法。
     在初始化bean的时候调用。
     AbstractHandlerMethodMapping.afterPropertiesSet()
     AbstractHandlerMethodMapping实现了这个接口,也就是实现了afterPropertiesSet()方法,
    在初始化bean的时候会调用 AbstractHandlerMethodMapping.afterPropertiesSet()
   
    3: initHandlerMethods()
   在afterPropertiesSet()方法中调用了initHandlerMethods()方法.
   这个方法用来从所有的bean中检测出符合规则的类和方法,
   然后生成一个调用RequestMappingHandlerMapping.getMappingForMethod() 产生一个 RequestMappingInfo对象,
   在registerHandlerMethod注册过程中:根据RequestMappingInfo对象生成一个HandlerMethod对象。
 
   4: 注册到AbstractHandlerMethodMapping的handlerMethods和urlMap中.
     这个是变量的声明
     Map<T, HandlerMethod> handlerMethods;
    MultiValueMap<String, T> urlMap;
   会把相关的对象保存到上面的Map中.
    涉及到的相关类:
     InitializingBean、AbstractHandlerMethodMapping、RequestMappingHandlerMapping、R equestMappingInfo、HandlerMethod.
  
   代码分析。。。:
  
   一: AbstractHandlerMethodMapping.initHandlerMethods()
   这个方法用来扫描容器的bean,监测出所以的映射方法(Handler Methods),并且注册到映射处理器中(HandlerMapping)。
   
/**  
  * Scan beans in the ApplicationContext, detect and register handler methods.
  * @see #isHandler(Class)
  * @see #getMappingForMethod(Method, Class)
  * @see #handlerMethodsInitialized(Map)
  */
 protected void initHandlerMethods() {
  if (logger.isDebugEnabled()) {
   logger.debug("Looking for request mappings in application context: " + getApplicationContext());
  }
   String[] beanNames = (this.detectHandlerMethodsInAncestorContexts ?
    BeanFactoryUtils.beanNamesForTypeIncludingAncestors(getApplicationContext(), Object.class) :
    getApplicationContext().getBeanNamesForType(Object.class));

  for (String beanName : beanNames) {
   if (isHandler(getApplicationContext().getType(beanName))){
    detectHandlerMethods(beanName);
   }
  }
  handlerMethodsInitialized(getHandlerMethods());
 }
   这个方法步骤如下:
   1 :从容器中获得所有bean的名字
   2:遍历容器中的bean,判断Bean是否属于Handler,如果是则检测出所以的HandlerMethod方法并且注册到容器中.
   3:handlerMethodsInitialized这个方法里的方法体为空,没有做任何的处理
注意:在上面的代码中,isHandler()方法是个抽象方法,由子类来提供具体的实现。
比如 RequestMappingHandlerMapping提供了实现,代码如下:
	@Override
	protected boolean isHandler(Class<?> beanType) {
		return ((AnnotationUtils.findAnnotation(beanType, Controller.class) != null) ||
				(AnnotationUtils.findAnnotation(beanType, RequestMapping.class) != null));
	}
 在这里来判断当前的bean是否有Controller或者RequestMapping注解。
再来看下detectHandlerMethods的代码
	protected void detectHandlerMethods(final Object handler) {
		Class<?> handlerType =
				(handler instanceof String ? getApplicationContext().getType((String) handler) : handler.getClass());

		// Avoid repeated calls to getMappingForMethod which would rebuild RequestMatchingInfo instances
		final Map<Method, T> mappings = new IdentityHashMap<Method, T>();
		final Class<?> userType = ClassUtils.getUserClass(handlerType);

		Set<Method> methods = HandlerMethodSelector.selectMethods(userType, new MethodFilter() {
			public boolean matches(Method method) {
				T mapping = getMappingForMethod(method, userType);
				if (mapping != null) {
					mappings.put(method, mapping);
					return true;
				}
				else {
					return false;
				}
			}
		});

		for (Method method : methods) {
			registerHandlerMethod(handler, method, mappings.get(method));
		}
	}
 
  大概步骤如下:
1 : 通过HandlerMethodSelector.selectMethods()方法来从当前的处理器中匹配出具有映射处理的方法。
2:取出映射的方法后,则调用registerHandlerMethod()注册到处理器中。
注意:
在上面代码中,getMappingForMethod(method, userType)是个抽象方法,具体由子类来实现。
比如RequestMappingHandlerMapping中提供了如下的实现:
	@Override
	protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
		RequestMappingInfo info = null;
		RequestMapping methodAnnotation = AnnotationUtils.findAnnotation(method, RequestMapping.class);
		if (methodAnnotation != null) {
			RequestCondition<?> methodCondition = getCustomMethodCondition(method);
			info = createRequestMappingInfo(methodAnnotation, methodCondition);
			RequestMapping typeAnnotation = AnnotationUtils.findAnnotation(handlerType, RequestMapping.class);
			if (typeAnnotation != null) {
				RequestCondition<?> typeCondition = getCustomTypeCondition(handlerType);
				info = createRequestMappingInfo(typeAnnotation, typeCondition).combine(info);
			}
		}
		return info;
	}
 具体步骤:
1:从当前类中取出RequestMapping 注解。
2:如果注解不为空的话,则进行一系列处理,生成一个RequestMappingInfo对象。

 再来看看registerHandlerMethod这个方法的实现,看看是是把HandlerMethod注册到哪里的?

代码:

 

/**
	 * Register a handler method and its unique mapping.
	 * @param handler the bean name of the handler or the handler instance
	 * @param method the method to register
	 * @param mapping the mapping conditions associated with the handler method
	 * @throws IllegalStateException if another method was already registered
	 * under the same mapping
	 */
	protected void registerHandlerMethod(Object handler, Method method, T mapping) {
		HandlerMethod newHandlerMethod = createHandlerMethod(handler, method);
		HandlerMethod oldHandlerMethod = this.handlerMethods.get(mapping);
		if (oldHandlerMethod != null && !oldHandlerMethod.equals(newHandlerMethod)) {
			throw new IllegalStateException("Ambiguous mapping found. Cannot map '" + newHandlerMethod.getBean() +
					"' bean method \n" + newHandlerMethod + "\nto " + mapping + ": There is already '" +
					oldHandlerMethod.getBean() + "' bean method\n" + oldHandlerMethod + " mapped.");
		}

		this.handlerMethods.put(mapping, newHandlerMethod);
		if (logger.isInfoEnabled()) {
			logger.info("Mapped \"" + mapping + "\" onto " + newHandlerMethod);
		}

		Set<String> patterns = getMappingPathPatterns(mapping);
		for (String pattern : patterns) {
			if (!getPathMatcher().isPattern(pattern)) {
				this.urlMap.add(pattern, mapping);
			}
		}
	}
从上面代码可以看出是 注册到下面两个变量中。
      
  private final Map<T, HandlerMethod> handlerMethods = new LinkedHashMap<T, HandlerMethod>();
   private final MultiValueMap<String, T> urlMap = new LinkedMultiValueMap<String, T>();
 
一看定义就知道, urlMap 的key是个String类型,表示的是请求url,而T应该是RequestMappingInfo.
handlerMethods 的key是RequestMappingInfo。
也就是请求的时候,会先根据请求url从urlMap中查找对应的RequestMappingInfoBean,
然后再从handlerMethods 方法中找到对应的HandlerMethod方法,这个HandlerMethod就是实际的业务处理逻辑。
总结:
这篇文章分析SpringMVC容器在初始化的时候,是如何初始化映射处理器的。
1:容器初始化的时候是在哪个入口 进入 初始化映射处理器的。
2: 初始化映射处理器的流程是如何检测出所有符合规则的 Handler类和HandlerMethod方法,并且是如何注册到容器中。
 在这个整个流程中涉及到了InitializingBean、RequestMappingHandlerMapping、RequestMappingHandlerMapping、RequestMappingInfo、HandlerMethod.等类.

 

 

分享到:
评论

相关推荐

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

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

    Spring MVC核心组件之HandlerMapping详解

    ### Spring MVC核心组件之HandlerMapping详解 #### 一、引言 在Java Web开发领域,Spring MVC框架因其灵活且强大的特性而备受青睐。它提供了一种简洁的方式来构建可维护、可扩展的应用程序。Spring MVC的核心组件之...

    Spring MVC jar包

    Spring MVC 是一个基于Java的轻量级Web应用框架,它为开发者提供了模型-视图-控制器(MVC)架构,使开发人员能够更好地组织和分离应用程序的业务逻辑、数据处理和用户界面。Spring MVC是Spring框架的一个核心组件,...

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

    二、Spring MVC核心类与接口:Spring MVC架构中包含许多核心组件,如DispatcherServlet、HandlerMapping、Controller、ViewResolver等。这些组件协同工作,处理用户的请求并返回相应的响应。 三、Spring MVC核心...

    spring-ext-handler-mapping.rar_ext_spring ext_spring mvc

    这是Spring的配置文件,可能包含了对自定义HandlerMapping的配置,例如初始化参数、bean定义等。通过这个文件,我们可以将自定义的映射策略集成到Spring应用上下文中,使它们在应用程序启动时生效。 总的来说,...

    spring mvc 的入门级资料

    在 Spring MVC 中,核心类和接口是 DispatcherServlet、HandlerMapping、HandlerAdapter、Controller、ViewResolver、HandlerInterceptor、View 等。 DispatcherServlet 是 Spring MVC 的前置控制器,它负责处理...

    spring MVC .docx

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

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

    - 在Spring MVC的Controller中,通过@Autowired注入Mapper接口,调用其方法进行数据操作。 4. **数据库脚本** - 提供的数据库脚本通常包含创建表、插入初始数据等内容,是项目启动前必要的准备工作。 - 使用...

    spring mvc

    - **初始化**:读取配置文件并初始化 Spring 容器。 - **请求处理**:处理所有进入系统的 HTTP 请求。 - **响应生成**:根据处理结果生成最终的 HTTP 响应。 #### 五、Spring MVC 双亲上下文说明 在 Spring MVC 中...

    Spring mvc整合mybatis例子

    - `web.xml`:配置DispatcherServlet和ContextLoaderListener,前者处理HTTP请求,后者初始化Spring的ApplicationContext。 - `spring-mvc.xml`:配置ViewResolver,比如InternalResourceViewResolver,用于解析...

    Spring MVC 学习记录总结1

    Spring MVC 提供了一套接口和类,允许开发者专注于业务处理,而无需关心底层实现细节。 2. Spring MVC 的核心组件 - **DispatcherServlet**:作为前端控制器,它是整个流程的入口点,负责调度其他组件并降低组件间...

    Spring MVC使用Demo

    它是Spring MVC的前端控制器,接收所有HTTP请求,然后根据请求信息选择合适的HandlerMapping找到对应处理器(Controller方法)。处理器执行后,通过HandlerAdapter将结果转换为ModelAndView对象,最后由ViewResolver...

    spring-MVC.zip_Java spring mvc_spring mvc_spring mvc

    10. **HandlerAdapter**:处理器适配器,用于调用Controller方法,因为不同的Controller可能会有不同的接口,HandlerAdapter使得Spring MVC可以适配各种类型的Controller。 在提供的"spring MVC.docx"文档中,可能...

    Spring MVC框架简介和使用

    - 最后,`DispatcherServlet` 的 `onRefresh()` 方法被调用来初始化相关的策略组件,例如 HandlerMapping、HandlerAdapter 等。 ### 常用注解介绍 Spring MVC 提供了一系列有用的注解来简化控制器类的定义和请求...

    Spring MVC 基础实例源码01

    Spring MVC 是一个基于Java的轻量级Web应用框架,它是Spring框架的重要组成部分,主要用于构建Web应用程序的后端控制器。这个"Spring MVC 基础实例源码01"的资源很可能是为了帮助初学者理解Spring MVC的核心概念和...

    开发Spring MVC应用程序补充—程序源码下载.rar_spring_spring mvc_spring mvc 源码_sp

    标题中的"开发Spring MVC应用程序补充—程序源码下载.rar_spring_spring mvc_spring mvc 源码_sp"表明这是一个关于Spring MVC框架的开发教程,其中包含了源代码供学习者参考。Spring MVC是Spring框架的一个核心组件...

    spring MVC配置详解

    Spring MVC 配置详解 Spring MVC 是一个基于 DispatcherServlet 的 MVC 框架,它是当前主流的 Web 框架之一。要想灵活运用 Spring MVC 来应对大多数的 Web 开发,就必须要掌握它的配置及原理。 一、Spring MVC ...

    Spring MVC源码深度剖析开源架构源码2021.pdf

    这个过程涉及到HandlerMapping接口的实现,比如BeanNameUrlHandlerMapping和RequestMappingInfoHandlerMapping。前者是通过bean的名称来匹配URL,后者则是通过注解@RequestMapping来匹配URL。 在getHandlerAdapter...

    Spring MVC Beginner’s Guide 源代码

    - **web.xml**:配置DispatcherServlet,定义Spring MVC的初始化参数。 - **servlet-context.xml**:Spring MVC的核心配置文件,声明拦截器、视图解析器、bean等。 3. **处理器映射器与适配器** - **...

Global site tag (gtag.js) - Google Analytics