SpringMVC第二部分


拦截器

SpringMVC 中的 Interceptor 拦截器也是相当重要和相当有用的,它的主要作用是拦截用户的请求并进行相应的处理。比如通过它来进行权限验证,或者是来判断用户是否登陆等操作

实现方式

实现HandlerInterceptor接口

实现接口类

public interface HandlerInterceptor {
default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
return true;
}

default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception {
}

default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception {
}
}

其中preHandle()方法表示在目标拦截方法执行前进行的操作,其返回值若为true则会继续执行目标方法,反之则不执行。postHandle()表示在方法执行后返回视图前进行的操作,afterCompletion()表示在目标Handler执行完毕后进行的操作

继承HandlerInterceptorAdapter抽象类

该类接入了AsyncHandlerInterceptor接口,而该接口接入了HandlerInterceptor接口,故本质与上个方法没有区别,而且该类目前已被弃用,不再过多赘述

配置拦截器

通过配置XML配置文件来进行拦截器的配置,有两种方法,方法一

<mvc:interceptors>
<!--
使用bean定义一个Interceptor
直接定义在mvc:interceptors根下面的Interceptor将拦截所有的请求-->
<bean class="path.to.your.interceptor"/>
</mvc:interceptors>

方法二

<mvc:interceptors>
<mvc:interceptor>
<!-- 通过 mvc:mapping 配置需要拦截的资源。支持通配符。可配置多个 -->
<!-- "/**"表示拦截所有的请求。 -->
<mvc:mapping path="/**"/>
<!-- 通过 mvc:exclude-mapping 配置不需要被拦截的资源。支持通配符。可配置多个 -->
<mvc:exclude-mapping path="/test/**"/>
<bean class="path.to.your.interceptor"/>
</mvc:interceptor>
</mvc:interceptors>

配置多个拦截器

