2020:0615 --- SpringMVC(二)

236 阅读21分钟

今日内容

第一章:相应数据和结果视图
第二章:SpringMVC实现文件上传
第三章:SpringMVC中的异常处理
第四章:SpringMVC中的拦截器

第一章:相应数据和结果视图

1.1 Controller中方法返回值分类

1.1.1 返回字符串
    controller方法返回字符串可以指定逻辑视图名,通过解析器解析为物理视图地址。
    
    return "success";  ----  通过视图解析器跳转到相应的页面
    
    ```
        @RequestMapping("/testString")
        public String testString(Model model){
            System.out.println("testString方法执行了...");
    
            //模拟从数据库中查询出user对象
            User user = new User();
            user.setUsername("美美");
            user.setPassword("123");
            user.setAge(30);
    
            //model:将数据存入Request域中
            model.addAttribute("user", user);
    
            return "success";
        }
    ```
1.1.2 返回值 -- void
1.默认情况

    * jsp
    ```
    <a href="user/testVoid">testVoid</a>
    ```
    
    * UserController
    ```
    @RequestMapping("/testVoid")
    public void testVoid(){
        System.out.println("testVoid方法执行了...");

    }
    ```
    
    * 效果: 404错误
        
        点击jsp链接后访问到了localhost:8080/day0615/user/testVoid资源。
    成功打印了输出语句:testVoid方法执行了...
    
        但是由于执行的Controller类中的方法没有返回值(void),默认会跳转到
    @RequestMapping(value="testVoid") testVoid.jsp页面。
    
        但是由于没有该页面,所以报404的错误。

2.解决:可以使用请求转发或者重定向跳转到指定的页面

    2.1 转发解决 -- 转发到localhost:8080/day0615/WEB-INF/pages/success.jsp
        * jsp
        ```
        <a href="user/testVoidRequest">testVoidRequest</a>
        ```
        
        * UserController
        ```
        @RequestMapping("/testVoidRequest")
        public void testVoidRequest(HttpServletRequest request, HttpServletResponse response) 
        throws Exception{
            System.out.println("testVoid方法执行了...");
    
            //编写请求转发程序

            request.getRequestDispatcher("/WEB-INF/pages/success.jsp").forward(request, response);
            System.out.println(request.getContextPath());
            //转发程序写完后,还会执行后续的代码,如果不想可以加一个return;
            return;
    
        }
        ```
        注意:编写请求转发是不写request.getRequestDispatcher("success"):
    因为是转发请求,不会默认去调用视图解析器。
    
    2.2 重定向 -- 重定向到localhost:8080/day0615/index.jsp
        * jsp
        ```
        <a href="user/testVoidResponse">testVoidResponse</a>
        ```
        
        * UserController
        ```
       @RequestMapping("/testVoidResponse")
        public void testVoid(HttpServletRequest request, HttpServletResponse response) throws Exception{
            System.out.println("testVoid方法执行了...");
            
            //编写重定向程序
            response.sendRedirect("/day0615_springmvc01_response/index.jsp");
            System.out.println(request.getContextPath());
    
            //转发程序写完后,还会执行后续的代码,如果不想可以加一个return;
            return;
        } 
        ```
        
        这里参数写的是绝对路径,所以最终的重定向URL:
            localhost:8080//day0615_springmvc01_response/index.jsp
        
        也可以这样写:
            response.sendRedirect(request.getContextPath()+"/index.jsp");
            request.getContextPath() : /day0615_springmvc01_response
        

3. 直接通过输出流把数据响应到页面上  
        * jsp
        ```
        <a href="user/testResponse">直接响应</a>
        ```
        
        * Controller
        ```
        @RequestMapping("/testResponse")
        public void testResponse(HttpServletRequest request, HttpServletResponse response) 
        throws Exception{
            System.out.println("testVResponse方法执行了...");
    
            //解决中文乱码
            response.setCharacterEncoding("UTF-8");
            //浏览器解析编码
            response.setContentType("text/html;charset=UTF-8");
    
            //拿到输出流
            response.getWriter().print("你好");
    
            return;
        }
        ```
        
        * 结果

