spring-mvc

回顾原生的Web应用

  1. 全局参数

  2. listener

  3. filter

  4. servlet

DispatcherServlet

  1. ContextLoaderListener做了什么

  2. DispatcherServlet创建一个子容器

  3. HandlerMapping(处理器映射器)

    1. BeanNameUrlHandlerMapping

    2. RequestMappingHandlerMapping

  4. HandlerAdapter(处理器适配器)

    1. SimpleControllerHandlerAdapter

    2. RequestMappingHandlerAdapter

  5. ViewResolver(视图解析器)

    1. InternalResourceViewResolver

    2. 义多个视图解析器(源码解析)

    3. 转发重定向

    4. ResourceBundleViewResolver

  6. HandlerExceptionResolver(异常拦截器)

    1. SimpleMappingExceptionResolver

    2. ResponseStatusExceptionResolver

    3. ExceptionHandlerExceptionResolver
  7. LocaleResolver(客户端地区解析)

    1. AcceptHeaderLocaleResolver

    2. CookieLocaleResolver

    3. SessionLocaleResolver

    4. ResourceBundleViewResolver和LocaleResolver配合做国际化

  8. ThemeResolver(切换主题)

    1. CookieThemeResolver

    2. 使用CookieThemeResolver做国际化主题

  9. MultipartResolver(多部件上传)

    1. CommonsMultipartResolver

  10. 本章节总结

SpringMvc中的注解

控制器相关

  1. @Controller

  2. @RequestMapping

  3. @ResponseBody

  4. @RestController

  5. @RequestMapping的变体

参数映射

  1. @PathVariable将url绑定到参数

  2. 参数映射的限制

    1. consumes

    2. produces

    3. params

    4. header

  3. @MatrixVariable矩阵变量

  4. @RequestParam参数别名

  5. @RequestHeader绑定请求头到参

  6. @CookieValue绑定cookie到参数

  7. 可自动加入ModelAndView的参数

  8. @ModelAttribute(坑深尽量不要用)

    1. @ModelAttribute添加到方法上,充当前置Controller

    2. @ModelAttribute添加到方法上,返回对象

    3. @ModelAttribute添加到方法上,返回对象,设置value属性

    4. @ModelAttribute和@GetMapping(包括其他Method的Mapping)一块使用

    5. @ModelAttribute注释参数,获取其他ModelAttribute的值

    6. @ModelAttribute注释返回值

回顾原生的Servlet

<!DOCTYPE web-app PUBLIC
        "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
        "http://java.sun.com/dtd/web-app_2_3.dtd" >

<web-app>


    <!-- 全局的配置参数,listener filter servlet 都可以获取参数 可以定义多个 -->
    <context-param>
        <param-name>Encoding</param-name>
        <param-value>UTF-8</param-value>
    </context-param>


    <!-- 过滤器 可以定义多个-->
    <filter>
        <filter-name>filter1</filter-name>
        <filter-class>filter.MyFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>filter1</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

    <!-- 监听器可以定义多个 -->
    <listener>
        <listener-class>listener.MyListener</listener-class>
    </listener>

    <!-- servlet可以定义多个 -->
    <servlet>
        <servlet-name>success</servlet-name>
        <servlet-class>servlet.UserServlet</servlet-class>
        <init-param>
            <param-name>key1</param-name>
            <param-value>value1</param-value>
        </init-param>
        <!-- 初始化加载顺序,会调用servlet的init方法 -->
        <load-on-startup>2</load-on-startup>
    </servlet>

    <servlet>
        <servlet-name>success2</servlet-name>
        <servlet-class>servlet.UserServlet2</servlet-class>
        <init-param>
            <param-name>key1</param-name>
            <param-value>value1</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>


    <!-- servlet映射,跟每一个Servlet对应 -->
    <servlet-mapping>
        <servlet-name>success</servlet-name>
        <url-pattern>/success</url-pattern>
    </servlet-mapping>

    <servlet-mapping>
        <servlet-name>success2</servlet-name>
        <url-pattern>/success2</url-pattern>
    </servlet-mapping>


</web-app>
View Code

全局参数

web容器启动后,会创建一个ServletContext上下文对象,在web.xml中配置<context-param>会被上下文对象存储,任何可以获取ServletContext上下文的地方都可以调用getInitParameter("key");获取初始化参数,属于全局范围。

listener

package listener;

import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;

public class MyListener implements ServletContextListener {
    @Override
    public void contextInitialized(ServletContextEvent servletContextEvent) {
        System.out.println("glistener1 init");
        ServletContext servletContext = servletContextEvent.getServletContext();
        String value = servletContext.getInitParameter("Encoding");
        System.out.println("当前设置编码" + value);
    }

    @Override
    public void contextDestroyed(ServletContextEvent servletContextEvent) {
        System.out.println("listener1 dest");
    }
}
View Code

listener监听器分为三类,分别监听ServletContext域,HttpSession域,ServletRequest域。每一类又分成监听域对象的创建、销毁和域对象内的属性的变化。在web.xml中可以定义多个监听器。同类型的监听器按照加载顺序执行。

以上代码片段创建了一个servletContextListener用来监听Servlet上下文的创建和销毁。

servletContextListener、 HttpSessionListener、 ServletRequestListener、 ServletContextAttributeListener、 HttpSessionAttributeListener、 ServletRequestAttributeListener

filter

package filter;/*
* @auther 顶风少年 
* @mail dfsn19970313@foxmail.com
* @date 2020-02-06 17:35
* @notify 
* @version 1.0
*/

import javax.servlet.*;
import java.io.IOException;

public class MyFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        System.out.println("filter1 init");
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {

        ServletContext servletContext = servletRequest.getServletContext();
        String encoding = servletContext.getInitParameter("Encoding");
        servletRequest.setCharacterEncoding(encoding);

        filterChain.doFilter(servletRequest,servletResponse);
    }

    @Override
    public void destroy() {
        System.out.println("filter1 destory");
    }
}
View Code

filter过滤器,在xml中通过<filter>和<filter-mapping>定义,两个为一组在mapping中定义要过滤的路径,匹配路径的都会进入filter。在filter类中有三个方法。分别是初始化(init),销毁(destroy)。doFilter,每一个匹配路径符合的都会进入doFilter(),如果方法内部业务逻辑正确,则可以使用FilterChain.doFilter(req,resp);表示放行。多个filter会组成过滤器链,当一个第一个放行后,则进入第二个。全部通过则会到达servlet,一个不通过,则不通过。

servlet

package servlet;/*
 * @auther 顶风少年
 * @mail dfsn19970313@foxmail.com
 * @date 2020-02-06 12:33
 * @notify
 * @version 1.0
 */

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Enumeration;

public class UserServlet extends HttpServlet {

    @Override
    public void init() throws ServletException {
        System.out.println("servlet1");
        super.init();
    }

    @Override
    public void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        resp.setContentType("text/html;charset=UTF-8");
        PrintWriter out = resp.getWriter();
        out.println("success");
        out.close();
    }
}
View Code

servlet服务,同样的在web.xml中通过<servlet>和<servlet-mapping>,每一个servletClass需要实现HttpServlet,类HttpServlet默认实现了各种请求方式的实现。我们需要根据自身需要覆盖对应的请求方式。配置<load-on-startup>1</load-on-startup>代表该Servlet的加载顺序,ServletContext创建后,会对设置了该标签的Servlet进行初始化,负值不初始化,值越小越先被初始化。所谓初始化就是调用Servlet的init()

DispatcherServlet

ContextLoaderListener做了什么

在这里我们跟踪源码,主要找到listener创建ApplicationContext和创建过程中在哪里加载的配置文件。

 <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>/WEB-INF/applicationconfig.xml</param-value>
    </context-param>

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

首先我们在web.xml配置了一个监听器ContextLoaderListener,另外配置了全局参数contextConfigLocation

public class ContextLoaderListener extends ContextLoader implements ServletContextListener {
    public ContextLoaderListener() {
    }

    public ContextLoaderListener(WebApplicationContext context) {
        super(context);
    }

    public void contextInitialized(ServletContextEvent event) {
        this.initWebApplicationContext(event.getServletContext());
    }

    public void contextDestroyed(ServletContextEvent event) {
        this.closeWebApplicationContext(event.getServletContext());
        ContextCleanupListener.cleanupAttributes(event.getServletContext());
    }
}
View Code

在全局配置参数的value中我指向了一个Spring配置文件,并且添加了一个bean。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/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.xsd">

    <bean class="pojo.Person" id="person"></bean>
</beans>
View Code

该ContextLoaderListener实现了ServletContextListener,那他的功能就是监听ServletContext创建和销毁。其次,它继承了ContextLoader,该类是Spring的类。我们先来看看contextInitialized()方法在ServletContext创建时都做了什么。

DispatcherServlet创建一个子容器

 <servlet>
        <servlet-name>app</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>/WEB-INF/springmvc.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>


    <servlet-mapping>
        <servlet-name>app</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>
View Code
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xmlns:p="http://www.springframework.org/schema/p"
       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
                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">



    <context:component-scan base-package="com.datang"/>


