Spring MVC 简述:从MVC框架普遍关注的问题说起

  任何一个完备的MVC框架都需要解决Web开发过程中的一些共性的问题,比如请求的收集与分发、数据前后台流转与转换,当前最流行的SpringMVC和Struts2也不例外。本文首先概述MVC模式的分层思想与MVC框架普遍关注的问题,并以此为契机结合SpringMVC的入门级案例简要地从原理、架构角度介绍了它对这些问题的处理,包括请求处理流程、消息转换机制和数据绑定机制等核心问题。最后,本文对目前最为流行的两个MVC框架SpringMVC 和
  
  一. MVC 模式与框架
  
  1、MVC 模式
  
  Java Web 应用的结构经历了 Model1 和 Model2 两个时代。在 Model1 模式下,整个 Web 应用几乎全部用JSP页面组成,只用少量的JavaBean来处理数据库连接、访问等操作。从工程化角度来看,JSP 不但充当了表现层角色,还充当了控制器角色,将控制逻辑和表现逻辑混杂在一起,导致代码重用率极低,使得应用极难扩展和维护。
  
  Model2 已经是基于MVC架构的设计模式。在 Model2 中,Servlet 作为控制器,负责接收客户端发送的请求,调用后端的JavaBean(业务逻辑组件)来处理业务逻辑并根据处理结果转发到相应的JSP页面处理显示逻辑。在 Model2 模式下,模型(Model)由 JavaBean 充当,视图(View)由JSP页面充当,而控制器则由 Servlet 充当。Model2 的流程示意图如下:
  
  这里写图片描述
  
  更具体地,在 Model2(标准MVC)中,角色分工如下:
  
  Model:由 JavaBean 充当,所有的业务逻辑、数据库访问都在Model中实现;
  
  View:由 JSP 充当,负责收集用户请求参数并将应用处理结果、状态数据呈现给用户;
  
  Controller:由 Servlet 充当,作用类似于调度员,即所有用户请求都发送给 Servlet,Servlet 调用 Model 来处理用户请求,并根据处理结果调用 JSP 来呈现结果;或者Servlet直接调用JSP将应用处理结果展现给用户。
  
  2、MVC 框架
  
  上述提到的MVC模式只是一种分层架构思想,并不包含任何具体的实现。在Model2中,我们分别为使用JavaBean、JSP和 Servlet分别充当模型(Model)、视图(View)和控制器,这可以看作是MVC模式最为基本的一种实现。但实际上,开发者使用Model2来开发Java WEB应用时,除了要专注于业务逻辑的开发以外,还需要额外考虑各种各样的问题,比如前后台数据之间的流转和转换问题、数据验证问题、消息转换问题等等,而且并没有实现各层之间的完全解耦。Model2存在的这些问题实际上都是一些共性的问题,换言之,Model2的抽象和封装程度还远远不够,开发者在使用Model2进行开发时不可避免地会重复造轮子,这就大大降低了程序的可维护性和复用性。
  
  为了解决这一问题,解放广大程序员的双手,一些MVC框架就应运而生了。Struts是全世界最早的MVC框架,特别地,其与WebWork分娩出的Struts2拥有众多优秀的设计,而且吸收了传统的Struts和WebWork两者的精华,曾一度是MVC框架中的王者。但是,与SpringMVC相比,Struts2又显得如此笨重、难用。与此同时,随着spring的广泛应用和开发者对轻量级框架的不懈追求,SpringMVC逐渐成为MVC框架中新的王者。
  
  让开发者只关注于业务逻辑的处理是MVC框架的终极目标。无论是昔日的Struts2还是今天的SpringMVC,它们的差别更多体现在设计上的优劣与细腻,但是作为一个MVC框架,它们都会封装并提供一些基本的组件和功能以便解放程序员的双手,比如:
  
  分发请求的前端控制器(Struts2中的StrutsPrepareAndExecuteFilter和SpringMVC中的DispatcherServlet);
  
  处理请求的业务控制器(Struts2中的Action和SpringMVC中的Controller);
  
  请求URI与请求处理方法的匹配(Struts2中的ActionMapper和SpringMVC中的HandlerMapping);
  
  请求处理方法的调用(Struts2中的ActionProxy和SpringMVC中的HandlerAdapter);
  
  类型转换问题 —— 前后台数据的流转;
  
  数据校验;
  
  异常配置;
  
  国际化和标签库;
  
  文件上传/下载;
  
  事实上,任何一个完备的MVC框架都会对以上功能进行抽象和封装。与Model2相比,MVC框架提取并完成了大量实际开发中需要重复解决的通用步骤,留给开发者的仅仅是与特定应用相关的部分,从而大大简化了程序的开发、提升程序的可维护性和增强代码复用性。
  
  二. Spring MVC 核心组件与执行流程
  
  Spring MVC是Spring框架提供的构建Web应用程序的全功能MVC模块,也是一种基于Java的实现了Web MVC设计模式的请求驱动类型的轻量级Web框架,很好地实现了MVC架构模式的思想并将web层进行职责解耦。一般地,在MVC框架中,控制器(Controller)用于执行业务逻辑并产生模型数据(Model),而视图(View)则用于渲染模型数据,当然SpringMVC也不例外,如下图所示:
  
  这里写图片描述
  
  1、SpringMVC 执行流程
  
  上图简要地描述了SpringMVC中请求处理的流程,但实际上,它并没有刻画出SpringMVC框架处理一个HTTP请求的全貌。下图详细描述了SpringMVC的请求处理过程,并给出了SpringMVC各核心组件之间的交互过程。
  
  这里写图片描述
  
  1、用户向服务器发送请求,请求被Spring MVC的前端控制器DispatcherServlet截获;
  
  2、DispatcherServlet对请求URL(统一资源定位符)进行解析,得到URI(请求资源标识符)。然后根据该URI,调用HandlerMapping获得该Handler配置的所有相关对象,包括Handler对象以及Handler对象对应的拦截器,这些对象会被封装到一个 HandlerExecutionChain对象 当中返回;
  
  3、DispatcherServlet根据获得的Handler,选择一个合适的HandlerAdapter。一个HandlerAdapter会被用于处理多种(一类)Handler,并调用Handler实际处理请求的方法;
  
  4、在调用Handler实际处理请求的方法之前,HandlerAdapter 首先会结合用户配置对请求消息进行转换(例如,将JSON/XML请求消息转换成一个Java对象),然后通过DataBinder将请求中的模型数据绑定到Handler(Controller)对应的处理方法的参数中。在消息转换和数据绑定过程中,Spring MVC会做一些额外的处理,比如数据类型转换、数据格式化工作和数据合法性校验等;
  
  5、Handler调用业务逻辑组件完成对请求的处理后,向DispatcherServlet返回一个ModelAndView对象,ModelAndView对象中应该包含视图名或者视图名和模型;
  
  6、DispatcherServlet根据返回的ModelAndView对象,选择一个合适的ViewResolver(视图解析器)返回给DispatcherServlet;
  
  7、DispatcherServlet调用视图解析器ViewResolver结合Model来渲染视图View;
  
  8、DispatcherServlet将视图渲染结果返回给客户端。
  
  在以上八个步骤中,DispatcherServlet、HandlerMapping、HandlerAdapter和ViewResolver等核心组件相互配合来完成Spring MVC 请求-响应的整个工作流程。这些核心组件所完成的工作对开发者是透明的,也就是说,开发者并不需要关心这些组件是如何工作的,开发者只需要专注在Handler(Controller)当中完成对请求的业务逻辑处理即可,这也正是MVC框架的价值体现。
  
  2、SpringMVC的消息转换器机制:HttpMessageConveter
  
  事实上,我们在向服务器进行请求时,可以采用各种各样的数据交换格式,比如轻量级的JSON和重量级的XML,当然也可以是其他自定义的数据交换格式。但无论在请求发送端采用何种数据交换格式,我们从请求流中读取到的只能是原始的字符串报文,同样地我们往响应流中也只能写原始的字符串。然而,在Java世界中,我们在调用模型组件处理业务逻辑时常常是以一个个有业务意义的对象为处理维度的,那么在请求消息到达SpringMVC和响应消息从SpringMVC出去的过程中就存在一个消息转换的问题,即请求消息(字符串)到Java对象的转换问题。
  
  张小龙在谈微信的本质时候说:“微信只是个平台,消息在其中流转”。在我们分析SpringMVC的消息转换器机制时,也可以领悟到类似的道理。在SpringMVC的设计者眼中,一次请求报文和一次响应报文分别被抽象为一个请求消息HttpInputMessage和一个响应消息HttpOutputMessage。在处理请求时,由合适的消息转换器将请求消息转换为请求处理方法中的形参对象并通过DataBinder组件绑定到请求处理方法的形参上,在这里,原始请求消息就可能有多种不同的形式,比如JSON和XML。同样地,当Controller响应请求时,请求处理方法的返回值也可以不是html页面,而是其他某种格式的数据,比如JSON和XML。
  
  我们知道Struts2本身对诸如JSON和XML等数据交换格式的支持不是特别好,常常需要借助于一些插件(比如,Struts2为了弥补不能原生支持JSON的不足,提供了struts2-json-plugin插件)来完成;而在SpringMVC中,采用的是HttpMessageConverter机制。具体而言,HttpMessageConveter负责将请求信息转换为一个对象,并通过DataBinder组件将该对象绑定请求方法的参数中或输出为响应信息。特别地,SpringMVC针对常用的不同消息形式提供了不同的HttpMessageConverter实现类来处理他们,而且我们也很容易扩展自定义的消息转换器。但是,只要这些消息所蕴含的“有效信息”是一致的,那么各种不同的消息转换器都会生成同样的转换结果。至于各种消息间解析细节的不同,就被屏蔽在不同的HttpMessageConverter实现类中了。
  
  正如下文提到的那样,在SpringMVC中可以使用@RequestBody和@ResponseBody两个注解分别完成请求消息到对象和对象到响应消息的转换,而底层这种灵活的消息转换机制就是由HttpMessageConverter支持的。
  
  3、SpringMVC的数据绑定组件:DataBinder
  
  事实上,上一节提到的SpringMVC的消息转换器机制HttpMessageConveter就是在SpringMVC的数据绑定组件DataBinder的基础上实现的。在HandlerAdapter调用Handler中具体的方法处理请求前,HandlerAdapter会根据请求方法签名的不同,将请求消息中的信息以一定的方式转换并绑定到请求方法的参数中以便请求的处理。也就是说,在请求消息到达真正调用处理方法的这一段时间内,SpringMVC还会完成很多其它的工作,包括请求信息的转换、数据转换、数据格式化以及数据校验等等。事实上,SpringMVC会通过反射机制对目标处理方法的签名进行分析,并将请求消息绑定到处理方法的参数中。数据绑定的核心部件是DataBinder,其机制如下:
  
  这里写图片描述
  
  Spring MVC框架将ServletRequest对象及其处理方法的参数对象实例传递给DataBinder,DataBinder调用装配在Spring Web 上下文中的ConversionService组件进行数据类型转换、数据格式化工作,并将ServletRequest中的消息填充到参数对象中去。然后,再调用Validator组件对已经绑定了请求消息数据的参数对象进行数据合法性校验,并最终生成数据绑定结果BindingResult对象。其中,BindingResult对象不但包含已完成数据绑定的参数对象,还包含相应的校验错误对象,Spring MVC 会抽取BindingResult对象中的参数对象及校验错误对象,并将它们赋给处理方法的相应参数。
  
  4、小结
  
  SpringMVC的请求处理流程可概括如下:当SpringMVC收到请求时,前端控制器DispatcherServlet会根据请求URI调用HandlerMapping将请求分发给具有一系列拦截器和业务控制器Controller的HandlerExecutionChain对象,然后该请求将依次通过该执行链的各个拦截器并最终到达业务控制器Controller。在业务控制器Controller处理该请求前,HandlerAdapter会对请求消息作进一步转换和解析并绑定到业务控制器Controller的具体请求处理方法上,然后该方法根据结合请求参数调用一系列业务逻辑组件去处理请求,并将包含模型数据和具体视图的处理结果交给视图解析器ViewResolver进行渲染,最终DispatcherServlet将视图渲染结果返回给客户端。
  
  从这个过程中我们可以直观看到SpringMVC解决了一系列MVC框架最主要关注的问题,比如请求的收集、分发和处理,前后台间数据的流转、转换、绑定。事实上,SpringMVC作为一个完备的MVC框架还解决了异常处理、国际化和标签库等基本问题,此不赘述。
  
  三. SpringMVC应用开发流程剖析:XML配置与注解配置
  
  在我们熟悉了SpringMVC请求处理流程后,本节提供了一个入门案例来深入理解SpringMVC的请求处理流程,同时熟悉SpringMVC的应用开发流程。开发一个SpringMVC应用,首先需要为我们的Web项目添加Spring支持,然后我们就可以采用基于XMl配置的方式或者基于注解配置方式进行应用的构建。本节将分别演示基于XML配置和Annotation配置的SpringMVC 应用。
  
  1、SpringMVC应用开发流程DEMO:XML配置
  
  (1). 在web.xml中配置前端控制器 DispatcherServlet
  
  <?xml version="1.0" encoding="UTF-8"?>
  
  <web-app xmlns:xsi="http://www.w3.org http://www.acnet.cn//2001/XMLSchema-instance"
  
  xmlns="http://java.sun.com/xml/ns/javaee"
  
  xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
  
  id="WebApp_ID" version="2.5">
  
  <display-name>SpringMVCDemo</display-name>
  
  <!-- 配置Spring MVC的前端控制器:DispatchcerServlet http://gouyily.cn/ -->
  
  <servlet>
  
  <servlet-name>springmvc</servlet-name>
  
  <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
  
  <!-- SpringMVC配置文件路径和名称设定 -->
  
  <init-param>
  
  <param-name>contextConfigLocation<http://yigouylpt2.com/ /param-name>
  
  <param-value>classpath:springmvc.xml</param-value>
  
  </init-param>
  
  <!-- Web应用启动时立即加载 -->
  
  <load-on-startup>1</load-on-startup>
  
  </servlet>
  
  <servlet-mapping>
  
  <servlet-name>springmvc</servlet-name>
  
  <url-pattern>/</url-pattern> <!-- 拦截所有请求 -->
  
  </servlet-mapping>
  
  </web-app>
  
  要想把SpringMVC框架应用到Web项目中,我们首先需要在web.xml添加一个Servlet —— DispatchcerServlet。DispatcherServlet是SpringMVC的集中访问点,其核心功能就是分发请求,而且能与Spring IoC容器无缝集成,从而可以获得Spring的所有好处。
  
  在配置DispatchcerServlet时,我们可以指定SpringMVC配置文件的路径,以便DispatchcerServlet查找并根据文件配置信息创建一个WebApplicationContext容器对象,即上下文环境。特别需要注意的是,WebApplicationContext继承自ApplicationContext容器,它的初始化方式和BeanFactory、ApplicationContext有所区别,因为WebApplicationContext需要在Web容器环境下才能完成启动Spring Web应用上下文的工作。在初始化WebApplicationContext容器后,开发者就可以很自然地使用Spring的IoC、AoP等特性了。
  
  DispatcherServlet作为Spring Web MVC的集中访问点,需要在Web应用启动时立即创建实例并初始化WebApplicationContext容器,因此在web.xml中将其设为 load-on-startup Servlet。
  
  (2). 在web.xml中指定路径配置SpringMVC的配置文件
  
  <?xml version="1.0" encoding="UTF-8"?>
  
  <beans xmlns="http://www.springframework.org/ http://www.huazongyule.com/ schema/beans"
  
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  
  xsi:schemaLocation="http://www.springframework.org/schema/beans
  
  http://www.springframework.org/schema/beans/spring-beans-4.0.xsd">
  
  <!-- 配置Handle,映射"/hello"请求 -->
  
  <bean name="/hello" class="cn.edu.tju.rico.controller.HelloController"/>
  
  <!-- 处理映射器将bean的name作为url进行查找,需要在配置Handle时指定name(即url) -->
  
  <bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/>
  
  <!-- SimpleControllerHandlerAdapter是一个处理器适配器,所有处理适配器都要实现HandlerAdapter接口 -->
  
  <bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"/>
  
  <!-- 视图解析器 -->
  
  <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"/>
  
  </beans>
  
  如果我们采用XML方式配置SpringMVC的Controller,那么在配置文件中我们需要指定具体的Controller及其所处理的请求URI。至于HandlerMapping、HandlerAdapter和ViewResolver等SpringMVC核心组件可以显式配置,也可以使用SpringMVC的默认配置。也就是说,上述关于HandlerMapping、HandlerAdapter和ViewResolver等核心组件的配置可以删除。SpringMVC关于以上核心组件的默认配置在与DispatcherServlet同一目录下面的DispatcherServlet.properties中,如下所示:
  
  # 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
  
  # 三个默认的ExceptionResolver
  
  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
  
  # 一个默认的InternalResourceViewResolver
  
  org.springframework.web.servlet.ViewResolver=org.springframework.web.servlet.view.InternalResourceViewResolver
  
  org.springframework.web.servlet.FlashMapManager=org.springframework.web.servlet.support.SessionFlashMapManager
  
  如果开发者在SpringMVC配置文件中没有配置HandlerMapping、HandlerAdapter等组件,那么DispatcherServlet会从上面的默认配置中选择合适的实现类进行请求匹配、处理、响应等操作。
  
  (3). 实现SpringMVC配置文件中配置的Controller
  
  public class HelloController implements Controller{
  
  public ModelAndView handleRequest(HttpServletRequest request,
  
  HttpServletResponse response) throws Exception {
  
  //创建准备返回的ModelAndView对象,如名所示,该对象通常包含了返回视图名、模型名称以及模型对象
  
  ModelAndView mv = new ModelAndView();
  
  //添加模型数据,可以是任意的POJO对象
  
  mv.addObject("message", "Hello, Rico...");
  
  // 设置逻辑视图名,视图解析器会根据该名字解析到具体的视图页面
  
  mv.setViewName("/WEB-INF/views/welcome.jsp");
  
  // 返回ModelAndView对象
  
  return mv;
  
  如果我们采用XML方式配置SpringMVC的Controller,那么我们具体的Controller必须Controller接口,并在handleRequest方法中调用业务逻辑组件去处理请求并生成响应。这里的响应是一个ModelAndView对象,DispatcherServlet会选择合适的ViewResolver并根据ModelAndView对象把Model填充到View中进行渲染然后返回给用户。
  
  注意到,Controller接口的实现类只能处理一个单一的请求动作,也就是说,一个Controller对应一个请求。稍后我们提到的基于注解的控制器可以支持同时处理多个请求动作,真正实现方法级别的请求拦截和处理。
  
  (4). 相应的视图页面
  
  <%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
  
  <%
  
  String path = request.getContextPath();
  
  String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
  
  %>
  
  <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
  
  <html>
  
  <head>
  
  <base href="<%=basePath%>">
  
  <title>welcome</title>
  
  <meta http-equiv="pragma" content="no-cache">
  
  <meta http-equiv="cache-control" content="no-cache">
  
  <meta http-equiv="expires" content="0">
  
  <meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
  
  <meta http-equiv="description" content="This is my page">
  
  </head>
  
  <body>
  
  ${requestScope.message} <br>
  
  </body>
  
  </html>
  
  在上面的视图中,DispatcherServlet会选择合适的ViewResolver并根据Controller返回的Model填充到View中展现给用户,如下图所示:
  
  XML-page.png-6.1kB
  
  2、SpringMVC应用开发流程DEMO:Annotation 配置
  
  (1). 在web.xml中配置前端控制器 DispatcherServlet
  
  <?xml version="1.0" encoding="UTF-8"?>
  
  <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  
  xmlns="http://java.sun.com/xml/ns/javaee"
  
  xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
  
  id="WebApp_ID" version="2.5">
  
  <display-name>SpringMVCDemo</display-name>
  
  <!-- 配置Spring MVC的前端控制器:DispatchcerServlet -->
  
  <servlet>
  
  <servlet-name>springmvc</servlet-name>
  
  <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
  
  <!-- SpringMVC配置文件路径和名称设定 -->
  
  <init-param>
  
  <param-name>contextConfigLocation</param-name>
  
  <param-value>classpath:springmvc.xml</param-value>
  
  </init-param>
  
  <!-- Web应用启动时立即加载 -->
  
  <load-on-startup>1</load-on-startup>
  
  </servlet>
  
  <servlet-mapping>
  
  <servlet-name>springmvc</servlet-name>
  
  <url-pattern>/</url-pattern> <!-- 拦截所有请求 -->
  
  </servlet-mapping>
  
  </web-app>
  
  DispatcherServlet的配置与基于XML配置的SpringMVC无异,此不赘述。
  
  (2). 在web.xml中指定路径配置SpringMVC的配置文件
  
  <?xml version="1.0" encoding="UTF-8"?>
  
  <beans xmlns="http://www.springframework.org/schema/beans"
  
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  
  xmlns:context="http://www.springframework.org/schema/context"
  
  xmlns:mvc="http://www.springframework.org/schema/mvc"
  
  xsi:schemaLocation="http://www.springframework.org/schema/beans
  
  http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
  
  http://www.springframework.org/schema/context
  
  http://www.springframework.org/schema/context/spring-context-4.0.xsd
  
  http://www.springframework.org/schema/mvc
  
  http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd">
  
  <!-- 配置Handle,映射"/hello"请求 -->
  
  <!-- <bean name="/hello" class="cn.edu.tju.rico.controller.HelloController"/> -->
  
  <!-- Spring自动扫描相关类并将Spring注解类注册为Spring的Bean -->
  
  <context:component-scan base-package="cn.edu.tju.rico"></context:component-scan>
  
  <!-- 处理映射器将bean的name作为url进行查找,需要在配置Handle时指定name(即url) -->
  
  <!-- <bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/> -->
  
  <bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping"/>
  
  <!-- SimpleControllerHandlerAdapter是一个处理器适配器,所有处理适配器都要实现HandlerAdapter接口 -->
  
  <!-- <bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"/> -->
  
  <bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter"/>
  
  <!-- 视图解析器 -->
  
  <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"/>
  
  </beans>
  
  如果我们采用Annotation方式配置SpringMVC的Controller,那么在配置文件中首先需要扫描SpringMVC应用中所有基于注解的控制器类。注意到,关于HandlerMapping、HandlerAdapter的配置我们分别使用的是RequestMappingHandlerMapping和RequestMappingHandlerAdapter两个实现类,而没有使用SpringMVC默认配置中对应的注解类:DefaultAnnotationHandlerMapping 和 AnnotationMethodHandlerAdapter,这是因为DefaultAnnotationHandlerMapping 和 AnnotationMethodHandlerAdapter这两个类已被Spring废弃。

转载于:https://www.cnblogs.com/chenergougou/p/6947463.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/370449.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

java方法调用机制_Java方法调用机制 - osc_bkdv2it5的个人空间 - OSCHINA - 中文开源技术交流社区...

最近在编程时&#xff0c;修改方法传入对象的对象引用&#xff0c;并没有将修改反映到调用方法中。奇怪为什么结果没有变化&#xff0c;原因是遗忘了Java对象引用和内存分配机制。本文介绍3个点&#xff1a;① 该问题举例说明② 简要阐述Java内存区域③ 介绍JVM中方法调用的机制…

CSS染色图标(图片)

之前一直以为用background引入的图标无法染色&#xff08;非字体图标&#xff09;&#xff0c;现在才知道有黑科技可以用&#xff0c;就是利用drop-shadow。 代码示例 <!DOCTYPE html> <html> <head lang"en"><meta charset"UTF-8"&…

eclipse安装java web插件

1 查看eclipse版本 找到eclipse的安装目录&#xff0c;找到readme文件&#xff0c;打开其中的html文件&#xff0c;我的是4.6版本的,代号是oxygen 2 安装 打开eclipse,点击help-Install new software-单击add&#xff0c;在弹出窗口中输入网址&#xff1a; http://download.ecl…

python正则表达式指南_Python正则表达式指南

1. 正则表达式基础1.1. 简单介绍正则表达式并不是Python的一部分。正则表达式是用于处理字符串的强大工具&#xff0c;拥有自己独特的语法以及一个独立的处理引擎&#xff0c;效率上可能不如str自带的方法&#xff0c;但功能十分强大。得益于这一点&#xff0c;在提供了正则表达…

Google Guava EventBus用于事件编程

在任何软件应用程序中都是如此&#xff0c;有些对象需要共享信息才能完成工作。 在Java应用程序中&#xff0c;实现信息共享的一种方法是拥有事件侦听器&#xff0c;其唯一目的是在发生所需事件时采取某些措施。 在大多数情况下&#xff0c;此过程有效&#xff0c;并且最有经验…

system类

package system.cn; /** system类的方法 都是静态方法&#xff0c;可以直接用类名直接调用* 常用的方法&#xff1a;* static long currentTimeMillis() 返回以毫秒为单位的当前时间。 static void exit(int status) 终止当前正在运行的 Java 虚拟机。 static void gc…

c await和java_blog/java/test/awaitility.zh.md at master · c-rainstorm/blog · GitHub

javaAtomicInteger atomic new AtomicInteger(0);// Do some async stuff that eventually updates the atomic integerawait().untilAtomic(atomic, equalTo(1));等待一个 AtomicBoolean 更简单&#xff1a;javaAtomicBoolean atomic new AtomicBoolean(false);// Do some a…

实现输入框小数多 自动进位展示,编辑时实际值不变

今天遇到个业务需求&#xff0c;要求输入框&#xff0c;输入数字的小数位数可以很多位&#xff0c;但移开后显示&#xff0c;只显示小数点后两位 &#xff08;四舍五入&#xff09;&#xff0c;当要编辑的时候&#xff0c;展现其原来的输入数据。 闲话不多说&#xff0c;当时也…

使用Jasper Reports以Java创建报告

上周&#xff0c;我试图使用Jasper创建报告。 在这篇文章中&#xff0c;我将记录一些资源和链接&#xff0c;以便对任何寻求类似信息的人都有用。 我将介绍Jasper报告&#xff0c;示例和Dynamic Jasper的生命周期。 Jasper Reports是世界上最受欢迎的开源报告引擎。 它完全用…

CentOS7 安装NodeJS

一、切换目录到/usr/local/src 命令行&#xff1a;cd /usr/local/src 二、下载node.js&#xff08;我这里下载的是二进制的源码&#xff09; 命令行&#xff1a; wget https://nodejs.org/dist/v8.9.1/node-v8.9.1-linux-x64.tar.xz 图片&#xff1a; 三、解压压缩包 命令行&am…

CSS3基础2(变形与动画)

<!DOCTYPE html5><html lang"en"><head> <meta charset"UTF-8"> <title>CSS3基础知识&#xff08;动画&#xff09;</title> <style> /*div{*/ /*width: 150px;*/ /*hei…

java对hashmap迭代_Java:通过HashMap迭代,这样更有效率?

第二个选项肯定更有效&#xff0c;因为在第一个选项中只进行一次查找&#xff0c;次数为n次。但是&#xff0c;没有什么比尝试它更好&#xff0c;当你可以。所以这里 –(不完美&#xff0c;但足够好验证假设和我的机器)public static void main(String args[]) {Map map new H…

html-edm(邮件营销)编写规则

最近写了一个edm邮件 以前没有接触过 使用的是很老的html页面编写规则 只能用table标签 在此记录一下edm编写的一些规则 个人参考的是这两个网址&#xff0c;转载一下 http://www.zcool.com.cn/article/ZMTM5MDgw.html https://www.cnblogs.com/lhweb15/p/6404626.html …

ASP.NET Core2.0 环境下MVC模式的支付宝PC网站支付接口-沙箱环境开发测试

1.新建.NET Core web项目 2.Controllers-Models-Views 分三个大部分 3.下载安装最新sdk 官方的SDK以及Demo都还是.NET Framework的&#xff0c;根据官方文档说明新建网站后还是需要引用官方SDK的源码&#xff0c; 在这里直接使用网上一位朋友的用.NET Standard 2.0 进行实现了支…

如何在redhat8里使用gcc命令_如何使用who命令检查用户登录信息

请关注本头条号&#xff0c;每天坚持更新原创干货技术文章。如需学习视频&#xff0c;请在微信搜索公众号“智传网优”直接开始自助视频学习1. 前言本教程主要介绍如何使用who命令检查用户登录信息。如何使用who命令检查用户登录信息Linux中的who命令列出了系统上的所有登录用户…

研究僵局–第4部分:修复代码

在这个简短的博客系列的最后BadTransferOperation中&#xff0c;我一直在讨论分析死锁&#xff0c;我将修复BadTransferOperation代码。 如果您看过本系列的其他博客 &#xff0c;那么您将知道&#xff0c;为了达到这一点&#xff0c;我创建了死锁的演示代码&#xff0c;展示了…

chrome插件2

转自&#xff1a;http://www.codeceo.com/article/15-chrome-extension.html 1. Web Developer 支持Chrome的Web Developer扩展&#xff0c;允许你通过添加一个小工具栏来使用不同的工具。 官方网站&#xff1a;https://chrome.google.com/webstore/detail/web-developer/bfbam…

java月历组件_vue之手把手教你写日历组件

---恢复内容开始---1.日历组件1.分析功能&#xff1a;日历基本功能&#xff0c;点击事件改变日期&#xff0c;样式的改变1.结构分析&#xff1a;html1.分为上下两个部分2.上面分为左按钮&#xff0c;中间内容展示&#xff0c;右按钮下面分为周几展示和日期展示3.基本结构页面ht…

HTML5和css3

超链接 <a target"页面打开位置" href"链接地址">内容</a>target:_blank 重新打开一个页面target:_self 当前页面打开 1.页面地址&#xff1a; 基础功能&#xff0c;用于进入该链接的页面&#xff1b; 2.锚点&#xff1a; 需要给标签名定义id…

python下载显示文件丢失_Microsoft.PythonTools.resources.dll

我该如何安装从金山毒霸下载的DLL文件&#xff1f;一&#xff1a;1、从金山毒霸下载压缩文件。2、将DLL文件解压到电脑上的某个地方。3、把该文件跟要求使用它的程序放在同一路径上。注意32位程序需要使用32位的DLL文件&#xff0c;64位程序需要使用64位的DLL文件。否则会出现0…