SpringMVC 基础

1、MVC模式

MVC 模式是指 Model-View-Controller(模型-视图-控制器),是开发 Web 应用程序时常用的一种代码分层方式。

Model - View - Controller 三个基本部分

  • 视图(View):负责格式化数据并把它们呈现给用户,包括数据展示、数据验证、界面设计等功能。对应组件:JSP 或 HTML 文件。

  • 控制器(Controller):负责接收并转发请求,对请求进行处理后指派视图并将响应结果发送给客户端。对应组件:Servlet

  • 模型(Model):模型对象拥有最多的处理任务,是应用程序的主体部分,它负责业务逻辑的处理和实现对数据的操作。对应组件:JavaBean
    在这里插入图片描述

分析出 MVC 整体的处理过程如下:

  1. 视图提供系统与用户交互的界面,并发送用户输入的请求给控制器。
  2. 控制器接收用户的请求,并决定应该调用的模型。
  3. 模型根据用户请求进行相应的业务逻辑处理,并返回处理结果(数据)。
  4. 控制器根据返回的处理结果,调用相应的视图格式化模型返回的数据,并通过视图呈现结果给用户。

MVC 设计模式可打造出一个松耦合 + 高重用性 + 高可实用性的完美构架,这也是架构设计的目标之一。

优点

  • MVC 三个模块相互独立,松耦合架构。
  • 多视图共享一个模型,大大提高代码的可重用性。
  • 控制器提高了应用程序的灵活性和可配置性。
  • 有利于软件工程化管理。

缺点

  • 增加了系统结构和实现的复杂性,不适合小型规模的项目。
  • 层与模块之间需要控制器做中间的连接控制,所有效率较低。

2、搭建 Spring MVC 环境

2.1、第一个 Spring MVC 程序
2.1.1、步骤1

引入 jar 文件

spring-webmvc.jar	Spring MVC 框架相关的所有类,包含框架的 Servlet、Web MVC 框架,以及对控制器和视图的支持
spring-web.jar	    开发 Web 应用时使用 Spring 框架所需的核心类
2.1.2、步骤2

编写配置文件

<?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"
       xmlns:context="http://www.springframework.org/schema/context"
       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
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd">
 
    <!-- 定义控制器,访问路径为 /hello-->
    <bean name="/hello" class="cn.cvs.controller.HelloController"/> 
</beans>

Spring MVC 框架会使用默认的处理器映射 BeanNameUrlHandlerMapping,即在 Spring 容器中查找与请求 URL 同名的 Bean,然后根据该 Bean 配置的 class 属性调用与之对应的 Controller。

配置 web.xml

<!--Spring MVC 的核心是一个 Servlet,称为前端控制器-->
<servlet>
    <servlet-name>springmvc</servlet-name>
    <servlet-class>
        org.springframework.web.servlet.DispatcherServlet
    </servlet-class>
    <!--加载 Spring MVC 配置文件-->
    <init-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:springmvc-servlet.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
    <servlet-name>springmvc</servlet-name>
    <url-pattern>/</url-pattern>
</servlet-mapping>
  1. 定义一个名为 springmvc 的 Servlet, 指向 DispatcherServlet。DispatcherServlet 是 Spring MVC 框架的核心,被成为前端控制器
  2. 在载入当前 Servlet 时,使用 init-param 标签,加载创建的配置文件
  3. 设置 load-on-startup 的值为1,表示当前 Servlet 会在系统启动时被载入
  4. 设置 url-pattern 的值为 “/” ,表示当前 Servlet 拦截所有请求
2.1.3、步骤3

编写代码

package cn.cvs.controller;

import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.AbstractController;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class HelloController extends AbstractController {
    @Override
    protected ModelAndView handleRequestInternal(HttpServletRequest req, HttpServletResponse resp) throws Exception { 
        System.out.println("Spring MVC 框架搭建成功。");
        return null;
    }
}

运行项目,在浏览器输入:http://localhost:8080/CVS_C09_01/hello,进行测试。

springmvc-servlet.xml

<!-- 定义控制器,访问路径为 /hello-->
<bean name="/hello" class="cn.cvs.controller.HelloController"/>
<!--完成视图的对应-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    <property name="prefix" value="/WEB-INF/jsp/"/>
    <property name="suffix" value=".jsp"/>
</bean>
2.2、使用注解开发 Spring MVC 框架

HandlerMapping 意为处理器映射,用于解析前端请求与 Controller 的映射关系。

Spring 框架提供了多种处理器映射的支持:

  • org.springframework.web.servlet.handler.SimplUrlHandlerMapping
  • org.springframework.web.servlet.mvc.annotation.DefalutAnnotationHandlerMapping
  • org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping等

使用注解开发,需要修改处理器映射的配置方式为支持注解的方式。

<?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"
       xmlns:context="http://www.springframework.org/schema/context"
       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
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd"> 
    <!--实战2-->
    <!--开启注解驱动-->
    <mvc:annotation-driven/>
    <!--注解扫描的包-->
    <context:component-scan base-package="cn.cvs.controller"/>

    <!--完成视图的对应-->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/jsp/"/>
        <property name="suffix" value=".jsp"/>
    </bean>
</beans>

​ <mvc:annotation-driven /> 配置该标签会自动注册 DefalutAnnotationHandlerMapping 与 AnnotationMethodHandlerAdapter 两个 Bean。

这两个 Bean 注入到容器中是使 SpringMVC 注解生效的必要条件。

​ <context:component-scan base-package=“cn.cvs.controller”/> 对包进行扫描,将标注了 Spring MVC 注解的 Bean 注册到 Spring 容器中,并定义为可以处理请求的 Controller 控制器。

@Controller
public class HelloController{
    @RequestMapping(value = "/hello")
    public String hello() throws Exception{
        System.out.println("Spring MVC 框架搭建成功。");
        //跳转到 hello 页面
        return "hello";
    }
}

@Controller 注解用来将 HelloController 类注入 Spring 容器中,并将其定义为一个控制器。

@RequestMapping 注解则可以配置 hello() 方法对应的请求 URL 为 “/hello”

2.3、Spring MVC 框架梳理

在这里插入图片描述

