SpringMVC 深入

1. 响应数据和结果视图

1.1 返回值分类

1.1.1 返回 String

  1. 原理

    controller 方法返回字符串可以指定逻辑视图名,通过视图解析器解析为物理视图地址。

  2. 实现

    1. 在 springmvc 中配置视图解析器

      <!-- 配置视图解析器 -->
      <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>
      
    2. UserController

      @Controller
      @RequestMapping("/user")
      public class UserController {
          @RequestMapping("/testString")
          public String testString(Model model){
              // 模拟从数据库中查询出 User 对象
              User user = new User();
              user.setName("张三");
              user.setAge(18);
              user.setGender("男");
              model.addAttribute("user1",user);
              // 指定逻辑视图名,经过视图解析器解析为 jsp 物理路径:/WEB-INF/pages/success.jsp
              return "success";
          }
      }
      
      
    3. success.jsp

      <%@ page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false" %>
      <html>
      <head>
          <title>Title</title>
      </head>
      <body>
          <h1>访问成功</h1>
          ${user1}
      </body>
      </html>
      

1.1.2 返回 void

  1. 原理

    如果控制器的方法返回值编写成 void,执行程序报 404 的异常,因为默认查找 JSP 页面没有找到。(默认查找 @RequestMapping 中配置的地址.jsp)

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

    @Controller
    @RequestMapping("/user")
    public class UserController {
        @RequestMapping("/testVoid")
        public void testVoid(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            // 请求转发
            // request.getRequestDispatcher("/WEB-INF/pages/success.jsp").forward(request, response);
            // 重定向
            // response.sendRedirect(request.getContextPath()+"/index.jsp");
        }
    }
    
    

1.1.3 返回 ModelAndView 对象

  1. 原理

    ModelAndView 对象是 Spring 提供的一个对象,可以用来调整具体的 JSP 视图

  2. 实例

    @Controller
    @RequestMapping("/user")
    public class UserController {
        @RequestMapping("/testModelAndView")
        public ModelAndView testModelAndView(){
            // 创建ModelAndView对象
            ModelAndView mv = new ModelAndView();
    
            // 模拟从数据库中查询出 User 对象
            User user = new User();
            user.setName("张三");
            user.setAge(18);
            user.setGender("男");
    
            // 把 user 对象存储到 mv 对象中,也会把 user 对象存入到 request 对象
            mv.addObject("user1",user);
    
            // 跳转到哪个页面
            mv.setViewName("success");
    
            return mv;
        }
    }
    
    

1.2 转发和重定向

  1. 原理

    使用关键字的方式进行转发或者重定向

  2. 实现

    /**
     * 使用关键字的方式进行转发或者重定向
     * @return
     */
    @RequestMapping("/testForwardOrRedirect")
    public String testForwardOrRedirect(){
        // 请求的转发
        // return "forward:/WEB-INF/pages/success.jsp";
    
        // 重定向
        return "redirect:/index.jsp";
    }
    

1.3 ResponseBody 响应 JSON 数据

1.3.1 作用

@ResponseBody 注解实现将 controller 方法返回 JavaBean 转换为 JSON 响应给客户端。

1.3.2 需求

使用 @RequestBody 注解获取请求体数据并封装为 JavaBean,再使用 @ResponseBody 注解实现将 controller 方法返回的 JavaBean 转换为 JSON 响应给客户端,从而实现客户端和服务端的交互。

1.3.3 环境搭建

  1. 因为 DispatcherServlet(前端控制器)会拦截所有的资源,所以静态资源(img、css、js)也会被拦截,从而静态资源不能被使用。所以要先配置静态资源不被拦截,在 springmvc.xml 配置文件添加如下配置

    <!-- 设置静态资源不过滤 -->
    <mvc:resources location="/css/" mapping="/css/**"/> <!-- 样式 -->
    <mvc:resources location="/images/" mapping="/images/**"/> <!-- 图片 -->
    <mvc:resources location="/js/" mapping="/js/**"/> <!-- javascript -->
    
  2. 导入 jackson 的依赖

    Springmvc 默认用 MappingJacksonHttpMessageConverter 对 json 数据进行转换,需要加入 jackson 的包。

    <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>
    

1.3.4 实现

User

public class User implements Serializable {
    private String name;
    private Integer age;
    private String gender;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public String getGender() {
        return gender;
    }