4. 用关键字做转发和重定向

    Controller方法提供了String类型的返回值之后,默认就是请求转发,是通过视图解析器实现的。
我们可以用关键字forward:实现这一结果。

    注意:用了关键字后,就不用在用视图解析器了,所以要补全path。
    
    1. 请求转发 
    ```
    @RequestMapping("/testForwardAndRedirect")
    public String testForwardAndRedirect(){
        System.out.println("testForwardAndRedirect方法执行了...");


        //关键字转发
        return "forward:/WEB-INF/pages/success.jsp";
    }
    ```
    
    2. 重定向
    ```
    @RequestMapping("/testForwardAndRedirect")
    public String testForwardAndRedirect(HttpServletRequest request){
        System.out.println("testForwardAndRedirect方法执行了...");

        //关键字重定向:
        return "redirect:"+request.getContextPath()+"/index.jsp";
    }
    ```
    注意:
    重定向的绝对路径要写项目名。
    但是在用redirect关键字时,springmvc其实会帮我们加上,所以这里也可以写:
    return "redirect:/index.jsp";
1.1.3 ResponseBody响应json数据
1 使用说明
作用:
    该注解用于将Controller的方法返回的对象,通过HttpMessageConverter接口转换为指定
格式的数据入:json,xml等,通过Response响应给客户端。

2. 案例演示

    1. 引入JQuery文件到jsp中

    2. 写一个ajax代码:response.jsp
    ```
    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <html>
    <head>
        <title>Title</title>
    
        <script src="js/jquery.min.js"></script>
    
        <script>
            //页面加载,绑定单击事件
            $(function () {
                $("#btn").click(function () {
                    alert("hello btn")
                });
            });
        </script>
        
    </head>
    
    <body>
        <button id="btn">发送ajax请求</button>
    </body>
    
    </html>
    ```
    
    3.我们发现ajax请求发送失败

    4.原因:
    
        我们在response.jsp中的引入了静态资源;
        ```
        <script src="js/jquery.min.js"></script>
        ```
        
        这个静态资源也会被我们在web.xml配置的前端控制器拦截下来:

        我们配置的前端控制器将项目路径下的所有路径都拦截了。
        
    5. 我们要告诉前端控制器不要拦截静态资源。
    
        在spring.xml文件中配置:告诉前端控制器,哪些静态资源不拦截-
        ```
        <mvc:resources location="/css/" mapping="/css/**"/> <!-- 样式 -->
        <mvc:resources location="/images/" mapping="/images/**"/> <!-- 图片 -->
        <mvc:resources location="/js/" mapping="/js/**"/> <!-- javascript -->
        ```
        
    注意:配置好后,要重新启动IDEA才能生效(引入的JQuery就不会被拦截了)
    
    6. 前置知识点
        Spring默认用MappingJacksonHttpMessageConverter对json数据进行转换,需要加入jackson包

        作用:将对象转成json字符串,或者将json字符串转成JavaBean对象
        
        引入依赖坐标:
        ```
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>2.9.0</version>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-core</artifactId>
            <version>2.9.0</version>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-annotations</artifactId>
            <version>2.9.0</version>
        </dependency>
        ```
        
    7. 继续写前端页面
        ```
            <script src="js/jquery-1.11.0.min.js"></script>

<script>
    // 页面加载,绑定单击事件
    $(function(){
            $("#btn").click(function(){
                //alert("hello btn");
                //$ : JQuery全局对象
                $.ajax({
                    //编写json格式,设置属性和值
                    url:"user/testAjax",
                    /指定给服务其传json类型数据
                    contentType:"application/json;charset=UTF-8",
                    //发送到服务器的数据:json格式
                    data:'{"username":"大宝","password":"123","age":"30"}',
                    //设置服务返回的数据格式
                    dataType:"json",
                    //请求方式
                    type:"post",
                    //请求成功后的处理函数
                    success:function (data) {
                        //data服务端响应的json数据,进行解析
                        alert(data);
                        alert(data.username);
                        alert(data.age);
                        alert(data.password);
                    }
                });

            });
        });

    </script>
        ```
        
    8. 写后端代码:将接收的json数据转成对象后,再转成json格式响应给客户端
    
        ```
        @RequestMapping("/testAjax")
        public @ResponseBody User testAjax(@RequestBody User user){
            //前台响应过来的json数据在请求体内:我们用@RequestBody注解来接收
            System.out.println("testAjax法执行了...");
    
            System.out.println(user);
    
    
            //json数据中的key如果能和JAVABean中的属性对应上的话,springmvc就正好可以帮我们封装
            //前提是导入几个jar包
    
            //做响应:模拟查询数据库
            user.setUsername("haha");
            user.setAge(40);
    
            //做响应:注意前端设定的返回类型是json
            //在返回值声明处:public @ResponseBody User
            return user;
    
        }
        ```
    9. 分析:
        前端页面将数据以放到request中以json格式发送过来,我们首先要接受。
        通过方法中的参数接收request中的数据。
        如果json数据中的key能和JAVABean中的属性对应上的话,springmvc就正好可以帮我们封装。
        所以我们直接将对象返回:return user;
            但是前台页面要接收的是json格式:所以用@ResponseBody User将方法返回的对象转成json。
