# Spring mvc 高级技术


<!--more-->


## 一、拦截器(Inteceptor)使⽤

### 1.监听器、过滤器、拦截器对比

- Servlet：处理Request请求和Response响应

- 过滤器（Filter）：对Request请求起到过滤的作⽤，作⽤在Servlet之前，如果配置为/*可以对所有的资源访问（servlet、js/css静态资源等）进⾏过滤处理

- 监听器（Listener）：实现了javax.servlet.ServletContextListener 接⼝的服务器端组件，它随Web应⽤的启动⽽启动，只初始化⼀次，然后会⼀直运⾏监视，随Web应⽤的停⽌⽽销毁

  - 作⽤⼀：做⼀些初始化⼯作，web应⽤中spring容器启动ContextLoaderListener

  - 作⽤⼆：监听web中的特定事件，⽐如HttpSession,ServletRequest的创建和销毁；变量的创建、销毁和修改等。可以在某些动作前后增加处理，实现监控，⽐如统计在线⼈数，利⽤HttpSessionLisener等。

- 拦截器（Interceptor）：是SpringMVC、Struts等表现层框架⾃⼰的，不会拦截jsp/html/css/image的访问等，只会拦截访问的控制器⽅法（Handler）。

从配置的⻆度也能够总结发现：serlvet、filter、listener是配置在web.xml中的，⽽interceptor是配置在表现层框架⾃⼰的配置⽂件中的

- 在Handler业务逻辑执⾏之前拦截⼀次

- 在Handler逻辑执⾏完毕但未跳转⻚⾯之前拦截⼀次

- 在跳转⻚⾯之后拦截⼀次

![img_3](https://img.zhaojq.top/20260727091750999.png "img_3")

### 2.拦截器执行流程

![img_4](https://img.zhaojq.top/20260727091751174.png "img_4")

### 3.多个拦截器

![img_5](https://img.zhaojq.top/20260727091751342.png "img_5")

示例：

```java
public class Intercepter01 implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("---------Intercepter01 preHandle------------");
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("---------Intercepter01 postHandle------------");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("---------Intercepter01 afterCompletion------------");
    }
}
```

```xml
    <mvc:interceptors>
        <mvc:interceptor>
            <!--配置当前拦截器的url拦截规则，**代表当前目录下及其子目录下的所有url-->
            <mvc:mapping path="/**"/>
            <!--exclude-mapping可以在mapping的基础上排除一些url拦截-->
            <!--<mvc:exclude-mapping path="/demo/**"/>-->
            <bean class="com.mvc.demo.intercepter.Intercepter01"/>
        </mvc:interceptor>

        <mvc:interceptor>
            <mvc:mapping path="/**"/>
            <bean class="com.mvc.demo.intercepter.Intercepter02"/>
        </mvc:interceptor>

    </mvc:interceptors>
```

## 二、处理multipart形式的数据

依赖

```xml
<dependency>
  <groupId>commons-fileupload</groupId>
  <artifactId>commons-fileupload</artifactId>
  <version>1.4</version>
</dependency>
```

配置文件上传解析器

```xml
<!-- 多元素解析器 id固定为multipartResolver -->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
  <!--设置上传文件大小上限，单位是字节，-1代表没有限制也是默认的-->
  <property name="maxUploadSize" value="5000000"/>
</bean>
```

示例代码：

```java
/**
     * 文件上传
     *
     * @return
     */
    @RequestMapping(value = "/upload")
    public ModelAndView upload(MultipartFile uploadFile, HttpSession session, ModelAndView modelAndView) throws IOException {
        // 处理上传文件
        String originalFilename = uploadFile.getOriginalFilename();// 原始名称
        System.out.println("----------- fileName" + originalFilename);

        // 存储,要存储到指定的文件夹，/uploads/yyyy-MM-dd，考虑文件过多的情况按照日期，生成一个子文件夹
        String realPath = session.getServletContext().getRealPath("/uploads");
        String datePath = new SimpleDateFormat("yyyy-MM-dd").format(new Date());
        File folder = new File(realPath + "/" + datePath);
        if (!folder.exists()) {
            folder.mkdirs();
        }

        // 存储文件到目录
        uploadFile.transferTo(new File(folder, originalFilename));

        modelAndView.addObject("date", "upload success");
        modelAndView.setViewName("success");
        return modelAndView;
    }
```

## 三、在控制器中处理异常

示例：

```java
@ControllerAdvice
public class ExceptionResolver {

    @ExceptionHandler(ArithmeticException.class)
    public ModelAndView handleException(ArithmeticException exception) {
        ModelAndView modelAndView = new ModelAndView();
        modelAndView.addObject("msg", exception.getMessage());
        modelAndView.setViewName("error");
        return modelAndView;
    }
}
```

## 四、基于Flash属性的跨重定向请求数据传递

重定向时请求参数会丢失，我们往往需要重新携带请求参数，我们可以进⾏⼿动参数拼接如下：

`return "redirect:handle01?name=" + name;`

但是上述拼接参数的⽅法属于get请求，携带参数⻓度有限制，参数安全性也不⾼，此时，我们可以使⽤SpringMVC提供的flash属性机制，向上下⽂中添加flash属性，框架会在session中记录该属性值，当跳转到⻚⾯之后框架会⾃动删除flash属性，不需要我们⼿动删除，通过这种⽅式进⾏重定向参数传递，参数⻓度和安全性都得到了保障，如下：

```java
/**
 * SpringMVC 重定向时参数传递的问题
 * 转发：A 找 B 借钱400，B没有钱但是悄悄的找到C借了400块钱给A
 * url不会变,参数也不会丢失,一个请求
 * 重定向：A 找 B 借钱400，B 说我没有钱，你找别人借去，那么A 又带着400块的借钱需求找到C
 * url会变,参数会丢失需要重新携带参数,两个请求
 */    
@RequestMapping("/handleRedirect")
public String handleRedirect(String name, RedirectAttributes redirectAttributes) {
  //return "redirect:handle01?name=" + name;  // 拼接参数安全性、参数长度都有局限
  // addFlashAttribute方法设置了一个flash类型属性，该属性会被暂存到session中，在跳转到页面之后该属性销毁
  redirectAttributes.addFlashAttribute("name", name);
  return "redirect:handle01";

}
```








