1、MVC模式
MVC 模式是指 Model-View-Controller(模型-视图-控制器),是开发 Web 应用程序时常用的一种代码分层方式。
Model - View - Controller 三个基本部分
-
视图(View):负责格式化数据并把它们呈现给用户,包括数据展示、数据验证、界面设计等功能。对应组件:JSP 或 HTML 文件。
-
控制器(Controller):负责接收并转发请求,对请求进行处理后指派视图并将响应结果发送给客户端。对应组件:Servlet。
-
模型(Model):模型对象拥有最多的处理任务,是应用程序的主体部分,它负责业务逻辑的处理和实现对数据的操作。对应组件:JavaBean。
分析出 MVC 整体的处理过程如下:
- 视图提供系统与用户交互的界面,并发送用户输入的请求给控制器。
- 控制器接收用户的请求,并决定应该调用的模型。
- 模型根据用户请求进行相应的业务逻辑处理,并返回处理结果(数据)。
- 控制器根据返回的处理结果,调用相应的视图格式化模型返回的数据,并通过视图呈现结果给用户。
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>
- 定义一个名为 springmvc 的 Servlet, 指向 DispatcherServlet。DispatcherServlet 是 Spring MVC 框架的核心,被成为前端控制器
- 在载入当前 Servlet 时,使用 init-param 标签,加载创建的配置文件
- 设置 load-on-startup 的值为1,表示当前 Servlet 会在系统启动时被载入
- 设置 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 框架处理用户请求的具体流程:
- 客户端发送的请求被前端控制器(DispatcherServlet)捕获。
- 前端控制器调用处理器映射(Handler Mapping),对请求信息进行解析。
- 前端控制器根据上一步的解析结果,调用具体的 Controller,处理业务。
- 业务处理完成之后,返回一个 ModerAndView (模型数据和逻辑视图)。
- 前端控制器收回控制权,然后根据返回的逻辑视图名选择相应的视图,并把模型数据传入以便视图渲染展示。
- 前端控制器将渲染结果返回给客户端,流程结束。
整个框架通过一个前端控制器接收所有的请求,并将具体工作委托给其他组件进行处理。
前端控制器负责协调组织不同组件完成请求处理,并返回响应。
- 客户端发起请求后,由 Servlet 容器将请求包装为 HttpServletRequest 对象,然后 Servlet 容器通过反射创建 DispatcherServlet 对象,调用其 init 方法并传入 ServletConfig 对象(存储了 web.xml 文件定义的 init-param 参数)初始化 DispatcherServlet,然后调用其 service 方法并传入 HttpServletRequest 和 HttpServletResponse 进行服务。
DispatcherServlet
接收到Servlet
容器传来的HttpServletRequest
和HttpServletResponse
后,会通过其 URL,找到其对应的HandlerMapping
。- 调用
HandlerMapping
实例的getHandler
方法并传入HttpServletRequest
,获取HandlerExecutionChain
(包含一个Handler
对象和多个HandlerInterceptor
) - 执行
HandlerExecutionChain
包含的HandlerInterceptor
中的前置处理方法。 - 根据
HandlerExecutionChain
包含的Handler
找到其对应的HandlerAdaptor
。 - 调用该
HandlerAdaptor
的handler
方法并传入HttpServletRequest
、HttpServletResponse
和Handler
对象,返回ModelAndView
对象。 - 如果
ModelAndView
指定的是视图名称而不是View
实例(一般情况),那么调用ViewResolve
的方法得到View
对象。 - 调用
View
对象的render
并传入模型数据,渲染视图然后写入到HttpServletResponse
。请求处理完成。
2.3.1、Spring MVC 框架的总结
- 角色划分清晰。Spring MVC 框架在 Model、View 和 Controller 方面提供了一个非常清晰的角色划分,这三个方面各司其职、各负其责。
- 配置功能灵活。因为 Spring 框架的核心是 IoC,同样在实现 MVC 框架上,也可以把各种类当做 Bean 来通过 XML 进行配置。
- 大量的控制器接口和实现类。开发者可以使用 Spring 框架提供的控制器实现类,也可以自己实现控制器接口。
- 可灵活选择 View 层实现技术。Spring MVC 框架不会强制开发者使用 JSP,可以根据项目需求使用 Velocity、XSLT 等技术,使用更加灵活。
- 支持国际化。Spring MVC 框架提供国际化支持,可以轻松实现不同语言之间的切换。
- 面向接口编程。开发者可以方便地对 Spring MVC 框架提供的拦截器、数据格式转换器等工具进行扩展以满足业务需要。
- 功能丰富。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 常用的方法
- 添加模型数据
- 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=2 | VS | /user/view/2 |
---|---|---|
/user/delete?id=2 | VS | /user/delete/2 |
/user/update?id=2 | VS | /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 框架时,有两种常用的方式效验输入的数据。
- 利用 Spring 框架自带的验证框架。
- 利用 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 接口提供了对文件上传功能的直接支持,可以将上传请求包装成可以直接获取的文件数据
有以下两个实现类
- StandardServletMultipartResolver
- 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 框架中的数据格式转换
五个步骤
- Spring MVC 框架将 ServletRequest 对象传递给 DataBinder
- Spring MVC 框架将处理方法的入参对象传递给 DataBinder
- DataBinder 调用 ConversionService 组件进行数据类型转换、数据格式化等工作,并将 ServletRequest 对象中的消息填充到参数对象中
- 调用 Validator 组件对已经绑定了请求消息数据的参数对象进行数据合法性效验
- 效验完成后会生成数据绑定结果 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>