    public void setGender(String gender) {
        this.gender = gender;
    }

    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", gender='" + gender + '\'' +
                '}';
    }
}

response.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
    <script src="js/jquery-3.3.1.min.js"></script>
</head>

<script>
    function fun() {
        $.ajax({
            // 编写json格式,设置属性和值
            url:"user/testJSON",
            contentType:"application/json;charset=UTF-8",
            data:JSON.stringify({"name":"jack","age":19,"gender":"man"}),
            dataType:"json",
            type:"post",
            success:function(data){
                alert(data);
                alert(data.name);
            }
        });

    }
</script>
<body>
    <input type="button" onclick="fun()" value="发送ajax请求">
</body>
</html>

UserController

@Controller
@RequestMapping("/user")
public class UserController {
    @RequestMapping("/testJSON")
    public @ResponseBody User testJSON(@RequestBody User user){
        System.out.println(user);
        return user;
    }
}

2. SpringMVC 实现文件上传

2.1 传统方式文件上传

  1. 导入文件上传的依赖

    <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. 编写文件上传的 jsp

    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <html>
    <head>
        <title>Title</title>
    </head>
    <body>
    <h3>文件上传</h3>
    <form action="user/fileupload1" method="post" enctype="multipart/form-data">
        选择文件:<input type="file" name="upload"/><br/>
        <input type="submit" value="上传文件"/>
    </form>
    </body>
    </html>
    
    
  3. 编写文件上传的控制器

    @Controller
    @RequestMapping("/user")
    public class UserController {
        @RequestMapping("/fileupload1")
        public String fileuoload1(HttpServletRequest request) throws Exception {
            // 使用fileupload组件完成文件上传
            // 上传的位置
            String path = request.getSession().getServletContext().getRealPath("/uploads/");
            // 判断,该路径是否存在
            File file = new File(path);
            if(!file.exists()){
                // 创建该文件夹
                file.mkdirs();
            }
    
            // 解析request对象,获取上传文件项
            DiskFileItemFactory factory = new DiskFileItemFactory();
            ServletFileUpload upload = new ServletFileUpload(factory);
            // 解析request
            List<FileItem> items = upload.parseRequest(request);
            // 遍历
            for(FileItem item:items){
                // 进行判断,当前item对象是否是上传文件项
                if(item.isFormField()){
                    // 说明普通表单向
                }else{
                    // 说明上传文件项
                    // 获取上传文件的名称
                    String filename = item.getName();
                    // 把文件的名称设置唯一值,uuid
                    String uuid = UUID.randomUUID().toString().replace("-", "");
                    filename = uuid+"_"+filename;
                    // 完成文件上传
                    item.write(new File(path,filename));
                    // 删除临时文件
                    item.delete();
                }
            }
    
            return "success";
        }
    }
    
    

2.2 SpringMVC 传统方式文件上传

  1. 原理

    在这里插入图片描述

  2. 实现

    1. 在 springmvc.xml 中配置文件解析器对象

      <!-- 配置文件解析器对象,要求id名称必须是multipartResolver -->
      <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
          <property name="maxUploadSize" value="10485760"/>
      </bean>
      
    2. 编写文件上传的 jsp

      <%@ page contentType="text/html;charset=UTF-8" language="java" %>
      <html>
      <head>
          <title>Title</title>
      </head>
      <body>
      <h3>文件上传</h3>
      <form action="user/fileupload2" method="post" enctype="multipart/form-data">
          选择文件:<input type="file" name="upload"/><br/>
          <input type="submit" value="上传文件"/>
      </form>
      </body>
      </html>
      
      
    3. 编写文件上传的控制器

      @RequestMapping("/fileupload2")
      public String fileuoload2(HttpServletRequest request, MultipartFile upload) throws Exception {
          // 使用fileupload组件完成文件上传
          // 上传的位置
          String path = request.getSession().getServletContext().getRealPath("/uploads/");
          // 判断,该路径是否存在
          File file = new File(path);
          if(!file.exists()){
              // 创建该文件夹
              file.mkdirs();
          }
      
          // 获取上传文件的名称
          String filename = upload.getOriginalFilename();
          // 把文件的名称设置唯一值,uuid
          String uuid = UUID.randomUUID().toString().replace("-", "");
          filename = uuid+"_"+filename;
          // 完成文件上传
          upload.transferTo(new File(path,filename));
      
          return "success";
      }
      