1.1.4 返回值 -- ModelAndView
    ModelAndView对象是Spring提供的一个对象,可以用来调整具体的JSP视图。
我们可以在方法的返回值上返回这个对象,它也可以通过视图解析器帮你跳转到某页面。

    Model可以存储JavaBean对象,View可以存储你想跳转的页面。
    
    Controller:
    ```
       @RequestMapping("/testModelAndView")
        public ModelAndView testModelAndView(){
            //创建ModelAndView对象
            ModelAndView mv = new ModelAndView();
    
            System.out.println("testModelAndView方法执行了...");
    
            //模拟从数据库中查询出user对象
            User user = new User();
            user.setUsername("小凤");
            user.setPassword("145");
            user.setAge(30);
    
            //把user对象存储到mv对象中,同时底层也会将user存到request对象中
            mv.addObject("user", user);
    
            //跳转的页面: 会调用视图解析器解析这个视图
            mv.setViewName("success");
            return mv;
        } 
    ```
    
    注意:
        视图解析器会根据配置:
    ```
    <bean id="internalResourceViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/pages/"></property>
        <property name="suffix" value=".jsp"></property>
    </bean>
        ```
        自动去补全前缀和后缀,找到页面。

第二章:SpringMVC实现文件上传

2.1 文件上传的回顾

A form表单的enctype取值必须是:multipart/form-data
    enctype:是表单请求正文的类型
    默认值是:application/x-www-form-urlencoded
    
B method属性取值必须是Post

C 提供一个文件选择域<input type=”file” />

2.2 文件上传的原理分析

    当form表单的enctype取值不是默认值后,request.getParameter()将失效。
    
        enctype="application/x-www-form-urlencoded"时,form表单在request中的的正文内容是:
        key=value&key=value&key=value

    当form表单的enctype取值为Mutilpart/form-data时,请求正文内容就变成:
        每一部分都是MIME类型描述的正文
        
        -----------------------------7de1a433602ac              分界符
        Content-Disposition: form-data; name="userName"         协议头
        aaa                                                     协议的正文
        -----------------------------7de1a433602ac
        Content-Disposition: form-data; name="file";
        filename="C:\Users\zhy\Desktop\fileupload_demofile\b.txt"
        Content-Type: text/plain                                协议的类型(MIME类型)
        bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
        -----------------------------7de1a433602ac--