Spring MVC 框架处理用户请求的具体流程:

  1. 客户端发送的请求被前端控制器(DispatcherServlet)捕获。
  2. 前端控制器调用处理器映射(Handler Mapping),对请求信息进行解析。
  3. 前端控制器根据上一步的解析结果,调用具体的 Controller,处理业务。
  4. 业务处理完成之后,返回一个 ModerAndView (模型数据和逻辑视图)。
  5. 前端控制器收回控制权,然后根据返回的逻辑视图名选择相应的视图,并把模型数据传入以便视图渲染展示。
  6. 前端控制器将渲染结果返回给客户端,流程结束。

整个框架通过一个前端控制器接收所有的请求,并将具体工作委托给其他组件进行处理。

前端控制器负责协调组织不同组件完成请求处理,并返回响应。

在这里插入图片描述

  1. 客户端发起请求后,由 Servlet 容器将请求包装为 HttpServletRequest 对象,然后 Servlet 容器通过反射创建 DispatcherServlet 对象,调用其 init 方法并传入 ServletConfig 对象(存储了 web.xml 文件定义的 init-param 参数)初始化 DispatcherServlet,然后调用其 service 方法并传入 HttpServletRequest 和 HttpServletResponse 进行服务。
  2. DispatcherServlet 接收到 Servlet 容器传来的 HttpServletRequestHttpServletResponse 后,会通过其 URL,找到其对应的 HandlerMapping
  3. 调用 HandlerMapping 实例的 getHandler 方法并传入 HttpServletRequest,获取 HandlerExecutionChain(包含一个Handler 对象和多个 HandlerInterceptor
  4. 执行 HandlerExecutionChain 包含的 HandlerInterceptor 中的前置处理方法。
  5. 根据 HandlerExecutionChain 包含的 Handler 找到其对应的 HandlerAdaptor
  6. 调用该 HandlerAdaptorhandler 方法并传入 HttpServletRequestHttpServletResponseHandler 对象,返回ModelAndView 对象。
  7. 如果 ModelAndView 指定的是视图名称而不是 View 实例(一般情况),那么调用 ViewResolve 的方法得到 View 对象。
  8. 调用 View 对象的 render并传入模型数据,渲染视图然后写入到 HttpServletResponse。请求处理完成。
2.3.1、Spring MVC 框架的总结
  1. 角色划分清晰。Spring MVC 框架在 Model、View 和 Controller 方面提供了一个非常清晰的角色划分,这三个方面各司其职、各负其责。
  2. 配置功能灵活。因为 Spring 框架的核心是 IoC,同样在实现 MVC 框架上,也可以把各种类当做 Bean 来通过 XML 进行配置。
  3. 大量的控制器接口和实现类。开发者可以使用 Spring 框架提供的控制器实现类,也可以自己实现控制器接口。
  4. 可灵活选择 View 层实现技术。Spring MVC 框架不会强制开发者使用 JSP,可以根据项目需求使用 Velocity、XSLT 等技术,使用更加灵活。
  5. 支持国际化。Spring MVC 框架提供国际化支持,可以轻松实现不同语言之间的切换。
  6. 面向接口编程。开发者可以方便地对 Spring MVC 框架提供的拦截器、数据格式转换器等工具进行扩展以满足业务需要。
  7. 功能丰富。Spring MVC 框架提供了 Web 应用开发的一整套流程,包含功能各异的技术工具。
2.4、前后端数据交互
2.4.1、@ReauestMapping 注解

负责把不同的请求映射到对应的控制器方法上。还可以添加到控制器类上。

@Controller
@RequestMapping(value = "/hello")
public class HelloController {
    private static final Logger log = Logger.getLogger(HelloController.class);
    @GetMapping(value = "/hello1")
    public String hello1() throws Exception { 
        log.debug("Spring MVC 框架搭建成功。");
        //跳转到 hello 页面
        return "hello";
    }
    @RequestMapping(value = "/hello2")
    public String hello2(HttpServletRequest req, HttpServletResponse resp) throws Exception { 
        log.info(new String("hello控制器下的另一个接口!"));
        //跳转到 hello 页面
        return "hello";
    }
}

注意

@RequestMapping 映射的请求信息必须保证全局唯一。不同控制器中,添加在方法上的 @RequestMapping 注解可以不同。

知识扩展

对于 @RequestMapping 的 value,通过查看源码可以发现它的类型是一个 String[],也就是说CVS_C09_03,可以写成如下格式:@RequestMapping({"/index","/"})。它的含义是请求的 URL 为 http://localhost:8080/index 或 http://localhost:8080/CVS_C08_03/,都可以进入该处理方法。

通常会使用 GET 类型请求访问查询接口,使用 POST 类型请求访问保存方法。@RequestMapping 注解就可以为接口设置访问类型

import org.springframework.web.bind.annotation.RequestMethod;

@Controller
@RequestMapping(value = "/user")
public class UserController {
    private Logger logger = Logger.getLogger(UserController.class);

    //查询用户详情 
    @RequestMapping(value = "/view", method = RequestMethod.GET)
    @GetMapping("/view")
    public String view(){
        logger.info("查询用户详情");
        return "hello";
    }

    @RequestMapping(value = "/save", method = RequestMethod.POST)
    @PostMapping("/save")
    public String save(){
        logger.info("保存用户信息");
        return "hello";
    }
}

通过 @RequestMapping 注解中的 method 属性可以为接口设置访问类型,对应得值是 Spring 定义得枚举 RequestMethod。

RequestMethod 中比较常用的类型有以下几种

  • GET:通常用于查询
  • POST:通常用于保存
  • PUT:通常用于修改
  • DELETE:通常用于删除

Spring MVC 框架还提供了 @GetMapping、@PostMapping 等注解实现类似功能。

注解中只有 value 一个参数时,属性名 “value” 是可以省略的,省略后简写为 @GetMapping("/view")。

2.5、入参处理

@RequestParam 注解,可以自动完成以上绝大部分工作。

@RequestMapping("/hello")
public String hello(@RequestParam String realName)throws Exception{
    log.info("你好" + realName + "欢迎来到 Spring MVC 课堂");
    return "hello";
}

@RequestParam 注解的使用方法非常简单,只要在控制器接口方法中定义希望传入的参数,并在该参数前添加注解即可。

测试

启动项目,测试接口,访问路径为 http://localhost:8080/CVS_C09_04/hello/hello?realName=诸葛亮,后台可正常获取参数。

当不传参数时,访问路径为 http://localhost:8080/CVS_C09_04/hello/hello,未提供必要参数的 报错 页面。

@RequestParam 注解提供了一些参数,比较常见的如下

  • name:参数名,同 value
  • value:参数名,同 name
  • required:是否必需,默认为 true,表示请求中必须包含对应的参数名。若不存在,将抛出异常
  • defaultValue:为参数赋默认值,如分页功能中的页码参数可以赋默认值为1。

默认情况下,被 @RequestParam 标注的参数是必传的。

@RequestParam 注解中的 name 和 value 两个参数用法相同,二者基本是等价的,用于接收参数,当前端传入的参数名与接口方法中的参数名不同时,可以使用 name 和 value 定义别名。

2.6、出参处理

View 层将会对返回的模型数据进行渲染并输入。

Spring MVC 框架提供了多种方式输出模型数据

  • 使用 ModelAndView 对象
  • 使用 Model 对象
  • 使用 Map 对象
2.6.1、ModelAndView

控制器处理方法的返回值若为 ModelAndView , 其中既可以包含视图信息,又可以包含模型数据信息。

@Controller
@RequestMapping(value = "/hello")
public class HelloController {
    private static final Logger log = Logger.getLogger(HelloController.class);@RequestMapping("/hello")
    public ModelAndView hello(@RequestParam String realName) throws Exception {
        log.info("你好【" + realName + "】欢迎来到 Spring MVC 课堂。");
        ModelAndView modelAndView = new ModelAndView();
        modelAndView.addObject("realName", realName);
        modelAndView.setViewName("hello");
        return modelAndView;
    }
}
  • 将 “/hello” 接口的返回值改为 ModelAndView 类型,在代码中创建了一个 ModelAndView 对象
  • 将 realName 添加到该对象
  • 使用 setViewName() 方法设置逻辑视图名最终返回、

ModelAndView 常用的方法

  1. 添加模型数据
  • ModelAndView addObject(String attributeName, Object attribute Value)

该方法的第一个参数为 key 值,第二个参数为 key 对应的 value。

  • ModelAndView addAllObject(Map<String,?> modelMap)

模型数据也是一个 Map 对象,可以添加 Map 对象到 Model 中

2 . 设置视图

  • void setView(View view):指定一个具体的视图对象。
  • void setViewName(String viewName):指定一个逻辑视图名。
2.6.2、Model

可以通过一个 Model 类型的入参对象访问到模型中的所有数据,当然也可以向模型中添加新的属性数据,

@RequestMapping("/hello2")
public String  hello(Model model, @RequestParam String realName) throws Exception {
    log.info("你好【" + realName + "】欢迎来到 Spring MVC 课堂。");
    model.addAttribute("realName", realName);
    return "/hello";
}

Model 对象也是一个 Map 类型的数据结构,并且对于 key 值的指定,其实并不是必需的

@RequestMapping("/hello2")
public String  hello(Model model, @RequestParam String realName) throws Exception {
    log.info("你好【" + realName + "】欢迎来到 Spring MVC 课堂。");
    model.addAttribute("realName", realName);
    model.addAttribute(realName);
    return "/hello";
}

realName(key:string) --> ${string} < /h1 >

realName 是 String 类型,那么可以以 “string” 为 key 从 Model 中获取数据。

2.6.3、Map

Spring MVC 框架的 Model 就像是一个 Map 的数据结构,起始使用 Map 作为处理方法入参,完成可行的。

 @RequestMapping("/hello2")
 public String hello(Map<String, Object> map, @RequestParam String realName) throws Exception {
     log.info("你好【" + realName + "】欢迎来到 Spring MVC 课堂。");
     map.put("realName", realName);
     return "hello";
 }

Spring MVC 控制器的处理方法中,若有 Map 或 Model 类型的入参,会将请求内的隐含模型对象传递给这些入参。推荐使用 Model。

Spring MVC 框架除使用以上三种方式向 View 层传递数据外,还可使用 @ModelAttribute 和 @SessionAttributes 注解

  • @ModelAttribute : 若希望将入参的数据对象放入数据模型中去,就需要在相应入参前使用 @ModelAttribute
  • @SessionAttributes:可以将模型中的属性存入 HttpSession 中,以便在多个请求之间共享该属性。
`@ModelAttribute`注解主要用来将请求转换为使用此注解指定的对象。例如,如果在`@ModelAttribute`旁边指定了一个`Article`实例,则与`Article`的字段对应的所有请求参数将被用作`Article`的字段值。什么意思呢,例如,`POST提交`后参数`title`的值将被设置为`Article`的`title` 字段

因此,此注解允许开发人员通过请求来持久化一个对象。没有它,Spring认为必须创建一个新对象。另外,它直接显示一个对象模型来查看。你不需要在方法中再调用model.setAttribute()。在视图部分,可以通过注解中的指定值查找指定对象(例如,@ModelAttribute(“articleView”)可以在jsp中通过`${articleView}`获取相应的值)或对象的类名称(例如`@ModelAttribute()Article article`将在视图层获取方式就是`${article}`)。
2.7、视图解析器

Spring MVC 框架在完成请求处理工作之后,会返回一个 ModelAndView 对象,其中包含视图逻辑名和数据模型。

那么返回值是 String 或其他类型的接口,Spring MVC 框架也会在内部将它们装配成一个 ModelAndView 对象,这需要借助视图解析器实现。

ViewResolver 是 Spring MVC 框架处理视图的重要接口,通过它可以将控制器返回的逻辑视图名解析技术的视图对象。

3、单例模式

单例模式是一种比较基础的设计模式,关键就是在系统运行期间,某个类有且只有一个实例。

这种设计模式的最大优点在于可以对资源进行重复利用,节约重复创建和销毁的成本,从而降低服务器压力,提高程序效率。

3.1、一个类只有一个实例

要满足这个关键点,就不能允许外界随意创建该类的实例,可以通过私有化构造方法达到这一目的。

public class ConfigManager { 
    private static ConfigManager configManager = null;
    private static Properties properties; 
    /**
     * 杜绝外界创建该类对象,私有构造方法
     */
    private ConfigManager(){
        String configFile = "database.properties";
        properties = new Properties();
        InputStream is = ConfigManager.class.getClassLoader().getResourceAsStream(configFile);
        try {
            properties.load(is);
            is.close();
        }catch (IOException e){
            e.printStackTrace();
        }
    }
}
3.2、对外提供实例对象

对外提供实例对象的方法应该是静态的且被 public 修饰的,该方法创建或获取本类中的静态私有对象并返回。

public class ConfigManager {
     …………
	 public static synchronized ConfigManager getInstance(){
         if (configManager != null) {
             configManager = new ConfigManager();
         }
     	return configManager;
 	 }
}
3.3、读取配置文件中的具体数据
public String getValue(String key){
    return properties.getProperty(key);
}

4、懒汉模式和饿汉模式

单例模式的实现方式称为懒汉模式。

懒汉模式是懒加载的意思,即在类加载时不创建其实例,而是被调用时再创建实例。这种方式的单例模式是非线程安全的,在高并发环境下存在一个严重的问题,可能会产生多个 ConfigManager 实例。

饿汉模式,可以理解为当前应用程序很“饥饿”,需要立即获取到类的实例对象,即在类加载的时候就完成了实例的创建工作。

因此类加载速度相对懒汉模式较慢,不过第一次获取实例的速度很快。

因为饿汉模式是在类初始化时就已经自行实例化,所有不存在线程安全问题。

对两种单例模式做简单对比

  • 懒汉模式:在类加载暂不创建实例,因此类加载速度快,但是运行时获取对象的速度相对较慢,具备延迟加载的特性,但是存在线程安全的问题。
  • 饿汉模式:在类加载时就完成了初始化,所以类加载较慢,但获取对象速度快。

懒汉模式是 ”时间换空间“、饿汉模式是 ”空间换时间“

知识扩展

如果要求饿汉模式同时具备延迟加载的特性,我们可以使用静态内部类的方式进行相应的改造实现。

public class Single {
    private static Single single;
    private Single(){}

    //静态内部类
    public static class SingleHelper{
        private static final Single INSTANCE = new Single();
    }
    public static Single getInstance(){
        single = SingleHelper.INSTANCE;
        return single;
    }
    public static Single test(){
        return single;
    }
}

测试

public class TestSingle {
    public static void main(String[] args) {
        Single single = Single.test();
        System.out.println("调用 Single.test() 方法获取实例对象:" + single);
        single = Single.getInstance();
        System.out.println("第一次调用 Single.test() 方法获取实例对象:" + single);
        single = Single.test();
        System.out.println("第二次调用 Single.test() 方法获取实例对象:" + single);
    }
}

5、乱码

当在页面输入 ”测试“ 关键字进行查询时,关键字进行查询时,控制台日志显示乱码

解决

在 web.xml 中增加 Spring 字符编码过滤器并配置字符编码为 utf-8 的方式解决

<filter>
    <filter-name>encodingFilter</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>encodingFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

6、搭建项目运行环境

  • 添加框架整合所依赖的 jar 包

  • 拆分 Spring 配置文件

  • <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:context="http://www.springframework.org/schema/context"
           xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans.xsd
           http://www.springframework.org/schema/context
           https://www.springframework.org/schema/context/spring-context.xsd">
    
        <!--扫描 Service、Dao 包,将添加了 @Service @Component @Repository 等这些注解的类注册到 Spring 容器-->
        <context:component-scan base-package="cn.cvs.service cn.cvs.dao"/>
    </beans>
    

    <context:component-scan /> 标签的 base-package 属性配置需要被 Spring 扫描的包,Spring 会扫描其下的所有类,若扫描到标注有 @Component、@Service、@Repository 等注解的类,会自动把这些类注册到 Spring 容器中。

  • 配置 web.xml 文件

  • <!--配置环境参数,指定 Spring 配置文件所在目录-->
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:applicationContext-*.xml</param-value>
    </context-param>
    <!--初始化 Spring 容器的 ContextLoaderListener 监听器-->
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
    

applicationContext-*.xml 中的 ” * “ 是通配符,表示加载 classpath 下所有以 ”applicationContext0-“ 开头的 XML 文件。

如果没有在 web.xml 中指定 contextConfigLocation 参数,contextConfigLocation 会默认去查找 /WEB-INF/applicationContext.xml。

静态资源文件的引用

<mvc:resources /> 标签可以解决,web.xml 中配置的 DispatcherServlet 请求映射为 ”/" ,所以 Spring MVC 框架将捕获对 Web 容器的所有请求,包括对静态资源的请求。

<mvc:resources mapping="/statics/**" location="statics"/>
<mvc:default-servlet-handler />

mapping 属性表示将静态资源映射到指定的路径下。location 属性表示本地静态资源文件所在的目录。

7、异常处理

使用 Spring MVC 框架提供的 HandlerExceptionResolver 进行处理,包括处理器异常、数据绑定异常及处理器执行时发生的异常。

当出现异常时,Spring MVC 框架会调用此方法,并转到 ModelAndView 对应的视图中,作为一个异常报告页面反馈给用户。

分为 局部异常处理全局异常处理

7.1、局部异常处理

@ExceptionHandler 注解实现,作用范围是当前 Controller。

@ExceptionHandler(value = {RuntimeException.class})
public String handlerException(RuntimeException e, HttpServletRequest req) {
    req.setAttribute("exception", e);
    return "login";
}

@ExceptionHandler 注解可以定义多个异常。

抛出异常的接口

@RequestMapping("/exLogin")
public String exLogin(@RequestParam String  account, @RequestParam String password){
    SysUser user = sysUserService.login(account, password);
    if (user == null) {
        throw new RuntimeException("用户名或者密码不正确!");
    }
    return "redirect:/user/toMain";
}
7.2、全局异常处理

将应用程序中抛出的所有异常进行统一捕获处理的一种机制。

在 Spring MVC 框架中可使用 SimpleMappingExceptionResolver 来实现,它将异常类名映射为视图名,即发生异常时使用对应的视图报告异常。

<!--全局异常处理-->
<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
    <property name="exceptionMappings">
        <props>
            <prop key="java.lang.RuntimeException">login</prop>
        </props>
    </property>
</bean>

当控制器发生 RuntimeException 异常时,使用 login 视图进行异常信息显示。可以在 props 标签内自定义多个异常。

Spring MVC 框架是无法自动对时间类型数据进行绑定,可以选择使用 @DateTimeFormat 注解来解决。

@Data
public class SysUser implements Serializable {
    @DateTimeFormat(pattern = "yyyy-MM-dd")
	private Date birthday; //出生日期     ---   年-月-日
}

添加注解后,Spring MVC 框架便可以将日期格式的字符串数据自动注入对应的属性中。

8、REST 风格

表述状态转移,是一种流行的软件构架规范。

使用传统 URL 和 REST 风格 URL 的对比

/user/view?id=2VS/user/view/2
/user/delete?id=2VS/user/delete/2
/user/update?id=2VS/user/update/2

@PathVariable 注解可以将 URL 中的 {id} 占位符参数绑定到控制器处理方法的入参中。

//查询用户详情
@GetMapping("/view/{id}")
public String view(@PathVariable String id, Model model){
    logger.debug("根据 id 查询用户详情" + id);
    SysUser userById = sysUserService.getUserById(Integer.parseInt(id));
    model.addAttribute(userById);
    return "sysUser/view";
}

9、Spring 表单标签

添加以下标签库

<%@ taglib prefix="fm" uri="http://www.springframework.org/tags/form" %>

示例

<fm:form method="post"  modelAttribute="sysUser"> 
    用户账户:<fm:input path="account"/> <br> 
    用户名称:<fm:input path="realName"/> <br> 
    用户密码:<fm:password path="password"/> <br> 
    用户生日:<fm:input path="birthday" Class="Wdate" readonly="readonly" onclick="WdatePicker()" class="Wdate"/> <br> 
    用户地址:<fm:input path="address"/> <br> 
    用户性别:
    <fm:radiobutton path="sex" checked="checked" value="1"/><fm:radiobutton path="sex" value="2"/><br> 
    联系电话:<fm:input path="phone"/> <br> 
    用户角色:
    <fm:radiobutton path="roleId" value="1" checked="checked"/> 系统管理员
    <fm:radiobutton path="roleId" value="2"/> 经理
    <fm:radiobutton path="roleId" value="3" /> 普通用户 <br> 
    <div>
        <input type="hidden" id="errorinfo" value="${uploadFileError}"/>
        <label style="width: auto" for="idPic">证件照</label>
        <input type="file" name="idPic" id="idPic"/>
        <font color="red"></font>
    </div> 
    <input type="submit" value="保存">
</fm:form>

标签的 modelAttribute 属性用来指定绑定的模型属性。

若不指定该属性,那么会默认从模型中尝试获取名为 " command " 的表单对象。若不存在此表单对象,将会报错。

标签名称说明
form渲染表单元素
input渲染 元素
password渲染 元素
hidden渲染 元素
textarea渲染 textarea 元素
checkbox渲染一个 元素
radiobutton渲染一个 元素
select渲染一个选择元素
option渲染一个选项元素
error在 span 元素中渲染字段错误

以上标签都有以下属性

  • path:属性路径,表示表单对象属性,如 account、realName 等
  • cssClass:表单组件对应的 CSS 样式类名
  • cssErrorClass:当提交表单后报错(服务端错误),采用 CSS 样式类
  • cssStyle:表单组件对应的 CSS 样式
  • htmlEscape:绑定的表单属性值是否要对 HTML 特殊字符进行转换,默认为 true
@RequestMapping(value="/add",method=RequestMethod.GET)
  public String toAdd(@ModelAttribute("sysUser") SysUser sysUser){
    return "sysUser/add";
}
//查询用户详情
@GetMapping("/view/{id}")
public String view(@PathVariable String id, Model model){
    logger.debug("根据 id 查询用户详情" + id);
    SysUser userById = sysUserService.getUserById(Integer.parseInt(id));
    model.addAttribute(userById);
    return "sysUser/view";
}

在接口参数上添加 @ModelAttribute 注解,可以把该参数添加到 Model 模型中,如果不添加这个注解则需要手动将数据添加到 Model 模型中。

@RequestMapping(value="/add",method=RequestMethod.GET)
  public String toAdd(SysUser sysUser, Model model){
	model.addAttribute("sysUser",sysUser);
    return "sysUser/add";
}
9.1、数据效验

使用 Spring MVC 框架时,有两种常用的方式效验输入的数据。

  1. 利用 Spring 框架自带的验证框架。
  2. 利用 JSR 303 实现。
约束说明
@Null被注释的元素必须为 null
@NotNull被注释的元素必须不为 null
@AssertTrue被注释的元素必须为 true
@AssertFalse被注释的元素必须为 false
@Min(value)被注释的元素必须是一个数字,其值必须大于等于指定的最小值
@Max(value)被注释的元素必须是一个数字,其值必须小于等于指定的最大值
@DecimalMin(value)被注释的元素必须是一个数字,其值必须大于等于指定的最小值
@DecimalMax(value)被注释的元素必须是一个数字,其值必须小于等于指定的最大值
@Size(max, min)被注释的元素的大小必须在指定的范围内
@Digits (integer, fraction)被注释的元素必须是一个数字,其值必须在可接受的范围内
@Past被注释的元素必须是一个过去的日期
@Futuret被注释的元素必须是一个将来的日期
@Pattern(value)被注释的元素必须符合指定的正则表达式
import lombok.Data;
import org.hibernate.validator.constraints.Length;
import org.springframework.format.annotation.DateTimeFormat;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.Past;
import java.io.Serializable;
import java.util.Date;

@Data
public class SysUser implements Serializable {
    @NotBlank(message="用户编码不能为空")
    private String account; //账户--登录时使用
    @Length(min = 6, max = 10, message = "用户密码长度为6-10")
    private String password;//用户密码
    @NotBlank(message = "用户名不能为空")
    private String realName;//用户名称
    @Past(message = "必须是一个过去的时间")
    @DateTimeFormat(pattern = "yyyy-MM-dd")
    private Date birthday; //出生日期     ---   年-月-
}

控制层

通过在 Controller 处理方法的入参上标注 @Valid 注解即可让 Spring MVC 在完成数据绑定之后,执行效验工作。

@RequestMapping(value="/add",method=RequestMethod.POST)
public String add(@Valid SysUser sysUser, BindingResult bindingResult, HttpSession session){
    System.out.println(sysUser);
    if (bindingResult.hasErrors()) {
        logger.debug("添加用户验证失败");
        return "sysUser/add";
    }
    SysUser attribute = (SysUser) session.getAttribute(Constants.USER_SESSION);
    sysUser.setCreatedUserId(attribute.getCreatedUserId());
    if(sysUserService.add(sysUser))
        return "redirect:/user/list";
    return "sysUser/add";
}

效验的结果会存入后面紧跟的入参对象中,这个入参对象必须是 BindingResult 或 Error类型。

在该方法体内,首先根据 BindingResult 来判断是否通过效验。若不通过,则跳转到用户添加页面,若通过,则继续保存新增的用户信息。

注意

@Valid 注解标识的参数后面,必须紧挨着一个 BindingResult 参数,否则 Spring 会在效验不通过时直接抛出异常。
<fm:form method="post"  modelAttribute="sysUser">
    <fm:errors path="account"/> <br>
    用户账户:<fm:input path="account"/> <br>
    <fm:errors path="realName"/> <br>
    用户名称:<fm:input path="realName"/> <br>
    <fm:errors path="password"/> <br>
    用户密码:<fm:password path="password"/> <br>
    <fm:errors path="birthday"/> <br>
    用户生日:<fm:input path="birthday" Class="Wdate" readonly="readonly" onclick="WdatePicker()" class="Wdate"/> <br>
    <fm:errors path="address"/> <br>
    用户地址:<fm:input path="address"/> <br>
    <fm:errors path="sex"/> <br>
    用户性别:
    <fm:radiobutton path="sex" checked="checked" value="1"/><fm:radiobutton path="sex" value="2"/><br>
    <fm:errors path="phone"/> <br>
    联系电话:<fm:input path="phone"/> <br>
    <fm:errors path="roleId"/> <br>
    用户角色:
    <fm:radiobutton path="roleId" value="1" checked="checked"/> 系统管理员
    <fm:radiobutton path="roleId" value="2"/> 经理
    <fm:radiobutton path="roleId" value="3" /> 普通用户 <br>

    <div>
        <input type="hidden" id="errorinfo" value="${uploadFileError}"/>
        <label style="width: auto" for="idPic">证件照</label>
        <input type="file" name="idPic" id="idPic"/>
        <font color="red"></font>
    </div> 
    <input type="submit" value="保存">
</fm:form>

通过 <fm:error /> 标签在可以将错误信息显示在 JSP 页面的指定位置。

10、Spring MVC 框架的文件上传功能

MultipartResolver 接口提供了对文件上传功能的直接支持,可以将上传请求包装成可以直接获取的文件数据

有以下两个实现类

  1. StandardServletMultipartResolver
  2. CommonsMultipartResolver

步骤

  • 导入依赖 jar 包

  • commons-io-2.4.jar     commons-fileupload-1.2.2.jar
    
  • 配置 MultipartResolver 解析器

  • <!--配置 MultipartResolver解析器,用于上传文件,使用 Spring 的 CommonsMultipartResolver-->
    <bean class="org.springframework.web.multipart.commons.CommonsMultipartResolver" id="multipartResolver">
        <property name="maxUploadSize" value="500000"/>
        <property name="defaultEncoding" value="UTF-8"/>
    </bean>
    

    maxUploadSize :上传文件的大小上限,单位为字节

    defaultEncoding : 请求的编码格式,默认为 ISO-8859-1,此处设置为 UTF-8

    注意

    处理文件上传的请求方式一定是 POST 请求,GET 请求无法处理文件上传。

    MultipartFile 对象可以接收上传的文件数据

    • getSize():获取文件大小(单位:字节)
    • getName():获取对应的参数名称
    • getOriginalFilename():获取上传文件的原始名称
    • isEmpty():判断上传文件是否为空
    • transferTo(File):将文件存储到指定位置
    • getContentType():获取上传文件的内容类型
    • getInputStream():获取一个输入流读取文件内容
@PostMapping("/add")
public String add(SysUser sysUser, HttpSession sessione, HttpServletRequest request, @RequestParam(value = "idPic", required = false)MultipartFile idPic){
    String idPicPath = null;
    //判断文件是否为空
    if(!idPic.isEmpty()){
        String path = request.getSession().getServletContext().getRealPath("statics" + File.separator + "uploadfiles");
        logger.info("上传文件路径" + path);
        String originalFile = idPic.getOriginalFilename();  //原文件名
        logger.info("原文件名为:" + originalFile);
        String prefix = FilenameUtils.getExtension(originalFile);  //原文件后缀
        logger.debug("原文件后缀为:" + prefix);
        int filesize = 500000;
        logger.debug("文件大小:" + idPic.getSize());
        if(idPic.getSize() > filesize){ //上传大小不得超过 500 kb
            request.setAttribute("uploadFileError", "* 上传大小不得超过 500kb");
            return "sysUser/add";
        }else if(prefix.equalsIgnoreCase("jpg") || prefix.equalsIgnoreCase("png") || prefix.equalsIgnoreCase("jpeg")
        || prefix.equalsIgnoreCase("pneg")){ //上传图片格式正确
            String fileName = System.currentTimeMillis() + RandomUtils.nextInt(1000000) + "_Personal." + prefix;
            logger.debug("新生成的文件名称为:" + fileName);
            File targetFile = new File(path);
            if(!targetFile.exists()){
                targetFile.mkdirs();
            }
            //保存
            try {
                idPic.transferTo(new File(targetFile, fileName));
            }catch (Exception e){
                e.printStackTrace();
                request.setAttribute("uploadFileError", "* 上传失败!");
                return "sysUser/add";
            }
            idPicPath = File.separator + "statics" + File.separator + "iploadfiles" + File.separator +fileName;
        }else {
            request.setAttribute("uploadFileError", " * 上传图片格式不正确");
            return "sysUser/add";
        }
    }
    //获取当前的用户
    SysUser attribute = (SysUser) sessione.getAttribute(Constants.USER_SESSION);
    //赋值用户的id
    sysUser.setCreatedUserId(attribute.getCreatedUserId());
    //赋值图片路径
    sysUser.setIdPicPath(idPicPath);
    if(sysUserService.add(sysUser))
        return "redirect:/user/list";
    return "sysUser/add";
}
10.1、文件上传共包括三部分代码
10.1.1、文件重命名

本系统的规则为当前系统时间 + 随机数 + “_Personal.” + prefix

10.1.2、文件存储
try {
    idPic.transferTo(new File(targetFile, fileName));
}catch (Exception e){
    e.printStackTrace();
    request.setAttribute("uploadFileError", "* 上传失败!");
    return "sysUser/add";
}
10.1.3、数据存储

文件上传成功后,还需要设置 sysUser 对象的 idPicPath 属性,记录上传文件的路径,最后进行调用业务层的 add() 方法保存数据

10.2、多文件上传
@PostMapping(value = "/add")
public String add(SysUser sysUser, HttpSession session, HttpServletRequest request, 
                  @RequestParam(value = "attachs", required = false)MultipartFile[] attachs){
    String idPicPath = null;
    String workPicPath = null;
    String errorInfo = null;
    Boolean flag = true;
    String  path = request.getSession().getServletContext().getRealPath("statics" + File.separator + "uploadfiles");
    logger.info("文件存储路径" + path);
    for (int i = 0; i < attachs.length; i++) {
        MultipartFile attach = attachs[i];
        if(!attach.isEmpty()){
            errorInfo = i == 0 ? "uploadFileError" : "uploadWpError";
            String oldFileName = attach.getOriginalFilename();  //原文件
            logger.info("原文件名:" + oldFileName);
            String prefix = FilenameUtils.getExtension(oldFileName);  //原文件后缀
            int filesize = 500000;
            if(attach.getSize() > filesize){
                request.setAttribute(errorInfo, " * 上传大小不得超过 500 kb");
                flag = false;
            }else if(prefix.equalsIgnoreCase("jpg")
                     || prefix.equalsIgnoreCase("png")
                     || prefix.equalsIgnoreCase("jpeg")
                     || prefix.equalsIgnoreCase("pneg")){
                String fileName = System.currentTimeMillis() + RandomUtils.nextInt(1000000) + "_Personal." + prefix;
                File targetFile = new File(path);
                try {
                    attach.transferTo(new File(targetFile, fileName));
                }catch (Exception e){
                    e.printStackTrace();
                    request.setAttribute(errorInfo, " * 上传失败!");
                    flag = false;
                }
                if(i == 0){
                    idPicPath = File.separator + "statics" + File.separator + "uploadfiles" + File.separator + fileName;
                }else
                    workPicPath = File.separator + "statics" + File.separator + "uploadfiles" + File.separator + fileName;
            }else {
                request.setAttribute(errorInfo, " * 上传图片格式不正确");
                flag = false;
            }
        }
    }
    if(flag){
        //获取当前的用户
        SysUser attribute = (SysUser) session.getAttribute(Constants.USER_SESSION);
        //赋值用户的id
        sysUser.setCreatedUserId(attribute.getCreatedUserId());
        //赋值图片路径
        sysUser.setIdPicPath(idPicPath);
        sysUser.setWorkPicPath(workPicPath);
        if(sysUserService.add(sysUser))
            return "redirect:/user/list";
    }
    return "sysUser/add";
}

MultipartFile 数组接收前端传入的多个文件数据,

11、Spring MVC 框架处理 JSON 数据

@ResponseBody("/userExist") 注解,将当前接口返回的数据直接写入 HTTP Response Body(Response 对象的 body 数据区)中

以下是处理 JSON 类型数据的几个常用工具

1、json-lib 工具

2、jackson 工具

3、Gson 工具

4、FastJson 工具

11.1、jSON 数据传递过程中的中文乱码和日期问题
11.1.1、JSON 中文乱码问题
解决方法
  • 方式一:在控制器方法上设置编码格式

  • @GetMapping(value = "/{id}/view", produces = {"application/json;charset=UTF-8",
                                                  "text/html;charset=UTF-8",
                                                  "application/xml;charset=UTF-8"})
    @ResponseBody
    public Object view(@PathVariable String id) {
        logger.debug("根据 id 查询用户详情id:" + id);
        SysUser sysUser = null;
        try {
            sysUser = sysUserService.getUserById(Integer.parseInt(id));
            logger.debug("查询到用户信息:" + sysUser);
        } catch (Exception e) {
            e.printStackTrace();
            return "failed";
        }
        return sysUser;
    }
    
  • 方式二:装配消息转换器 StringHttpMessageConverter

  • <mvc:annotation-driven>
        <mvc:message-converters>
               <bean class="org.springframework.http.converter.StringHttpMessageConverter">
                   <property name="supportedMediaTypes">
                       <list>
                           <value>application/json;charset=UTF-8</value>
                       </list>
                   </property>
               </bean>
        </mvc:message-converters>
    </mvc:annotation-driven>
    

    StringHttpMessageConverter 消息转换器的媒体类型设置为 application/json,字符编码设置为 UTF-8

11.1.2、JSON 日期格式问题
解决方法
  • 方式一:注解方式

  • @JSONField(format = "yyyy-MM-dd")
    private Date birthday; //出生日期     ---   年-月-日
    private String address; //地址
    
  • 方式二:配置消息转换器

  • <mvc:annotation-driven>
        <mvc:message-converters>
            <bean class="org.springframework.http.converter.StringHttpMessageConverter">
                <property name="supportedMediaTypes">
                    <list>
                        <value>application/json;charset=UTF-8</value>
                    </list>
                </property>
            </bean>
            <bean class="com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter">
                <property name="supportedMediaTypes">
                    <list>
                        <value>text/html;charset=UTF-8</value>
                        <value>application/json</value>
                    </list>
                </property>
                <property name="features">
                    <list>
                        <!--Date 的日期转换器-->
                        <value>WriteDateUseDateFormat</value>
                    </list>
                </property>
            </bean>
        </mvc:message-converters>
    </mvc:annotation-driven>
    

    FastJsonHttpMessageConverter 消息转换器的 features 属性指定输出时的日期转换器 WriteDateUseDateFormat ,可以按照 FastJson 默认的日期格式进行转换输出。

12、多视图解析器

将 Spring MVC 框架对 Controller 返回的逻辑视图名解析为真正的物理视图名,然后进行渲染,将最终的结果返回给用户。

多视图解析管理器

<bean class="org.springframework.web.accept.ContentNegotiationManagerFactoryBean"
      id="contentNegotiationManager">
    <!--是否启动 format 参数支持,默认为 true-->
    <property name="favorParameter" value="true"/>
    <!--是否支持.html、.xml 等扩展名,默认为 true-->
    <property name="favorPathExtension" value="true"/>
    <!--默认 ContentType-->
    <property name="defaultContentType" value="text/html"/>
    <!--配置映射关系-->
    <property name="mediaTypes">
        <value>
            json=application/json
            xml=application/xml
            html=text/html
        </value> 
    </property>
</bean>

内容协商视图解析器

<bean class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver">
    <!--内容协商管理器 用于决定 media type-->
    <property name="contentNegotiationManager" ref="contentNegotiationManager"/>
    <!--默认视图-->
    <property name="defaultViews">
        <list>
            <bean class="com.alibaba.fastjson.support.spring.FastJsonJsonView">
                <property name="charset" value="UTF-8"/>
            </bean>
        </list>
    </property>
    <property name="viewResolvers">
        <list>
            <!--完成视图的对应-->
            <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
                <property name="prefix" value="/WEB-INF/jsp/"/>
                <property name="suffix" value=".jsp"/>
            </bean>
        </list>
    </property>
</bean>

多视图解析规则,需要配置以下属性

  • favorParameter:表示支持参数匹配,可以根据请求参数的值确定 MIME 类型,默认的请求参数为 format,默认值为 true。
  • favorPathExtension:表示是否支持扩展名,扩展名指 × × ×.json、× × ×.xml 等形式,默认为 true。
  • defaultContentType:配置默认的 ContentType 类型
  • mediaTypes:根据请求参数的 MIME 类型决定接口返回数据的展示类型,包括 json=application/json、xml=application/xml、html=text/html等。

ContentNegotiatingViewResolver 中主要配置以下三项内容

  • contentNegotiationManager :注入自定义的多视图解析管理器,将自定义的多视图解析管理器 contentNegotiationManager 注入多视图解析器中。
  • defaultViews:指定默认视图
  • viewResolvers:设置视图解析器。

13、Spring MVC 框架中的数据格式转换

五个步骤

  1. Spring MVC 框架将 ServletRequest 对象传递给 DataBinder
  2. Spring MVC 框架将处理方法的入参对象传递给 DataBinder
  3. DataBinder 调用 ConversionService 组件进行数据类型转换、数据格式化等工作,并将 ServletRequest 对象中的消息填充到参数对象中
  4. 调用 Validator 组件对已经绑定了请求消息数据的参数对象进行数据合法性效验
  5. 效验完成后会生成数据绑定结果 BindingResult 对象,Spring MVC 框架会将 BindingResult 对象中的内容赋给处理方法的相应参数

关键组件讲解

  • DataBinder:数据绑定的核心部件,它在整个流程中起到核心调度的作用
  • Validator:用于数据效验
  • BindingResult:包含已完成数据绑定的入参对象和相应的效验错误对象,Spring MVC 框架会抽取 BindingResult 中的入参对象效验错误对象,将它们赋给处理方法的相应入参。
  • ConversionService:Spring 类型转换体系的核心接口。
13.1、编写自定义转换器

Spring 在 org.springframework.core.convert.converter 包中定义了最简单的 Converter 转换器接口,它仅包括一个接口方法 convert()

Converter 转换器的作用是把数据从一种类型转换成另一种类型。

创建数据格式转换器的步骤如下

步骤1:创建自定义数据格式转换器

/**
 * 自定义的字符串转日期转换器
 */
public class String2DateConverter implements Converter<String, Date> {

    private Logger logger = Logger.getLogger(String2DateConverter.class);

    private String datePatter;

    /**
     * 一个日期格式参数的构造函数
     * @param datePattern 日期格式
     */
    public String2DateConverter(String datePattern){
        logger.info("加载 String2DateConverter");
        this.datePatter = datePattern;
    }
    /**
     * 具体的字符串转日期功能方法
     * @param s
     * @return
     */
    @Override
    public Date convert(String s) {
        Date date = null;
        try {
            date = new SimpleDateFormat(datePatter).parse(s);
            logger.info("String2DateConverter convert date:" + date);
        }catch (ParseException e){
            logger.error("日期转换失败:" + s);
            e.printStackTrace();
        }
        return date;
    }
}

步骤2:装配自定义的 ConversionService

<mvc:annotation-driven conversion-service="myConversionService" >
<bean class="org.springframework.context.support.ConversionServiceFactoryBean" id="myConversionService">
    <property name="converters">
        <list>
            <bean class="cn.cvs.web.converter.String2DateConverter">
                <constructor-arg type="java.lang.String" value="yyyy-MM-dd"/>
            </bean>
        </list>
    </property>
</bean>
13.2、使用 @InitBinder 装配自定义编辑器

在控制器中抽象出一个父类 BaseController.java,在该类中使用 @InitBinder 注解实现日期类型转换功能

public class BaseController {
    private Logger logger = Logger.getLogger(BaseController.class);
    /**
     * 使用 @InitBinder 解决 SpringMVC 日期类型无法绑定的问题
     */
    @InitBinder
    public void initBinder(WebDataBinder dataBinder){
        logger.info("进入 BaseController 的 initBinder 方法");
        dataBinder.registerCustomEditor(Date.class,
                                        new CustomDateEditor(new SimpleDateFormat("yyyy-MM-dd"),true)  );
    }
}

@InitBinder用于在@Controller中标注于方法上,表示为当前控制器注册一个属性编辑器,只对当前的Controller有效。@InitBinder标注的方法必须有一个参数WebDataBinder。所谓的属性编辑器可以理解就是帮助我们完成参数绑定。

其他控制器都继承 BaseController 类

@Controller
@RequestMapping("/user")
public class SysUserController extends BaseController {
    private Logger logger = Logger.getLogger(SysUserController.class);

    @Resource
    private SysUserService sysUserService;

标注 @InitBinder 注解的方法会在控制器初始化时被调用。

initBinder() 方法内,通过 dataBinder 的registerCustomEditor() 方法注册一个自定义编辑器。

它的第一参数表示编辑器为日期类型、第二个参数则表示使用自定义的日期编辑器,时间格式为 yyyy-MM-dd,第三个参数为是否允许为空。

14、拦截器

拦截器 HandlerInterceptor 是一个接口,其中包含三个方法

  • preHandle():前置处理方法。
  • postHandle():后置处理方法。
  • afterCompletion():后置处理方法。在响应已经被渲染之后执行该方法,可用于释放资源。

创建拦截器

public class SysInterceptor extends HandlerInterceptorAdapter {
    private Logger logger = Logger.getLogger(SysInterceptor.class);

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        logger.debug("进入自定义拦截器==>SysInterceptor");
        return true;
    }
}

springmvc-servlet.xml 文件中配置拦截器

<!--配置 interceptors-->
<mvc:interceptors>
    <mvc:interceptor>
        <mvc:mapping path="/user/sys/**"/> 
        <bean class="cn.cvs.web.interceptor.SysInterceptor"/>
    </mvc:interceptor>
</mvc:interceptors>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值