</beans>
View Code

在这里我们跟踪源码,查看DispatcherServlet创建子容器的,和加载配置文件的源码。前边我们说过,Servlet初始化会调用init()方法。而DispatcherServlet也继承自HttpServlet。

HandlerMapping 

HandlerMapping的作用就是解析请求链接,然后根据请求链接找到执行这个请求的类(HandlerMapping所说的handler,也就是我们写的Controller或是Action)。

BeanNameUrlHandlerMapping

BeanNameUrlHandlerMapping处理器映射器,处理器必须实现Controller接口,并且将该类配置成bean,bean的id需要是/开头,标识它是一个处理器。

package com.datang.controller;/*
 * @auther 顶风少年
 * @mail dfsn19970313@foxmail.com
 * @date 2020-02-07 17:44
 * @notify
 * @version 1.0
 */


import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class BeanNameUrlHandlerMappingController implements Controller {
    @Override
    public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {
        System.out.println("我是根据BeanNameUrlHandlerMapping找到的controller,但是显然的,我是错误的,因为我返回的是null");
        return null;
    }
}
View Code
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xmlns:p="http://www.springframework.org/schema/p"
       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
                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">


    <bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/>

    <bean id="/beannameurlhandlermappingcontroller"
          class="com.datang.controller.BeanNameUrlHandlerMappingController"></bean>

    <context:component-scan base-package="com.datang"/>


</beans>
View Code

RequestMappingHandlerMapping

RequestMappingHandlerMapping处理器映射器,需要使用@Controller注册bean然后对方法上带有@RequestMapping注解的方法,注册为处理器映射器。

package com.datang.controller;/*
 * @auther 顶风少年
 * @mail dfsn19970313@foxmail.com
 * @date 2020-02-07 18:04
 * @notify
 * @version 1.0
 */

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.ModelAndView;

@Controller
public class RequestMappingHandlerMappingController {

    @RequestMapping(value = "requestmappinghandlermappingcontroller",method = RequestMethod.GET)
    public ModelAndView handler() {
        System.out.println("我是根据RequestMappingHandlerMapping找到的controller,但是显然的,我是错误的,因为我返回的是null");
        return null;
    }
}
View Code
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xmlns:p="http://www.springframework.org/schema/p"
       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
                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">

    <!-- 根据beanName找到处理器 -->
    <bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/>
    <bean id="/beannameurlhandlermappingcontroller"
          class="com.datang.controller.BeanNameUrlHandlerMappingController"></bean>

    <!-- 根据@RequestMapping 找到处理器-->
    <bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping"/>

    <context:component-scan base-package="com.datang"/>


</beans>
View Code

HandlerAdapter

HandlerAdapter的作用是,执行handlerMapping找到的处理器。处理器有多种,显然的每一种的处理器所执行的方法都不一样。比如上边的两种,继承自Controller的处理器方法固定的是handleRequest()而使用@RequestMapping注解的则方法名不同。所以处理器适配器的存在是有必要的。

SimpleControllerHandlerAdapter

SimpleControllerHandlerAdapter这个处理器适配器是配合BeanNameUrlHandlerMapping处理器映射器使用的。定义了SimpleControllerHandlerAdapter便不会有其他的适配器。这里请求beannameurlhandlermappingcontroller会成功,但是请求requestmappinghandlermappingcontroller是不成功的。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xmlns:p="http://www.springframework.org/schema/p"
       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
                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">

    <!-- 根据beanName找到处理器 -->
    <bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/>
    <bean id="/beannameurlhandlermappingcontroller"
          class="com.datang.controller.BeanNameUrlHandlerMappingController"></bean>

    <!-- 根据@RequestMapping 找到处理器-->
    <bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping"/>

    <!-- 配合BeanNameUrlHandlerMapping使用 -->
    <bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"/>


    <context:component-scan base-package="com.datang"/>


</beans>
View Code

RequestMappingHandlerAdapter

RequestMappingHandlerAdapter这个处理器适配器是配合RequestMappingHandlerMapping处理器映射器使用的。这次就可以正确访问requestmappinghandlermappingcontroller。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xmlns:p="http://www.springframework.org/schema/p"
       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
                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">

    <!-- 根据beanName找到处理器 -->
    <bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/>
    <bean id="/beannameurlhandlermappingcontroller"
          class="com.datang.controller.BeanNameUrlHandlerMappingController"></bean>

    <!-- 根据@RequestMapping 找到处理器-->
    <bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping"/>

    <!-- 配合BeanNameUrlHandlerMapping使用 -->
    <bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"/>

    <!-- 配合RequestMappingHandlerMapping使用 -->
    <bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter"/>

    <context:component-scan base-package="com.datang"/>


</beans>
View Code

ViewResolver

InternalResourceViewResolver

根据Controller返回的逻辑视图名,寻找物理视图。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xmlns:p="http://www.springframework.org/schema/p"
       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
                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">

    <!-- 根据beanName找到处理器 -->
    <bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/>
    <bean id="/beannameurlhandlermappingcontroller"
          class="com.datang.controller.BeanNameUrlHandlerMappingController"></bean>

    <!-- 根据@RequestMapping 找到处理器-->
    <bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping"/>

    <!-- 配合BeanNameUrlHandlerMapping使用 -->
    <bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"/>

    <!-- 配合RequestMappingHandlerMapping使用 -->
    <bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter"/>

    
    <!-- 视图解析器,配置视图的前缀和后缀 -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/jsps/"/>
        <property name="suffix" value=".jsp"/>
    </bean>

    <context:component-scan base-package="com.datang"/>


</beans>
View Code
package com.datang.controller;/*
 * @auther 顶风少年
 * @mail dfsn19970313@foxmail.com
 * @date 2020-02-07 21:09
 * @notify
 * @version 1.0
 */

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.ModelAndView;


@Controller
public class ReturnJsp {

    @RequestMapping(value = "returnjsp", method = RequestMethod.GET)
    public String handler() {
        return "page1";
    }
}
View Code
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
<h1>我是page1</h1>
</body>
</html>
View Code

定义多个视图解析器(源码解析)

我们可以定义多个视图解析器,属性order规定视图解析器的优先级。DispatcherServlet试图从所有的视图解析器中匹配视图,如果匹配成功则不继续匹配。那么就让我们来碰碰壁吧。可以看出第二个路径出错了,问题就在于,它好像根本没有走我们的第二个视图解析器呀!看源码吧。(不想看源码,直接看解决办法。)

package com.datang.controller;/*
 * @auther 顶风少年
 * @mail dfsn19970313@foxmail.com
 * @date 2020-02-07 21:09
 * @notify
 * @version 1.0
 */

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.view.InternalResourceView;


@Controller
public class ReturnJsp {

    @RequestMapping(value = "returnjsp", method = RequestMethod.GET)
    public String handler() {
        return "page1";
    }


    @RequestMapping(value = "returnjsp2", method = RequestMethod.GET)
    public String handler2() {
        return "page2";
    }
}
View Code
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xmlns:p="http://www.springframework.org/schema/p"
       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
                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">

    <!-- 根据beanName找到处理器 -->
    <bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/>
    <bean id="/beannameurlhandlermappingcontroller"
          class="com.datang.controller.BeanNameUrlHandlerMappingController"></bean>

    <!-- 根据@RequestMapping 找到处理器-->
    <bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping"/>

    <!-- 配合BeanNameUrlHandlerMapping使用 -->
    <bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"/>

    <!-- 配合RequestMappingHandlerMapping使用 -->
    <bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter"/>


    <!-- 视图解析器,配置视图的前缀和后缀 -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/jsps/"/>
        <property name="suffix" value=".jsp"/>
        <property name="order" value="1"/>
    </bean>

    <!-- 第二个视图解析器 -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/jsps2/"/>
        <property name="suffix" value=".jsp"/>
        <property name="order" value="2"/>
    </bean>

    <context:component-scan base-package="com.datang"/>


</beans>
View Code

入口类,就从DispatcherServlet开始(因为我已经debug了好久才找到从这么看最直观!!!)

解决办法

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xmlns:p="http://www.springframework.org/schema/p"
       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
                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">

    <!-- 根据beanName找到处理器 -->
    <bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/>
    <bean id="/beannameurlhandlermappingcontroller"
          class="com.datang.controller.BeanNameUrlHandlerMappingController"></bean>

    <!-- 根据@RequestMapping 找到处理器-->
    <bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping"/>

    <!-- 配合BeanNameUrlHandlerMapping使用 -->
    <bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"/>

    <!-- 配合RequestMappingHandlerMapping使用 -->
    <bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter"/>


    <!-- 视图解析器,配置视图的前缀和后缀 -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <!-- 使用自己的viewClass -->
        <property name="viewClass" value="com.datang.view.MyInternalResourceView"/>
        <property name="prefix" value="/WEB-INF/jsps/"/>
        <property name="suffix" value=".jsp"/>
        <property name="order" value="1"/>
    </bean>

    <!-- 第二个视图解析器 -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <!-- 使用自己的viewClass -->
        <property name="viewClass" value="com.datang.view.MyInternalResourceView"/>
        <property name="prefix" value="/WEB-INF/jsps2/"/>
        <property name="suffix" value=".jsp"/>
        <property name="order" value="2"/>
    </bean>

    <context:component-scan base-package="com.datang"/>


