Spring MVC 3 深入总结(转)

转自:http://blog.csdn.net/sunitjy/article/details/6782431

一、前言:

大家好,Spring3 MVC是非常优秀的MVC框架,由其是在3.0版本发布后,现在有越来越多的团队选择了Spring3 MVC了。Spring3 MVC结构简单,应了那句话简单就是美,而且他强大不失灵活,性能也很优秀。

官方的下载网址是:http://www.springsource.org/download   (本文使用是的Spring 3.0.5版本)

Spring3 MVC的优点:

1、Spring3 MVC的学习难度小于Struts2,Struts2用不上的多余功能太多。呵呵,当然这不是决定因素。

2、Spring3 MVC很容易就可以写出性能优秀的程序,Struts2要处处小心才可以写出性能优秀的程序(指MVC部分)

3、Spring3 MVC的灵活是你无法想像的,Spring的扩展性有口皆碑,Spring3 MVC当然也不会落后,不会因使用了MVC框架而感到有任何的限制。

Struts2的众多优点:略...   (呵呵,是不是不公平?)

众多文章开篇时总要吹些牛,吸引一下读者的眼球,把读者的胃口调起来,这样大家才有兴趣接着往后看。本文也没能例外。不过保证你看了之后不会后悔定有收获。

二、核心类与接口:

先来了解一下,几个重要的接口与类。现在不知道他们是干什么的没关系,先混个脸熟,为以后认识他们打个基础。

DispatcherServlet   -- 前置控制器

Spring MVC 3 深入总结(转)

HandlerMapping接口 -- 处理请求的映射

HandlerMapping接口的实现类:

SimpleUrlHandlerMapping  通过配置文件,把一个URL映射到Controller

DefaultAnnotationHandlerMapping  通过注解,把一个URL映射到Controller类上

Spring MVC 3 深入总结(转)

HandlerAdapter接口 -- 处理请求的映射

AnnotationMethodHandlerAdapter类,通过注解,把一个URL映射到Controller类的方法上

Spring MVC 3 深入总结(转)

Controller接口 -- 控制器

由于我们使用了@Controller注解,添加了@Controller注解注解的类就可以担任控制器(Action)的职责,

所以我们并没有用到这个接口。

Spring MVC 3 深入总结(转)

HandlerInterceptor 接口--拦截器

无图,我们自己实现这个接口,来完成拦截的器的工作。

ViewResolver接口的实现类

UrlBasedViewResolver类 通过配置文件,把一个视图名交给到一个View来处理

InternalResourceViewResolver类,比上面的类,加入了JSTL的支持

Spring MVC 3 深入总结(转)

View接口

JstlView类

Spring MVC 3 深入总结(转)

LocalResolver接口

Spring MVC 3 深入总结(转)

HandlerExceptionResolver接口 --异常处理

SimpleMappingExceptionResolver实现类

Spring MVC 3 深入总结(转)

ModelAndView类

无图。

三、核心流程图

本图是我个人画的,有不严谨的地方,大家对付看吧。总比没的看强。

Spring MVC 3 深入总结(转)


四、DispatcherServlet说明

使用Spring MVC,配置DispatcherServlet是第一步。

DispatcherServlet是一个Servlet,所以可以配置多个DispatcherServlet。

DispatcherServlet是前置控制器,配置在web.xml文件中的。拦截匹配的请求,Servlet拦截匹配规则要自已定义,把拦截下来的请求,依据某某规则分发到目标Controller(我们写的Action)来处理。

“某某规则”:是根据你使用了哪个HandlerMapping接口的实现类的不同而不同。

先来看第一个例子:

<listener>   
  <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>   
</listener>   

Spring会创建一个全局的WebApplicationContext上下文,称为根上下文 ,保存在 ServletContext中,key是WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE属性的值。可以使用工具类取出上下文:WebApplicationContextUtils.getWebApplicationContext(ServletContext);