2.4 借助第三方组件实现文件上传

    使用Commons-fileupload组件实现文件上传,需要导入该组件相应的支撑jar包:
    
    Commons-fileupload和commons-io。
    
    。commons-io 不属于文件上传组件的开发jar文件,但Commons-fileupload 组件从1.1 
版本开始,它工作时需要commons-io包的支持。

    需要注入的坐标依赖:
    ```
    <dependency>
        <groupId>commons-fileupload</groupId>
        <artifactId>commons-fileupload</artifactId>
        <version>1.3.1</version>
    </dependency>
    <dependency>
        <groupId>commons-io</groupId>
        <artifactId>commons-io</artifactId>
        <version>2.4</version>
    </dependency>
    ```

2.5 传统方式上传文件

1. 前端JSP
```
<body>
    <h3>文件上传</h3>
    
    <form action="user/fileupload1" method="post" enctype="multipart/form-data">
        选择文件:<input type="file" name="upload"/>
        <input type="submit" value="上传"/>
    </form>
</body>
```

2. 后端Controller
```
@Controller
@RequestMapping("/user")
public class UserController {

    /**
     * 文件上传
     * @return
     */
    @RequestMapping("/fileupload1")
    public String fileupload1(HttpServletRequest request) throws Exception {
        System.out.println("文件上传...");
        
        //上传的位置
        String realPath = request.getSession().getServletContext().getRealPath("/uploads/");

        //判断该路径是否存在
        File file = new File(realPath);
        if(!file.exists()){
            //创建文件夹
            file.mkdirs();
        }

        //解析request对象,获取上传文件项。new 磁盘文件项工厂
        //文件项:表单上传文件项
        DiskFileItemFactory factory = new DiskFileItemFactory();
        ServletFileUpload upload = new ServletFileUpload(factory);

        //解析request:返回一个文件项list
        List<FileItem> items = upload.parseRequest(request);
        //遍历
        for (FileItem item : items) {
            //判断当前的item对象是否是上传文件项
            if(item.isFormField()){
                //普通表单项

            }else {
                //上传文件项
                //获取上传文件的名称
                String itemName = item.getName();

                //当传的是同名文件时:不覆盖,添加
                //把文件名称设一个唯一值:uuid
                String uuid = UUID.randomUUID().toString().replace("-", "");
                itemName = uuid+"-"+itemName;

                //完成文件上传
                item.write(new File(realPath, itemName));

                //删除临时文件:上传文件大于10kb,就会生成临时文件。小于10kb在内存中生成缓存(不用处理)。
                item.delete();
            }
        }
        return "success";
    }
}
```

分析一下代码:
    1.
    首先要明确:选择要上传的文件并且提交表单后,数据是先封装到Request域中
    
    2.
    String realPath = request.getSession().getServletContext().getRealPath("/uploads/");
    String realPath1 = request.getSession().getServletContext().getRealPath("");
    
    当部署的项目名是:
        day0615_springmvc02_fileupload:war expload,
    项目部署在工程源码目录下的target下
        E:\Idea_Projects\day0615_springmvc02_fileupload\target\day0615_springmvc02_fileupload
    那么当前当前项目的绝对根目录:
        E:\Idea_Projects\day0615_springmvc02_fileupload\target\day0615_springmvc02_fileupload\
    所以realPath:
        E:\Idea_Projects\day0615_springmvc02_fileupload\target\day0615_springmvc02_fileupload\uploads\
    所以realPath1:
        E:\Idea_Projects\day0615_springmvc02_fileupload\target\day0615_springmvc02_fileupload\
        
    当部署的项目名是:
        day0615_springmvc02_fileupload:war
    项目部署在tomcat目录下:
        D:\Developer_Tools\apache-tomcat-8.5.31\webapps\day0615_springmvc02_fileupload_war
    那么当前当前项目的绝对根目录:
        D:\Developer_Tools\apache-tomcat-8.5.31\webapps\day0615_springmvc02_fileupload_war\
    所以realPath:
        D:\Developer_Tools\apache-tomcat-8.5.31\webapps\day0615_springmvc02_fileupload_war\uploads\
    所以realPath1:
        D:\Developer_Tools\apache-tomcat-8.5.31\webapps\day0615_springmvc02_fileupload_war\
        
    
    3. new 磁盘文件项工厂和核心操作类
    DiskFileItemFactory factory = new DiskFileItemFactory();
    ServletFileUpload upload = new ServletFileUpload(factory);
    
    4. 核心操作类对象upload解析request对象,获取上传文件项
    List<FileItem> items = upload.parseRequest(request);
    
    5.当传的是同名文件时:不覆盖,添加。
      给文件名称设一个唯一值:uuid
      String uuid = UUID.randomUUID().toString().replace("-", "");
      itemName = uuid+"-"+itemName;
      
    6. 删除临时文件:上传文件大于10kb,就会生成临时文件。小于10kb在内存中生成缓存(不用处理)。
    item.delete();