</beans>
View Code
package com.datang.view;/*
 * @auther 顶风少年
 * @mail dfsn19970313@foxmail.com
 * @date 2020-02-08 14:49
 * @notify
 * @version 1.0
 */

import org.springframework.web.servlet.view.InternalResourceView;

import java.io.File;
import java.util.Locale;

public class MyInternalResourceView extends InternalResourceView {

    @Override
    public boolean checkResource(Locale locale) throws Exception {
        //这里,super.getUrl就是prefix+viewName+suffix
        File file = new File(this.getServletContext().getRealPath("/") + super.getUrl());
        // 判断该页面是否存在
        return file.exists();
    }
}
View Code

转发重定向

 //转发到returnjsp
    @RequestMapping(value = "returnjsp3", method = RequestMethod.GET)
    public String handler3() {
        return "forward:returnjsp";
    }

    //重定向到returnjsp
    @RequestMapping(value = "returnjsp4", method = RequestMethod.GET)
    public String handler4() {
        return "redirect:returnjsp";
    }
View Code

ResourceBundleViewResolver

Controller中返回ModelAndView,设置view的名称。ResourceBundleViewResolver根据view名称查询views下的路径配置。

 @RequestMapping(value = "returnjsp9", method = RequestMethod.GET)
    public ModelAndView handler9() {
        return new ModelAndView("user1");
    }
View Code
user1.(class)=org.springframework.web.servlet.view.InternalResourceView
user1.url=/WEB-INF/jsps/page3.jsp
View Code
  <!-- 根据view名称匹配资源路径 -->
    <bean class="org.springframework.web.servlet.view.ResourceBundleViewResolver">
        <property name="order" value="3"/>
        <property name="basename" value="views"/>
    </bean>
View Code

HandlerExceptionResolver

handlerExcetpionResolver用于处理异常,可以同时注入多个异常处理方案,指定order属性,值越小,优先级越高。

SimpleMappingExceptionResolver

SimpleMappingExceptionResolver会拦截住controller中抛出的异常,分发到对应的异常页面,并且向异常页面传递异常信息。注意了!如果是页面找不到的异常不会被拦截。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xmlns:p="http://www.springframework.org/schema/p"
       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
                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">

    <!-- 根据beanName找到处理器 -->
    <bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/>
    <bean id="/beannameurlhandlermappingcontroller"
          class="com.datang.controller.BeanNameUrlHandlerMappingController"></bean>

    <!-- 根据@RequestMapping 找到处理器-->
    <bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping"/>

    <!-- 配合BeanNameUrlHandlerMapping使用 -->
    <bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"/>

    <!-- 配合RequestMappingHandlerMapping使用 -->
    <bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter"/>


    <!-- 视图解析器,配置视图的前缀和后缀 -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <!-- 使用自己的viewClass -->
        <property name="viewClass" value="com.datang.view.MyInternalResourceView"/>
        <property name="prefix" value="/WEB-INF/jsps/"/>
        <property name="suffix" value=".jsp"/>
        <property name="order" value="1"/>
    </bean>

    <!-- 第二个视图解析器 -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <!-- 使用自己的viewClass -->
        <property name="viewClass" value="com.datang.view.MyInternalResourceView"/>
        <property name="prefix" value="/WEB-INF/jsps2/"/>
        <property name="suffix" value=".jsp"/>
        <property name="order" value="2"/>
    </bean>


    <!-- SimpleMappingExceptionResolver -->
    <bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
        <!-- 为所有的异常定义默认的异常处理页面,exceptionMappings未定义的异常使用本默认配置 -->
        <property name="defaultErrorView" value="error"></property>
        <!--         定义异常处理页面用来获取异常信息的变量名,默认名为exception -->
        <property name="exceptionAttribute" value="ex"></property>
        <!--
        定义需要特殊处理的异常,用类名或完全路径名作为key,异常页文件名作为值,
        将不同的异常映射到不同的页面上。
       -->
        <property name="exceptionMappings">
            <props>
                <prop key="java.io.IOException">ioerror</prop>
            </props>
        </property>
        <!-- 返回状态码 -->
        <property name="defaultStatusCode" value="250"/>
    </bean>

    <context:component-scan base-package="com.datang"/>


</beans>
View Code
  //数学异常
    @RequestMapping(value = "returnjsp5", method = RequestMethod.GET)
    public String handler5() {
        int i = 1 / 0;
        return "xixi";
    }

    //IO异常
    @RequestMapping(value = "returnjsp6", method = RequestMethod.GET)
    public String handler6() throws IOException {
        FileInputStream fileInputStream = new FileInputStream("");
        return "xixi";
    }

    //找不到页面
    @RequestMapping(value = "returnjsp7", method = RequestMethod.GET)
    public String handler7() {
        return "xixi";
    }
View Code
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
<h1>SimpleMappingExceptionResolver的异常页面${requestScope.ex}</h1>
</body>
</html>
View Code
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
<h1>SimpleMappingExceptionResolver的IO异常页面${requestScope.ex}</h1>
</body>
</html>
View Code

ResponseStatusExceptionResolver

ResponseStatusExceptionResolver拦截使用了@ResponseStatus的自定义异常。在自定义异常中,可以规定返回状态,错误原因。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xmlns:p="http://www.springframework.org/schema/p"
       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
                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">

    <!-- 根据beanName找到处理器 -->
    <bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/>
    <bean id="/beannameurlhandlermappingcontroller"
          class="com.datang.controller.BeanNameUrlHandlerMappingController"></bean>

    <!-- 根据@RequestMapping 找到处理器-->
    <bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping"/>

    <!-- 配合BeanNameUrlHandlerMapping使用 -->
    <bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"/>

    <!-- 配合RequestMappingHandlerMapping使用 -->
    <bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter"/>


    <!-- 视图解析器,配置视图的前缀和后缀 -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <!-- 使用自己的viewClass -->
        <property name="viewClass" value="com.datang.view.MyInternalResourceView"/>
        <property name="prefix" value="/WEB-INF/jsps/"/>
        <property name="suffix" value=".jsp"/>
        <property name="order" value="1"/>
    </bean>

    <!-- 第二个视图解析器 -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <!-- 使用自己的viewClass -->
        <property name="viewClass" value="com.datang.view.MyInternalResourceView"/>
        <property name="prefix" value="/WEB-INF/jsps2/"/>
        <property name="suffix" value=".jsp"/>
        <property name="order" value="2"/>
    </bean>

    <!-- ResponseStatusExceptionResolver -->
    <bean class="org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver">
        <!-- 优先级-->
        <property name="order" value="1"/>
    </bean>

    <!-- SimpleMappingExceptionResolver -->
    <bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
        <!-- 优先级-->
        <property name="order" value="2"/>
        <!-- 为所有的异常定义默认的异常处理页面,exceptionMappings未定义的异常使用本默认配置 -->
        <property name="defaultErrorView" value="error"></property>
        <!--         定义异常处理页面用来获取异常信息的变量名,默认名为exception -->
        <property name="exceptionAttribute" value="ex"></property>
        <!--
        定义需要特殊处理的异常,用类名或完全路径名作为key,异常页文件名作为值,
        将不同的异常映射到不同的页面上。
       -->
        <property name="exceptionMappings">
            <props>
                <prop key="java.io.IOException">ioerror</prop>
            </props>
        </property>
        <!-- 返回状态码 -->
        <property name="defaultStatusCode" value="250"/>
    </bean>


    <context:component-scan base-package="com.datang"/>


</beans>
View Code
//ResponseStatusExceptionResolver
    @RequestMapping(value = "returnjsp8", method = RequestMethod.GET)
    public String handler8() {
        throw new MyException1();
    }
View Code
package com.datang.exception;/*
* @auther 顶风少年 
* @mail dfsn19970313@foxmail.com
* @date 2020-02-09 13:31
* @notify 
* @version 1.0
*/

import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;

@ResponseStatus(value = HttpStatus.FORBIDDEN,reason = "我就是测试下异常,没别的意思")
public class MyException1 extends RuntimeException {

}
View Code

ExceptionHandlerExceptionResolver

ExceptionHandlerExceptionResolver用于全局处理异常,将异常放到一个被@ControllerAdvice注释的类上,该类的每个带有@ExceptionHandler的方法都关联一个异常类,其他Controller中抛出的异常如果匹配则走该异常处理。别标注@ExceptionHandler的方法如果在@Controller中,表示该异常处只在本类中有效。

package com.datang.exception;/*
 * @auther 顶风少年
 * @mail dfsn19970313@foxmail.com
 * @date 2020-02-09 13:31
 * @notify
 * @version 1.0
 */

import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;

import java.io.IOException;

@ControllerAdvice
public class MyException2 {

