HandlerMapping是用来找到url对应的处理handler对象(beans),而不是找到url对应的处理函数。Handleradapter则是用来匹配到handler的某个具体的处理函数上,然后调度执行这个函数。
ViewResolver:这个是用来解析视图,并返回资源到前端用到。
- import javax.servlet.http.HttpServletRequest;
- import javax.servlet.http.HttpServletResponse;
- import org.springframework.web.servlet.ModelAndView;
- import org.springframework.web.servlet.mvc.Controller;
- public class HomeAction implements Controller{
- @Override
- public ModelAndView handleRequest(HttpServletRequest request,
- HttpServletResponse response) throws Exception {
- return new ModelAndView("hello");
- }
- }
下面就要说说原理了。
用过python Django框架的都知道Django对于访问方式的配置就是,一个url路径和一个函数配对,你访问这个url,就会直接调用这个函数,简单明了。对于java的面向对象来说,就要分两步走。第一步首先要找到是哪个对象,即handler,本工程的handler则是HomeAction对象。第二步要找到访问的函数,即HomeAction的handleRequest方法。所以就出现了两个源码接口 HandlerMapping和HandlerAdapter,前者负责第一步,后者负责第二步。借用网上的SpringMVC架构图。
HandlerMapping接口的实现(只举了我认识的几个) :
-
BeanNameUrlHandlerMapping :通过对比url和bean的name找到对应的对象
SimpleUrlHandlerMapping :也是直接配置url和对应bean,比BeanNameUrlHandlerMapping功能更多
DefaultAnnotationHandlerMapping : 主要是针对注解配置@RequestMapping的,已过时
RequestMappingHandlerMapping :取代了上面一个
HandlerAdapter 接口实现:
-
HttpRequestHandlerAdapter : 要求handler实现HttpRequestHandler接口,该接口的方法为 void handleRequest(HttpServletRequest request, HttpServletResponse response)也就是 handler必须有一个handleRequest方法
SimpleControllerHandlerAdapter:要求handler实现Controller接口,该接口的方法为ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response),也就是本工程采用的
AnnotationMethodHandlerAdapter :和上面的DefaultAnnotationHandlerMapping配对使用的,也已过时
RequestMappingHandlerAdapter : 和上面的RequestMappingHandlerMapping配对使用,针对@RequestMapping
先简单的说下这个工程的流程,访问http://localhost:8080/index首先由DispatcherServlet进行转发,通过BeanNameUrlHandlerMapping(含有 /index->HomeAction的配置),找到了HomeAction,然后再拿HomeAction和每个adapter进行适配,由于HomeAction实现了Controller接口,所以最终会有SimpleControllerHandlerAdapter来完成对HomeAction的handleRequest方法的调度。然后就顺利的执行了我们想要的方法,后面的内容不在本节中说明。
了解了大概流程,然后就需要看源代码了。
首先就是SpringMVC的入口类,DispatcherServlet,它实现了Servlet接口,不再详细说DispatcherServlet的细节,不然又是一大堆的内容。每次请求都会调用它的doService->doDispatch,我们关注的重点就在doDispatch方法中。
- protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
- HttpServletRequest processedRequest = request;
- HandlerExecutionChain mappedHandler = null;
- boolean multipartRequestParsed = false;
- WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
- try {
- ModelAndView mv = null;
- Exception dispatchException = null;
- try {
- processedRequest = checkMultipart(request);
- multipartRequestParsed = (processedRequest != request);
- //这个是重点,第一步由HandlerMapping找到对应的handler
- // Determine handler for the current request.
- mappedHandler = getHandler(processedRequest);
- if (mappedHandler == null || mappedHandler.getHandler() == null) {
- noHandlerFound(processedRequest, response);
- return;
- }
- // Determine handler adapter for the current request.
- //这是第二步,找到合适的HandlerAdapter,然后由它来调度执行handler的方法
- HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
- // Process last-modified header, if supported by the handler.
- String method = request.getMethod();
- boolean isGet = "GET".equals(method);
- if (isGet || "HEAD".equals(method)) {
- long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
- if (logger.isDebugEnabled()) {
- logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified);
- }
- if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
- return;
- }
- }
- if (!mappedHandler.applyPreHandle(processedRequest, response)) {
- return;
- }
- try {
- // Actually invoke the handler.
- mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
- }
- finally {
- if (asyncManager.isConcurrentHandlingStarted()) {
- return;
- }
- }
- applyDefaultViewName(request, mv);
- mappedHandler.applyPostHandle(processedRequest, response, mv);
- }
- catch (Exception ex) {
- dispatchException = ex;
- }
- processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
- }
- catch (Exception ex) {
- triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
- }
- catch (Error err) {
- triggerAfterCompletionWithError(processedRequest, response, mappedHandler, err);
- }
- finally {
- if (asyncManager.isConcurrentHandlingStarted()) {
- // Instead of postHandle and afterCompletion
- mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
- return;
- }
- // Clean up any resources used by a multipart request.
- if (multipartRequestParsed) {
- cleanupMultipart(processedRequest);
- }
- }
- }
第一步详细查看:
- protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
- for (HandlerMapping hm : this.handlerMappings) {
- if (logger.isTraceEnabled()) {
- logger.trace(
- "Testing handler map [" + hm + "] in DispatcherServlet with name '" + getServletName() + "'");
- }
- HandlerExecutionChain handler = hm.getHandler(request);
- if (handler != null) {
- return handler;
- }
- }
- return null;
- }
可以看到就是通过遍历所有已注册的HandlerMapping来找到对应的handler,然后构建出一个HandlerExecutionChain,它包含了handler和HandlerMapping本身的一些拦截器,如下
- public class HandlerExecutionChain {
- private final Object handler;
- private HandlerInterceptor[] interceptors;
- private List<HandlerInterceptor> interceptorList;
- //其他代码省略
- }
其中HandlerMapping的getHandler实现:
- 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)是个抽象方法,由具体的HandlerMapping来实现,获取到的handler如果为空,则获取默认配置的handler,如果handler为String类型,则表示这个则会去Spring容器里面去找这样名字的bean。
再看下BeanNameUrlHandlerMapping的getHandlerInternal(request)的具体实现(通过一系列的接口设计,之后再好好看看这个设计,到BeanNameUrlHandlerMapping这只用实现该方法中的一部分),如下
- public class BeanNameUrlHandlerMapping extends AbstractDetectingUrlHandlerMapping {
- /**
- * 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 (String alias : aliases) {
- if (alias.startsWith("/")) {
- urls.add(alias);
- }
- }
- return StringUtils.toStringArray(urls);
- }
- }
这里面注释说,bean的name必须以/开头,它才处理,将信息存储在Map<String, Object> handlerMap中,对于本工程来说就是{'/index':HomeAction对象}。
至此这里完成了第一步,下面开始第二步,即方法HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());的具体实现:
- protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
- for (HandlerAdapter ha : this.handlerAdapters) {
- if (logger.isTraceEnabled()) {
- logger.trace("Testing handler adapter [" + ha + "]");
- }
- if (ha.supports(handler)) {
- return ha;
- }
- }
- throw new ServletException("No adapter for handler [" + handler +
- "]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
- }
遍历所有的HandlerAdapter,判断他们是否支持这个handler。
我们来看下HttpRequestHandlerAdapter的supports(handler)方法:
- public class HttpRequestHandlerAdapter implements HandlerAdapter {
- @Override
- public boolean supports(Object handler) {
- //就是判断handler是否实现了HttpRequestHandler接口
- return (handler instanceof HttpRequestHandler);
- }
- @Override
- public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
- throws Exception {
- //若handler实现了HttpRequestHandler接口,则调用该接口的方法,执行我们在该方法中写的业务逻辑
- ((HttpRequestHandler) handler).handleRequest(request, response);
- return null;
- }
- @Override
- public long getLastModified(HttpServletRequest request, Object handler) {
- if (handler instanceof LastModified) {
- return ((LastModified) handler).getLastModified(request);
- }
- return -1L;
- }
- }
同理SimpleControllerHandlerAdapter也是这样类似的逻辑
- public class SimpleControllerHandlerAdapter implements HandlerAdapter {
- @Override
- public boolean supports(Object handler) {
- return (handler instanceof Controller);
- }
- @Override
- public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
- throws Exception {
- return ((Controller) handler).handleRequest(request, response);
- }
- @Override
- public long getLastModified(HttpServletRequest request, Object handler) {
- if (handler instanceof LastModified) {
- return ((LastModified) handler).getLastModified(request);
- }
- return -1L;
- }
- }
剩余两个AnnotationMethodHandlerAdapter和RequestMappingHandlerAdapter就比较复杂,我也没看。
按照本工程的配置,则SimpleControllerHandlerAdapter是支持HomeAction的,然后就会执行SimpleControllerHandlerAdapter的handle(processedRequest, response, mappedHandler.getHandler())方法。本质上就会调用HomeAction实现Controller接口的方法。至此就分析完了。
了解过程了之后,然后就是最重要的也是经常配置出问题的地方。DispatcherServlet的handlerMappings和handlerAdapters的来源问题。
DispatcherServlet初始化的时候,会调用一个方法如下:
- protected void initStrategies(ApplicationContext context) {
- initMultipartResolver(context);
- initLocaleResolver(context);
- initThemeResolver(context);
- //初始化一些HandlerMapping
- initHandlerMappings(context);
- //初始化一些HandlerAdapter
- initHandlerAdapters(context);
- initHandlerExceptionResolvers(context);
- initRequestToViewNameTranslator(context);
- initViewResolvers(context);
- initFlashMapManager(context);
- }
这里可以看到,它会初始化一些HandlerMapping和HandlerAdapter,这两个方法非常重要,理解了这两个方法你就会知道,配置不对问题出在哪里,下面具体看下这两个方法:
- private void initHandlerMappings(ApplicationContext context) {
- this.handlerMappings = null;
- if (this.detectAllHandlerMappings) {
- // Find all HandlerMappings in the ApplicationContext, including ancestor contexts.
- Map<String, HandlerMapping> matchingBeans =
- BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
- if (!matchingBeans.isEmpty()) {
- this.handlerMappings = new ArrayList<HandlerMapping>(matchingBeans.values());
- // We keep HandlerMappings in sorted order.
- OrderComparator.sort(this.handlerMappings);
- }
- }
- else {
- try {
- HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class);
- this.handlerMappings = Collections.singletonList(hm);
- }
- catch (NoSuchBeanDefinitionException ex) {
- // Ignore, we'll add a default HandlerMapping later.
- }
- }
- // Ensure we have at least one HandlerMapping, by registering
- // a default HandlerMapping if no other mappings are found.
- if (this.handlerMappings == null) {
- this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
- if (logger.isDebugEnabled()) {
- logger.debug("No HandlerMappings found in servlet '" + getServletName() + "': using default");
- }
- }
- }
detectAllHandlerMappings是DispatcherServlet的一个属性,你是可以在web.xml中配置的,默认是true,如果为true,则会去从本工程mvc-servlet.xml文件中去探测所有实现了HandlerMapping的bean,如果有,则加入DispatcherServlet的handlerMappings中。如果detectAllHandlerMappings为false,则直接去容器中找id="handlerMapping"且实现了HandlerMapping的bean.如果以上都没找到,则会去加载默认的HandlerMapping。
- /** Detect all HandlerMappings or just expect "handlerMapping" bean? */
- private boolean detectAllHandlerMappings = true;
本工程由于没有配置HandlerMapping,所以它会去加载默认的,下面看看默认的配置是什么
- protected <T> List<T> getDefaultStrategies(ApplicationContext context, Class<T> strategyInterface) {
- String key = strategyInterface.getName();
- //defaultStrategies存储了默认的配置
- String value = defaultStrategies.getProperty(key);
- if (value != null) {
- String[] classNames = StringUtils.commaDelimitedListToStringArray(value);
- List<T> strategies = new ArrayList<T>(classNames.length);
- for (String className : classNames) {
- try {
- Class<?> clazz = ClassUtils.forName(className, DispatcherServlet.class.getClassLoader());
- Object strategy = createDefaultStrategy(context, clazz);
- strategies.add((T) strategy);
- }
- catch (ClassNotFoundException ex) {
- throw new BeanInitializationException(
- "Could not find DispatcherServlet's default strategy class [" + className +
- "] for interface [" + key + "]", ex);
- }
- catch (LinkageError err) {
- throw new BeanInitializationException(
- "Error loading DispatcherServlet's default strategy class [" + className +
- "] for interface [" + key + "]: problem with class file or dependent class", err);
- }
- }
- return strategies;
- }
- else {
- return new LinkedList<T>();
- }
- }
继续看看defaultStrategies是如何初始化的:
- private static final Properties defaultStrategies;
- static {
- // Load default strategy implementations from properties file.
- // This is currently strictly internal and not meant to be customized
- // by application developers.
- try {
- //这里的DEFAULT_STRATEGIES_PATH就是DispatcherServlet.properties
- ClassPathResource resource = new ClassPathResource(DEFAULT_STRATEGIES_PATH, DispatcherServlet.class);
- defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);
- }
- catch (IOException ex) {
- throw new IllegalStateException("Could not load 'DispatcherServlet.properties': " + ex.getMessage());
- }
- }
这里使用静态代码块来加载配置文件DispatcherServlet.properties,它所在位置就是和DispatcherServlet同一目录下面的,如下图所示:
该默认的配置文件的内容如下:
- # Default implementation classes for DispatcherServlet's strategy interfaces.
- # Used as fallback when no matching beans are found in the DispatcherServlet context.
- # Not meant to be customized by application developers.
- org.springframework.web.servlet.LocaleResolver=org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver
- org.springframework.web.servlet.ThemeResolver=org.springframework.web.servlet.theme.FixedThemeResolver
- #这里就是默认的HandlerMapping的配置
- org.springframework.web.servlet.HandlerMapping=org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,\
- org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping
- #这里就是默认的HandlerAdapter的配置
- org.springframework.web.servlet.HandlerAdapter=org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,\
- org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,\
- org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter
- org.springframework.web.servlet.HandlerExceptionResolver=org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerExceptionResolver,\
- org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver,\
- org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver
- org.springframework.web.servlet.RequestToViewNameTranslator=org.springframework.web.servlet.view.DefaultRequestToViewNameTranslator
- org.springframework.web.servlet.ViewResolver=org.springframework.web.servlet.view.InternalResourceViewResolver
- org.springframework.web.servlet.FlashMapManager=org.springframework.web.servlet.support.SessionFlashMapManager
也就是说,当你什么都没有配置时,默认会加载以上的配置。正是由于有了上述默认配置的BeanNameUrlHandlerMapping(它要求name必须是以/开头的),它才会存储我们在mvc-servlet.xml中配置的<bean name="/index" class="com.lg.mvc.HomeAction"></bean>,同样正是由于有了SimpleControllerHandlerAdapter(由于handler实现了Controller接口,所以它的support方法支持我们的handler),才会调度执行HomeAction的handleRequest方法。
相关推荐
SpringMVC学习分享 SpringMVC是基于Java的Web应用程序框架,属于模型-视图-控制器(MVC)架构模式。它提供了一个灵活的方式来构建Web应用...HandlerMapping和ViewResolver是SpringMVC框架中的请求映射和视图解析组件。
总结,Spring MVC 的设计思想是解耦和模块化,通过 HandlerMapping、HandlerAdapter、ViewResolver 等组件协同工作,实现了高效的 MVC 请求处理流程。了解这些核心组件的工作原理,有助于更好地理解和使用 Spring ...
在 Spring MVC 应用中,我们需要配置 Spring 配置文件(如 `servlet-context.xml`),声明 DispatcherServlet、HandlerMapping 和 ViewResolver 等组件。此外,还需要创建 Controller 类,使用 `@Controller` 注解...
2. 配置Spring MVC:编写servlet-context.xml,设置DispatcherServlet、HandlerMapping和ViewResolver等。 3. 编写Controller:创建一个简单的Controller类,使用@RequestMapping注解处理HTTP请求。 4. 视图层实现...
2. 配置 Spring MVC:设置 DispatcherServlet,配置 HandlerMapping 和 ViewResolver,以及定义 MVC 的相关配置。 3. 配置 MyBatis:创建 MyBatis 的 SqlSessionFactory,配置数据源,以及编写 Mapper 映射文件。 4....
- **MVC组件**:理解DispatcherServlet、HandlerMapping、HandlerAdapter、ViewResolver等核心组件的作用。 - **异常处理**:学习如何自定义异常处理器,以及使用@ControllerAdvice进行全局异常处理。 - **视图解析*...
在 Spring MVC 中,核心类和接口是 DispatcherServlet、HandlerMapping、HandlerAdapter、Controller、ViewResolver、HandlerInterceptor、View 等。 DispatcherServlet 是 Spring MVC 的前置控制器,它负责处理...
核心组件包括DispatcherServlet、HandlerMapping、HandlerAdapter、ModelAndView以及ViewResolver等。DispatcherServlet作为前端控制器,接收所有的HTTP请求,然后根据HandlerMapping找到对应的处理器。...
核心组件包括DispatcherServlet、HandlerMapping、HandlerAdapter、ViewResolver等。 - DispatcherServlet:它是整个Spring MVC的前端控制器,负责接收请求并分发到相应的处理器。 - HandlerMapping:负责将请求...
Spring MVC的类图涉及到众多组件,包括DispatcherServlet、HandlerMapping、HandlerAdapter、ModelAndView、ViewResolver、Interceptor等。这些组件通过接口和实现类相互协作,共同完成请求处理流程。 五、Web 配置...
此外,还可以查看DispatcherServlet、HandlerMapping、ViewResolver等组件的源码,理解它们在Spring MVC框架中的作用和实现方式。 **六、工具支持** 在开发过程中,IDE如IntelliJ IDEA或Eclipse提供了丰富的Spring...
Spring MVC的核心组件包括DispatcherServlet、HandlerMapping、HandlerAdapter和ViewResolver等。 ### 事务管理 Spring框架提供了一套强大的事务管理机制,可以方便地与各种持久化技术(如Hibernate、JPA等)集成...
Spring MVC 框架的核心组件包括 DispatcherServlet、HandlerMapping、HandlerAdapter、ViewResolver 等。 DispatcherServlet 是 Spring MVC 的前端控制器,负责处理所有的 HTTP 请求。HandlerMapping 负责将请求映射...
- **spring-servlet.xml**:Spring MVC 的配置文件,定义 HandlerMapping、ViewResolver 等。 - **jsp** 文件夹:存放 JSP 视图文件。 ### 3. 编写 Controller 创建一个 Controller 类,例如 `HelloController....
4. 配置文件:配置DispatcherServlet、HandlerMapping、ViewResolver等,以及Spring的依赖注入。 5. 视图层:使用JSP或其他模板技术进行页面渲染。 通过这个示例项目,开发者可以深入理解Spring MVC的运行流程,...
- **组件**:主要包括Model、View、Controller、HandlerMapping、HandlerAdapter、ViewResolver等组件,每个组件都有明确的职责。 - **ModelAndView**:Controller处理完请求后,返回一个ModelAndView对象,包含了...
SpringMVC核心架构流程与...SpringMVC的核心架构流程包括DispatcherServlet、HandlerMapping、HandlerAdapter、解决器、ViewResolver和View等组件。通过配置这些组件,开发者可以快速构建基于SpringMVC的Web应用程序。
本书首先介绍了Spring MVC的基础知识,包括如何搭建Spring MVC环境,以及它的核心组件如DispatcherServlet、ModelAndView、HandlerMapping和HandlerAdapter等的工作原理。这些组件在Web请求处理过程中扮演着重要角色...
web.xml中配置DispatcherServlet,Spring配置文件中定义HandlerMapping、HandlerAdapter、ViewResolver等组件。 五、实战演练 "Spring-MVC-step-by-step2.pdf"这份教程详细介绍了Spring MVC的每个步骤,包括环境...
Spring MVC 包含了多个组件,如DispatcherServlet(前端控制器)、HandlerMapping(处理器映射)、HandlerAdapter(处理器适配器)、ModelAndView(模型和视图)、ViewResolver(视图解析器)等。这些组件协同工作...