2.6 springmvc上传文件

2.6.1 原理分析

1. 在springmvc.xml中先配置一个文件解析器
```
<!--配置文件解析器对象-->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
    <!--上传文件最大值.单位字节Byte: 1024Byte=1KB 104857600=1024*1024*100-->
    <property name="maxUploadSize" value="104857600"></property>
</bean>
```

2. 前端页面发送请求后,会先经过前端控制器。
    它会调文件解析器,然后文件解析器解析request中的文件上传项拿到上传文件对象。
传给fileupload2方法中的参数MultipartFile upload。
    注意参数MultipartFile upload 必须和表单提交的name名一致
2.6.2 代码实现
3. 前端JSP
```
<h3>SpringMVC文件上传</h3>
<form action="user/fileupload2" method="post" enctype="multipart/form-data">

    选择文件:<input type="file" name="upload"/><br/>
    <input type="submit" value="上传"/>

</form>
```

4. 后端Controller
```
@RequestMapping("/fileupload2")
//注意参数二MultipartFile upload 必须和表单提交的name名一致
public String fileupload2(HttpServletRequest request, MultipartFile upload) throws Exception {
    System.out.println("springMVC方式上传路径...");

    //上传的位置
    String path = request.getSession().getServletContext().getRealPath("/MVCUploads/");
    //判断该路径是否存在
    File file = new File(path);
    if(!file.exists()){
        file.mkdirs();
    }

    //不用我们自己写代码解析request,mvc帮我们解析

    //获取上传文件名称
    String filename = upload.getOriginalFilename();
    //给名称值设置一个唯一值
    String uuid = UUID.randomUUID().toString().replace("_", "");
    filename = uuid+"_"+filename;

    //完成文件上传
    upload.transferTo(new File(path, filename));

    return "success";
}
```

2.7 springmvc跨服务器方式的上传文件

2.7.1 跨服务器方式的上传文件

    在实际开发中,我们会有很多处理不同功能的服务器。例如:
        应用服务器:负责部署我们的应用 
        数据库服务器:运行我们的数据库 
        缓存和消息服务器:负责处理大并发访问的缓存和消息 
        文件服务器:负责存储用户上传文件的服务器。
        
    分服务器处理的目的是让服务器各司其职,从而提高我们项目的运行效率。

2.7.2 步骤

    1. 我们先部署图片服务器:FileUploadServer
        不用配置文件,设置一下端口,建一个上传文件夹。

    2.相关jar包:帮我们实现跨服务器上传文件
    在应用服务器day0615_springmvc02_fileupload 导入必要的jar包:
    ```
    <dependency>
      <groupId>com.sun.jersey</groupId>
      <artifactId>jersey-core</artifactId>
      <version>1.18.1</version>
    </dependency>
    <dependency>
      <groupId>com.sun.jersey</groupId>
      <artifactId>jersey-client</artifactId>
      <version>1.18.1</version>
    </dependency>
    ```
    