2.3 SpringMVC 跨服务器方式文件上传

  1. 跨服务器文件上传的目的

    在实际开发中,我们会有很多处理不同功能的服务器。 分服务器处理的目的是让服务器各司其职,从而提高我们项目的运行效率。

    在这里插入图片描述

  2. 实现

    1. 搭建两台服务器,一台作为应用服务器(端口号:8080),一台作为文件服务器(端口号:9090)

    2. 在 springmvc.xml 中配置文件解析器对象

      <!-- 配置文件解析器对象,要求id名称必须是multipartResolver -->
      <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
          <property name="maxUploadSize" value="10485760"/>
      </bean>
      
    3. 在应用服务器导入开发所需的依赖

      <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>
      
    4. 在应用服务器编写文件上传的 JSP 页面

      <%@ page contentType="text/html;charset=UTF-8" language="java" %>
      <html>
      <head>
          <title>Title</title>
      </head>
      <body>
      <h3>文件上传</h3>
      <form action="user/fileupload3" method="post" enctype="multipart/form-data">
          选择文件:<input type="file" name="upload"/><br/>
          <input type="submit" value="上传文件"/>
      </form>
      </body>
      </html>
      
      
    5. 在应用服务器编写文件上传的控制器

      /**
       * 跨服务器文件上传
       * @return
       */
      @RequestMapping("/fileupload3")
      public String fileuoload3(MultipartFile upload) throws Exception {
          // 定义上传文件服务器路径
          String path = "http://localhost:9090/uploads/";
      
          // 说明上传文件项
          // 获取上传文件的名称
          String filename = upload.getOriginalFilename();
          // 把文件的名称设置唯一值,uuid
          String uuid = UUID.randomUUID().toString().replace("-", "");
          filename = uuid+"_"+filename; 
      
          // 创建客户端的对象
          Client client = Client.create();
      
          // 和图片服务器进行连接
          WebResource webResource = client.resource(path + filename);
      
          // 上传文件
          webResource.put(upload.getBytes());
      
          return "success";
      }
      
  3. 几种报错问题的解决办法

    以下三种错误我都遇到了:

    1. 错误 400

      这是由于你上传的文件名是中文,解决办法就是改成英文

    2. 错误403

      1. 打开你本地的 apache-tomcat-x.x.x\conf 中的 web.xml,找到下面的内容

        <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>listings</param-name>
                <param-value>false</param-value>
            </init-param>
            <load-on-startup>1</load-on-startup>
        </servlet>
        
      2. 添加 readonly:flase 标签,如下:

        <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>
        
      3. 重新启动服务器

    3. 错误 409

      在文件服务器中的 target/项目名下,新建一个 uploads 文件夹

3. SpringMVC 的异常处理

3.1 异常处理的思路

系统的 dao、service、controller 出现异常都向上抛出,最后由 springmvc 前端控制器交由异常处理器进行异常处理

在这里插入图片描述

3.2 实现步骤

  1. 编写异常类

    package com.zt.exception;
    
    public class SysException extends Exception {
        private String message;
    
        public SysException(String message) {
            this.message = message;
        }
    
        @Override
        public String getMessage() {
            return message;
        }
    
        public void setMessage(String message) {
            this.message = message;
        }
    }
    
    
  2. 编写错误页面

    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <html>
    <head>
        <title>Title</title>
    </head>
    <body>
        ${errorMsg}
    </body>
    </html>
    
    
  3. 编写自定义异常处理类

    public class SysExceptionResolver implements HandlerExceptionResolver {
        @Override
        public ModelAndView resolveException(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) {
            // 获取到异常对象
            SysException sysException = null;
            if(e instanceof SysException){
                sysException = (SysException)e;
            }else{
                e = new SysException("系统正在维护....");
            }
            // 创建ModelAndView对象
            ModelAndView mv = new ModelAndView();
            mv.addObject("errorMsg",e.getMessage());
            mv.setViewName("error");
            return mv;
        }
    }
    
    
  4. 在 springmvc.xml 中配置异常处理器

    <!--配置异常处理器-->
    <bean id="sysExceptionResolver" class="com.zt.exception.SysExceptionResolver"></bean>
    
    
  5. 编写控制器

    @Controller
    @RequestMapping("/user")
    public class UserController {
        @RequestMapping("/testException")
        public String testException() throws SysException {
            try {
                int a = 1/0;
            } catch (Exception e) {
                e.printStackTrace();
                throw new SysException("出错了");
            }
            return "success";
        }
    }
    
  6. 编写 JSP 页面

    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <html>
    <head>
        <title>Title</title>
    </head>
    <body>
        <a href="user/testException">testException</a>
    </body>
    </html>
    
    