    @ExceptionHandler({NullPointerException.class})
    public String exception(NullPointerException e) {
        System.out.println(e.getMessage());
        e.printStackTrace();
        return "空指针异常了";
    }

}
View Code
 //ResponseStatusExceptionResolver
    @RequestMapping(value = "returnjsp8", method = RequestMethod.GET)
    public String handler8() {
        throw new NullPointerException();
    }
View Code
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xmlns:p="http://www.springframework.org/schema/p"
       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
                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">

    <!-- 根据beanName找到处理器 -->
    <bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/>
    <bean id="/beannameurlhandlermappingcontroller"
          class="com.datang.controller.BeanNameUrlHandlerMappingController"></bean>

    <!-- 根据@RequestMapping 找到处理器-->
    <bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping"/>

    <!-- 配合BeanNameUrlHandlerMapping使用 -->
    <bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"/>

    <!-- 配合RequestMappingHandlerMapping使用 -->
    <bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter"/>


    <!-- 视图解析器,配置视图的前缀和后缀 -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <!-- 使用自己的viewClass -->
        <property name="viewClass" value="com.datang.view.MyInternalResourceView"/>
        <property name="prefix" value="/WEB-INF/jsps/"/>
        <property name="suffix" value=".jsp"/>
        <property name="order" value="1"/>
    </bean>

    <!-- 第二个视图解析器 -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <!-- 使用自己的viewClass -->
        <property name="viewClass" value="com.datang.view.MyInternalResourceView"/>
        <property name="prefix" value="/WEB-INF/jsps2/"/>
        <property name="suffix" value=".jsp"/>
        <property name="order" value="2"/>
    </bean>



    <!-- ExceptionHandlerExceptionResolver -->
    <bean class="org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver">
        <!-- 优先级-->
        <property name="order" value="0"/>
    </bean>

    <!-- ResponseStatusExceptionResolver -->
    <bean class="org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver">
        <!-- 优先级-->
        <property name="order" value="1"/>
    </bean>

    <!-- SimpleMappingExceptionResolver -->
    <bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
        <!-- 优先级-->
        <property name="order" value="2"/>
        <!-- 为所有的异常定义默认的异常处理页面,exceptionMappings未定义的异常使用本默认配置 -->
        <property name="defaultErrorView" value="error"></property>
        <!--         定义异常处理页面用来获取异常信息的变量名,默认名为exception -->
        <property name="exceptionAttribute" value="ex"></property>
        <!--
        定义需要特殊处理的异常,用类名或完全路径名作为key,异常页文件名作为值,
        将不同的异常映射到不同的页面上。
       -->
        <property name="exceptionMappings">
            <props>
                <prop key="java.io.IOException">ioerror</prop>
            </props>
        </property>
        <!-- 返回状态码 -->
        <property name="defaultStatusCode" value="250"/>
    </bean>


    <context:component-scan base-package="com.datang"/>


</beans>
View Code

LocaleResolver

LocaleResolver,获取客户端时区和地区。

AcceptHeaderLocaleResolver

通过客户端头部信息Accept-Language获取客户端时区,AcceptHeaderLocaleResolver不能获取时区。
    @RequestMapping(value = "returnjsp10", method = RequestMethod.GET)
    public String handler10(Locale locale) {
        return "page1";
    }
View Code
<bean class="org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver"></bean>
View Code

CookieLocaleResolver

先访问returnjsp11设置cookie,注意cookie的key需要和xml中设置一样,值的格式也是固定的,前半部分是地区,这个地区不能瞎搞,必须是Locale枚举类定义好的值,而后半部分也是固定的,我就知道一个GMT+8东八区。。如果不要时区,则/不要后边也不要。这里有一点特别需要注意!CookieLocaleResolver的id必须设置,值必须是localeResolver。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xmlns:p="http://www.springframework.org/schema/p"
       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
                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">

    <!-- 映射器 根据beanName找到处理器 -->
    <bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/>
    <bean id="/beannameurlhandlermappingcontroller"
          class="com.datang.controller.BeanNameUrlHandlerMappingController"></bean>

    <!-- 映射器 根据@RequestMapping 找到处理器-->
    <bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping"/>

    <!-- 处理器适配器 配合BeanNameUrlHandlerMapping使用 -->
    <bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"/>

    <!-- 处理器适配器 配合RequestMappingHandlerMapping使用 -->
    <bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter"/>


    <!-- 视图解析器,配置视图的前缀和后缀 -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <!-- 使用自己的viewClass -->
        <property name="viewClass" value="com.datang.view.MyInternalResourceView"/>
        <property name="prefix" value="/WEB-INF/jsps/"/>
        <property name="suffix" value=".jsp"/>
        <property name="order" value="1"/>
    </bean>

    <!-- 第二个视图解析器 -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <!-- 使用自己的viewClass -->
        <property name="viewClass" value="com.datang.view.MyInternalResourceView"/>
        <property name="prefix" value="/WEB-INF/jsps2/"/>
        <property name="suffix" value=".jsp"/>
        <property name="order" value="2"/>
    </bean>

    <!-- 根据view名称匹配资源路径 -->
    <bean class="org.springframework.web.servlet.view.ResourceBundleViewResolver">
        <property name="order" value="3"/>
        <property name="basename" value="views"/>
    </bean>

    <!-- ExceptionHandlerExceptionResolver 配合@ControllerAdvice和@ExceptionHandler使用 -->
    <bean class="org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver">
        <!-- 优先级-->
        <property name="order" value="0"/>
    </bean>

    <!-- ResponseStatusExceptionResolver 配合@ResponseStatus使用 -->
    <bean class="org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver">
        <!-- 优先级-->
        <property name="order" value="1"/>
    </bean>

    <!-- SimpleMappingExceptionResolver 规定异常页面-->
    <bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
        <!-- 优先级-->
        <property name="order" value="2"/>
        <!-- 为所有的异常定义默认的异常处理页面,exceptionMappings未定义的异常使用本默认配置 -->
        <property name="defaultErrorView" value="error"></property>
        <!--         定义异常处理页面用来获取异常信息的变量名,默认名为exception -->
        <property name="exceptionAttribute" value="ex"></property>
        <!--
        定义需要特殊处理的异常,用类名或完全路径名作为key,异常页文件名作为值,
        将不同的异常映射到不同的页面上。
       -->
        <property name="exceptionMappings">
            <props>
                <prop key="java.io.IOException">ioerror</prop>
            </props>
        </property>
        <!-- 返回状态码 -->
        <property name="defaultStatusCode" value="250"/>
    </bean>

    
    <!-- 从cookie中读取cookieName的对应值。 -->
    <bean id="localeResolver" class="org.springframework.web.servlet.i18n.CookieLocaleResolver">
        <property name="cookieName" value="localeCoolie"/>
    </bean>


    <bean class="org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver"></bean>


    <context:component-scan base-package="com.datang"/>


</beans>
View Code
 @RequestMapping(value = "returnjsp10", method = RequestMethod.GET)
    public String handler10(Locale locale, TimeZone timeZone) {
        return "page1";
    }

    @RequestMapping(value = "returnjsp11", method = RequestMethod.GET)
    public String handler11(HttpServletResponse httpResponse) {
        Cookie cookie = new Cookie("localeCoolie", "en_US/GMT+8");
        cookie.setMaxAge(120);
        httpResponse.addCookie(cookie);
        return "page1";
    }
View Code

SessionLocaleResolver

和cookie的用法类似,但是细节上有很大的差别。localeAttributeNametimeZoneAttributeName分别设置两个session的key,注意,值必须是对象。不能是字符串。这里被坑惨了。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xmlns:p="http://www.springframework.org/schema/p"
       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
                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">

    <!-- 映射器 根据beanName找到处理器 -->
    <bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/>
    <bean id="/beannameurlhandlermappingcontroller"
          class="com.datang.controller.BeanNameUrlHandlerMappingController"></bean>

    <!-- 映射器 根据@RequestMapping 找到处理器-->
    <bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping"/>

    <!-- 处理器适配器 配合BeanNameUrlHandlerMapping使用 -->
    <bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"/>

    <!-- 处理器适配器 配合RequestMappingHandlerMapping使用 -->
    <bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter"/>


    <!-- 视图解析器,配置视图的前缀和后缀 -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <!-- 使用自己的viewClass -->
        <property name="viewClass" value="com.datang.view.MyInternalResourceView"/>
        <property name="prefix" value="/WEB-INF/jsps/"/>
        <property name="suffix" value=".jsp"/>
        <property name="order" value="1"/>
    </bean>

    <!-- 第二个视图解析器 -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <!-- 使用自己的viewClass -->
        <property name="viewClass" value="com.datang.view.MyInternalResourceView"/>
        <property name="prefix" value="/WEB-INF/jsps2/"/>
        <property name="suffix" value=".jsp"/>
        <property name="order" value="2"/>
    </bean>

    <!-- 根据view名称匹配资源路径 -->
    <bean class="org.springframework.web.servlet.view.ResourceBundleViewResolver">
        <property name="order" value="3"/>
        <property name="basename" value="views"/>
    </bean>

    <!-- ExceptionHandlerExceptionResolver 配合@ControllerAdvice和@ExceptionHandler使用 -->
    <bean class="org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver">
        <!-- 优先级-->
        <property name="order" value="0"/>
    </bean>

    <!-- ResponseStatusExceptionResolver 配合@ResponseStatus使用 -->
    <bean class="org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver">
        <!-- 优先级-->
        <property name="order" value="1"/>
    </bean>

    <!-- SimpleMappingExceptionResolver 规定异常页面-->
    <bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
        <!-- 优先级-->
        <property name="order" value="2"/>
        <!-- 为所有的异常定义默认的异常处理页面,exceptionMappings未定义的异常使用本默认配置 -->
        <property name="defaultErrorView" value="error"></property>
        <!--         定义异常处理页面用来获取异常信息的变量名,默认名为exception -->
        <property name="exceptionAttribute" value="ex"></property>
        <!--
        定义需要特殊处理的异常,用类名或完全路径名作为key,异常页文件名作为值,
        将不同的异常映射到不同的页面上。
       -->
        <property name="exceptionMappings">
            <props>
                <prop key="java.io.IOException">ioerror</prop>
            </props>
        </property>
        <!-- 返回状态码 -->
        <property name="defaultStatusCode" value="250"/>
    </bean>