3. 接收服务器:设置读写权限
   web.xml
   ```
    <!DOCTYPE web-app PUBLIC
        "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
        "http://java.sun.com/dtd/web-app_2_3.dtd" >
    
    <!--suppress ALL -->
    <web-app>
      <display-name>Archetype Created Web Application</display-name>
    
      <servlet>
        <servlet-name>default</servlet-name>
        <servlet-class>org.apache.catalina.servlets.DefaultServlet</servlet-class>
        <init-param>
          <param-name>debug</param-name>
          <param-value>0</param-value>
        </init-param>
        <init-param>
          <param-name>readonly</param-name>
          <param-value>false</param-value>
        </init-param>
        <init-param>
          <param-name>listings</param-name>
          <param-value>false</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
      </servlet>
    </web-app>
   ```
   
   4. 在项目部署路径下创建一个接受目录

  5. 前端jsp  ---  应用服务器
    ```
    <h3>跨服务器方式的文件上传</h3>
    <form action="user/fileupload3" method="post" enctype="multipart/form-data">

        选择文件:<input type="file" name="upload"/><br/>
        <input type="submit" value="上传"/>

    </form>
    ```
    
  6. 后端Controller --- 应用服务器
  ```
    @RequestMapping("/fileupload3")
    //注意参数二MultipartFile upload 必须和表单提交的name名一致
    public String fileupload3(MultipartFile upload) throws Exception {
        System.out.println("跨服务器方式文件上传...");

        //1.定义上传文件服务器的路径
        String path = "http://192.168.3.126:8080/springmvc02/uploads";

        //获取上传文件名称
        String filename = upload.getOriginalFilename();
        //给名称值设置一个唯一值
        String uuid = UUID.randomUUID().toString().replace("_", "");
        filename = uuid+"_"+filename;


        //创建客户端对象
        Client client = Client.create();

        //和图片服务器进行连接:拿到一个web资源
        WebResource webResource = client.resource(path + "/" + filename);

        //跨服务器上传文件
        webResource.put(upload.getBytes());
        return "success";
    }
  ```

第三章:SpringMVC中的异常处理

3.1 SpringMVC处理异常的流程

    系统中异常包括两类:预期异常和运行时异常RuntimeException,
前者通过捕获异常从而获取异常信息,后者主要通过规范代码开发、测试通过手段减少运行时异常的发生。
    系统的dao、service、controller出现都通过throws Exception向上抛出,
最后由springmvc前端控制器交由异常处理器进行异常处理,如下图:

    在前端控制器捕获到有下面抛上来的异常后,交由异常处理器组件来处理
这个异常。目的是给客户端展示一个良好的错误提示页面。

3.2 演示自定义异常处理器

配置我们自定义的异常处理器类。

1. 编写自定义异常类(做提示信息)
    ```
    public class SysException extends Exception{

        //存储提示信息
        private String msg;
    
        public String getMsg() {
            return msg;
        }
    
        public void setMsg(String msg) {
            this.msg = msg;
        }
    
        public SysException(String msg) {
            this.msg = msg;
        }
    }
    ```


2. 编写异常处理器
```
public class SysExceptionResolver implements HandlerExceptionResolver {

    /**
     * 当Controller类将异常抛给DispatcherServlet后,DispatcherServlet
     * 会调用异常处理器组件来处理这个异常
     *
     * 我们自定义了一个异常处理器,在里面写我们的逻辑。
     * @param ex  接受我们在Controller抛出的异常
     */
    @Override
    public ModelAndView resolveException(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception ex) {

        //1.获取到异常对象
        SysException e = null;

        if(ex instanceof SysException){
            e = (SysException)ex;
        }else{
            e = new SysException("系统正在维护");
        }

        //注意返回值:ModelAndView。其中的View可以设置我们跳转的页面
        ModelAndView mv = new ModelAndView();
        mv.addObject("errorMsg", e.getMsg());
        
        //视图解析器会根据配置:自动去补全前缀和后缀,找到页面
        mv.setViewName("error");
        
        return mv;
    }
}
```
3. 配置异常处理器(跳转到提示页面)  
spingmvc.xml
```
<!--配置我自定义的异常处理器-->
<bean id="sysExceptionResolver" class="cn.itcast.exception.SysExceptionResolver"></bean>
```

