springmvc细节篇

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

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

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

@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;
        }

}

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

}

  页面代码:

 <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";
    }
}

页面代码:

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

  上面说了,@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("[email protected]");
        // 返回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>

springmvc细节篇

时间: 2024-08-30 06:41:05

springmvc细节篇的相关文章

分布式系统之缓存的微观应用经验谈(一) 【基础细节篇】

分布式系统之缓存的微观应用经验谈(一) [基础细节篇] 前言 近几个月一直在忙些琐事,几乎年后都没怎么闲过.忙忙碌碌中就进入了2018年的秋天了,不得不感叹时间总是如白驹过隙,也不知道收获了什么和失去了什么.最近稍微休息,买了两本与技术无关的书,其一是Yann Martel 写的<The High Mountains of Portugal>(葡萄牙的高山),发现阅读此书是需要一些耐心的,对人生暗喻很深,也有足够的留白,有兴趣的朋友可以细品下.好了,下面回归正题,尝试写写工作中缓存技术相关的一

走进spring之springmvc实战篇(二)

本篇继篇一之后加入了jdbc并使用了注解 篇一进行了对spingmvc的基础配置http://www.cnblogs.com/liuyanhao/p/4798989.html 首先让我们先了解下注解的好处: 注解方式与配置文件方式相比,使用注解方式可以省略大部分配置文件,并且在IOC方面更能凸显出其特性. 本篇使用的注解进行归纳总结: -----持续更新中------- 2015-09-15 11:03:11更新 注意:使用注解就必须在配置文件中加入<context:component-scan

springMVC基础篇

 对于一个不懂技术的人来说springMVC是什么呢?有人会说:"春天里的MVC"这也许就是春天里的几个字母,不错在我没有接触java框架的时候留给我的记忆就是这样的.那么现在接触到了就让我们来好好讨论一下springMVC到底是什么吧? 一.是什么? 百度百科上这样解释:Spring MVC属于SpringFrameWork的后续产品,已经融合在Spring WebFlow里面.Spring 框架提供了构建 Web 应用程序的全功能 MVC 模块.使用 Spring 可插入的 M

springmvc基础篇—掌握三种处理器

随着springmvc的广泛使用,关于它的很多实用有效的功能应该更多的被大家所熟知,下面就介绍一下springmvc的三种处理器: 一.BeanName处理器(默认) <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/

springmvc基础篇—处理图片静态资源文件

当我们在web.xml中对DispatcherServlet的过滤设置为/ 的时候,表示对所有的路径进行拦截过滤,那么不可避免的就会产生一个问题,那就是像图片这种静态资源文件我明明引用路径有,但就是加载不出来图片的情况,针对这种情况,下面做一些处理: 找到spring-mvc.xml配置文件 在里面写加上这行代码即可. <!-- 处理图片静态资源 --> <mvc:resources location="/images/" mapping="/images/

springmvc基础篇—使用注解方式为前台提供数据

一.新建一个Controller package cn.cfs.springmvc.service; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import org.springframework.stereotype.Service; import cn.cfs.springmvc.domain.User; @Service public

springmvc基础篇—修改默认的配置文件名称及位置

springmvc的默认配置文件是放在WEB-INF下的,叫action-servlet.xml.根据咱们编程的习惯,一般都将配置文件放到src的根目录下,那么如何将这个文件迁移过来呢?其实很简单,请看: 第一步:将原有的action-servlet.xml文件拷贝一份到src目录下,改名为:spring-mvc.xml(这个名称就任意起啦,注意和下面web.xml中引用的文件名对应上即可). 第二步:修改web.xml文件,找到配置spring分发器的servlet,也就是配置Dispatch

SpringMVC总结篇

SpringMVC是基于MVC设计理念的一款优秀的Web框架,是目前最流行的MVC框架之一,SpringMVC通过一套注解,让POPJ成为处理请求的控制器,而无需实现任何接口,然后使用实现接口的控制器也完全没问题:支持REST风格的URL请求:采用松散耦合架构,比其他MVC框架更具有灵活性和扩展性.关于SpringMVC工程如何搭建请点击:minglisoft.cn/technology 0 XML配置文件 web.xml配置DispatcherServlet,DispatcherServlet

SpringMVC学习篇—SpringMVC原理

先来看两副图 Spring MVC工作流程图 图1: 图2: Spring工作流程描述 1. 用户向服务器发送请求,请求被Spring 前端控制Servelt DispatcherServlet捕获: 2. DispatcherServlet 对请求URL进行解析,得到请求资源标识符(URI).然后根据该URI,调用HandlerMapping获得该Handler配置的所有相关的对象 (包括Handler对象以及Handler对象对应的拦截器),最后以HandlerExecutionChain对