<!--     从cookie中读取cookieName的对应值。 -->
<!--    <bean id="localeResolver"  class="org.springframework.web.servlet.i18n.CookieLocaleResolver">-->
<!--        <property name="cookieName" value="localeCoolie"/>-->
<!--    </bean>-->


    <!-- 从客户端的accept中获取地区 -->
    <bean class="org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver"></bean>

    <!-- 从session中获取时区和地区 -->
    <bean id="localeResolver" class="org.springframework.web.servlet.i18n.SessionLocaleResolver">
        <property name="localeAttributeName" value="sessionLocale"/>
        <property name="timeZoneAttributeName" value="sessionZone"/>
    </bean>

    <context:component-scan base-package="com.datang"/>


</beans>
View Code
  @RequestMapping(value = "returnjsp12", method = RequestMethod.GET)
    public String handler12(HttpServletRequest request) {
        HttpSession session = request.getSession();

        Locale locale = new Locale("en_US");
        session.setAttribute("sessionLocale",locale);
        //session.setAttribute("sessionLocale","en_US");

        TimeZone timeZone = new SimpleTimeZone(8,"GMT");
        session.setAttribute("sessionZone",timeZone);
        //session.setAttribute("sessionZone","GMT+8");
        return "page1";
    }
View Code
  @RequestMapping(value = "returnjsp10", method = RequestMethod.GET)
    public String handler10(Locale locale, TimeZone timeZone) {
        return "page1";
    }
View Code

ResourceBundleViewResolver和LocaleResolver配合做国际化

LocaleResolver前边我们说过,根据视图名字,从配置文件中找到对应的视图路径。做国际化也很简单,除了视图我们在设置一个模型,模型设置Locale,而Locale就是从参数,也就是ResourceBundleViewResolver获取。然后根据不同的地区,设置多个配置文件。下边代码片段,为了省事我用的AcceptHeaderLocaleResolver从请求头获取地区信息。

    // ResourceBundleViewResolver配合时区,做国际化
    @RequestMapping(value = "returnjsp13", method = RequestMethod.GET)
    public ModelAndView handler13(Locale locale) {
        ModelAndView modelAndView = new ModelAndView("user2");
        modelAndView.addObject("locale", locale);
        return modelAndView;
    }
View Code
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xmlns:p="http://www.springframework.org/schema/p"
       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
                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">

    <!-- 映射器 根据beanName找到处理器 -->
    <bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/>
    <bean id="/beannameurlhandlermappingcontroller"
          class="com.datang.controller.BeanNameUrlHandlerMappingController"></bean>

    <!-- 映射器 根据@RequestMapping 找到处理器-->
    <bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping"/>

    <!-- 处理器适配器 配合BeanNameUrlHandlerMapping使用 -->
    <bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"/>

    <!-- 处理器适配器 配合RequestMappingHandlerMapping使用 -->
    <bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter"/>


    <!-- 视图解析器,配置视图的前缀和后缀 -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <!-- 使用自己的viewClass -->
        <property name="viewClass" value="com.datang.view.MyInternalResourceView"/>
        <property name="prefix" value="/WEB-INF/jsps/"/>
        <property name="suffix" value=".jsp"/>
        <property name="order" value="1"/>
    </bean>

    <!-- 第二个视图解析器 -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <!-- 使用自己的viewClass -->
        <property name="viewClass" value="com.datang.view.MyInternalResourceView"/>
        <property name="prefix" value="/WEB-INF/jsps2/"/>
        <property name="suffix" value=".jsp"/>
        <property name="order" value="2"/>
    </bean>

    <!-- 根据view名称匹配资源路径 -->
    <bean class="org.springframework.web.servlet.view.ResourceBundleViewResolver">
        <property name="order" value="3"/>
        <property name="basename" value="views"/>
    </bean>

    <!-- ExceptionHandlerExceptionResolver 配合@ControllerAdvice和@ExceptionHandler使用 -->
    <bean class="org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver">
        <!-- 优先级-->
        <property name="order" value="0"/>
    </bean>

    <!-- ResponseStatusExceptionResolver 配合@ResponseStatus使用 -->
    <bean class="org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver">
        <!-- 优先级-->
        <property name="order" value="1"/>
    </bean>

    <!-- SimpleMappingExceptionResolver 规定异常页面-->
    <bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
        <!-- 优先级-->
        <property name="order" value="2"/>
        <!-- 为所有的异常定义默认的异常处理页面,exceptionMappings未定义的异常使用本默认配置 -->
        <property name="defaultErrorView" value="error"></property>
        <!--         定义异常处理页面用来获取异常信息的变量名,默认名为exception -->
        <property name="exceptionAttribute" value="ex"></property>
        <!--
        定义需要特殊处理的异常,用类名或完全路径名作为key,异常页文件名作为值,
        将不同的异常映射到不同的页面上。
       -->
        <property name="exceptionMappings">
            <props>
                <prop key="java.io.IOException">ioerror</prop>
            </props>
        </property>
        <!-- 返回状态码 -->
        <property name="defaultStatusCode" value="250"/>
    </bean>


<!--     从cookie中读取cookieName的对应值。 -->
<!--    <bean id="localeResolver"  class="org.springframework.web.servlet.i18n.CookieLocaleResolver">-->
<!--        <property name="cookieName" value="localeCoolie"/>-->
<!--    </bean>-->


    <!-- 从客户端的accept中获取地区 -->
    <bean class="org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver"></bean>

    <!-- 从session中获取时区和地区 -->
<!--    <bean id="localeResolver" class="org.springframework.web.servlet.i18n.SessionLocaleResolver">-->
<!--        <property name="localeAttributeName" value="sessionLocale"/>-->
<!--        <property name="timeZoneAttributeName" value="sessionZone"/>-->
<!--    </bean>-->

    <context:component-scan base-package="com.datang"/>


</beans>
View Code
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
<h1>我是外国版本</h1>
</body>
</html>
View Code
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
<h1>我是中国版本</h1>
</body>
</html>
View Code

ThemeResolver

ThemeResolver对主题做切换。

CookieThemeResolver

通过cookie切换主题。ResourceBundleThemeSource设置主题文件所在文件夹,注意文件夹一定要在classe下。basenamePrefix需要带 . 不带会报错的。CookieThemeResolver通过cookie设置主题文件。从cookieName这个cookie