第四章:SpringMVC中的拦截器

4.1 拦截器概述

    Spring MVC 的处理器拦截器类似于Servlet开发中的过滤器Filter,用
对处理器(Controller类)进行预处理和后处理。
    用户可以自己定义一些拦截器来实现特定的功能。放行之前会编写一写逻
辑代码(预处理),放行之后也会编写一写代码(后处理)。Controller执行
完之后,想往前跳就执行放行后的代码。
    
    拦截器链(Interceptor Chain):多个拦截器
    拦截器链就是将拦截器按一定的顺序联结成一条链。在访问被拦截的方
法或字段时,拦截器链中的拦截器就会按其之前定义的顺序被调用。
    (这里是它也是AOP思想的具体应用:将Controller类中的公共代码都抽
取出来,按照一定的顺序配置多个拦截器)

    拦截器和过滤器是有几分相似,但是也有区别,接下来我们就来说说他
们的区别:
    1.过滤器是servlet规范中的一部分,任何java web工程都可以使用。
是sun公司提供的web规范,SrpingMVC Web项目也可以使用过滤器技术。

    2.拦截器是SpringMVC框架自己的,只有使用了SpringMVC框架的工程才能用。
    
    3.过滤器在url-pattern中配置了/*之后,可以对所有要访问的资源拦截。
    4.拦截器它是只会拦截访问的控制器(Controller类中的方法)方法,
如果访问的是jsp,html,css,image或者js是不会进行拦截的。
    拦截器只拦Controller。

    我们要想自定义拦截器, 要求必须实现:HandlerInterceptor接口。

4.2 自定义拦截器的步骤

1. 第一步:编写一个拦截器类。
    
    编写一个普通类实现HandlerInterceptor接口
```
    public class MyInterceptor1 implements HandlerInterceptor {
    //发现实现这个接口,却不用重写方法。里面没有方法吗?
    //JDK1.8 接口增强。 接口的方法已经实现了

    /**
     * 预处理:controller中的方法执行前先走该方法。
     * return true放行,执行下一个拦截器,如果没有,执行Controller中的方法。
     * return false不放行:我们可以用参数中的req,resp跳转到某个页面。
     * 即Controller中的方法没执行,直接跳到某个页面。可给一些提示信息。
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("MyInterceptor1执行了...");

        return true;
    }
}
```
    
2. 第二步:配置拦截器  在springmvc.xml中配置
``
<!--配置拦截器-->
<mvc:interceptors>
    <mvc:interceptor>
        <!--要拦截的具体方法,没配置的剩下的方法就不拦-->
        <mvc:mapping path="/user/*"/>
        <!--不要拦截的方法,没配置的剩下的方法就拦
        <mvc:exclude-mapping path=""/>-->
        <!--配置我定义的拦截器对象-->
        <bean class="cn.itcast.interceptor.MyInterceptor1"></bean>
    </mvc:interceptor>
</mvc:interceptors>`
``

分析一下执行顺序:
    1.JSP发送请求user/testInterceptor,请求:
    Controller中的testInterceptor方法
    
    2. 被拦截器拦截到,执行了预处理后放行,执行下一个拦截器。
    
    3. 没有拦截器了,执行Controller中的testInterceptor方法

    4. testInterceptor的方法执行完,想跳转到success.jsp页面。
       发现拦截器中没有重写后处理方法,放行。
    
    5. 顺利走到success.jsp页面。

4.3 分析一下HandlerInterceptor接口中的三个方法

Controller类中的方法:跳转到success.jsp页面
```
@Controller
@RequestMapping("/user")
public class UserController {

    @RequestMapping("/testInterceptor")
    public String testException() {

        System.out.println("testInterceptor执行了...");
        
        return "success";
    }
}
```


1. 预处理:不放行,转发到其他页面
```
public class MyInterceptor1 implements HandlerInterceptor {
    //发现实现这个接口,却不用重写方法。里面没有方法吗?
    //JDK1.8 接口增强。 接口的方法已经实现了

    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("MyInterceptor1执行了...");

        //如果有问题,不放行,跳转到其他页面
        request.getRequestDispatcher("/WEB-INF/pages/error.jsp").forward(request,response);
        return false;
    }
}
```

2.后处理
    
    没有返回值,所以一定会向前面放行。
    
    在前置处理放行后,Controller中的方法执行完,想要跳转到某个页面,
会被后置处理拦截到。

```
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
    System.out.println("MyInterceptor1执行了后置处理...");
}
```

    并且如果你给他加一个跳转页面的操作,那么他就不会跳转到原来的页面了。
    
```
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
    System.out.println("MyInterceptor1执行了后置处理...");

    //跳转到一个新的页面,原来的页面就不跳转了
    request.getRequestDispatcher("/WEB-INF/pages/error.jsp").forward(request,response);
}
```

    注意:虽然拦截器中的后处理会设置一个新的跳转页面,但是因为是从
Controller中的方法执行过来的,所以Controller中的跳转页面的操作会最
后执行,只是不跳转页面。(会执行页面中的java脚本,或者一些输出语句)

3. afterCompletion  最终处理

```
/**
 * success.jsp页面执行后,该方法会执行
 * 执行最终处理时,不能再跳页面了。
 * @param request
 * @param response
 * @param handler
 * @param ex
 * @throws Exception
 */
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
    System.out.println("MyInterceptor1执行了最终处理....");
}
```

注意:Controller中方法要跳转的页面执行完后,才会执行该方法

4.4 两个拦截器

1. 写两个拦截器:
1.1 拦截器1
```
public class MyInterceptor1 implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("MyInterceptor1执行了前置处理...1111");

        //如果有问题,不放行,跳转到其他页面
        //request.getRequestDispatcher("/WEB-INF/pages/error.jsp").forward(request,response);
        return true;
    }

    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("MyInterceptor1执行了后置处理...1111");

        //跳转到一个新的页面,原来的页面就不跳转了
        request.getRequestDispatcher("/WEB-INF/pages/error.jsp").forward(request,response);
    }

    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("MyInterceptor1执行了最终处理....11111");
    }
}
```

1.2  拦截器2
```
public class MyInterceptor2 implements HandlerInterceptor {

    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("MyInterceptor2执行了前置处理...2222");