<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/**" />
<bean class="path.to.your.interceptor1" />
</mvc:interceptor>
<mvc:interceptor>
<mvc:mapping path="/**" />
<bean class="path.to.your.interceptor2" />
</mvc:interceptor>
</mvc:interceptors>

多个拦截器的执行顺序是先配置的拦截器的preHandle()方法先执行,postHandle()afterCompletion()方法后执行


文件上传

环境配置

添加commons-fileupload依赖

<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.3.2</version>
</dependency>

修改servlet-context.xml设置相关配置,数字的单位是字节

<bean id="multipartResolver"
class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<!-- 允许文件上传的最大尺寸 -->
<property name="maxUploadSize">
<value>104857600</value>
</property>
<!--
设置文件放入临时文件夹的最大限制
此值是阈值,低于此值,则保存在内存中,如高于此值,则生成硬盘上的临时文件
-->
<property name="maxInMemorySize">
<value>4096</value>
</property>
</bean>

单文件上传

前端表单

<form action="uploadFile" method="post" enctype="multipart/form-data">
<input type="file" name="file" />
<button type="submit">提交</button>
</form>

type属性值代表传递文件,enctype属性值代表以二进制形式传输数据,action属性值表示后端处理的路径

后端处理逻辑

后端通过MultipartFile获得文件,之后需设置文件上传路径,返回页面渲染

@RequestMapping("/uploadFile")
public String uploadFile(HttpServletRequest request, @RequestParam("file") MultipartFile file) {

if (!file.isEmpty()) {
try {
String path = request.getServletContext().getRealPath("/");
File uploadFile = new File(path + "/upload");
if (!uploadFile.exists()) {
uploadFile.mkdir();
}

String originalName = file.getOriginalFilename();
String suffix = originalName.substring(originalName.lastIndexOf("."));
String fileName = System.currentTimeMillis() + suffix;
file.transferTo(new File(path + "/upload/" + fileName));
request.setAttribute("msg", "文件上传成功!");

} catch (IOException e) {
e.printStackTrace();
request.setAttribute("msg", "文件上传失败!");
}
} else {
request.setAttribute("msg", "文件不存在!");
}

return "result";
}

此处的后端逻辑还修改的上传后的文件名为系统毫秒时

多文件上传

前端表单

<form action="uploadFile" method="post" enctype="multipart/form-data">
<input type="file" name="files" />
<input type="file" name="files" />
<input type="file" name="files" />
<button type="submit">提交</button>
</form>

后端处理逻辑

后端获取文件集合遍历,再依次执行前面的处理逻辑即可

@RequestMapping("/uploadFiles")
public String uploadFiles(HttpServletRequest request, @RequestParam("files") List<MultipartFile> files) {

if (files != null && files.size() > 0) {
for (MultipartFile file : files) {
saveFile(request, file);
}
}

return "result";
}

SSM框架集成

SSM框架是Spring + Spring MVC + MyBatis三个开源框架的整合,是Java企业级开发中非常流行的轻量级框架组合,主要用于构建Web应用程序

环境配置

配置pom.xml

依赖项配置pom.xml,此处包括junitspring-contextspring-testspring-jdbcspring-txaspectjweaverc3p0mybatismybatis-springmysql-connector-javaslf4j-log4jpagehelplerspring-webspring-webmvcjavax.servlet-apijackson-corecommons-fileupload等,版本还需要自己斟酌

Maven 项目中,如果源代码(src/main/java)存在 xml、properties、tld 等文件,Maven 默认不会自动编译该文件到输出目录,如果要编译源代码中 xml、properties、tld 等文件,需要显式配置 resources 标签

<resources>
<resource>
<directory>src/main/resources</directory>
</resource>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.xml</include>
<include>**/*.properties</include>
<include>**/*.tld</include>
</includes>
<filtering>false</filtering>
</resource>
</resources>

此外,后面还有配置web.xml配置servlet-content.xml配置spring.xml配置mybatis.xml配置db.properties配置log4j.properties

可以发现,原生spring框架下配置工作非常繁琐,这促成了SpringBoot的产生,其约定大于配置的特性极大地减少了机械性的配置操作,不久我们将接触这一领先技术


RestFul URL

RestFul 风格的 API 是一种软件架构风格,设计风格而不是标准,只是提供了一组设计原则和约束条件。它主要用于客户端和服务器交互类的软件。基于这个风格设计的软件可以更简洁,更有层次,更易于实现缓存等机制

在 RESTful 风格中,用户请求的 url 使用同一个 url,用请求方式:getpostdeleteput等方式对请求的处理方法进行区分,这样可以在前后台分离式的开发中使用前端开发人员不会对请求的资源地址产生混淆和大量的检查方法名的麻烦,形成一个统一的接口

请求方法 对应 SQL 操作 说明
GET SELECT 从服务器查询,可以通过请求的参数区分查询方式
POST CREATE(INSERT) 在服务器端新建一个资源,执行插入操作
PUT UPDATE 在服务器端更新资源(通常需要提供完整资源),执行更新操作
PATCH UPDATE 在服务器端更新资源(客户端仅提供需要改变的属性)
DELETE DELETE 从服务器端删除资源,执行删除操作

核心是将传统的传参变为路径传参,比如?id=1可改写为/1?,SpringMVC 是通过 @RequestMapping@PathVariable注解提供的。@PathVariable一般标记在形参前

查询操作

@GetMapping("/account/{id}")
@ResponseBody
public Account queryAccount(@PathVariable Integer id) {
return accountService.select(id);
}

也可以多参

@GetMapping("/account/{id}/{name}")
@ResponseBody
public Account queryAccount(@PathVariable Integer id, @PathVariable String name) {
return accountService.select(id,name);
}

删除操作

@DeleteMapping("/account/{id}")
public int deleteAccountById(@PathVariable Integer id) {
return accountService.delete(id);
}

更新操作

@PutMapping("/account/{id}")
public int updateAccount(@RequestBody Account account, Integer id) {
account.setId(id);
return accountService.update(account,id);
}

添加操作

@PostMapping("/account")
public int addAccount(@RequestBody Account account) {
return accountService.add(account);
}

全局异常处理

SpringMVC 对于异常处理这块提供了支持,通过 SpringMVC 提供的全局异常处理机制,能够将所有类型的异常处理从各处理过程解耦出来,既保证了相关处理过程的功能较单一,也实现了异常信息的统一处理和维护

使用SimpleMappingExceptionResolver对象

配置文件中配置异常处理对象

<!-- 配置全局异常统一处理的 Bean (简单异常处理器) -->
<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
<!-- 页面在转发时出现异常,设置默认的错误页面(error代表的是一个视图) -->
<property name="defaultErrorView" value="error"></property>
<!-- 异常发生时,设置异常的变量名 -->
<property name="exceptionAttribute" value="ex"></property>
</bean>

error.jsp中,可以使用${ex}在前端页面打印异常信息。

此外,也可以自定义异常抛出并定位到相应页面

<!-- 设置自定义异常和页面的映射 -->
<property name="exceptionMappings">
<props>
<prop key="path.to.your.exception1">exception1</prop>
<prop key="path.to.your.exception2">exception2</prop>
</props>
</property>

配置成功若抛出exception1则会跳转到exception1.jsp页面

实现HandlerExceptionResolver接口

接口定义

public interface HandlerExceptionResolver {
@Nullable
ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex);
}

编写处理类,使用@Component导入IOC容器,与异常绑定

@Component
public class GlobalExceptionResolver implements HandlerExceptionResolver {
@Override
public ModelAndView resolveException(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object handler, Exception ex ) {
ModelAndView mv = new ModelAndView("error");
mv.addObject("ex", "Error Message");
return mv;
}
}

使用if判断设置自定义异常

@Component
public class GlobalExceptionResolver implements HandlerExceptionResolver {
@Override
public ModelAndView resolveException(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object handler, Exception ex ) {
ModelAndView mv = new ModelAndView();
mv.addObject("ex", "Error Message");

if(ex instanceof IOException) {
mv.setViewName("IOExceptionPage");
IOException e = (IOException) ex;
mv.addObject("ex",e.getMessage());
}

return mv;
}
}

Controller类继承BaseController

作为其他 Controller 的父类,提供公共的异常处理、日志记录、权限检查等方法

public class BaseController {
@ExceptionHandler
public String exc(HttpServletRequest request, HttpServletResponse response, Exception ex) {
request.setAttribute("ex", ex);
if (ex instanceof ParamsException) {
return "error_param";
}
if (ex instanceof BusinessException) {
return "error_business";
}
return "error";
}
}

使用 @ExceptionHandler 注解实现异常处理,具有集成简单、有扩展性好(只需要将要异常处理的 Controller 类继承于 BaseController 即可)、不需要附加 Spring 配置等优点,但该方法对已有代码存在入侵性(需要修改已有代码,使相关类继承于 BaseController),在异常处理时不能获取除异常以外的数据

未捕获异常处理

未捕获异常导致的404、500等错误页面可通过修改web.xml进行全局的配置

<!-- 出错页面定义 -->
<error-page>
<exception-type>java.lang.Throwable</exception-type>
<location>/500.jsp</location>
</error-page>
<error-page>
<error-code>500</error-code>
<location>/500.jsp</location>
</error-page>
<error-page>
<error-code>404</error-code>
<location>/404.jsp</location>
</error-page>