中取主题文件名,如果没有对应的cookie则主题文件名为defaultThemeName的值。在jsp中必须引入spring标签库。然后使用正确的格式找配置参数。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xmlns:p="http://www.springframework.org/schema/p"
       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
                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">

    <!-- 映射器 根据beanName找到处理器 -->
    <bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/>
    <bean id="/beannameurlhandlermappingcontroller"
          class="com.datang.controller.BeanNameUrlHandlerMappingController"></bean>

    <!-- 映射器 根据@RequestMapping 找到处理器-->
    <bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping"/>

    <!-- 处理器适配器 配合BeanNameUrlHandlerMapping使用 -->
    <bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"/>

    <!-- 处理器适配器 配合RequestMappingHandlerMapping使用 -->
    <bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter"/>


    <!-- 视图解析器,配置视图的前缀和后缀 -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <!-- 使用自己的viewClass -->
        <property name="viewClass" value="com.datang.view.MyInternalResourceView"/>
        <property name="prefix" value="/WEB-INF/jsps/"/>
        <property name="suffix" value=".jsp"/>
        <property name="order" value="1"/>
    </bean>

    <!-- 第二个视图解析器 -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <!-- 使用自己的viewClass -->
        <property name="viewClass" value="com.datang.view.MyInternalResourceView"/>
        <property name="prefix" value="/WEB-INF/jsps2/"/>
        <property name="suffix" value=".jsp"/>
        <property name="order" value="2"/>
    </bean>

    <!-- 根据view名称匹配资源路径 -->
    <bean class="org.springframework.web.servlet.view.ResourceBundleViewResolver">
        <property name="order" value="3"/>
        <property name="basename" value="views"/>
    </bean>

    <!-- ExceptionHandlerExceptionResolver 配合@ControllerAdvice和@ExceptionHandler使用 -->
    <bean class="org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver">
        <!-- 优先级-->
        <property name="order" value="0"/>
    </bean>

    <!-- ResponseStatusExceptionResolver 配合@ResponseStatus使用 -->
    <bean class="org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver">
        <!-- 优先级-->
        <property name="order" value="1"/>
    </bean>

    <!-- SimpleMappingExceptionResolver 规定异常页面-->
    <bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
        <!-- 优先级-->
        <property name="order" value="2"/>
        <!-- 为所有的异常定义默认的异常处理页面,exceptionMappings未定义的异常使用本默认配置 -->
        <property name="defaultErrorView" value="error"></property>
        <!--         定义异常处理页面用来获取异常信息的变量名,默认名为exception -->
        <property name="exceptionAttribute" value="ex"></property>
        <!--
        定义需要特殊处理的异常,用类名或完全路径名作为key,异常页文件名作为值,
        将不同的异常映射到不同的页面上。
       -->
        <property name="exceptionMappings">
            <props>
                <prop key="java.io.IOException">ioerror</prop>
            </props>
        </property>
        <!-- 返回状态码 -->
        <property name="defaultStatusCode" value="250"/>
    </bean>


    <!--         从cookie中读取cookieName的对应值。-->
    <bean id="localeResolver" class="org.springframework.web.servlet.i18n.CookieLocaleResolver">
        <property name="cookieName" value="localeCoolie"/>
    </bean>


    <!-- 从客户端的accept中获取地区 -->
    <bean class="org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver"></bean>

    <!-- 从session中获取时区和地区 -->
    <!--    <bean id="localeResolver" class="org.springframework.web.servlet.i18n.SessionLocaleResolver">-->
    <!--        <property name="localeAttributeName" value="sessionLocale"/>-->
    <!--        <property name="timeZoneAttributeName" value="sessionZone"/>-->
    <!--    </bean>-->


    <!-- cookie设置css样式 -->
    <bean id="themeResolver" class="org.springframework.web.servlet.theme.CookieThemeResolver">
        <property name="defaultThemeName" value="theme1"/>
        <property name="cookieName" value="theme"/>
    </bean>

    <!-- 样式路径 -->
    <bean id="themeSource" class="org.springframework.ui.context.support.ResourceBundleThemeSource">
        <property name="basenamePrefix" value="theme."/>
    </bean>

    <context:component-scan base-package="com.datang"/>


</beans>
View Code
    //设置主题 theme1 theme2 en_US zh_CN
    @RequestMapping(value = "returnjsp14", method = RequestMethod.GET)
    public String handler14(String t, String l, HttpServletResponse httpResponse) {
        //在本类设置cookie
        Cookie cookie = new Cookie("theme", t);
        cookie.setMaxAge(120);
        httpResponse.addCookie(cookie);

        if (l != null && !l.equals("")) {
            Cookie cookie2 = new Cookie("localeCoolie", l);
            cookie2.setMaxAge(120);
            httpResponse.addCookie(cookie2);
        }
        return "page1";
    }

    //设置主题
    @RequestMapping(value = "returnjsp15", method = RequestMethod.GET)
    public String handler15() {
        return "page6";
    }
View Code
<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>

<span style="color:<spring:theme code='color'/>">我是什么颜色</span>

</body>
</html>
View Code
# theme1.properties
color=red
View Code
# theme2.properties
color=deeppink
View Code

使用CookieThemeResolver做国际化主题

做国际化很简单,只要我们使用CookieLocaleResolver引入CookieLocale,设置对应地区Locale。同时的,要引入对应的国家化配置文件。

# theme1_en_US
color=green
View Code
# theme1_zh_CN
color=blue
View Code
# theme2_en_US
color=aquamarine
View Code
# theme2_zh_CN
color=chartreuse
View Code

MultipartResolver

MultipartResolver负责文件上传。

CommonsMultipartResolver

CommonsMultipartResolver基于Commons FileUpload需要引入对应jar包。

    <dependency>
            <groupId>commons-fileupload</groupId>
            <artifactId>commons-fileupload</artifactId>
            <version>1.3.3</version>
        </dependency>
View Code
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xmlns:p="http://www.springframework.org/schema/p"
       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
                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">

    <!-- 映射器 根据beanName找到处理器 -->
    <bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/>
    <bean id="/beannameurlhandlermappingcontroller"
          class="com.datang.controller.BeanNameUrlHandlerMappingController"></bean>

    <!-- 映射器 根据@RequestMapping 找到处理器-->
    <bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping"/>

    <!-- 处理器适配器 配合BeanNameUrlHandlerMapping使用 -->
    <bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"/>

    <!-- 处理器适配器 配合RequestMappingHandlerMapping使用 -->
    <bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter"/>


    <!-- 视图解析器,配置视图的前缀和后缀 -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <!-- 使用自己的viewClass -->
        <property name="viewClass" value="com.datang.view.MyInternalResourceView"/>
        <property name="prefix" value="/WEB-INF/jsps/"/>
        <property name="suffix" value=".jsp"/>
        <property name="order" value="1"/>
    </bean>

    <!-- 第二个视图解析器 -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <!-- 使用自己的viewClass -->
        <property name="viewClass" value="com.datang.view.MyInternalResourceView"/>
        <property name="prefix" value="/WEB-INF/jsps2/"/>
        <property name="suffix" value=".jsp"/>
        <property name="order" value="2"/>
    </bean>

    <!-- 根据view名称匹配资源路径 -->
    <bean class="org.springframework.web.servlet.view.ResourceBundleViewResolver">
        <property name="order" value="3"/>
        <property name="basename" value="views"/>
    </bean>

    <!-- ExceptionHandlerExceptionResolver 配合@ControllerAdvice和@ExceptionHandler使用 -->
    <bean class="org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver">
        <!-- 优先级-->
        <property name="order" value="0"/>
    </bean>

    <!-- ResponseStatusExceptionResolver 配合@ResponseStatus使用 -->
    <bean class="org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver">
        <!-- 优先级-->
        <property name="order" value="1"/>
    </bean>

    <!-- SimpleMappingExceptionResolver 规定异常页面-->
    <bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
        <!-- 优先级-->
        <property name="order" value="2"/>
        <!-- 为所有的异常定义默认的异常处理页面,exceptionMappings未定义的异常使用本默认配置 -->
        <property name="defaultErrorView" value="error"></property>
        <!--         定义异常处理页面用来获取异常信息的变量名,默认名为exception -->
        <property name="exceptionAttribute" value="ex"></property>
        <!--
        定义需要特殊处理的异常,用类名或完全路径名作为key,异常页文件名作为值,
        将不同的异常映射到不同的页面上。
       -->
        <property name="exceptionMappings">
            <props>
                <prop key="java.io.IOException">ioerror</prop>
            </props>
        </property>
        <!-- 返回状态码 -->
        <property name="defaultStatusCode" value="250"/>
    </bean>


    <!--         从cookie中读取cookieName的对应值。-->
    <bean id="localeResolver" class="org.springframework.web.servlet.i18n.CookieLocaleResolver">
        <property name="cookieName" value="localeCoolie"/>
    </bean>


    <!-- 从客户端的accept中获取地区 -->
    <bean class="org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver"></bean>

    <!-- 从session中获取时区和地区 -->
    <!--    <bean id="localeResolver" class="org.springframework.web.servlet.i18n.SessionLocaleResolver">-->
    <!--        <property name="localeAttributeName" value="sessionLocale"/>-->
    <!--        <property name="timeZoneAttributeName" value="sessionZone"/>-->
    <!--    </bean>-->


    <!-- cookie设置css样式 -->
    <bean id="themeResolver" class="org.springframework.web.servlet.theme.CookieThemeResolver">
        <property name="defaultThemeName" value="theme1"/>
        <property name="cookieName" value="theme"/>
    </bean>

    <!-- 样式路径 -->
    <bean id="themeSource" class="org.springframework.ui.context.support.ResourceBundleThemeSource">
        <property name="basenamePrefix" value="theme."/>
    </bean>


    <!-- 文件上传 -->
    <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"></bean>

    <context:component-scan base-package="com.datang"/>


</beans>
View Code
<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
<form enctype="multipart/form-data" action="returnjsp17" method="post">
    <input type="file" name="file">
    <input type="submit" value="提交">
</form>
</body>
</html>
View Code
 //返回一个带上传组件的视图
    @RequestMapping(value = "returnjsp16", method = RequestMethod.GET)
    public String handler16() {
        return "page7";
    }

    @RequestMapping(value = "returnjsp17", method = RequestMethod.POST)
    public String handler17(MultipartFile file) {
        return "page1";
    }
View Code

本章节总结

