zoukankan      html  css  js  c++  java
  • springmvc细节篇

      前面讲了入门篇,现在来了解下springmvc的细节.mvc框架都有请求映射、数据绑定、请求处理、视图解析这几个过程,现在我们来了解springmvc中的这些细节。

    1.使用@RequestMapping来配置springmvc请求映射的url

      springmvc中的请求映射有多种方式,当然用得最多的是基于注解的请求映射方式.开发中,我们中使用@RequestMapping这个注解来表示controller和方法的url.@RequestMapping的源码:

    @Target({ElementType.METHOD, ElementType.TYPE}) // 注解可以用在类和方法上
    @Retention(RetentionPolicy.RUNTIME)  // 注解在运行期有效
    @Documented
    @Mapping
    public @interface RequestMapping {
       String[] value()
    default {};
       RequestMethod[] method()
    default {};
       String[] params()
    default {};
       String[] headers()
    default {};
       String[] consumes()
    default {};
       String[] produces()
    default {}; }

      其中value属性用来表示url,其他属性用于限定请求,也就是只有满足某些条件才处理请求.

    • value : 指定请求的实际地址,最常用的属性,例如:@RequestMapping(value="/xxx"),value的值可以以"/"开头,也可以不用"/",springmvc会自动补"/".
    • method: 指定访问方式,包括POST or GET or others...,默认都支持.以RequestMethod枚举来表示.

            例如:@RequestMapping(method=RequestMethod.POST)

    • heads: 限定request中必须包含的请求头

        例如:限定Content-Type @RequestMapping(headers={"Content-Type=application/x-www-form-urlencoded"})

    • consumes: 限定请求头中的Content-Type

        例如 : @RequestMapping(value = "/pets", method = RequestMethod.POST, consumes="application/json")该方法仅处理ContentType=application/json的请求

    • produces: 限定request中Accept

        例如:@RequestMapping(value = "/pets/{petId}", method =RequestMethod.GET, produces="application/json")该方法仅处理request中Accept包含application/json的请求

      consumes和produces用的比较少,其他几个属性比较常见.

      @RequestMapping使用的实例:

         @Controller
            @RequestMapping(value="/hello")
            public class HelloworldController {
            
                @RequestMapping(value="/say", //--> 请求url
                                method={RequestMethod.POST}, // 请求方式为 post
                                headers={"Content-Type=application/x-www-form-urlencoded"}, // 必须包含ContentType=application/x-www-form-urlencoded,普通表单提交
                                params={"name=xxx","email=yyy"}) // 请求中必须包含 name=xxx&email=yyy
                                //params={"name=xxx"})--->请求参数中必须包含
                                //headers={"Content-Type=application/x-www-form-urlencoded"}) --> 限制请求头
                public String sayHello(){
                    System.out.println("hello");
                    return "hello";
                }
                
            }

    2.使用@RequestParam和@ModelAttribute完成数据绑定

      直白点说,就是如何接收request中的参数呢?springmvc的做法是在方法中定义形式参数,在调用方法时将request中参数赋值给对应的参数,通过注解来描述形参和request中参数的对应关系.

      简单例子,调用sayHello方法时,会传一个name参数.程序中如何接收呢?

    @RequestMapping(value="/say")
        public String sayHello(@RequestParam(value="nm",required=true,defaultValue="zzz")String name){
            
            System.out.println("hello "+name);
            
            return "hello";
        }

      RequestParam有三个属性:

    • value: 将request中value对应的参数值绑定到应用注解的变量上,对应上面的例子中:把request中nm的值传给name.
    • required:该参数是否必须,true:必须,request不包含此参数则抛异常
    • defaultValue:如果request中没有包含value参数,则使用defaultValue值绑定到变量

      如果request参数名称和方法中形参名字一样,代码上可以不声明@RequestParam,框架会自动注入.

      @RequestParam用来接收单个变量,如果我们要接收的是一个对象呢?例如User(name,email)对象.这时我们可以用另外一个注解@ModelAttribute.该注解的作用就是把request中参数绑定到一个对象上.

        @RequestMapping(value="process")
            public String process(@ModelAttribute("user")User user){
                System.out.println(user);
                return "index";
            }

       接收request的参数搞定了,那如何把数据放到request中,然后带回页面呢?这里有两种一种,一种就是通过ModelAndView对象,一种就是在方法中声明参数.前一种方法在之后视图解析时再说,这里我们将第二种方法.springmvc为解决这个问题提供了一种很优雅的方法,在方法声明Model或者Map的实例,把数据放到Model或者Map中(实际上Model中也是封装了一个Map,只不过Model是以接口的形式提供的),框架在执行方法时会把Model和Map的实例方法request中,这样就可以在页面上通过EL表达式取出页面显示了.这个过程在源码分析那篇中有提到.

        @RequestMapping(value="process")
            public String process(@ModelAttribute("user")User user,Model model){
                System.out.println(user);
           // 带参数到页面 jsp上${name}就可以取值
           model.addAttribute("name",user.getName());
    return "index"; }
      
        @RequestMapping(value="process2")
            public String process(@ModelAttribute("user")User user,Map<String,Object> result){
                System.out.println(user);
           // 带参数到页面 jsp上${name}就可以取值
           result.put("name",user.getName());
    return "index"; }
     

     3.结果页面的返回

      请求处理过程就是调用url对应的方法完成请求的处理的过程,在源码分析篇详细分析了这个过程。这里我们直接讲结果页面的返回.springmvc中提供了一个ModelAndView对象封装结果视图。之前的处理中我们都是返回字符串形式的视图名称,这个字符串最终也会成为ModelAndView对象中的viewName属性,然后交由springmvc中结果视图组件解析.

      结果页面返回主要有转发和重定向两种方式,这部分直接看代码:

    package cn.springmvc.controller;
    
    import java.util.HashMap;
    import java.util.Map;
    
    import org.springframework.context.annotation.Scope;
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.servlet.ModelAndView;
    import org.springframework.web.servlet.view.RedirectView;
    
    @Controller("resultPageController")
    @RequestMapping(value="result")
    @Scope("prototype")
    public class ResultPageController {
        
            /****************** 以forward方式返回结果页面  **************************/
            @RequestMapping(value="forward1")
            public String toViewByForwad1(){
                // 逻辑处理
                
                //  转发到index页面-->默认是转发
    //            return "forward:index";=== return "index";
                return "index";
            }
            @RequestMapping(value="forward2")
            public ModelAndView toViewByForwar2(){
                
                // TODO:逻辑处理
                
                ModelAndView mv = new ModelAndView();
                mv.setViewName("index");// 默认是转发方式 可以在逻辑视图加forward:也可以不加
                // 用ModelAndView可以往request中添加参数
                mv.addObject("name","xxx");
                return mv;
            }
            
            
            /********************** 重定向 的三种方式  ******************************************/
            @RequestMapping(value="redirect")
            public String redirect(){
                // "forword:xxx"
                return "redirect:http://www.baidu.com?wd=xx";
            }
            
            @RequestMapping(value="redirect2")
            public ModelAndView redirect2(){
                ModelAndView mv = new ModelAndView();
                mv.setViewName("redirect:http://www.baidu.com");
                mv.addObject("wd","xx");
                return mv;
            }
            
            @RequestMapping(value="redirect3")
            public ModelAndView redirect3(){
                System.out.println("redirect3");
                ModelAndView mv = new ModelAndView();
                RedirectView redirectView = new RedirectView("http://www.baidu.com");
                Map<String,Object> params = new HashMap<String,Object>();
                params.put("wd", "xxxx");
                redirectView.setAttributesMap(params);
                mv.setView(redirectView);
                return mv;
            }
            
    }
    View Code

    4. CharacterEncodingFilter

     web.xml文件中配置编码过滤器.GET方式的乱码问题可以设置tomcat的属性.

    <!-- CharacterEncodingFilter 过滤器解决POST方式乱码问题 -->
        <filter>
            <filter-name>characterEncoding</filter-name>
            <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
            <init-param>
                <param-name>encoding</param-name>
                <param-value>UTF-8</param-value>
            </init-param>
            <init-param>
                <param-name>forceEncoding</param-name>
                <param-value>true</param-value>
            </init-param>
        </filter>
        <filter-mapping>
            <filter-name>characterEncoding</filter-name>
            <url-pattern>*</url-pattern>
        </filter-mapping>

    5.springmvc中的文件上传和下载

      springmvc文件上传使用的是apache的fileupload组件,所以在springmvc中使用文件上传时先要导入fileupload的jar包.还要在配置文件中配置文件上传的Resolver.

    <!-- 文件上传配置 -->
    <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"> <property name="defaultEncoding" value="utf-8" /> <property name="maxUploadSize" value="10485760000" /> <property name="maxInMemorySize" value="40960" /> </bean>

      文件上传下载代码示例:

    import java.io.File;
    import java.io.FileInputStream;
    import java.io.FileNotFoundException;
    import java.io.FileOutputStream;
    import java.io.IOException;
    import java.util.List;
    
    import javax.servlet.ServletContext;
    import javax.servlet.http.HttpServletResponse;
    
    import org.apache.commons.fileupload.util.Streams;
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RequestParam;
    import org.springframework.web.context.ServletContextAware;
    import org.springframework.web.multipart.MultipartFile;
    
    @Controller
    @RequestMapping(value="file")
    public class FileController implements ServletContextAware{
        
        private ServletContext servletContext;
        /**
         *  1.导入apache fileupload组件
         *  2.配置上传文件解析器
         *  
         *  上传单个文件
         * @param file
         * @return
         */
        @RequestMapping("upload")
        public String upload(@RequestParam("file") MultipartFile file){
            try {
                if(file.getSize()>0){ // 判断是否上传了文件
                    System.out.println(">>>>>>>>> "+file.getOriginalFilename());
                    String path = servletContext.getRealPath("/up/");
                    Streams.copy(file.getInputStream(),
                                 new FileOutputStream(path+File.separatorChar+file.getOriginalFilename()),
                                 true);
                }
            } catch (FileNotFoundException e) {
                // TODO 异常处理
                e.printStackTrace();
            } catch (IOException e) {
                // TODO 异常处理
                e.printStackTrace();
            }
            return "index";
        }
        /**
         * 多文件上传
         * @param files
         * @return
         */
        @RequestMapping("uploads")
        public String uploads(@RequestParam("files")List<MultipartFile> files){
            try {
                System.out.println(">>>>>>>>>>>> "+files.size());
                String path = servletContext.getRealPath("/up/");
                MultipartFile file = null;
                for(int i=0;i<files.size();++i){
                    file = files.get(i);
                    if(file.getSize()>0) // 判断是否选择了文件
                    Streams.copy(files.get(i).getInputStream(), 
                                 new FileOutputStream(path+File.separatorChar+file.getOriginalFilename()),
                                 true);
                }
            } catch (FileNotFoundException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            return "index";
        }
        
        /**
         * 文件下载
         */
        @RequestMapping(value="download")
        public void download(HttpServletResponse response){
            try {
                // 下载文件路径
                String filePath = this.servletContext.getRealPath("/download/lalalala.png");
                File file = new File(filePath);
                
                // 设置消息头
                response.setContentType("application/force-download");
                response.setHeader("Content-Disposition", "attachement;filename="+file.getName());
                response.setContentLength((int)file.length());
                
                // 写数据
                Streams.copy(new FileInputStream(file),response.getOutputStream(), true);
                
            } catch (FileNotFoundException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
        
        public void setServletContext(ServletContext servletContext) {
            this.servletContext = servletContext;
        }
        
        
    }
    View Code

      页面代码:

     <h4>单文件上传</h4>
        <form action="${pageContext.request.contextPath }/file/upload.action" method="post" enctype="multipart/form-data">
            <input type="file" name="file"/><br/>
            <input type="submit" value="上传">
        </form>
        
        <h4>多文件上传</h4>
        <form action="${pageContext.request.contextPath}/file/uploads.action" method="post" enctype="multipart/form-data">
            <input type="file" name="files"/><br/>
            <input type="file" name="files"/><br/>
            <input type="file" name="files"/><br/>
            <input type="submit" value="上传">
        </form>

     6.springmvc中的json处理 

      讲springmv与ajax的整合前,先讲两个注解.

      @RequestBody:将http请求中的正文(请求体)赋值给应用了注解的参数,通过这个注解可以直接获取请求体的内容.需要注意的是:post请求才有请求体.

      @ResponseBody:将数据作为response的正文(响应体)返回.用这个注解可以直接把方法的返回值写到response对象中.通常用于完成ajax请求后服务端数据的返回.

      先看两个注解的应用代码:

    import org.springframework.context.annotation.Scope;
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.RequestBody;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.ResponseBody;
    
    import com.alibaba.fastjson.JSON;
    
    import cn.jack.domain.User;
    
    @Controller("ajaxController")
    @RequestMapping(value="ajax")
    @Scope("prototype")
    public class AjaxController {
        /**
         * 通过@RequestBody 获取request中的正文
         * @param requestBody
         * @return
         */
        @RequestMapping(value="doReq")
        public String process(@RequestBody String requestBody){
            System.out.println("----->>>>>>>> "+requestBody);
            // do other works
            return "index";
        }
        
        /**
         * 通过@ResponseBody 把数据写到response正文中
         * @return
         */
        @RequestMapping(value="doResp")
        @ResponseBody
        public String doResp(){
            System.out.println(">>>>>>>>>>:reponseBody");
            return "retVal";
        }
    }
    View Code

         页面代码:

    <script type="text/javascript" src="${ctx }/js/jquery-1.7.2.js"></script>
        <script type="text/javascript">
            function doResp(){
                var url = '${pageContext.request.contextPath}/ajax/doResp.action';
                $.post(url,function(data){
                    alert(data);
                });
            }
            
    </script>
    
      <body>
        <h4>@RequestBody: 将消息体绑定到使用了注解的变量,POST请求才有消息体</h4>
        <form action="${pageContext.request.contextPath}/ajax/doReq.action" method="post">
            <input type="text" name="name" id="name"/><br/>
            <input type="text" name="email" id="email"/><br/>
            <input type="submit" value="RequestBody" />
        </form>
        <br/>
        <h4>@ResponseBody:将内容写到response响应正文</h4>
        <button onclick="doResp();">ResonseBody</button>
    </body>
    View Code

      上面说了,@RequestBody和@ResponseBody的作用.接下来使用这两个注解来处理json数据.下面程序的json转换用的是fastjson.

      用@ResponseBody返回json字符串.代码如下:

       /**
         * 用@ResponseBody返回json字符串
         */
        @RequestMapping(value="retJson")
        @ResponseBody
        public String retJson(@RequestParam("userId")Integer userId){
            System.out.println("userId>>>>>>>>>>>>>>:"+userId);
            User user = new User(); // get User from db by userId
            user.setName("zhangsan");
            user.setEmail("zhangsan@126.com");
            // 返回json字符串
            String json = JSON.toJSONString(user);
            return json;
        }
    
    页面代码:
       function _retJson(){
                var url = '${pageContext.request.contextPath}/ajax/retJson.action';
                var param = {userId:1};
                $.post(url,param,function(data){
                    alert(data);
                    var user = eval("("+data+")");
                    alert(user.name+"..."+user.email);
                });
            }
    <button onclick="_retJson();">retJson</button>

      @RequestBody接收json字符串,@ResponseBody返回json字符串.  

         /**
         * 用@RequestBody接收json字符串,用@ResponseBody返回json字符串
         */
        @RequestMapping(value="doJson")
        @ResponseBody
        public String doJson(@RequestBody String jsonStr){
            System.out.println("requestbody>>>>>>>>:"+jsonStr);
         // json字符串 转成对象 User user
    = JSON.parseObject(jsonStr,User.class); user.setName("_"+user.getName()); user.setEmail("_"+user.getEmail());
         // 对象转成json字符串 String retStr
    = JSON.toJSONString(user); return retStr; } 页面代码: function _doJson(){ // 获取参数值,拼接json字符串 var name = $("#name").val(); var email = $("#email").val(); // json对象 var json = {name:name,email:email}; // json对象转成json字符串 var jsonStr = JSON.stringify(json); /** * 也可以手动拼接json字符串 var jsonStr = "{"name":""+name+"","email":""+email+""}"; */ var url = '${pageContext.request.contextPath}/ajax/doJson.action'; //因为发送的json字符串,要设置 Content-Type=application/json $.ajaxSetup({ contentType:'application/json;charset=UTF-8' }); // 发送请求 $.post(url,jsonStr,function(data){ alert(data); // json字符串转json对象 var user = eval("("+data+")"); alert(user.name+"..."+user.email); }); } <input type="text" name="name" id="name"><br/> <input type="text" name="email" id="email"><br/> <button onclick="_doJson();">doJson</button>

     @ResponseBody乱码问题的解决,springmvc中StringHttpMessageConverter类默认用的是ISO8859-1编码,所以用@ResponseBody返回中文时会产生乱码问题.解决办法是基于StringHttpMessageConverter写一个Converter,该编码为UTF-8.

    import java.nio.charset.Charset;
    
    import org.springframework.http.MediaType;
    import org.springframework.http.converter.StringHttpMessageConverter;
    
    public class MyStringConvert extends StringHttpMessageConverter{
        private static final MediaType mt = new MediaType("text","html",Charset.forName("UTF-8"));// text/html;charset=utf-8
        @Override
        protected MediaType getDefaultContentType(String t) {
            return mt;
        }
    }

       声明AnnotationMethodHandlerAdapter使用自定义的converter.在spring-servlet.xml中配置:

       <!-- @ResponseBody返回中文乱码问题 -->
          <bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
              <property name="messageConverters" >
                  <bean class="cn.jack.util.MyStringConvert"></bean>
              </property>
          </bean>
  • 相关阅读:
    1.第一个java程序
    5.第三章 运算符和基本选择结构
    7.关系运算符
    4.第二章章末总结
    3.计算员工工资
    JAVA并发操作——Thread常见用法(Sleep,yield,后台线程)
    JAVA 对象序列化(一)——Serializable
    JAVA 线程中的异常捕获
    JAVA反射机制实例
    JAVA 对象序列化(二)——Externalizable
  • 原文地址:https://www.cnblogs.com/heavenyes/p/3908717.html
Copyright © 2011-2022 走看看