4. SpringMVC 的拦截器

4.1 拦截器的概念

Spring MVC 的处理器拦截器类似于 Servlet 开发中的过滤器 Filter,用于对处理器进行预处理和后处理。

在这里插入图片描述

4.2 拦截器和过滤器的区别

  1. 过滤器是 Servlet 规范的一部分,任何 java web 工程都可以使用过滤器。拦截器是 SpringMVC 框架独有的。
  2. 过滤器可以拦截任何资源。拦截器只会对控制器中的方法进行拦截。

4.3 拦截器链的概念

拦截器链,连接器链就是将拦截器按着一定的顺序结成一条链,在访问被拦截的方法时,拦截器链中的拦截器会按着定义的顺序执行。

4.4 拦截器的实现

  1. 自定义拦截器

    public class MyIntercepter implements HandlerInterceptor {
        /**
         * 预处理,controller方法执行前
         * return true 放行,执行下一个拦截器,如果没有,执行 controller 中的方法
         * return false 不放行
         * @param request
         * @param response
         * @param handler
         * @return
         */
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
            System.out.println("preHandle...");
            return true;
        }
    
        /**
         * 后处理方法,controller方法执行后,success.jsp执行之前
         * @param request
         * @param response
         * @param handler
         * @param modelAndView
         */
        @Override
        public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) {
            System.out.println("postHandle...");
        }
    
        /**
         * success.jsp页面执行后,该方法会执行
         * @param request
         * @param response
         * @param handler
         * @param ex
         */
        @Override
        public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
            System.out.println("afterCompletion...");
        }
    }
    
    
  2. 在 springmvc.xml 中配置拦截器

    <!--设置拦截器-->
    <mvc:interceptors>
        <mvc:interceptor>
            <!--要拦截的方法-->
            <mvc:mapping path="/user/*"/>
            <!--配置拦截对象-->
            <bean class="com.zt.intercepter.MyIntercepter"></bean>
        </mvc:interceptor>        
    </mvc:interceptors>
    
  3. 编写控制器

    @Controller
    @RequestMapping("/user")
    public class UserController {
        @RequestMapping("/testInterceptor")
        public String testInterceptor(){
            System.out.println("testInterceptor...");
            return "success";
        }
    }
    
  4. 打印结果

    preHandle...
    testInterceptor...
    postHandle...
    success.jsp
    afterCompletion...
    
    

4.5 HandlerInterceptor 接口中的方法

  1. preHandle 方法是 controller 方法执行前拦截的方法

    1. 可以使用 request 或者 response 跳转到指定的页面

    2. return true 放行,执行下一个拦截器,如果没有拦截器,执行 controller 中的方法。

    3. return false 不放行,不会执行 controller 中的方法。

  2. postHandle 是 controller 方法执行后执行的方法,在 JSP 视图执行前。

    1. 可以使用 request 或者 response 跳转到指定的页面
    2. 如果指定了跳转的页面,那么 controller 方法跳转的页面将不会显示。
  3. postHandle 方法是在 JSP 执行后执行

    1. request 或者 response 不能再跳转页面了

4.6 配置拦截器链

  1. 在配置一个自定义拦截器

  2. 在 springmvc.xml 中再配置一个拦截器

    <!--设置拦截器-->
    <mvc:interceptors>
        <mvc:interceptor>
            <!--要拦截的方法-->
            <mvc:mapping path="/user/*"/>
            <!--配置拦截对象-->
            <bean class="com.zt.intercepter.MyIntercepter"></bean>
        </mvc:interceptor>
        <mvc:interceptor>
            <!--要拦截的方法-->
            <mvc:mapping path="/user/*"/>
            <!--配置拦截对象-->
            <bean class="com.zt.intercepter.MyIntercepter2"></bean>
        </mvc:interceptor>
    </mvc:interceptors>
    
发布了64 篇原创文章 · 获赞 20 · 访问量 6494

猜你喜欢

转载自blog.csdn.net/bm1998/article/details/102490615