DispatcherServlet是一个Servlet,可以同时配置多个,每个 DispatcherServlet有一个自己的 WebApplicationContext上下文,这个上下文继承了 根上下文 中所有东西。 保存在 ServletContext中,key是"org.springframework.web.servlet.FrameworkServlet.CONTEXT"+Servlet名称。当一个Request对象产生时,会把这个WebApplicationContext上下文保存在Request对象中,key是DispatcherServlet.class.getName() + ".CONTEXT"。可以使用工具类取出上下文:RequestContextUtils.getWebApplicationContext(request);

Spring中的 ApplicationContext实例可以被限制在不同的作用域(scope)中。
在web MVC框架中,每个 DispatcherServlet有它自己的WebApplicationContext ,这个context继承了根 WebApplicationContext 的所有bean定义。
这些继承的bean也可以在每个serlvet自己的所属的域中被覆盖(override),覆盖后的bean 可以被设置上只有这个servlet实例自己使用的属性。

总结:不使用listener监听器来加载spring的配置,改用DispatcherServlet来加载spring的配置,不要双亲上下文,只使用一个DispatcherServlet,事情就简单了,什么麻烦事儿也没有了。

六、springMVC-mvc.xml 配置文件片段讲解 (未使用默认配置文件名)

public class MyInteceptor implements HandlerInterceptor {     
    略。。。  
}    

Spring MVC并没有总的拦截器,不能对所有的请求进行前后拦截。
Spring MVC的拦截器,是属于HandlerMapping级别的,可以有多个HandlerMapping ,每个HandlerMapping可以有自己的拦截器。
当一个请求按Order值从小到大,顺序执行HandlerMapping接口的实现类时,哪一个先有返回,那就可以结束了,后面的HandlerMapping就不走了,本道工序就完成了。就转到下一道工序了。
拦截器会在什么时候执行呢? 一个请求交给一个HandlerMapping时,这个HandlerMapping先找有没有处理器来处理这个请求,如何找到了,就执行拦截器,执行完拦截后,交给目标处理器。
如果没有找到处理器,那么这个拦截器就不会被执行。


在spring MVC的配置文件中配置有三种方法:


方案一,(近似)总拦截器,拦截所有url

   <mvc:interceptors>  
    <bean class="com.app.mvc.MyInteceptor" />  
</mvc:interceptors>  

为什么叫“近似”,前面说了,Spring没有总的拦截器。

<mvc:interceptors/>会为每一 个HandlerMapping,注入一个拦截器。总有一个HandlerMapping是可以找到处理器的,最多也只找到一个处理器,所以这个拦截器总会被执行的。起到了总拦截器的作用。

 
方案二, (近似) 总拦截器, 拦截匹配的URL。

package test;  
import javax.servlet.http.HttpServletRequest;  
import javax.servlet.http.HttpServletResponse;  
import org.junit.BeforeClass;  
import org.springframework.mock.web.MockServletContext;  
import org.springframework.web.context.WebApplicationContext;  
import org.springframework.web.context.support.XmlWebApplicationContext;  
import org.springframework.web.servlet.HandlerAdapter;  
import org.springframework.web.servlet.HandlerExecutionChain;  
import org.springframework.web.servlet.HandlerMapping;  
import org.springframework.web.servlet.ModelAndView;  
import org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter;  
import org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping;  
/**  
* 说明: JUnit测试action时使用的基类 
*  
* @author  赵磊 
* @version 创建时间:2011-2-2 下午10:27:03   
*/   
public class JUnitActionBase {  
    private static HandlerMapping handlerMapping;  
    private static HandlerAdapter handlerAdapter;  
    /** 
     * 读取spring3 MVC配置文件 
     */  
    @BeforeClass  
 public static void setUp() {  
        if (handlerMapping == null) {  
            String[] configs = { "file:src/springConfig/springMVC.xml" };  
            XmlWebApplicationContext context = new XmlWebApplicationContext();  
            context.setConfigLocations(configs);  
            MockServletContext msc = new MockServletContext();  
            context.setServletContext(msc);         context.refresh();  
            msc.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, context);  
            handlerMapping = (HandlerMapping) context  
                    .getBean(DefaultAnnotationHandlerMapping.class);  
            handlerAdapter = (HandlerAdapter) context.getBean(context.getBeanNamesForType(AnnotationMethodHandlerAdapter.class)[0]);     
        }  
    }  
  
    /** 
     * 执行request对象请求的action 
     *  
     * @param request 
     * @param response 
     * @return 
     * @throws Exception 
     */  
    public ModelAndView excuteAction(HttpServletRequest request, HttpServletResponse response)  
 throws Exception {  
        HandlerExecutionChain chain = handlerMapping.getHandler(request);  
        final ModelAndView model = handlerAdapter.handle(request, response,  
                chain.getHandler());  
        return model;  
    }  
}  