这一个章节围绕DispatcherServlet这个类。DispatcherServlet是一个被Spring包装好的Servlet,它把全部的请求都接收到DispatcherServlet,然后通过HandlerMapping(处理器映射器)去寻找对应的controller,当寻找到controller后,不会直接执行而是寻找能够处理controller的HandlerAdapter(处理器适配器),通过HandlerAdapter去执行controller,执行完毕后,拿到ModelAndView并且返回一个ViewResolver(视图解析器),ModelAndView负责将View的逻辑视图找到对应的物理视图,并将Model填充到View中,并返回。如果在Controller处理过程中出现错误,则交给HandlerExceptionResolver(异常解析器)处理。除此之外SpringMvc对处理国际化也有对应的方式。LocaleResolver(地区转换器)通过请求头或cookie或session获取客户端地区和时区,在配合ViewResolver找到对应地区的视图。ThemeResolver(主题解析器)可以根据不同的选择或者地区给页面渲染不同的主题。最后在处理多部件参数时使用MultipartResolver有效的将请求中的文件参数绑定到controller的参数上。其实在默认情况下,如果我们不知道选择什么样的配置,也完全不必担心。SpringMvc默认的会加载默认配置。

SpringMvc中的注解

控制器相关

@Controller

使用@Controller的注解的类不需要实现Controller接口便可成为处理器类。注意不能使用@Component代替。

@RequestMapping

RequestMappingHandlerMapping会找到所有被@RequestMapping注解的方法,匹配映射。也可以注解到类上会拼接映射路劲。

@ResponseBody

使用该注解意味着直接将返回值填充到Response的响应体中,因此被@ResponseBody注解的方法,不会再走视图解析器。

package com.datang.controller;/*
* @auther 顶风少年 
* @mail dfsn19970313@foxmail.com
* @date 2020-02-11 09:41
* @notify 
* @version 1.0
*/

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
@RequestMapping(value = "anno/")
public class Anno {
    @RequestMapping(value = "g1",method = RequestMethod.GET)
    @ResponseBody
    public String get1(){
        return "haha";
    }
}
View Code

@RestController

@Controller和@ResponseBody的结合体。被@RestController注释的类是处理器类,类中的方法如果是处理器方法则返回值都直接填充到Response响应体中。

package com.datang.controller;/*
* @auther 顶风少年 
* @mail dfsn19970313@foxmail.com
* @date 2020-02-11 09:41
* @notify 
* @version 1.0
*/

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping(value = "rest/")
public class RestAnno {
    @RequestMapping(value = "g1",method = RequestMethod.GET)
    public String get1(){
        return "haha";
    }
}
View Code

@RequestMapping的变体

把RequestMapping加到方法上需要method参数对请求进行筛选的操作是必要的。于是Spring扩展了一下注解专门针对不同的请求方式设计不同的注解。在这些注解上我们依然可以对headers等其他的条件进行筛选。

@GetMappinig

    //使用@GetMapping标识该方法只接受Get请求
    @GetMapping(value = "g2")
    public String get2() {
        return "g2";
    }
View Code

@PostMapping

@PutMapping

@DeleteMapping

@PatchMapping

参数映射

@PathVariable将url绑定到参数

这种方法准确来说,是将url绑定到参数,所以参数名必须要和{path}一样

    //将路径映射到参数上
    @GetMapping(value = "g3{p1}/{p2}")
    public String get3(@PathVariable String p1, @PathVariable String p2) {
        return p1 + p2;
    }
View Code

参数映射的限制

在使用@RequestMapping或者其他变体时,我们指定method参数,标识只接收对应方法的请求。这个注解还有其他的映射限制参数。

consumes

接收指定参数类型的请求。一下代码片段只接收参数类型是text/xml或者text/plain

   //将路径映射到参数上
    @GetMapping(value = "g4",consumes = {"text/xml","text/plain"})
    public String get4() {
        return "g4";
    }
View Code

produces

一下代码片段标识服务端返回的数据格式是application/json而客户端只接收text/html所以是错误的。

  //只接受客户端要求的返回值类型包含application/json
    @GetMapping(value = "g5",produces = {"application/json"})
    public String get5() {
        return "g5";
    }
View Code

params

要求请求中必须指定某个参数,或某个参数的值必须匹配。

    //要求客户端必须要参数 a,参数b的值必须是100
    @GetMapping(value = "g6", params = {"a", "b=100"})
    public String get6(String a, Integer b) {
        return "g6";
    }
View Code

headers

要求客户端请求必须有指定的请求头,或请求头的值必须符合

@MatrixVariable矩阵变量

矩阵变量也是从url中获取参数,但是有几个条件必须满足。1、必须使用@PathVariable 2、必须使用@MatrixVariable 3、RequestMappingHandlerMapping的removeSemicolonContent必须是false

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xmlns:p="http://www.springframework.org/schema/p"
       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
                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">

    <!-- 映射器 根据beanName找到处理器 -->
    <bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/>
    <bean id="/beannameurlhandlermappingcontroller"
          class="com.datang.controller.BeanNameUrlHandlerMappingController"></bean>

    <!-- 映射器 根据@RequestMapping 找到处理器-->
    <bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping">
        <property name="removeSemicolonContent" value="false"/>
    </bean>

    <!-- 处理器适配器 配合BeanNameUrlHandlerMapping使用 -->
    <bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"/>

    <!-- 处理器适配器 配合RequestMappingHandlerMapping使用 -->
    <bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter"/>


    <!-- 视图解析器,配置视图的前缀和后缀 -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <!-- 使用自己的viewClass -->
        <property name="viewClass" value="com.datang.view.MyInternalResourceView"/>
        <property name="prefix" value="/WEB-INF/jsps/"/>
        <property name="suffix" value=".jsp"/>
        <property name="order" value="1"/>
    </bean>

    <!-- 第二个视图解析器 -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <!-- 使用自己的viewClass -->
        <property name="viewClass" value="com.datang.view.MyInternalResourceView"/>
        <property name="prefix" value="/WEB-INF/jsps2/"/>
        <property name="suffix" value=".jsp"/>
        <property name="order" value="2"/>
    </bean>

    <!-- 根据view名称匹配资源路径 -->
    <bean class="org.springframework.web.servlet.view.ResourceBundleViewResolver">
        <property name="order" value="3"/>
        <property name="basename" value="views"/>
    </bean>

    <!-- ExceptionHandlerExceptionResolver 配合@ControllerAdvice和@ExceptionHandler使用 -->
    <bean class="org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver">
        <!-- 优先级-->
        <property name="order" value="0"/>
    </bean>

    <!-- ResponseStatusExceptionResolver 配合@ResponseStatus使用 -->
    <bean class="org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver">
        <!-- 优先级-->
        <property name="order" value="1"/>
    </bean>

    <!-- SimpleMappingExceptionResolver 规定异常页面-->
    <bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
        <!-- 优先级-->
        <property name="order" value="2"/>
        <!-- 为所有的异常定义默认的异常处理页面,exceptionMappings未定义的异常使用本默认配置 -->
        <property name="defaultErrorView" value="error"></property>
        <!--         定义异常处理页面用来获取异常信息的变量名,默认名为exception -->
        <property name="exceptionAttribute" value="ex"></property>
        <!--
        定义需要特殊处理的异常,用类名或完全路径名作为key,异常页文件名作为值,
        将不同的异常映射到不同的页面上。
       -->
        <property name="exceptionMappings">
            <props>
                <prop key="java.io.IOException">ioerror</prop>
            </props>
        </property>
        <!-- 返回状态码 -->
        <property name="defaultStatusCode" value="250"/>
    </bean>


    <!--         从cookie中读取cookieName的对应值。-->
    <bean id="localeResolver" class="org.springframework.web.servlet.i18n.CookieLocaleResolver">
        <property name="cookieName" value="localeCoolie"/>
    </bean>


    <!-- 从客户端的accept中获取地区 -->
    <bean class="org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver"></bean>

    <!-- 从session中获取时区和地区 -->
    <!--    <bean id="localeResolver" class="org.springframework.web.servlet.i18n.SessionLocaleResolver">-->
    <!--        <property name="localeAttributeName" value="sessionLocale"/>-->
    <!--        <property name="timeZoneAttributeName" value="sessionZone"/>-->
    <!--    </bean>-->


    <!-- cookie设置css样式 -->
    <bean id="themeResolver" class="org.springframework.web.servlet.theme.CookieThemeResolver">
        <property name="defaultThemeName" value="theme1"/>
        <property name="cookieName" value="theme"/>
    </bean>

    <!-- 样式路径 -->
    <bean id="themeSource" class="org.springframework.ui.context.support.ResourceBundleThemeSource">
        <property name="basenamePrefix" value="theme."/>
    </bean>


    <!-- 文件上传 -->
    <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"></bean>

    <context:component-scan base-package="com.datang"/>


</beans>
View Code
  //矩阵变量,defaultValue可设置默认值
    @GetMapping(value = "g8/{a}")
    public String get8(@PathVariable String a, @MatrixVariable(defaultValue = "bbbb") String b, @MatrixVariable List c) {
        return a + b + c.toString();
    }
View Code

@RequestParam参数别名

