SpringMVC是spring为展现层提供的基于MVC设计理念的优秀WEB框架,是目前最主流的MVC框架之一。SpringMVC通过一套注解,可以让普通的JAVA类成为contrllor控制器,无需继承Servlet,实现了控制层和Servlet之间的解耦。SpringMVC支持Rest风格的URL写法。SpringMVC采用了松耦合,可热插的主键结构,比其他的框架更具扩展性和灵活性。

DispatcherServlet

Spring MVC和许多其他web框架一样,都是围绕前端控制器模式设计的,其中一个中心Servlet, DispatcherServlet为请求处理提供了一个共享算法,而实际工作是由可配置的委托组件执行的。该模型灵活,支持多种工作流程。

与任何Servlet一样,DispatcherServlet需要通过Java配置或web.xml来根据Servlet规范声明和映射。反过来,DispatcherServlet使用Spring配置来发现请求映射、视图解析、异常处理等所需的委托组件。

  1. public class MyWebApplicationInitializer implements WebApplicationInitializer {
  2. @Override
  3. public void onStartup(ServletContext servletContext) {
  4. // Load Spring web application configuration
  5. AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext();
  6. context.register(AppConfig.class);
  7. // Create and register the DispatcherServlet
  8. DispatcherServlet servlet = new DispatcherServlet(context);
  9. ServletRegistration.Dynamic registration = servletContext.addServlet("app", servlet);
  10. registration.setLoadOnStartup(1);
  11. registration.addMapping("/app/*");
  12. }
  13. }
  1. <web-app>
  2. <listener>
  3. <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
  4. </listener>
  5. <context-param>
  6. <param-name>contextConfigLocation</param-name>
  7. <param-value>/WEB-INF/app-context.xml</param-value>
  8. </context-param>
  9. <servlet>
  10. <servlet-name>app</servlet-name>
  11. <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
  12. <init-param>
  13. <param-name>contextConfigLocation</param-name>
  14. <param-value></param-value>
  15. </init-param>
  16. <load-on-startup>1</load-on-startup>
  17. </servlet>
  18. <servlet-mapping>
  19. <servlet-name>app</servlet-name>
  20. <url-pattern>/app/*</url-pattern>
  21. </servlet-mapping>
  22. </web-app>

容器层次

DispatcherServlet需要WebApplicationContext(普通ApplicationContext的扩展)作为它自己的配置。WebApplicationContext有一个到ServletContext的链接和与之关联的Servlet。它也被绑定到ServletContext,如果他们需要访问它,可以使用RequestContextUtils上的静态方法来查找WebApplicationContext,。

对于许多应用程序来说,拥有一个WebApplicationContext就足够了。也可以有一个上下文层次结构,其中一个Root WebApplicationContext在多个DispatcherServlet(或其他Servlet)实例之间共享,每个实例都有自己的子WebApplicationContext配置。

Root WebApplicationContext通常包含基础设施bean,比如需要跨多个Servlet实例共享的数据存储库和业务服务。这些bean被有效地继承,并且可以在特定于Servlet的子WebApplicationContext中被重写(即重新声明),该子WebApplicationContext通常包含给定Servlet的本地bean。下图显示了这种关系:
image.png
如果应用程序上下文层次结构不是必需的,应用程序可以只配置一个“根”上下文,并让contextConfigLocation Servlet参数为空。

<web-app>

    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>

  // 配置root WebApplicationContex
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>/WEB-INF/root-context.xml</param-value>
    </context-param>

    <servlet>
        <servlet-name>app1</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>

      // 配置 sub WebbApplicationContex
      <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>/WEB-INF/app1-context.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>

    <servlet-mapping>
        <servlet-name>app1</servlet-name>
        <url-pattern>/app1/*</url-pattern>
    </servlet-mapping>

</web-app>
public class MyWebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {

    @Override
    protected Class<?>[] getRootConfigClasses() {
        return new Class<?>[] { RootConfig.class };
    }

    @Override
    protected Class<?>[] getServletConfigClasses() {
        return new Class<?>[] { App1Config.class };
    }

    @Override
    protected String[] getServletMappings() {
        return new String[] { "/app1/*" };
    }
}

一些组件Bean

Bean type Explanation
HandlerMapping 将一个请求映射到一个处理程序,以及一个用于预处理和后期处理的拦截器列表。映射基于一些标准,HandlerMapping实现的细节各不相同。

两个主要的HandlerMapping实现是RequestMappingHandlerMapping(它支持@RequestMapping注释方法)和SimpleUrlHandlerMapping(它维护URI路径模式到处理程序的显式注册)。
HandlerAdapter 帮助DispatcherServlet调用映射到请求的处理程序,无论该处理程序实际是如何调用的。例如,调用带注释的控制器需要解析注释。HandlerAdapter的主要目的是保护DispatcherServlet不受这些细节的影响。
HandlerExceptionResolver 解决异常的策略,可能将异常映射到处理程序、HTML错误视图或其他目标。看到异常。
ViewResolver 解析从处理程序返回的基于逻辑字符串的视图名称到实际的视图,并将其呈现给响应。
LocaleResolver, LocaleContextResolver 为了能够提供国际化的视图,解析客户端正在使用的Locale,可能还有他们的时区。
ThemeResolver 解析您的web应用程序可以使用的主题——例如,提供个性化的布局
MultipartResolver 在多部分解析库的帮助下解析多部分请求(例如,浏览器表单文件上传)的抽象。
FlashMapManager 存储和检索“输入”和“输出”FlashMap,可以用来将属性从一个请求传递到另一个请求,通常是通过重定向

Web MVC Config

应用程序可以声明处理请求所需的特殊Bean类型中列出的基础设施Bean。DispatcherServlet检查每个特殊bean的WebApplicationContext。如果没有匹配的bean类型,则返回DispatcherServlet.properties中列出的默认类型。在大多数情况下,MVC Config是最好的起点。它用Java或XML声明所需的bean,并提供更高级别的配置回调API来对其进行定制。

Servlet Config

您可以选择以编程方式注册一个DispatcherServlet:

import org.springframework.web.WebApplicationInitializer;

public class MyWebApplicationInitializer implements WebApplicationInitializer {

    @Override
    public void onStartup(ServletContext container) {
        XmlWebApplicationContext appContext = new XmlWebApplicationContext();
        appContext.setConfigLocation("/WEB-INF/spring/dispatcher-config.xml");

        ServletRegistration.Dynamic registration = container.addServlet("dispatcher", new DispatcherServlet(appContext));
        registration.setLoadOnStartup(1);
        registration.addMapping("/");
    }
}

WebApplicationInitializer是Spring MVC提供的一个接口,它可以确保你的实现被检测到,并自动用于初始化任何Servlet 3容器。WebApplicationInitializer的抽象基类实现名为AbstractDispatcherServletInitializer,通过重写方法来指定servlet映射和DispatcherServlet配置的位置,使注册DispatcherServlet变得更加容易。

public class MyWebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {

    @Override
    protected Class<?>[] getRootConfigClasses() {
        return null;
    }

    @Override
    protected Class<?>[] getServletConfigClasses() {
        return new Class<?>[] { MyWebConfig.class };
    }

    @Override
    protected String[] getServletMappings() {
        return new String[] { "/" };
    }
}

如果你使用基于xml的Spring配置,你应该直接从AbstractDispatcherServletInitializer扩展,如下所示:

public class MyWebAppInitializer extends AbstractDispatcherServletInitializer {

    @Override
    protected WebApplicationContext createRootApplicationContext() {
        return null;
    }

    @Override
    protected WebApplicationContext createServletApplicationContext() {
        XmlWebApplicationContext cxt = new XmlWebApplicationContext();
        cxt.setConfigLocation("/WEB-INF/spring/dispatcher-config.xml");
        return cxt;
    }

    @Override
    protected String[] getServletMappings() {
        return new String[] { "/" };
    }
}

添加Filter实例,并让它们自动映射到DispatcherServlet

public class MyWebAppInitializer extends AbstractDispatcherServletInitializer {

    // ...

    // 每个过滤器都会根据其具体类型添加一个默认名称,并自动映射到DispatcherServlet
    @Override
    protected Filter[] getServletFilters() {
        return new Filter[] {
            new HiddenHttpMethodFilter(), new CharacterEncodingFilter() };
    }
}

执行流程

image.png

  • DispatcherServlet:前端控制器

用户请求到达前端控制器,它就相当于 mvc 模式中的 c,dispatcherServlet 是整个流程控制的中心,由 它调用其它组件处理用户的请求,dispatcherServlet 的存在降低了组件之间的耦合性。

  • HandlerMapping:处理器映射器

HandlerMapping 负责根据用户请求找到 Handler 即处理器,SpringMVC 提供了不同的映射器实现不同的 映射方式,例如:配置文件方式,实现接口方式,注解方式等。

  • Handler:处理器 (自己定义的Controller处理单元)

它就是我们开发中要编写的具体业务控制器。由 DispatcherServlet 把用户请求转发到 Handler。由 Handler 对具体的用户请求进行处理。

  • HandlAdapter:处理器适配器

通过 HandlerAdapter 对处理器进行执行,这是适配器模式的应用,通过扩展适配器可以对更多类型的处理器进行执行

  • View Resolver:视图解析器

View Resolver 负责将处理结果生成 View 视图,View Resolver 首先根据逻辑视图名解析成物理视图名 即具体的页面地址,再生成 View 视图对象,最后对 View 进行渲染将处理结果通过页面展示给用户。

  • View:视图

SpringMVC 框架提供了很多的 View 视图类型的支持,包括:jstlView、freemarkerView、pdfView等。我们最常用的视图就是 jsp。 一般情况下需要通过页面标签或页面模版技术将模型数据通过页面展示给用户,需要由程序员根据业务需求开 发具体的页面。

  • 说明

在 SpringMVC 的各个组件中,处理器映射器、处理器适配器、视图解析器称为 SpringMVC 的三大组件。
使 用 自动加载 RequestMappingHandlerMapping (处理映射器) 和 RequestMappingHandlerAdapter ( 处 理 适 配 器 ) , 可 用 在 SpringMVC.xml 配 置 文 件 中 使 用 替代注解处理器和适配器的配置。

拦截器

所有HandlerMapping实现都支持处理程序拦截器,当您想要将特定的功能应用于特定的请求时,这些拦截器非常有用——例如,检查主体。拦截器需实现实现HandlerInterceptor,这三个方法应该能够提供足够的灵活性来进行各种预处理和后处理

  • preHandle(..):在实际处理程序运行之前。

preHandle(..)方法返回布尔值。您可以使用此方法中断或继续执行链的处理。当此方法返回true时,处理程序继续执行链。当它返回false时,DispatcherServlet假定拦截器本身已经处理了请求,并且不会继续执行执行链中的其他拦截器和实际处理程序。

  • postHandle(..): 在处理程序运行之后
  • afterCompletion(..):在完成请求之后

    创建Interceptor

  1. 通过实现HandlerInterceptor接口,或继承HandlerInterceptor接口的实现类(如HandlerInterceptorAdapter)来定义。
  2. 通过实现WebRequestInterceptor接口,或继承WebRequestInterceptor接口的实现类来定义。

    Interceptor 与 Filter区别

  3. 拦截器SpringMVC的,而过滤器是servlet的。

  4. 拦截器不依赖与servlet容器,由spring容器初始化,过滤器依赖与servlet容器,由servlet容器初始化。
  5. 拦截器只能对action请求起作用,而过滤器则可以对几乎所有的请求起作用。
  6. 拦截器可以访问action上下文、值栈里的对象,而过滤器不能访问。
  7. 在action的生命周期中,拦截器可以多次被调用,而过滤器只能在容器初始化时被调用一次。
  8. 拦截器可以获取IOC容器中的各个bean,而过滤器就不太方便,这点很重要,在拦截器里注入一个service,可以调用业务逻辑。

    异常处理器

    如果在请求映射期间发生异常或从请求处理程序(例如@Controller)抛出异常,DispatcherServlet将委托给HandlerExceptionResolver b来解决异常并提供替代处理,这通常是一个错误响应。
HandlerExceptionResolver Description
SimpleMappingExceptionResolver 常类名和错误视图名之间的映射。用于在浏览器应用程序中呈现错误页面。
DefaultHandlerExceptionResolver 解决Spring MVC引发的异常,并将它们映射到HTTP状态码。
ResponseStatusExceptionResolver 使用@ResponseStatus注释解决异常,并根据注释中的值将它们映射到HTTP状态码。
ExceptionHandlerExceptionResolver 通过调用@Controller或@ControllerAdvice类中的@ExceptionHandler方法来解决异常。

在Spring配置中声明多个HandlerExceptionResolver bean,并根据需要设置它们的order属性,你可以形成一个异常解析器链。order属性越高,异常解析器定位的越晚。

异常处理实现

  1. 使用@ExceptionHandler注解处理异常。缺点:只能处理当前Controller中的异常。 ```java @Controller public class ControllerDemo1 { @RequestMapping(“test1.action”) public String test1(){
     int i = 1/0;
     return "success.jsp";
    
    } @RequestMapping(“test2.action”) public String test2(){
     String s =null;
     System.out.println(s.length());
     return "success.jsp";
    
    } @ExceptionHandler(value ={ArithmeticException.class,NullPointerException.class} ) public ModelAndView handelException(){
     ModelAndView mv =new ModelAndView();
     mv.setViewName("error1.jsp");
     return mv;
    
    }

2.  使用:@ControllerAdvice+@ExceptionHandler。此处优先级低于局部异常处理器。
```java
@ControllerAdvice
public class GloableExceptionHandler1 {
    @ExceptionHandler(value ={ArithmeticException.class,NullPointerException.class} )
    public ModelAndView handelException(){
        ModelAndView mv =new ModelAndView();
        mv.setViewName("error1.jsp");
        return mv;
    }
}
  1. 使用:SimpleMappingExceptionResolver ```java

public class GloableExceptionHandler2{ @Bean public SimpleMappingExceptionResolver getSimpleMappingExceptionResolver(){ SimpleMappingExceptionResolver resolver = new SimpleMappingExceptionResolver(); Properties prop = new Properties(); prop.put(“java.lang.NullPointerException”,”error1.jsp”); prop.put(“java.lang.ArithmeticException”,”error2.jsp”); resolver.setExceptionMappings(prop); return resolver; } }


4.  自定义的HandlerExceptionResolver
```java
@Configuration
public class GloableExceptionHandler3 implements HandlerExceptionResolver {
@Override
public ModelAndView resolveException(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) {
    ModelAndView mv = new ModelAndView();
    if(e instanceof NullPointerException){
            mv.setViewName("error1");
    }
    if(e instanceof ArithmeticException){
            mv.setViewName("error2");
    }
    mv.addObject("msg",e);
    return mv;
}}

视图解析器

Spring MVC定义了ViewResolver和View接口,它们让你可以在浏览器中渲染模型,而不用把你绑在特定的视图技术上。ViewResolver提供了视图名称和实际视图之间的映射。View处理的是在移交给特定视图技术之前数据的准备工作。

ViewResolver Description
AbstractCachingViewResolver AbstractCachingViewResolver的子类缓存它们解析的视图实例。缓存可以提高某些视图技术的性能。可以通过将cache属性设置为false来关闭缓存。此外,如果您必须在运行时刷新某个视图(例如,当FreeMarker模板被修改时),您可以使用removeFromCache(String viewName, Locale loc)方法。
UrlBasedViewResolver ViewResolver接口的简单实现,可以直接将逻辑视图名解析为url,而不需要显式的映射定义。如果您的逻辑名称与视图资源的名称以一种直接的方式匹配,而不需要任何映射,那么这是合适的。
InternalResourceViewResolver UrlBasedViewResolver的子类,支持InternalResourceView(servlet和jsp)和其子类,如JstlView和TilesView。你可以使用setViewClass(..)为这个解析器生成的所有视图指定视图类。
FreeMarkerViewResolver UrlBasedViewResolver的的子类,支持FreeMarkerView和自定义子类。
ContentNegotiatingViewResolver ViewResolver接口的实现,该接口基于请求文件名或Accept头解析视图。
BeanNameViewResolver ViewResolver接口的实现,该接口在当前应用程序上下文中将视图名称解释为bean名称。这是一个非常灵活的变体,它允许基于不同的视图名称混合和匹配不同的视图类型。每个这样的视图都可以定义为一个bean,例如在XML或配置类中。

使用注解

Request Mapping

可以使用@RequestMapping注释将请求映射到控制器方法。它有各种属性来匹配URL、HTTP方法、请求参数、头信息和媒体类型。

  • @GetMapping:支持Get请求方式
  • @PostMapping:支持Post请求方式
  • @PutMapping:支持Put请求方式
  • @DeleteMapping:支持Delete请求方式
  • @PatchMapping

    URL模式

    @RequestMapping方法可以使用URL模式进行映射。有两种选择

  • PathPattern:匹配URL路径的预解析模式也被预解析为PathContainer。

  • AntPathMatcher:匹配String路径的字符串模式。这是Spring配置中用来选择类路径、文件系统和其他位置上的资源的原始解决方案。它的效率较低,而且String路径输入对于有效地处理url的编码和其他问题是一个挑战。

一些示例:

  • “/resources/ima?e.png” - 匹配路径段中的一个字符
  • “/resources/*.png” - 匹配一个路径段中的零个或多个字符
  • “/resources/**” - 匹配多个路径段
  • “/projects/{project}/versions” -匹配一个路径段并将其捕获为一个变量
  • “/projects/{project:[a-z]+}/versions” - 用正则表达式匹配并捕获一个变量

可以通过@PathVariable捕获的URI变量

@GetMapping("/owners/{ownerId}/pets/{petId}")
public Pet findPet(@PathVariable Long ownerId, @PathVariable Long petId) {
    // ...
}

指定consumes属性

可以根据请求的Content-Type来缩小请求映射

@PostMapping(path = "/pets", consumes = "application/json") 
public void addPet(@RequestBody Pet pet) {
    // ...
}

指定produces属性

你可以根据Accept请求头和控制器方法生成的内容类型列表来缩小请求映射,如下面的例子所示:

@GetMapping(path = "/pets/{petId}", produces = "application/json") 
@ResponseBody
public Pet getPet(@PathVariable String petId) {
    // ...
}

指定Parameters, headers属性

@GetMapping(path = "/pets/{petId}", params = "myParam=myValue") 
public void findPet(@PathVariable String petId) {
    // ...
}

@GetMapping(path = "/pets", headers = "myHeader=myValue") 
public void findPet(@PathVariable String petId) {
    // ...
}

Handler Methods

@RequestMapping处理程序方法有一个灵活的签名,可以从一系列受支持的控制器方法参数和返回值中进行选择。

方法参数

Controller method argument Description
WebRequest, NativeWebRequest 对请求参数、请求和会话属性的通用访问,而不直接使用Servlet API。
javax.servlet.ServletRequest, javax.servlet.ServletResponse 选择任何特定的请求或响应类型——例如,ServletRequest、HttpServletRequest或Spring的MultipartRequest、MultipartHttpServletRequest
javax.servlet.http.HttpSession 强制会话的存在。因此,这样的论点永远不会为空。请注意,会话访问不是线程安全的。如果允许多个请求同时访问一个会话,考虑将RequestMappingHandlerAdapter实例的synchronizeOnSession标志设置为true。
javax.servlet.http.PushBuilder Servlet 4.0 push builder API用于编程HTTP / 2资源推送。注意,只要Servlet规范,如果客户机不支持HTTP / 2特性,那么注入的PushBuilder实例就可以为空。
java.security.Principal 当前已通过身份验证的用户——如果已知,可能是一个特定的Principal实现类。
请注意,这个参数不会被热切解析,如果它被注释了,以便在通过HttpServletRequest#getUserPrincipal返回到默认解析之前,允许一个自定义解析器来解析它。例如,Spring Security Authentication实现了Principal,并且会通过HttpServletRequest#getUserPrincipal这样被注入,除非它也被@AuthenticationPrincipal注释,在这种情况下,它会通过Authentication#getPrincipal被自定义的Spring Security解析器解析。
HttpMethod 请求的HTTP方法。
java.util.Locale 当前请求区域设置,由可用的最特定的LocaleResolver(实际上,配置的LocaleResolver或LocaleContextResolver)确定。
java.util.TimeZone + java.time.ZoneId 与当前请求相关联的时区,由LocaleContextResolver确定。
java.io.InputStream, java.io.Reader 用于访问Servlet API公开的原始请求体。
java.io.OutputStream, java.io.Writer 用于访问Servlet API公开的原始响应体。
@PathVariable 访问URI路径变量。
@MatrixVariable 用于访问URI路径段中的名称-值对。看到矩阵变量。
@RequestParam 访问Servlet请求参数,包括多部分文件。参数值被转换为声明的方法参数类型。参见@RequestParam和Multipart。
注意,对于简单的参数值,@RequestParam是可选的。
@RequestHeader 访问请求头。头值被转换为声明的方法参数类型。
@CookieValue 用于访问cookie。cookie值被转换为声明的方法参数类型。
@RequestBody 用于访问HTTP请求体。通过使用HttpMessageConverter实现,主体内容被转换为声明的方法参数类型。
HttpEntity 用于访问请求头和正文。主体被HttpMessageConverter转换。
@RequestPart 为了访问多部件/表单数据请求中的部件,可以使用HttpMessageConverter转换部件的主体。
java.util.Map, org.springframework.ui.Model, org.springframework.ui.ModelMap 用于访问HTML控制器中使用的模型,并作为视图呈现的一部分暴露给模板。
RedirectAttributes 指定重定向(即附加到查询字符串)时要使用的属性和要临时存储的flash属性,直到重定向后的请求。
@ModelAttribute 用于访问模型中已有的属性(如果不存在则实例化),并应用数据绑定和验证。注意,@ModelAttribute的使用是可选的。
Errors, BindingResult 用于访问命令对象(即@ModelAttribute参数)的验证和数据绑定中的错误,或@RequestBody或@RequestPart参数的验证中的错误。您必须在验证的方法参数之后立即声明Errors或BindingResult参数。
SessionStatus + class-level @SessionAttributes 标记表单处理完成,这将触发清理通过类级@SessionAttributes注释声明的会话属性。
UriComponentsBuilder 用于准备一个相对于当前请求的主机、端口、方案、上下文路径和servlet映射的文字部分的URL。
@SessionAttribute 用于访问任何会话属性,而不是作为类级别@SessionAttributes声明的结果存储在会话中的模型属性。
@RequestAttribute 访问请求属性
Any other argument 如果一个方法参数与该表中的任何早期值不匹配,并且它是一个简单类型(由BeanUtils#isSimpleProperty确定,它将被解析为@RequestParam。否则,它将被解析为@ModelAttribute。

返回值

Controller method return value Description
@ResponseBody 返回值通过HttpMessageConverter实现转换并写入响应。
HttpEntity, ResponseEntity 指定完整响应(包括HTTP头和正文)的返回值将通过HttpMessageConverter实现转换并写入响应。
HttpHeaders 返回一个没有正文的响应。
String ViewResolver实现解析并与隐式模型一起使用的视图名——通过命令对象和@ModelAttribute方法确定。处理程序方法还可以通过声明model参数(参见显式注册)以编程方式充实模型。
View 用于与隐式模型一起呈现的View实例——通过命令对象和@ModelAttribute方法确定。处理程序方法还可以通过声明model参数(参见显式注册)以编程方式充实模型。
java.util.Map, org.springframework.ui.Model 要添加到隐式模型中的属性,视图名通过RequestToViewNameTranslator隐式确定。
@ModelAttribute 要添加到模型中的属性,视图名通过RequestToViewNameTranslator隐式确定。注意@ModelAttribute是可选的。
ModelAndView object 要使用的视图和模型属性,以及响应状态(可选)。
void 一个返回类型为void(或返回值为null)的方法,如果它也有ServletResponse、OutputStream参数或@ResponseStatus注释,则被认为已经完全处理了响应。如果控制器做了一个积极的ETag或lastModified时间戳检查,同样也是正确的。如果以上都不正确,void返回类型也可以表示REST控制器的“无响应体”,或者HTML控制器的默认视图名称选择。
DeferredResult 从任何线程异步地生成前面的任何返回值—例如,作为某个事件或回调的结果。参见异步请求和DeferredResult。
Callable 在一个Spring mvc管理的线程中异步地生成上述任何一个返回值。参见异步请求和可调用请求。
ListenableFuture, java.util.concurrent.CompletionStage, java.util.concurrent.CompletableFuture 作为DeferredResult的替代,方便(例如,当一个底层服务返回其中之一时)。
ResponseBodyEmitter, SseEmitter 通过HttpMessageConverter实现异步发送一个对象流,并将其写入响应中。也支持作为ResponseEntity的主体。请参阅异步请求和HTTP流。
StreamingResponseBody 异步写入响应OutputStream。也支持作为ResponseEntity的主体。
Reactive types — Reactor, RxJava, or others through ReactiveAdapterRegistry 可选的DeferredResult与多值流(例如,Flux, Observable)收集到一个列表。对于流场景(例如,文本/事件流,应用程序/json+流),使用SseEmitter和ResponseBodyEmitter,其中ServletOutputStream阻塞I/O是在Spring mvc管理的线程上执行的,并对每个写操作的完成施加背压。
Any other return value 何返回值不匹配表中的任何前面的值,而这是一个字符串或void被视为视图名称(通过requesttoviewname译者应用的默认视图名称选择),如果它不是一个简单的类型,就像BeanUtils # isSimpleProperty所决定的那样。简单类型的值仍未得到解决。

Matrix Variables

矩阵变量可以出现在任何路径段中,每个变量用分号分隔,多个值用逗号分隔(如,/cars;color=red,green;year=2012)。还可以通过重复的变量名指定多个值(如,color=red;color=green;color=blue)。如果期望URL包含矩阵变量,那么控制器方法的请求映射必须使用URI变量来屏蔽变量内容,并确保请求能够成功匹配,而不受矩阵变量顺序和存在的影响。

// GET /pets/42;q=11;r=22

@GetMapping("/pets/{petId}")
public void findPet(@PathVariable String petId, @MatrixVariable int q) {

    // petId == 42
    // q == 11
}

考虑到所有的路径段都可能包含矩阵变量,有时您可能需要消除矩阵变量应该在哪个路径变量中的歧义。

// GET /owners/42;q=11/pets/21;q=22

@GetMapping("/owners/{ownerId}/pets/{petId}")
public void findPet(
        @MatrixVariable(name="q", pathVar="ownerId") int q1,
        @MatrixVariable(name="q", pathVar="petId") int q2) {

    // q1 == 11
    // q2 == 22
}

矩阵变量可以定义为可选的,并指定默认值,

// GET /pets/42

@GetMapping("/pets/{petId}")
public void findPet(@MatrixVariable(required=false, defaultValue="1") int q) {

    // q == 1
}

为了获得所有的矩阵变量,你可以使用MultiValueMap

// GET /owners/42;q=11;r=12/pets/21;q=22;s=23

@GetMapping("/owners/{ownerId}/pets/{petId}")
public void findPet(
        @MatrixVariable MultiValueMap<String, String> matrixVars,
        @MatrixVariable(pathVar="petId") MultiValueMap<String, String> petMatrixVars) {

    // matrixVars: ["q" : [11,22], "r" : 12, "s" : 23]
    // petMatrixVars: ["q" : 22, "s" : 23]
}

@RequestParam

使用@RequestParam注释将Servlet请求参数(即查询参数或表单数据)绑定到控制器中的方法参数。

@Controller
@RequestMapping("/pets")
public class EditPetForm {
    // 使用@RequestParam绑定petId
    @GetMapping
    public String setupForm(@RequestParam("petId") int petId, Model model) { 
        Pet pet = this.clinic.loadPet(petId);
        model.addAttribute("pet", pet);
        return "petForm";
    }
}

@RequestHeader

使用@RequestHeader注释获取请求头参数,绑定到控制器中的方法参数。

GET / HTTP/1.1
Host: www.baidu.com
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:98.0) Gecko/20100101 Firefox/98.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate, br
Connection: keep-alive
Cookie: BAIDUID=21ADE347AA4B28BE2A711549E6670E0F:FG=1; BIDUPSID=21ADE347AA4B28BE8EDA6B82CB710D59; PSTM=1646574258; BD_UPN=13314752; BDUSS=XNIVH5xRmVaZDlma0p-RTJLbXJlQjExMngtZWRjcndJbFlRa295NEdUYlhSMHhpRVFBQUFBJCQAAAAAAAAAAAEAAACMGXCdsKzQodCh0-O2-QAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAANe6JGLXuiRic; sug=3; sugstore=1; ORIGIN=0; bdime=0
Upgrade-Insecure-Requests: 1
Sec-Fetch-Dest: document
Sec-Fetch-Mode: navigate
Sec-Fetch-Site: none
Sec-Fetch-User: ?1
@GetMapping("/demo")
public void handle(
    // 获取了Accept-Encoding和Keep-Alive报头的值
    @RequestHeader("Accept-Encoding") String encoding, 
    @RequestHeader("Keep-Alive") long keepAlive) { 
    //...
}

@CookieValue

使用@CookieValue注释将获取HTTP cookie的值,绑定到控制器中的方法参数

@GetMapping("/demo")
public void handle(@CookieValue("BAIDUID") String cookie) { 
    //...
}

@ModelAttribute

您可以在方法参数上使用@ModelAttribute注释来访问模型中的属性,或者在不存在时将其实例化。model属性也覆盖了来自HTTP Servlet请求参数的值,这些参数的名称与字段名称匹配。这被称为数据绑定,它使您不必处理单个查询参数和表单字段的解析和转换。

@PostMapping("/owners/{ownerId}/pets/{petId}/edit")
public String processSubmit(@ModelAttribute Pet pet) {
    // method logic...
}

使用@ModelAttribute方法提供模型属性或依赖框架创建模型属性的一种替代方法是使用Converter来提供实例。当模型属性名称与请求值的名称(如路径变量或请求参数)相匹配时,这将被应用,并且从String到模型属性类型有一个转换器。在下面的例子中,模型属性名称是account,它匹配URI路径变量account,并且有一个注册的Converter,它可以从数据存储中加载account:

@PutMapping("/accounts/{account}")
public String save(@ModelAttribute("account") Account account) {
    // ...
}

@SessionAttributes

@SessionAttributes用于在请求之间的HTTP Servlet会话中存储模型属性。它是一个类型级注释,声明特定控制器使用的会话属性。这通常列出了模型属性的名称或模型属性的类型,这些属性应该透明地存储在会话中,以供后续请求访问。

@Controller
@SessionAttributes("pet")  // 将Pet值存储在Servlet Session中
public class EditPetForm {

    @PostMapping("/pets/{id}")
    public String handle(Pet pet, BindingResult errors, SessionStatus status) {
        if (errors.hasErrors) {
            // ...
        }
        status.setComplete();  // 从Servlet会话中清除Pet值
    }
}

@SessionAttribute

如果你需要访问已存在的全局管理的会话属性(即,控制器之外的—例如,通过过滤器),并且可能存在也可能不存在,你可以在一个方法参数上使用@SessionAttribute注释,

@RequestMapping("/")
public String handle(@SessionAttribute User user) { 
    // ...
}

对于需要添加或删除会话属性的用例,考虑将org.springframework.web.context.request.WebRequest或javax.servlet.http.HttpSession注入到控制器方法中。对于作为控制器工作流一部分的会话中的模型属性的临时存储,可以考虑使用@SessionAttributes,如@SessionAttributes中所述。

@RequestAttribute

类似于@SessionAttribute,你可以使用@RequestAttribute注解来访问之前创建的预先存在的请求属性(例如,通过Servlet Filter或HandlerInterceptor)

@GetMapping("/")
public String handle(@RequestAttribute Client client) { 
    // ...
}

Redirect 属性

@PostMapping("/files/{path}")
public String upload(...) {
    // ...
    return "redirect:files/{path}";
}

Multipart

启用MultipartResolver后,带有multipart/form-data的POST请求的内容将被解析并作为常规请求参数访问。下面的例子访问一个常规的表单字段和一个上传的文件:

@Controller
public class FileUploadController {

    @PostMapping("/form")
    public String handleFormUpload(@RequestParam("name") String name,
            @RequestParam("file") MultipartFile file) {

        if (!file.isEmpty()) {
            byte[] bytes = file.getBytes();
            // store the bytes somewhere
            return "redirect:uploadSuccess";
        }
        return "redirect:uploadFailure";
    }
}

//-------------------------------------------------------------------------------------
class MyForm {

    private String name;

    private MultipartFile file;

    // ...
}

@Controller
public class FileUploadController {

    @PostMapping("/form")
    public String handleFormUpload(MyForm form, BindingResult errors) {
        if (!form.getFile().isEmpty()) {
            byte[] bytes = form.getFile().getBytes();
            // store the bytes somewhere
            return "redirect:uploadSuccess";
        }
        return "redirect:uploadFailure";
    }
}

@RequestBody

您可以使用@RequestBody注释,通过HttpMessageConverter读取请求体并将其反序列化为一个Object

@PostMapping("/accounts")
public void handle(@RequestBody Account account) {
    // ...
}

HttpEntity

HttpEntity或多或少与使用@RequestBody相同,但它基于一个公开请求头和请求体的容器对象。

PostMapping("/accounts")
public void handle(HttpEntity<Account> entity) {
    // ...
}

@ResponseBody

您可以在方法上使用@ResponseBody注释,通过HttpMessageConverter将返回序列化到响应体。

@GetMapping("/accounts/{id}")
@ResponseBody
public Account handle() {
    // ...
}

ResponseEntity

ResponseEntity类似于@ResponseBody,但有状态和头部

@GetMapping("/something")
public ResponseEntity<String> handle() {
    String body = ... ;
    String etag = ... ;
    return ResponseEntity.ok().eTag(etag).build(body);
}

Jackson JSON

JSON Views

pring MVC提供了内置的支持Jackson的序列化视图,它只允许渲染对象中所有字段的子集。要在@ResponseBody或ResponseEntity控制器方法中使用它,你可以使用Jackson的@JsonView注释来激活一个序列化视图类,

@RestController
public class UserController {

    @GetMapping("/user")
    @JsonView(User.WithoutPasswordView.class)
    public User getUser() {
        return new User("eric", "7!jd#h23");
    }
}

public class User {

    public interface WithoutPasswordView {};
    public interface WithPasswordView extends WithoutPasswordView {};

    private String username;
    private String password;

    public User() {
    }

    public User(String username, String password) {
        this.username = username;
        this.password = password;
    }

    @JsonView(WithoutPasswordView.class)
    public String getUsername() {
        return this.username;
    }

    @JsonView(WithPasswordView.class)
    public String getPassword() {
        return this.password;
    }
}
@RestController
public class UserController {

    @GetMapping("/user")
    public MappingJacksonValue getUser() {
        User user = new User("eric", "7!jd#h23");
        MappingJacksonValue value = new MappingJacksonValue(user);
        value.setSerializationView(User.WithoutPasswordView.class);
        return value;
    }
}

Model

@ModelAttribute

  • 在@RequestMapping方法的方法参数上,从模型中创建或访问对象,并通过WebDataBinder将其绑定到请求
  • 作为@Controller或@ControllerAdvice类中的方法注释,帮助在任何@RequestMapping方法调用之前初始化模型
  • 在@RequestMapping方法上标记其返回值是一个模型属性

    @ModelAttribute
    public void populateModel(@RequestParam String number, Model model) {
      model.addAttribute(accountRepository.findAccount(number));
      // add more ...
    }
    

    您还可以使用@ModelAttribute作为@RequestMapping方法的方法级注释,在这种情况下,@RequestMapping方法的返回值被解释为一个模型属性。这通常是不需要的,因为这是HTML控制器的默认行为,除非返回值是一个字符串会被解释为一个视图名。@ModelAttribute也可以自定义模型属性名。

    @GetMapping("/accounts/{id}")
    @ModelAttribute("myAccount")
    public Account handle() {
      // ...
      return account;
    }
    

    Exceptions

    Controller和@ControllerAdvice类可以有@ExceptionHandler方法来处理控制器方法的异常(异常为方法参数)

    @Controller
    public class SimpleController {
    
      @ExceptionHandler
      public ResponseEntity<String> handle(IOException ex) {
      }
    }
    
    @ExceptionHandler({FileSystemException.class, RemoteException.class})
    public ResponseEntity<String> handle(IOException ex) {
      // ...
    }
    

    跨域CORS

    @CrossOrigin

    @CrossOrigin注解允许在带注释的控制器方法上进行跨源请求。@CrossOrigin注解默认设置:

  • 所有的请求域

  • 所有的请求头
  • 所有的请求方法

    @RestController
    @RequestMapping("/account")
    public class AccountController {
    
      @CrossOrigin
      @GetMapping("/{id}")
      public Account retrieve(@PathVariable Long id) {
          // ...
      }
      @DeleteMapping("/{id}")
      public void remove(@PathVariable Long id) {
          // ...
      }
    }
    

    Global Configuration

    @Configuration
    public class CorsConfig {
    
      private CorsConfiguration buildConfig() {
          CorsConfiguration configuration = new CorsConfiguration();
          //  你需要跨域的地址  注意这里的 127.0.0.1 != localhost
          // * 表示对所有的地址都可以访问
          configuration.addAllowedOrigin("http://localhost:8000");
          //  跨域的请求头
          configuration.addAllowedHeader("*");
          //  跨域的请求方法
          configuration.addAllowedMethod("*");
          //加上了这一句,大致意思是可以携带 cookie
          //最终的结果是可以 在跨域请求的时候获取同一个 session
          configuration.setAllowCredentials(true);
          return configuration;
      }
    
      @Bean
      public CorsFilter corsFilter() {
          UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
          source.registerCorsConfiguration("/**", buildConfig());
          return new CorsFilter(source);
    
      }
    }
    
    @Configuration
    @EnableWebMvc
    public class WebConfig implements WebMvcConfigurer {
      @Override
      public void addCorsMappings(CorsRegistry registry) {
          registry.addMapping("/api/**")
              .allowedOrigins("https://domain2.com")
              .allowedMethods("PUT", "DELETE")
              .allowedHeaders("header1", "header2", "header3")
              .exposedHeaders("header1", "header2")
              .allowCredentials(true).maxAge(3600);
    
          // Add more mappings...
      }
    }
    

    ```xml

<a name="jgZjJ"></a>
### CORS Filter
```java
CorsConfiguration config = new CorsConfiguration();

// Possibly...
// config.applyPermitDefaultValues()

config.setAllowCredentials(true);
config.addAllowedOrigin("https://domain1.com");
config.addAllowedHeader("*");
config.addAllowedMethod("*");

UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", config);

CorsFilter filter = new CorsFilter(source);

PDF AND Excel

Spring提供了一些方法来返回HTML之外的输出,包括PDF和Excel电子表格。
为了使用Excel视图,需要添加 Apache POI依赖;使用PDF库,需添加OpenPDF依赖。

PDF视图

简单的word列表PDF视图可以扩展org.springframework.web.servlet.view.document.AbstractPdfView并实现buildPdfDocument()方法,如下所示:

public class PdfWordList extends AbstractPdfView {
    protected void buildPdfDocument(Map<String, Object> model, Document doc, PdfWriter writer,
            HttpServletRequest request, HttpServletResponse response) throws Exception {
        List<String> words = (List<String>) model.get("wordList");
        for (String word : words) {
            doc.add(new Paragraph(word));
        }
    }
}

Excel视图

从Spring Framework 4.2开始,org.springframework.web.servlet.view.document.AbstractXlsView被提供为Excel视图的基类。它基于Apache POI,用专用的子类(AbstractXlsxView和AbstractXlsxStreamingView)取代了过时的AbstractExcelView类。
编程模型类似于AbstractPdfView,使用buildExcelDocument()作为中心模板方法,控制器能够从外部定义(按名称)返回这样的视图,或者从处理程序方法返回视图实例。

MVC java 配置示例

在Java配置中,可以使用@EnableWebMvc注释来启用MVC配置;XML配置,你可以使用元素启用mvc配置

@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {


    // 配置类型转换器
     @Override
    public void addFormatters(FormatterRegistry registry) {
        DateTimeFormatterRegistrar registrar = new DateTimeFormatterRegistrar();
        registrar.setUseIsoFormat(true);
        registrar.registerFormatters(registry);
    }
   // 配置Validation
     @InitBinder
    protected void initBinder(WebDataBinder binder) {
        binder.addValidators(new FooValidator());
    }
   // 配置拦截器
     @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new LocaleChangeInterceptor());
        registry.addInterceptor(new ThemeChangeInterceptor()).addPathPatterns("/**").excludePathPatterns("/admin/**");
        registry.addInterceptor(new SecurityInterceptor()).addPathPatterns("/secure/*");
    }

    // 配置内容类型 Content Types
     @Override
    public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
        configurer.mediaType("json", MediaType.APPLICATION_JSON);
        configurer.mediaType("xml", MediaType.APPLICATION_XML);
    }

    // 配置消息转换器 Message Converters
    @Override
    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
        Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder()
                .indentOutput(true)
                .dateFormat(new SimpleDateFormat("yyyy-MM-dd"))
                .modulesToInstall(new ParameterNamesModule());
        converters.add(new MappingJackson2HttpMessageConverter(builder.build()));
        converters.add(new MappingJackson2XmlHttpMessageConverter(builder.createXmlMapper(true).build()));
    }

    // 配置视图控制器 View Controllers
    Override
    public void addViewControllers(ViewControllerRegistry registry) {
        registry.addViewController("/").setViewName("home");
    }

    // 配置视图解析器
    @Override
    public void configureViewResolvers(ViewResolverRegistry registry) {
        registry.enableContentNegotiation(new MappingJackson2JsonView());
        registry.jsp();
    }

    // 配置静态资源
    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/resources/**")
                .addResourceLocations("/public", "classpath:/static/")
                .setCacheControl(CacheControl.maxAge(Duration.ofDays(365)));
    }

    // 配置默认servlet
     @Override
    public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
        configurer.enable();
    }

    // 配置请求路径匹配
     @Override
    public void configurePathMatch(PathMatchConfigurer configurer) {
        configurer
            .setPatternParser(new PathPatternParser())
            .addPathPrefix("/api", HandlerTypePredicate.forAnnotation(RestController.class));
    }
    private PathPatternParser patternParser() {
        // ...
    }


}

SSM整合

<?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:mvc="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation="
                           http://www.springframework.org/schema/beans
                           http://www.springframework.org/schema/beans/spring-beans.xsd
                           http://www.springframework.org/schema/mvc
                           http://www.springframework.org/schema/mvc/spring-mvc.xsd
                           ">
  <!--扫描controller-->
  <context:component-scan base-package="com.msb.controller"></context:component-scan>
  <!--这里配置三大组件-->
  <mvc:annotation-driven />
  <!--视图解析器-->
  <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
  </bean>
  <!--配置静态资源放行-->
  <!--<mvc:resources mapping="/js/**" location="/js/"></mvc:resources>-->
  <!--<mvc:resources mapping="/css/**" location="/css/"></mvc:resources>-->
  <!--<mvc:resources mapping="/img/**" location="/img/"></mvc:resources>-->
  <!--<mvc:resources mapping="/static/**" location="/static/"></mvc:resources>-->
</beans>
<?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:p="http://www.springframework.org/schema/p"
       xmlns:c="http://www.springframework.org/schema/c"
       xmlns:util="http://www.springframework.org/schema/util"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation="
                           http://www.springframework.org/schema/beans
                           http://www.springframework.org/schema/beans/spring-beans.xsd
                           http://www.springframework.org/schema/util
                           http://www.springframework.org/schema/util/spring-util.xsd
                           http://www.springframework.org/schema/context
                           http://www.springframework.org/schema/context/spring-context.xsd
                           http://www.springframework.org/schema/aop
                           http://www.springframework.org/schema/aop/spring-aop.xsd
                           http://www.springframework.org/schema/tx
                           http://www.springframework.org/schema/tx/spring-tx.xsd
                           http://www.springframework.org/schema/mvc
                           http://www.springframework.org/schema/mvc/spring-mvc.xsd
                           ">
  <!--加载外部属性文件-->
  <context:property-placeholder location="classpath:jdbc.properties"/>
  <!--扫描service层-->
  <context:component-scan base-package="com.msb.service"/>

  <!--配置德鲁伊数据源-->
  <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
    <property name="username" value="${jdbc_username}"></property>
    <property name="password" value="${jdbc_password}"></property>
    <property name="url" value="${jdbc_url}"></property>
    <property name="driverClassName" value="${jdbc_driver}"></property>
  </bean>

  <!--配置sqlSessionFactory-->
  <bean id="factory" class="org.mybatis.spring.SqlSessionFactoryBean">
    <property name="dataSource"  ref="dataSource"></property>
    <property name="typeAliasesPackage" value="com.msb.pojo"></property>
  </bean>

  <!--配置MapperScanner 扫描mapper.xml 和接口-->
  <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
    <!--配置SQLSessionFactory-->
    <property name="sqlSessionFactoryBeanName" value="factory"></property>
    <!--扫描mapper-->
    <property name="basePackage" value="com.msb.mapper"></property>
  </bean>

  <!--配置事务管理器-->
  <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource"></property>
  </bean>

  <!--开启事务注解-->
  <tx:annotation-driven  transaction-manager="transactionManager"/>

</beans>
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">

    <!--spring核心配置文件位置-->
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:applictionContext.xml</param-value>
    </context-param>

    <!--spring Listener-->
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>

    <!--编码过滤器-->
    <filter>
        <filter-name>encFilter</filter-name>
        <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
        <init-param>
            <param-name>encoding</param-name>
            <param-value>utf-8</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>encFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

    <!--DispatcherServlet-->
    <servlet>
        <servlet-name>dispatcherServlet</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:springmvc.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>dispatcherServlet</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>
</web-app>