这是个JUnit测试类,我们可以new Request对象,来参与测试,太方便了。给request指定访问的URL,就可以请求目标Action了。

package test.com.app.user;  
import org.junit.Assert;  
import org.junit.Test;  
import org.springframework.mock.web.MockHttpServletRequest;  
import org.springframework.mock.web.MockHttpServletResponse;  
import org.springframework.web.servlet.ModelAndView;  
  
import test.JUnitActionBase;  
  
/**  
* 说明: 测试OrderAction的例子 
*  
* @author  赵磊  
* @version 创建时间:2011-2-2 下午10:26:55   
*/   
  
public class TestOrderAction extends JUnitActionBase {  
    @Test  
    public void testAdd() throws Exception {  
    MockHttpServletRequest request = new MockHttpServletRequest();  
        MockHttpServletResponse response = new MockHttpServletResponse();  
        request.setRequestURI("/order/add");  
        request.addParameter("id", "1002");  
        request.addParameter("date", "2010-12-30");  
        request.setMethod("POST");  
        // 执行URI对应的action  
        final ModelAndView mav = this.excuteAction(request, response);  
        // Assert logic  
        Assert.assertEquals("order/add", mav.getViewName());  
        String msg=(String)request.getAttribute("msg");  
        System.out.println(msg);  
    }  
}  

 需要说明一下 :由于当前最想版本的Spring(Test) 3.0.5还不支持@ContextConfiguration的注解式context file注入,所以还需要写个setUp处理下,否则类似于Tiles的加载过程会有错误,因为没有ServletContext。3.1的版本应该有更好的解决方案,参见: https://jira.springsource.org/browse/SPR-5243 

 十四、转发与重定向

可以通过redirect/forward:url方式转到另一个Action进行连续的处理。

可以通过redirect:url 防止表单重复提交 。

写法如下:

return "forward:/order/add";

return "redirect:/index.jsp";

 十五、处理ajax请求

1、引入下面两个jar包,我用的是1.7.2,好像1.4.2版本以上都可以,下载地址: http://wiki.fasterxml.com/JacksonDownload

jackson-core-asl-1.7.2.jar 

jackson-mapper-asl-1.7.2.jar

2、spring的配置文件中要有这一行,才能使用到spring内置支持的json转换。如果你手工把POJO转成json就可以不须要使用spring内置支持的json转换。

<mvc:annotation-driven />

3、使用@ResponseBody注解

/** 
 * ajax测试 
* http://127.0.0.1/mvc/order/ajax 
 */  
  
@RequestMapping("/ajax")  
@ResponseBody  
public Object ajax(HttpServletRequest request){  
    List<String> list=new ArrayList<String>();  
    list.add("电视");  
nbsp;       list.add("洗衣机");  
    list.add("冰箱");  
    list.add("电脑");  
    list.add("汽车");  
    list.add("空调");  
    list.add("自行车");  
    list.add("饮水机");  
    list.add("热水器");  
    return list;  
}  

相关推荐