请求中的参数名和方法参数不一致时,可以使用@RequestParam。注意使用该注解的参数默认是必须的,否则报错。可以使用required = false

    //参数别名映射。required设置是否是必须的参数
    @GetMapping(value = "g9")
    public String get9(@RequestParam(value = "v2",required = false) String v1) {
        return v1;
    }
View Code

@RequestHeader绑定请求头到参数

 //请求头映射成参数。value代表要映射的head,如果head为空则报错,但是可以设置required或着defaultValue参数。
    //如果参数类型是Map则会把所有的head全部映射到map中
    @GetMapping(value = "g10")
    public String get10(@RequestHeader(value = "head1") String head1,
                        @RequestHeader(value = "head2", required = false) String head2,
                        @RequestHeader(value = "head3", defaultValue = "head333") String head3,
                        @RequestHeader Map heads) {
        return "requestHeader";
    }
View Code

@CookieValue绑定cookie到参数

    //获取cookie映射到参数。required设置是否是必须的参数。
    // defaultValue如果没有对应cookie则选择默认的
    @GetMapping(value = "g11")
    public String get10(@CookieValue(value = "JSESSIONID") String sessionId, @CookieValue(value = "a", defaultValue = "aaa") String a,
                        @CookieValue(value = "b", required = false) String c) {
        return sessionId;
    }
View Code

可自动加入ModelAndView的

<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
    <div>
        ${k1} ${k2} ${k3} ${k4}
    </div>
</body>
</html>
View Code
package com.datang.controller;/*
 * @auther 顶风少年
 * @mail dfsn19970313@foxmail.com
 * @date 2020-02-11 17:56
 * @notify
 * @version 1.0
 */

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.ui.ModelMap;
import org.springframework.validation.support.BindingAwareConcurrentModel;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;

import java.util.HashMap;
import java.util.Map;

@Controller
@RequestMapping(value = "moa/")
public class ModelAttributeD {

    //通常的我们返回ModelAndView,addObject()则相当于request.addAttribute("","");
    @GetMapping("g1")
    public ModelAndView g1() {
        ModelAndView modelAndView = new ModelAndView();
        modelAndView.addObject("k1", "v1");
        modelAndView.setViewName("page8");
        return modelAndView;
    }
}
View Code
    /* 除此之外我们还可以在参数中携带  Model , Map  ModelMap
      这三个参数也可以自动的封装到request中。注意,只能是在参数中。
      如果是局部变量则不会自动封装。*/
    @RequestMapping(value = "g2")
    public ModelAndView g1(Model model, Map map, ModelMap modelMap) {

        // Model model, Map map, ModelMap modelMap

        ModelAndView modelAndView = new ModelAndView();
        modelAndView.addObject("k1", "v1");
        modelAndView.setViewName("page8");


        //  Model model = new BindingAwareConcurrentModel();
        model.addAttribute("k2", "v2");


        //  Map map = new HashMap();
        map.put("k3", "v3");


        // ModelMap modelMap = new ModelMap();
        modelMap.addAttribute("k4", "v4");


        return modelAndView;
    }
View Code

 /*   如果返回值是String,并且没有使用@ResponseBody也会填充
    这三个参数,但是ModeleAndView则不会自动填充*/
    @RequestMapping(value = "g3")
    public String g3(ModelAndView modelAndView, Model model, Map map, ModelMap modelMap) {


        modelAndView.addObject("k1", "v1");
        modelAndView.setViewName("page8");


        model.addAttribute("k2", "v2");


        map.put("k3", "v3");


        modelMap.addAttribute("k4", "v4");


        return "page8";
    }
View Code

@ModelAttribute

@ModelAttribute添加到方法上,充当前置Controller

被@ModelAttribute注释的方法,没有返回值。它会在每一个controller处理前先执行。如果参数中有Model,Map,modelMap。则会自动填充到所有的Controller中的Model中。

    @ModelAttribute
    public void m1(Model model, Map map, ModelMap modelMap) {
        model.addAttribute("m1", "v1");
        map.put("m2", "v2");
        modelMap.put("m3", "v3");
    }

    @GetMapping(value = "g4")
    public String g4() {
        return "page9";
    }
View Code
<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
    <div>
        ${m1} ${m2} ${m3}
    </div>
</body>
</html>
View Code

@ModelAttribute添加到方法上,返回对象

@ModelAttribute注释的方法返回一个具体的对象,虽然未显示的指定model的Key,则默认的将其类型小写作为model的Key

  @ModelAttribute
    public String m2(){
        return "m4";
    }

    @GetMapping(value = "g4")
    public String g4() {
        return "page9";
    }
View Code
<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
    <div>
        ${m1} ${m2} ${m3} ${string}
    </div>
</body>
</html>
View Code

@ModelAttribute添加到方法上,返回对象,设置value属性

通过设置Value属性,指定返回值得Key。

 @ModelAttribute(value = "m5")
    public String m3(){
        return "v5";
    }

    @GetMapping(value = "g4")
    public String g4() {
        return "page9";
    }
View Code
<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
<div>
    ${m1} ${m2} ${m3} ${string} ${m5}
</div>
</body>
</html>
View Code

@ModelAttribute和@GetMapping(包括其他Method的Mapping)一块使用

这种使用方法最难理解,坑也最多。先看第一个代码片段,这个的mapping的值,其实是视图的名字。而@ModelAttribute的value则是model的Key。

package com.datang.controller;/*
 * @auther 顶风少年
 * @mail dfsn19970313@foxmail.com
 * @date 2020-02-11 17:56
 * @notify
 * @version 1.0
 */

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;

import java.util.Map;

@Controller
public class ModelAttributeD2 {

    @ModelAttribute(value = "m1")
    @GetMapping(value = "page10")
    public String g5() {
        return "v1";
    }
}
View Code
<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
<div>
    ${m1}
</div>
</body>
</html>
View Code

如果是多层路径则会报错,明显的,它说找不到view。

@Controller
@RequestMapping("/test")
public class ModelAttributeD2 {

    @ModelAttribute(value = "m1")
    @GetMapping(value = "page10")
    public String g5() {
        return "v1";
    }
}
View Code

同时其他的@ModelAttribute也会生效。

package com.datang.controller;/*
 * @auther 顶风少年
 * @mail dfsn19970313@foxmail.com
 * @date 2020-02-11 17:56
 * @notify
 * @version 1.0
 */

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;

import java.util.Map;

@Controller
//@RequestMapping("/test")
public class ModelAttributeD2 {

    @ModelAttribute
    public void m1(Model model){
        model.addAttribute("m2","v2");
    }

    @ModelAttribute(value = "m1")
    @GetMapping(value = "page10")
    public String g5() {
        return "v1";
    }
}
View Code
<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
<div>
    ${m1} ${m2}
</div>
</body>
</html>
View Code

@ModelAttribute注释参数,获取其他ModelAttribute的值

以下代码片段,我们获取了m1()方法的model。此时就算我们通过请求参数设置属性也不会自动绑定。

package com.datang.controller;/*
 * @auther 顶风少年
 * @mail dfsn19970313@foxmail.com
 * @date 2020-02-11 17:56
 * @notify
 * @version 1.0
 */

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;

import java.util.Map;

@Controller
//@RequestMapping("/test")
public class ModelAttributeD2 {

    @ModelAttribute
    public void m1(Model model) {
        model.addAttribute("m2", "v2");
    }

    @GetMapping(value = "g4")
    public String g4() {
        return "page9";
    }

    @ModelAttribute(value = "m1")
    @GetMapping(value = "page10")
    public String g5() {
        return "v1";
    }

    @ResponseBody
    @GetMapping(value = "g5")
    public String g6(@ModelAttribute("m2") String m2) {
        return m2;
    }
}
View Code

@ModelAttribute注释返回值

最坑的用法,首先@GetMapping的作用还是设置视图,那问题就出现了,我们已经有一个方法的@GetMapping是page10了,尽管它俩都是指向的视图,还是会报错。所以我这另外开了一个视图。

注解到返回值,就是将返回值填充到Model中,key为value的值。

package com.datang.controller;/*
 * @auther 顶风少年
 * @mail dfsn19970313@foxmail.com
 * @date 2020-02-11 17:56
 * @notify
 * @version 1.0
 */

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.ModelAndView;

import java.util.Map;

@Controller
//@RequestMapping("/test")
public class ModelAttributeD2 {

    @ModelAttribute
    public void m1(Model model) {
        model.addAttribute("m2", "v2");
    }

    @GetMapping(value = "g4")
    public String g4() {
        return "page9";
    }

    @ModelAttribute(value = "m1")
    @GetMapping(value = "page10")
    public String g5() {
        return "v1";
    }

    @ResponseBody
    @GetMapping(value = "g5")
    public String g6(@ModelAttribute("m2") String m2) {
        return m2;
    }

    @GetMapping(value = "page11")
    public @ModelAttribute(value = "m3") String g7() {
        return "haha";
    }
}
View Code
<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
<div>
    ${m3}
</div>
</body>
</html>
View Code



原文地址:https://www.cnblogs.com/zumengjie/p/12269916.html