        //如果有问题,不放行,跳转到其他页面
        //request.getRequestDispatcher("/WEB-INF/pages/error.jsp").forward(request,response);
        return true;
    }

    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("MyInterceptor2执行了后置处理...2222");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("MyInterceptor2执行了最终处理....2222");
    }
}
```

2. 配置两个拦截器

拦截器1,Interceptor1会先拦截,
    打印:MyInterceptor1执行了前置处理...1111  然后放行。
拦截器2,Interceptor2拦截到,
    打印:MyInterceptor2执行了前置处理...2222  然后放行。
执行Controller中的testException方法:
    打印:testInterceptor执行了...  然后跳转success.jsp
被拦截器2又拦截到,执行后置处理:
    打印:MyInterceptor2执行了后置处理...2222 继续向前走
被拦截器1又拦截到,执行后置处理:
    打印:MyInterceptor1执行了后置处理...1111 跳转到响应页面
执行跳转页面上的打印输出操作
    打印:error.jsp执行了
执行Controller中的testException方法要跳转的页面中的操作
    打印:success.jsp执行了
执行拦截器2的最终处理:
    打印:MyInterceptor2执行了最终处理....2222
执行拦截器1的最终处理:
    打印:MyInterceptor1执行了最终处理....1111
    
```
public String testException() {

    System.out.println("testInterceptor执行了...");

    return "success";
}
```