• SpringMVC入门学习三


    今天是Springmvc学习的第三天,今天我将主要介绍一下:

    • 常用注解的使用
    • 关于非post、get请求的处理
    • 文件上传与下载
    • 拦截器

     

    常用注解的使用

    老大在此

    @Controller

    @Controller放在类的上面,用于将一个类标记为Controller,处理DispatcherServlet分发的请求,当然真正处理请求的还是使用@RequestMapping注解的方法。


    处理request URI的注解

    @RequestMapping

    @RequestMapping是一个用来处理请求地址映射的注解,可用于类上面【代表模块】和方法上面【代表业务】

    里面参数属性,其中所有的参数都可以传入数组的形式:

    1. value:代表请求的地址如"/login",可以写多个【使用数组】,只写它时,可以省略不写

    2. name:value的别名,作用是一样的。

    3. method:请求的method类型,里面要传入RequestMethod类型的数据,如GET,POST,PUT等等,可以指令多个(使用数组传递即可)
      它的衍生品:

      • @GetMapping:专门处理get请求
      • @PostMapping:专门处理Post请求
    4. params:设置需要的参数,如果没有,请求就过不去,还可以设置参数是否等于或则不等于

      // 当穿过来的参数data不为not才进行响应
      @RequestMapping(value = "/test1",params = "data!=not")
      // 当穿过来的参数data为hava才进行响应
      @RequestMapping(value = "/test1",params = "data=have")
      

      ps:中间注意别打空格

    5. headers
      指定request中必须包含某些指定的header值,该方法才会处理请求。

      @RequestMapping(value="/hello",headers="Referer=https://baidu.com")
      
    6. consumes 消费者,指令请求提交内容的类型(Content-Type),例如可以限令必须为application/json;charset=UTF-8,指令多个使用数组形式

    7. produces 生产者,指令放回的内容的类型,但是必须是请求头所包含的类型

    @PathVariable

    这个是restful风格的请求方式,用于将URL中的模板变量映射到方法的参数上面,这个在我的上一篇博客有所介绍。


    共享数据

    @ModelAttribute

    @ModelAttribute注解可以被应用在方法参数上面和方法声明上面。其中被@ModelAttribute注解的方法会优先于controller方法(被@RequestMapping注解)之前执行,因为模型对象优先于controller方法创建。

    简单的来说,@ModelAttribute有两个作用,将数据保存在model上面【标注在方法上面】,将数据从model取出来【标注在参数里面】

    将数据保存在model上面

    1. 使用@ModelAttribute注解无返回值方法

      @ModelAttribute
      public void model1(Model model){
          model.addAttribute("modle1","你好呀");
      }
      
      @RequestMapping("/modleC")
      public String modleC(){
          return "test";
      }
      

      其实这个就相当于

      @RequestMapping("/modleC")
      public String modleC(Model model){
          model.addAttribute("modle1","你好呀");
          return "test";
      }
      
    2. 注解有返回值的方法

      /**
       * 注解有返回值的
       * 在这种情况下,放回值对象会默认的放在隐藏的Model中。
       * 其key为放回值 **类型** 首字母小写,当然也可以在@ModelAttribute
       * 加上value或则name的属性来指令key
       * @return
       */
      @ModelAttribute
      public String modeler02(){
          String Hello = "你好呀";
          return Hello;
          //在这里面key为string
      }
      
    3. 与@RequestParam结合使用
      @RequestParam的具体用法将在下面介绍

      加入此时我们发送了一个这样的请求:/test/name=googboy

      // 取出get请求的参数name,将其存入modle中
      @ModelAttribute("name")
      public String modeler02(@RequestParam()String name){
          return name;
      }
      
      @RequestMapping("/test")
      public String test(){
          return "test";
      }
      

    将数据从model取出来
    假如此时有了一个model,key为name,value为Tom数据,那么我们可以通过@ModelAttribute将数据取出来。

    @RequestMapping("/test")
    public String test(@ModelAttribute("name")String name){
        // 输出是  名字是Tom
        System.out.println("名字是"+name);
        return "test";
    }
    

    保存数据

    @SessionAttributes

    假如希望在多个请求之间共用数据,则可以在控制器类上面标注一个@SessionAttributes,其中value指令是model中的哪一个存入session,type为数据类型,两者都可以放数组类型的数据。


     

    取出数据

    @SessionAttribute

    @SessionAttribute的作用很简单,就是获得session的值

    假如此时有一个session key为name,value为Tom,获得session

    @RequestMapping("/test2")
    public String test2(@SessionAttribute("name") String name){
        System.out.println("session结果是"+name);
        return "test";
    }
    

    假如此时不存在就会报错,这是可以使用required来确定是否必须

    @RequestMapping("/test2")
        //非必须,同时value/name必须加上
        public String test2(@SessionAttribute(value = "name",required = false) String name){
            System.out.println("session结果是"+name);
            return "test";
        }
    

    对于取出参数的处理

    @RequestParam

    @RequestParam主要是用于在Springmvc后台控制层获得参数,类似request.getParameter("name")

    它有下列参数

    • value:value表示要取出的参数,当只有value这个参数时,可以省略不写。

    • name:value的别名,作用和value一样。

    • required:默认值是true,当传入的参数值不存在时,程序就会报错,这时候就可以将required设置为false。

    • defaultValue:当设置defaultValue时,会自动的将required设置为false,如果此时请求参数不存在,就会默认的将参数设置为defaultValue。当然,如果参数存在,defaultValue也就是不会发挥作用了。

    举个栗子:

    @RequestMapping("/test3")
    public String test3(@RequestParam(value = "name",defaultValue = "TomCat") String name){
        // 请求网址是: /test3
        // 输出是  名字是TomCat
        System.out.println("名字是"+name);
        return "test";
    }
    

    @RequestBody

    这时候大家可能会问,既然有@RequestParam来处理方式了,为什么我们还要使用@RequestBody来获取参数呢?

    这个主要主要是因为他们处理数据的类型Content-Type不一样

    • @RequestParam:处理application/x-www-form-urlencoded编码的内容,提交方式为Get,Post。

    • @RequestBody:通常是来处理非application/x-www-form-urlencoded编码格式的内容的数据,比如说application/jsonapplication/xml

    既然是处理json数据,那么就需要使用json的jar包。

    <!--====>jackson包导入start-->
    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-databind</artifactId>
        <version>2.9.7</version>
    </dependency>
    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-core</artifactId>
        <version>2.9.7</version>
    </dependency>
    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-annotations</artifactId>
        <version>2.9.7</version>
    </dependency>
    <!--====>jackson包导入end-->
    

    当发了一个如下所示的json请求时

    $(document).ready(function () {
        $("button").click(function () {
            $.ajax({
                url: "/test4",
                type:"post",
                data:JSON.stringify({
                    "name":"TODO"
                }),
                dataType:"json",
                contentType:"application/json; charset=utf-8",
                success:function (data) {
                    console.log(data)
                }
            });
        });
    });
    

    controller代码,在参数里面加上@RequestBody:

    @RequestMapping("/test4")
    public String test4(@RequestBody String name){
        // 名字是{"name":"TOFO"}
        System.out.println("名字是"+name);
        return "test";
    }
    

    对于返回数据的处理

    @ResposeBody

    返回json数据

    在大多数的情况下,我们不一定是想返回一个视图,只想返回一个json数据或者说xml数据,那么这时候我们就要使用@RequestBody了。@ResponseBody注解被应用于方法上,标志着响应会写到响应体里面去,而不是放到视图Model里面去。

    首先在springmvc配置文件中,启动`<mvc:annotation-driven/>`

    那么就可以这样使用:

    @RequestMapping(value = "/test4")
    @ResponseBody
    public User test4(@RequestBody String name){
        // 名字是{"name":"TOFO"}
        System.out.println("名字是"+name);
        User u = new User();
        u.setName("小明");
        u.setAge("17");
        return u;
    }
    

    返回xml数据

    要导入额外的jacksonxml jar包

    <dependency>
        <groupId>com.fasterxml.jackson.dataformat</groupId>
        <artifactId>jackson-dataformat-xml</artifactId>
        <version>2.9.7</version>
    </dependency>
    
    // 由于要返回xml格式的数据,所以需要设置produces为application/xml
    // 使用MediaType里面的静态常量,可以防止写错
    @ResponseBody
    @RequestMapping(value = "/test5",produces = org.springframework.http.MediaType.APPLICATION_XML_VALUE)
    public User test5(){
        User u = new User();
        u.setName("小红");
        u.setAge("18");
        return u;
    }
    

    SpringMVC关于非post、get请求的处理

    在HTML5的规范中,表单元素唯二允许的HTTP的方法是GET和POST,但是如果我们想使用PUT或则DELET方法,那怎么办呢?

    • 在浏览器中装作自己是post或则get请求,实际上是PUT请求
    <!-- 装成自己是post请求 -->
    <form action="/put" method="post">
        <!-- 实际上想发送的PUT请求 -->
        <input type="hidden" name="_method" value="PUT">
        姓名 <input  name="name" type="text">
        <input type="submit" value="提交">
    </form>
    
    • 在web.xml文件中,从_method找到真正的http请求

    加入

    <filter>
        <!-- 原理就是从_method找到真正想要http请求,然后分发给controller 
             所以这个必须写在DispatcherServlet的前面
        -->
        <filter-name>HiddenHttpMethodFilter</filter-name>
        <filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>HiddenHttpMethodFilter</filter-name>
        <servlet-name>springmvc</servlet-name>
    </filter-mapping>
    
    <!--注册一个前端控制器-->
    <servlet>
        <servlet-name>springmvc</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <!--上下文配置的位置的制定-->
            <param-name>contextConfigLocation</param-name>
            <!--此时是在类路径下面去寻找-->
            <param-value>classpath:springmvc.xml</param-value>
        </init-param>
    </servlet>
    
    <!--servlet的映射配置-->
    <servlet-mapping>
        <servlet-name>springmvc</servlet-name>
        <!--这里统一写/-->
        <url-pattern>/</url-pattern>
    </servlet-mapping>
    

    文件上传与下载

    文件上传

    文件上传同时也是使用form表单进行提交,一般我们的表单的提交是文本型的数据,实际上就是进行字符串的拼接。那么文件便不是这样的进行处理,这时候我们就要使用到multipart表单了。

    首先我们的配置一个MultipartResolver来告诉DispatchServlet如何来处理multipart请求。

    在Servlet3.0中,Spring会默认注册一个StandardServletMultipartResolver,我们只需要在web.xml启用<multipart-config>就行。

    <multipart-config>
        <location>/tmp</location>
    </multipart-config>
    
    属性作用
    location 上传文件所存放的临时目录。必须指定
    max-file-size 文件的最大大小,单位为字节。默认没有限制
    max-request-size 请求的最大大小,单位为字节。默认没有限制
    file-size-threshold 文件大小阈值,当大于这个阈值时将写入到磁盘,否则在内存中。默认值为0

    前端代码

    <form action="/upload" method="post" enctype="multipart/form-data">
        文件 <input type="file" name="file"/>
        <input type="submit" value="提交">
    </form>
    

    控制文件上传的代码

    @RequestMapping("/upload")
    public String upload(@RequestParam("file")MultipartFile file, HttpServletRequest request) {
        // 假如没有文件
        if (!file.isEmpty()) {
            // 获得文件原始名字
            String fileName = file.getOriginalFilename();
            // 获得文件的后缀名
            String fileSuffix = fileName.substring(fileName.lastIndexOf("."));
            // 取新的名字 UUID可以保证产生的名字不一样,产生全球唯一的ID
            String newName = UUID.randomUUID() + fileSuffix;
            // 获取文件存储目录地址
            String filePath = request.getSession().getServletContext().getRealPath("/upload");
    
            File fileForPath = new File(filePath);
            // 如果文件不存在
            if (!fileForPath.exists()) {
                // 新建文件
                fileForPath.mkdir();
            }
    
            File targetFile = new File(filePath, newName);
            try {
                // 将文件写入
                file.transferTo(targetFile);
            } catch (IOException e) {
                e.printStackTrace();
            }
        } else {
            System.out.println("文件不存在");
        }
        return "test";
    }
    

    文件下载

    @RequestMapping("down")
    public String down(HttpServletResponse response,HttpServletRequest request){
    
        // 假如我的文件在/home/xiaohiu/images/鬼刀.jpg
        String fileName= "鬼刀.jpg";
    
        String parentPath = "/home/xiaohiu/images";
    
        // 这时候path.toString就是/home/xiaohiu/images/鬼刀.jpg了
        Path path = Paths.get(parentPath,fileName);
    
        // 假如文件存在
        if (Files.exists(path)){
            // 取出后缀名 jpg
            String fileSuffix = fileName.substring(fileName.lastIndexOf(".")+1);
            // 设置ContentType,只有设置它,才会去下载
            response.setContentType("application/"+fileSuffix);
    
            try {
                // 添加头信息 同时对文件名重新编码防止中文名乱码
                response.addHeader("Content-Disposition","attachment;filename="+new String(fileName.getBytes("utf-8"),"ISO8859-1"));
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            }
    
            try {
            // 使用输出流写出去
                Files.copy(path,response.getOutputStream());
            } catch (IOException e) {
                e.printStackTrace();
            }
        }else {
            System.out.println("文件不存在");
        }
        return "test";
    }
    

    SpringMVC拦截器

    拦截器实在我们请求之前做检查,同时有权决定我们接下来是否继续,同时也可以对我们的请求进行加工处理,可以设置多个拦截器。

    看到这里,是不是想到了我在前面介绍的AOP,其实我们可以理解为Spring拦截器是SpringMVC对AOP的一种实现方式。AOP是通过配置<aop:config>进行配置,而拦截器是通过<mvc:interceptors>进行配置。

    那么我们怎么实现呢?

    1. 实现HandlerInterceptorAdapter接口

      在SpringMVC中,为实现拦截器功能,有两种方式,一个是实现HandlerInterceptor接口,第二个是实现WebRequestInterceptor接口,这里我们选择使用HandlerInterceptor。

      在HandlerInterceptor接口中,定义了三个方法

      • preHandle():请求之前调用,返回true或则false,返回true,接下来的postHandle和afterCompletion才会起作用。

      • postHandle():在请求之后调用,也就是controller方法执行完后再调用,但是却是在DispatcherServlet进行视图返回渲染之前被调用。也就是说,这个可以对modle进行操作。

      • afterCompletion():在DispatcherServlet进行完试图渲染之后才执行。

      public class TestInterceptor implements HandlerInterceptor {
      
          @Override
          public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
      
              System.out.println("请求前进行拦截");
      
              // 返回true才会查找下一个拦截器,如果没有下一个拦截器,则返回controller
              return true;
          }
      
          @Override
          public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
              System.out.println("视图渲染前进行拦截");
          }
      
          @Override
          public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
              System.out.println("视图渲染后进行拦截");
          }
      }
      
    2. 拦截器的配置

      在springmvc的xml配置文件中

      <!--配置拦截器-->
      <mvc:interceptors>
          <!--配置一个拦截器
              可以配置多个,那么拦截顺序则是从上到下
          -->
          <mvc:interceptor>
              <!--拦截的URI
                  /* 代表拦截这一层的任意字符
                  /** 代表拦截任意层次的任意字符
              -->
              <mvc:mapping path="/*"/>
              <!--不进行拦截的uri-->
              <mvc:exclude-mapping path="/get"/>
              <bean class="cc.weno.interceptor.TestInterceptor"/>
          </mvc:interceptor>
      </mvc:interceptors>
      

      这时候就可以进行拦截了。

    3. 拦截器顺序的问题

      拦截器顺序是根据配置的顺序来决定的,但是pre、post、after却有些区别,这张图就可以表示了。


       

    好了,今天的Springmvc就到了这里了。

    君子不行陌路,管它咫尺还是天涯

  • 相关阅读:
    OpenGL学习之路(四)
    OpenGL学习之路(三)
    ajax请求成功后新开窗口window.open()被拦截解决方法
    Mysql日志解析
    Kibana+Logstash+Elasticsearch 日志查询系统
    谈谈Redis的SETNX
    常用前端开发工具合集
    Media Queries 详解
    启动mysql时显示:/tmp/mysql.sock 不存在的解决方法
    找不到mysql.sock,mysql.sock丢失问题解决方法
  • 原文地址:https://www.cnblogs.com/xiaohuiduan/p/9977891.html
Copyright © 2020-2023  润新知