目录
Spring MVC概述
Spring Web MVC是一种基于Java的实现了Web MVC设计模式的请求驱动类型的轻量级Web 框架,即使用了MVC架构模式的思想,将web 层进行职责解耦,基于请求驱动指的就是使用请求-响应模型,框架的目的就是帮助我们简化开发。
Spring Web MVC作用
- 非常简单的设计出干净的Web 层和薄薄的Web 层
- 提供强大的约定大于配置的契约式编程支持
- 支持灵活的URL到页面控制器的映射
- 灵活的数据验证、格式化和数据绑定机制,能使用任何对象进行数据绑定,不 必实现特定框架的API
- 提供一套强大的JSP标签库,简化JSP开发; 支持灵活的本地化、主题等解析
- 简单的异常处理
- 支持Restful风格
Spring MVC模型概述
Spring MVC请求处理流程
Spring MVC 开发环境搭建
<!-- applicationContext-mvc.xml配置 -->
<beans xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/mvc
https://www.springframework.org/schema/mvc/spring-mvc.xsd
">
<!-- 配置视图解析器 -->
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!-- 配置视图页面的前缀 -->
<property name="prefix" value="/view/"></property>
<!-- 配置视图页面的后缀 -->
<property name="suffix" value=".jsp"></property>
</bean>
<!-- 配置驱动 -->
<mvc:annotation-driven></mvc:annotation-driven>
</beans>
<!-- web.xml配置 -->
<!-- 配置前端控制器,拦截所有请求 -->
<servlet>
<servlet-name>DispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:config/applicationContext-mvc.xml</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>DispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
Hello Spring MVC
简单的Spring MVC实例:
@Controller //注解方式自动扫描
@RequestMapping("/hello") //访问地址目录
//推荐在类上也配置@RequestMapping增加访问层次,同一层次不能重名,否则报错
public class helloSpringMVC {
/**
* 将sayHello作RequestMapping的key,方法作value
* RequestMapping的value是一个数组,可以定义多个路径
* method属性:表示接受设置的请求方法:不设置时表示接受所有(GET、POST、PUT...)
*
*/
@RequestMapping("/sayHello")
public ModelAndView sayHello() {
ModelAndView mav = new ModelAndView("hello");//返回设置目录下的hello文件
mav.addObject("attributeName", "通过ModelAndView配置Model");
return mav;
}
/**
* 模糊查找
* ? -> 表示任意一个字符
* * -> 表示任意个字符
* ** -> 表示匹配任意深度
*/
@RequestMapping("/**/strHello")
public String strHello(Map<String, Object> map) {
map.put("attributeName", "通过String传递ModelAndView");
return "hello";//返回设置目录下的hello文件
}
<!-- webapp/view/hello.jsp-->
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<h1>HELLO Spring MVC!</h1>
<h2>${attributeName }</h2>
</body>
</html>
实例运行结果:
Restful风格的Spring MVC
Restful风格增删改查可以将原4个url的操作集成在同一个rul(例:manage)中实现,使有更好的用户体验。客户端使用GET、POST、PUT、DELETE4个表示操作方式的动词对服务端资源进行操作:GET用来获取资源,POST用来新建资源(也可以用于更新资源),PUT用来更新资源,DELETE用来删除资源。
//java中的增删改查
@Controller
@RequestMapping("/userRestful")
public class UserRestController {
@Autowired
private UserService userService;//自动配置Service
@RequestMapping(value = "/manage", method = {RequestMethod.GET})
public String printAllUser(Map<String, Object> map) {
map.put("users", userService.selectUser());
return "userRest/printUser";
}
@RequestMapping("/insertInit")
public String insertInit() {
return "userRest/insertUser";
}
@RequestMapping(value = "/manage", method = {RequestMethod.POST})
public String insert(UserInfo ui) {
userService.insertUser(ui);
return "redirect:/userRest/manage";//重定向
}
@RequestMapping(value = "/manage", method = {RequestMethod.POST})
public String insert(UserInfo ui) {
userService.insertUser(ui);
return "redirect:/userRest/manage";
}
@RequestMapping(value = "/manage", method = {RequestMethod.DELETE})
public String delete(String id) {
userService.deleteUserByUserId(id);
return "redirect:manage";
}
运行流程:updateInit -> update.jsp -> manage(PUT) -> update
@RequestParam: 用来绑定单个请求参数值
- 属性name代表Form表单中的name属性值
- 属性defaultValue代表如果没有提交此参数,则使用默认值
- 属性required代表用户提交信息时,必须提交此参数,否则抛出异常,required默认值为true
@RequestMapping("/updateInit")
public String updateInit(@RequestParam("id") String id, Map<String, Object> map) {
map.put("user", userService.selectUserById(id));
return "userRest/updateUser";
}
@RequestMapping(value = "/manage", method = {RequestMethod.PUT})
public String update(UserInfo ui) {
自动装箱:spring mvc可以把这些要提交的字段封装在一个对象中,从而请求类型就是一个project.
userService.UpdateUser(ui);
return "redirect:manage";
}
/**
* 一般情况下,控制器方法返回字符串类型的值会被当成逻辑视图名处理
* 如果返回的字符串中带 forward: 或 redirect: . 前缀时,SpringMVC 会对他们进行特殊处理:
* redirect:会完成一个重定向的操作(客户端行为)
* forward会完成一个转发操作(服务器内部行为)
*/
}
<!-- update.jsp中的改操作 -->
<body>
<div align="center">
<h1>用户信息</h1>
<br>
<form action="${pageContext.request.contextPath }/userRest/manage" method="post">
<input type="hidden" name="_method" value="put">
<!-- 通过 "_method" 修改请求方式为PUT-->
用户账号: <input type="text" name="id" value="${user.id }"><br>
密码:<input type="password" name="password" value="${user.password }"><br>
真实姓名: <input type="text" name="userName" value="${user.userName }"><br>
盐值: <input type="text" name="salt" value="${user.salt }"><br>
<input type="submit" value="提交"><input type="reset" value="重置">
</form>
</div>
</body>
<!-- web.xml中关于 _method 的配置 -->
<!-- 提交方式为Put Delete Patch时都需要使用 _method -->
<filter>
<filter-name>HiddenHttpMethodFilter</filter-name>
<filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>HiddenHttpMethodFilter</filter-name>
<servlet-name>DispatcherServlet</servlet-name>
</filter-mapping>
文件上传FileUpload
Spring MVC提供了更简便的文件上传模板,该模板避免了原有的流传输的麻烦。
@Controller
public class FileUpload {
@RequestMapping("/fileuploadInit")
public String fileuploadInit() {
return "fileupload";
}
@RequestMapping("/fileupload")
public String fileupload(MultipartFile file) throws IllegalStateException, IOException {
if(file.isEmpty()) {//判断文件是否为空
return "error";
}
//获取源文件名称
String orignName = file.getOriginalFilename();
String Name = file.getName();
System.out.println("orignName:" + orignName);
System.out.println("Name:" + Name);
//定义存储上传文件位置 UUID名 + . + 文件后缀名
String destFileName = UUID.randomUUID().toString() + "." + FilenameUtils.getExtension(orignName);
File destFile = new File("C:\\WorkSpace\\JAVA\\FileRepository\\" + destFileName);
//文件传输
file.transferTo(destFile);
return "success";
}
JOSN数据交互
Spring MVC 提供了更加简便的JSON数据交互,通过@ResponseBody注解和@RequestBody注解的方式,提供一个传递数据并自动在JSON和特定数据之间解析的接口。
@Controller
public class WebService {
@Autowired
private UserService userService;
/**
* @ResponseBody:注解用于将Controller的方法返回的对象,通过springmvc提供的HttpMessageConverter接口转换为指定格式的数据如:json,xml等,通过Response响应给客户端。
*/
@RequestMapping("/ws")
@ResponseBody
public List<UserInfo> userWsList() {
return userService.selectUser();
}
/**
* @RequestBody:用于读取http请求的内容(字符串),通过springmvc提供的HttpMessageConverter接口将读到的内容(json数据)自动转换为java对象并绑定到Controller方法的参数上。
* 用户可以通过此接口传递一个JSON数据给系统
*/
@RequestMapping("/postUser")
@ResponseBody
public String getUser(@RequestBody List<UserInfo> user) {
//数据的校验
return "SUCCESS!";//@ResponseBody将return跳转的网页转义为String
}
}
通过Postman可以测试此接口的工作情况:
控制台打印结果显示JSON自动解析为了list数据。
Spring MVC 拦截器
Spring MVC也可以使用拦截器对请求进行拦截处理(类似于AOP思想),用户可以自定义拦截器来实现特定的功能,自定义的拦截器必须实现HandlerInterceptor接口。
HandlerInterceptor中包含了三个default方法:preHandle、postHandle、afterCompletion。
- preHandle():这个方法在业务处理器处理请求之前被调用,在该方法中对用户请求 request 进行处理。如果程序员决定该拦截器对请求进行拦截处理后还要调用其他的拦截器,或者是业务处理器去进行处理,则返回true;如果程序员决定拒绝此请求,则返回false。
- postHandle():这个方法在业务处理器处理完请求后,但是DispatcherServlet 向客户端返回响应前被调用,在该方法中对用户请求request进行处理。
- afterCompletion():这个方法在视图解析完成被调用,可以在该方法中进行一些资源清理的操作。
public class FirstHandlerInterceptor implements org.springframework.web.servlet.HandlerInterceptor{
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
System.out.println("拦截器preHandle!");
return true;
}
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
System.out.println("拦截器postHandle!");
if(modelAndView != null) {
modelAndView.setViewName("error");
}
}
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
throws Exception {
System.out.println("拦截器afterCompletion!");
}
}
一次可以设置多个拦截器。每个拦截器可以作用不同的url,需要在应用上下文中配置拦截器才能使之生效。
<mvc:interceptors>
<mvc:interceptor>
<bean class="com.zzxtit.spring.mvc.interceptor.action.FirstHandlerInterceptor"></bean>
<!-- mvc:mapping 中配置需要拦截的路径 -->
<mvc:mapping path="/**"/>
<!-- mvc:exclude-mapping 中配置不拦截的路径 -->
<mvc:exclude-mapping path="/ws"/>
</mvc:interceptor>
<mvc:interceptor>
<bean class="com.zzxtit.spring.mvc.interceptor.action.SecHandlerInterceptor"></bean>
<mvc:mapping path="/**"/>
</mvc:interceptor>
</mvc:interceptors>
当设置两层拦截器时,其方法的运行顺序如下:
异常处理
Spring MVC 提供了更加轻便的异常处理模板。可以通过注解或配置的方式直接处理异常。
作用域class的异常处理
public class myException {
@RequestMapping("/exception")
public String exceptionTest(int index) {
//该方法用于产生异常
return "success";
}
/**
* 在使用@ExceptionHandler进行异常处理时,不可以将exception直接设置到方法参数中声明的modle, 必须返回ModelAndView
* @ExceptionHandler 只能作用本类中的异常信息处理
*/
@ExceptionHandler
public ModelAndView myEexception(Exception e) {
ModelAndView mav = new ModelAndView("error");//初始化ModelAndView 使发生异常时跳转至error界面。
mav.addObject("exception", e);
return mav;
}
/**
* @ExceptionHandler 异常处理有优先级的问题,抛出的异常与@ExceptionHandler声明的异常关系越近,就会被那个一个@ExceptionHandler处理
* 如果处理工程中所有的异常信息, 当Handler找不到@ExceptionHandler标注的方法时,需要使用@ControllerAdvice标注的类中@ExceptionHandler来处理。
*/
@ExceptionHandler(RuntimeException.class)
public ModelAndView sechandleException(RuntimeException e)
ModelAndView mav = new ModelAndView("error");
mav.addObject("exception", e);
return mav;
}
}
基于@ControllerAdvice的全局异常处理
上述方法只能作用于同一个class中方法产生的异常,而日常编程中我们经常需要处理所有异常,将其统一化指向一个异常页面。此时我们需要运用@ControllerAdvice注解,将异常处理独立为一个 机制。
@ControllerAdvice
public class exception {
// @ExceptionHandler
public ModelAndView exception(Exception e) {
ModelAndView mav = new ModelAndView("error");
mav.addObject("exception", e);
return mav;
}
}
基于应用上下文的全局的异常处理
在Spring MVC中,配置的思想贯彻整个框架,而异常处理同样可以适用配置的方法。
<bean name="exceptionResolver" class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
<property name="exceptionMappings">
<props>
<!-- 配置产生的异常类型及对应跳转的页面 -->
<prop key="java.lang.ArrayIndexOutOfBoundsException">error</prop>
</props>
</property>
<!-- 配置exceptionAttribute可以获取产生的异常并在页面中通过${myException }显示。若不配置此项,其默认值为${exception } -->
<property name="exceptionAttribute" value="myException"></property>
</bean>