zoukankan      html  css  js  c++  java
  • Spring MVC学习总结(3)——Spring3 MVC详解

    DispatcherServlet 前置控制器

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

    [html] view plaincopy
    1. <span style="white-space:pre">  </span><servlet>  
    2.         <servlet-name>spring3</servlet-name>  
    3.         <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>  
    4.         <init-param>  
    5.             <param-name>contextConfigLocation</param-name>  
    6.             <param-value>/WEB-INF/spring/mvc-config.xml</param-value>  
    7.         </init-param>  
    8.         <load-on-startup>1</load-on-startup>  
    9.     </servlet>  
    10.     <servlet-mapping>  
    11.         <servlet-name>spring3</servlet-name>  
    12.         <url-pattern>/</url-pattern>  
    13.     </servlet-mapping>  
    DispatcherServlet是一个Servlet,可以同时配置多个,每个 DispatcherServlet有一个自己的 WebApplicationContext上下文,这个上下文继承了 根上下文 中所有东西。 保存在 ServletContext中,key是"org.springframework.web.servlet.FrameworkServlet.CONTEXT"+Servlet名称。当一个Request对象产生时,会把这个WebApplicationContext上下文保存在Request对象中,key是DispatcherServlet.class.getName() + ".CONTEXT"。可以使用工具类取出上下文:RequestContextUtils.getWebApplicationContext(request);


    使用listener监听器来加载配置

    [html] view plaincopy
    1. <listener>     
    2.   <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>     
    3. </listener>    
    Spring会创建一个全局的WebApplicationContext上下文,称为根上下文 ,保存在 ServletContext中,key是WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE属性的值。可以使用工具类取出上下文:WebApplicationContextUtils.getWebApplicationContext(ServletContext);


    Spring  mvc-config.xml 配置文件片段讲解 
    [html] view plaincopy
    1. <span style="font-size:10px;">   <!-- 自动扫描的包名 -->    
    2.    <context:component-scan base-package="com.app,com.core,JUnit4" ></context:component-scan>    
    3.        
    4.    <!-- 默认的注解映射的支持 -->    
    5.    <mvc:annotation-driven />    
    6.        
    7.    <!-- 视图解释类 -->    
    8.    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">    
    9.     <property name="prefix" value="/WEB-INF/jsp/"/>    
    10.     <property name="suffix" value=".jsp"/><!--可为空,方便实现自已的依据扩展名来选择视图解释类的逻辑  -->    
    11.     <property name="viewClass" value="org.springframework.web.servlet.view.JstlView" />    
    12.    </bean>    
    13.        
    14.    <!-- 拦截器 -->    
    15.    <mvc:interceptors>    
    16.     <bean class="com.core.mvc.MyInteceptor" />    
    17.    </mvc:interceptors>         
    18.     
    19.     <!-- 对静态资源文件的访问  方案一 (二选一) -->    
    20.     <mvc:default-servlet-handler/>    
    21.         
    22.     <!-- 对静态资源文件的访问  方案二 (二选一)-->    
    23. <mvc:resources mapping="/images/**" location="/images/" cache-period="31556926"/>    
    24. <mvc:resources mapping="/js/**" location="/js/" cache-period="31556926"/>    
    25. <mvc:resources mapping="/css/**" location="/css/" cache-period="31556926"/>  </span>  

    <context:component-scan/> 扫描指定的包中的类上的注解,常用的注解有:

    @Controller 声明Action组件
    @Service    声明Service组件    @Service("myMovieLister") 
    @Repository 声明Dao组件
    @Component   泛指组件, 当不好归类时. 
    @RequestMapping("/menu")  请求映射
    @Resource  用于注入,( j2ee提供的 ) 默认按名称装配,@Resource(name="beanName") 
    @Autowired 用于注入,(srping提供的) 默认按类型装配 
    @Transactional( rollbackFor={Exception.class}) 事务管理
    @ResponseBody
    @Scope("prototype")   设定bean的作用域

    <mvc:annotation-driven /> 是一种简写形式,完全可以手动配置替代这种简写形式,简写形式可以让初学都快速应用默认配置方案。
    <mvc:annotation-driven /> 会自动注册DefaultAnnotationHandlerMapping与AnnotationMethodHandlerAdapter 两个bean,是spring MVC为@Controllers分发请求所必须的。
    并提供了:数据绑定支持,@NumberFormatannotation支持,@DateTimeFormat支持,@Valid支持,读写XML的支持(JAXB),读写JSON的支持(Jackson)。
    后面,我们处理响应ajax请求时,就使用到了对json的支持。
    后面,对action写JUnit单元测试时,要从spring IOC容器中取DefaultAnnotationHandlerMapping与AnnotationMethodHandlerAdapter 两个bean,来完成测试,取的时候要知道是<mvc:annotation-driven />这一句注册的这两个bean。

    <mvc:interceptors/> 是一种简写形式。可以配置多个HandlerMapping。<mvc:interceptors/>会为每一个HandlerMapping,注入一个拦截器。其实我们也可以手动配置为每个HandlerMapping注入一个拦截器。

    <mvc:default-servlet-handler/> 使用默认的Servlet来响应静态文件。

    <mvc:resources mapping="/images/**" location="/images/" cache-period="31556926"/> 匹配URL  /images/**  的URL被当做静态资源,由Spring读出到内存中再响应http。

     

    访问到静态的文件,如jpg,js,css
      

    如何你的DispatcherServlet拦截 *.do这样的URL,就不存在访问不到静态资源的问题。如果你的DispatcherServlet拦截“/”,拦截了所有的请求,同时对*.js,*.jpg的访问也就被拦截了。

    目的:可以正常访问静态文件,不要找不到静态文件报404。


    方案一:激活Tomcat的defaultServlet来处理静态文件
    [html] view plaincopy
    1. <servlet-mapping>     
    2.     <servlet-name>default</servlet-name>    
    3.     <url-pattern>*.jpg</url-pattern>       
    4. </servlet-mapping>      
    5. <servlet-mapping>         
    6.     <servlet-name>default</servlet-name>      
    7.     <url-pattern>*.js</url-pattern>      
    8. </servlet-mapping>      
    9. <servlet-mapping>          
    10.     <servlet-name>default</servlet-name>         
    11.     <url-pattern>*.css</url-pattern>        
    12. </servlet-mapping>      
    13. 要配置多个,每种文件配置一个  

    要写在DispatcherServlet的前面, 让 defaultServlet先拦截,这个就不会进入Spring了 


    Tomcat, Jetty, JBoss, and GlassFish  默认 Servlet的名字 -- "default"
    Google App Engine 默认 Servlet的名字 -- "_ah_default"
    Resin 默认 Servlet的名字 -- "resin-file"
    WebLogic 默认 Servlet的名字  -- "FileServlet"
    WebSphere  默认 Servlet的名字 -- "SimpleFileServlet" 


    方案二: 在spring3.0.4以后版本提供了mvc:resources 
    mvc:resources 的使用方法:
    [html] view plaincopy
    1. <!-- 对静态资源文件的访问 -->      
    2. <mvc:resources mapping="/images/**" location="/images/" />    

    [html] view plaincopy
    1. <span style="font-weight: normal;">mapping<span style="font-family: Arial; line-height: 1.5em; ">映射到ResourceHttpRequestHandler进行处理  
    2. location指定静态资源的位置.可以是web application根目录下、jar包里面,这样可以把静态资源压缩到jar包中  
    3. cache-period 可以使得静态资源进行web cache </span></span>  

    如果出现下面的错误,可能是没有配置<mvc:annotation-driven />的原因。 
    报错WARNING: No mapping found for HTTP request with URI [/mvc/user/findUser/lisi/770] in DispatcherServlet with name 'springMVC'

     

    使用<mvc:resources/>元素,把mapping的URI注册到SimpleUrlHandlerMapping的urlMap中,key为mapping的URI pattern值,而value为ResourceHttpRequestHandler,这样就巧妙的把对静态资源的访问由HandlerMapping转到ResourceHttpRequestHandler处理并返回,所以就支持classpath目录,jar包内静态资源的访问.另外需要注意的一点是,不要对SimpleUrlHandlerMapping设置defaultHandler.因为对static uri的defaultHandler就是ResourceHttpRequestHandler,否则无法处理static resources request.


    方案三 ,使用<mvc:default-servlet-handler/>
    [html] view plaincopy
    1. <mvc:default-servlet-handler/>  

    会把"/**" url,注册到SimpleUrlHandlerMapping的urlMap中,把对静态资源的访问由HandlerMapping转到org.springframework.web.servlet.resource.DefaultServletHttpRequestHandler处理并返回.DefaultServletHttpRequestHandler使用就是各个Servlet容器自己的默认Servlet.

     

    补充说明:多个HandlerMapping的执行顺序问题:

    DefaultAnnotationHandlerMapping的order属性值是:0
    <mvc:resources/ >自动注册的 SimpleUrlHandlerMapping的order属性值是: 2147483646

    <mvc:default-servlet-handler/>自动注册 的SimpleUrlHandlerMapping 的order属性值是: 2147483647

    spring会先执行order值比较小的。当访问一个a.jpg图片文件时,先通过 DefaultAnnotationHandlerMapping 来找处理器,一定是找不到的,我们没有叫a.jpg的Action。再按order值升序找,由于最后一个 SimpleUrlHandlerMapping 是匹 "/**"的,所以一定会匹配上,再响应图片。



     请求如何映射到具体的Action中的方法

    方案一:基于xml配置映射,可以利用SimpleUrlHandlerMapping、BeanNameUrlHandlerMapping进行Url映射和拦截请求。
     
    方案二:基于注解映射,可以使用DefaultAnnotationHandlerMapping。

    [html] view plaincopy
    1. <bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping">  </bean>     
    2. 简写  
    3. <span style="font-family: Arial; line-height: 24px; text-align: left; "><span style="font-size:10px;"><mvc:annotation-driven /></span></span>  
     并在action类上使用:
    @Controller
    @RequestMapping("/user")


    Spring中的拦截器:
    Spring为我们提供了:org.springframework.web.servlet.HandlerInterceptor接口,org.springframework.web.servlet.handler.HandlerInterceptorAdapter适配器,实现这个接口或继承此类,可以非常方便的实现自己的拦截器。

    [java] view plaincopy
    1. public boolean preHandle(HttpServletRequest request,HttpServletResponse response, Object handler);  
    [java] view plaincopy
    1. public void postHandle(HttpServletRequest request,HttpServletResponse response, Object handler,ModelAndView modelAndView);  
    [java] view plaincopy
    1. public void afterCompletion(HttpServletRequest request,HttpServletResponse response, Object handler, Exception ex)  
    分别实现预处理、后处理(调用了Service并返回ModelAndView,但未进行页面渲染)、返回处理(已经渲染了页面) 
    在preHandle中,可以进行编码、安全控制等处理; 
    在postHandle中,有机会修改ModelAndView; 
    在afterCompletion中,可以根据ex是否为null判断是否发生了异常,进行日志记录。 
    参数中的Object handler是下一个拦截器。

    自定义一个拦截器,要实现HandlerInterceptor接口:
    [java] view plaincopy
    1. public class MyInteceptor implements HandlerInterceptor  

    Spring MVC并没有总的拦截器,不能对所有的请求进行前后拦截。Spring MVC的拦截器,是属于HandlerMapping级别的,可以有多个HandlerMapping ,每个HandlerMapping可以有自己的拦截器。

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

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

    [html] view plaincopy
    1. <mvc:interceptors>    
    2.     <bean class="com.app.mvc.MyInteceptor" />    
    3. </mvc:interceptors>    
    <mvc:interceptors/>会为每一 个HandlerMapping,注入一个拦截器。总有一个HandlerMapping是可以找到处理器的,最多也只找到一个处理器,所以这个拦截器总会被执行的。起到了总拦截器的作用。

    方案二  
    (近似) 总拦截器, 拦截匹配的URL。比方案一多一个URL匹配。
    [html] view plaincopy
    1. <mvc:interceptors >      
    2.   <mvc:interceptor>      
    3.         <mvc:mapping path="/user/*" /> <!-- /user/*  -->      
    4.         <bean class="com.mvc.MyInteceptor"></bean>      
    5.     </mvc:interceptor>      
    6. </mvc:interceptors>   

    方案三   HandlerMapping上的拦截器
    [html] view plaincopy
    1. <bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping">         
    2.  <property name="interceptors">         
    3.      <list>         
    4.          <bean class="com.mvc.MyInteceptor"></bean>        
    5.      </list>         
    6.  </property>         
    7. </bean>     

    <mvc:annotation-driven />会自动注册DefaultAnnotationHandlerMapping 与AnnotationMethodHandlerAdapter 这两个bean,所以就没有机会再给它注入interceptors属性,就无法指定拦截器。

    如果我们手动配置上面的两个Bean,不使用 <mvc:annotation-driven />,就可以 给interceptors属性 注入拦截器了。



    实现全局的异常处理

    [html] view plaincopy
    1. <!-- 总错误处理-->    
    2. <bean id="exceptionResolver" class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">    
    3.     <property name="defaultErrorView">      
    4.         <value>/error/error</value>    
    5.     </property>    
    6.     <property name="defaultStatusCode">      
    7.         <value>500</value>    
    8.     </property>       
    9.     <property name="warnLogCategory">      
    10.         <value>org.springframework.web.servlet.handler.SimpleMappingExceptionResolver</value>    
    11.     </property>       
    12. </bean>     

    通过SimpleMappingExceptionResolver我们可以将不同的异常映射到不同的jsp页面(通过exceptionMappings属性的配置)。如果所抛出的异常在exceptionMappings中没有对应的映射,则Spring将用此默认配置显示异常信息(通过defaultErrorView属性的配置)。
    [html] view plaincopy
    1. <html>    
    2. <head>    
    3. <meta http-equiv="Content-Type" content="text/html; charset=GBK">    
    4. <title>错误页面</title>    
    5. </head>    
    6. <body>    
    7. <h1>出错了</h1>    
    8. <%    
    9. Exception e = (Exception)request.getAttribute("exception");    
    10. out.print(e.getMessage());    
    11. %>    
    12. </body>    
    13. </html>   

    其中一句:request.getAttribute("exception"),key是exception,也是在SimpleMappingExceptionResolver类默认指定的,是可能通过配置文件修改这个值的。

    每个域设定Controller,做好URI规划
    Spring MVC 3.x是完全支持Restful的,我们把URI做好规划,对于诸如ACL的实现会有很大的帮助。建议的URI规划如下:{Domain}[/{SubDomain}]/{BusinessAction}/{ID}。比如:hotels/bookings/cancel/{id} ——表示此URI匹配hotels域的bookings子域,将要进行的是取消某项booking的操作。

    1. @Controller  
    2. @RequestMapping(value = "/hotels")  
    3. public class HotelsController extends AbstractController {  
    4. ...  
    5.     @RequestMapping(value = "/bookings/cancel/{id}", method = RequestMethod.POST)  
    6.     public String deleteBooking(@PathVariable long id) {  
    7.         bookingService.cancelBooking(id);  
    8.         //use prefix 'redirect' to avoid duplicate submission  
    9.         return "redirect:../../search";  
    10.     }  
    11. ...  

    另外还有几个重要原因:
    1、由于Spring的DefaultAnnotationHandlerMapping.java在做Mapping的时候,先做是否有类匹配,再找方法,把基本的mapping放在类上面,可以加速匹配效率;
    2、后续可以通过更细腻的支持Ant path style的AntPathMatcher来规划URI Template资源,做ACL控制(请参考后面的心得体会)。

    转发与重定向

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

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

    写法如下:

    return "forward:/order/add";

    return "redirect:/index.jsp";



    Spring MVC注解
    • Spring自带的@Component注解及扩展@Repository、@Service、@Controller 
       
    • JSR-250 1.1版本中中定义的@ManagedBean注解,是Java EE 6标准规范之一,不包括在JDK中,需要在应用服务器环境使用(如Jboss)  


    • JSR-330的@Named注解




    @Component注解及扩展



    [java] view plaincopy
    1. @Component("标识符")     
    2. POJO类    
    @Component注解,表示该类定义为Spring管理Bean,使用默认value(可选)属性表示Bean标识符。

    @Repository:

    @Repository注解的POJO类表示DAO层实现,从而见到该注解就想到DAO层实现,使用方式和@Component相同;

    [java] view plaincopy
    1. @Repository("testHibernateDao")     
    2. public class TestHibernateDaoImpl {}<span style="white-space:pre">  </span>  


    @Service
    @Service注解的POJO类表示Service层实现,从而见到该注解就想到Service层实现,使用方式和@Component相同;
    [java] view plaincopy
    1. @Service("testService")    
    2. public class TestServiceImpl {    
    3.     @Autowired    
    4.     @Qualifier("testHibernateDao")    
    5.     private TestHibernateDaoImpl dao;    
    6.     public TestHibernateDaoImpl getDao() {    
    7.         return dao;    
    8.     }    
    9. }  

    @Controller

    @Controller注解的类表示Web层实现,从而见到该注解就想到Web层实现,使用方式和@Component相同;

    [java] view plaincopy
    1. <pre name="code" class="java">@Controller    
    2. public class TestAction {    
    3.     @Autowired    
    4.     private TestServiceImpl testService;</pre>}  

    自定义扩展
    Spring内置了三种通用的扩展注解@Repository、@Service、@Controller ,大多数情况下没必要定义自己的扩展,在此我们演示下如何扩展@Component注解来满足某些特殊规约的需要;
    在此我们可能需要一个缓存层用于定义缓存Bean,因此我们需要自定义一个@Cache的注解来表示缓存类。只需要在扩展的注解上注解@Component即可。可以使用@Cache来表示被注解的类是Cache层Bean。

    [java] view plaincopy
    1. @Target({ElementType.TYPE})     
    2. @Retention(RetentionPolicy.RUNTIME)     
    3. @Documented    
    4. @Component    
    5. public @interface Cache{     
    6.        String value() default "";     
    7. }    


    细粒度控制Bean定义扫描

    [html] view plaincopy
    1. <context:component-scan     
    2.         base-package=""    
    3.         resource-pattern="**/*.class"    
    4.         name-generator="org.springframework.context.annotation.AnnotationBeanNameGenerator"    
    5.         use-default-filters="true"    
    6.         annotation-config="true">     
    7.                 <context:include-filter type="aspectj" expression=""/>     
    8.                 <context:exclude-filter type="regex" expression=""/>     
    9. </context:component-scan>  


    • base-package:表示扫描注解类的开始位置,即将在指定的包中扫描,其他包中的注解类将不被扫描,默认将扫描所有类路径;
    • resource-pattern:表示扫描注解类的后缀匹配模式,即“base-package+resource-pattern”将组成匹配模式用于匹配类路径中的组件,默认后缀为“**/*.class”,即指定包下的所有以.class结尾的类文件;
    • name-generator:默认情况下的Bean标识符生成策略,默认是AnnotationBeanNameGenerator,其将生成以小写开头的类名(不包括包名);可以自定义自己的标识符生成策略;
    • use-default-filters:默认为true表示过滤@Component、@ManagedBean、@Named注解的类,如果改为false默认将不过滤这些默认的注解来定义Bean,即这些注解类不能被过滤到,即不能通过这些注解进行Bean定义;
    • annotation-config:表示是否自动支持注解实现Bean依赖注入,默认支持,如果设置为false,将关闭支持注解的依赖注入,需要通过<context:annotation-config/>开启。
    • <context:include-filter>:表示过滤到的类将被注册为Spring管理Bean;
    • <context:exclude-filter>:表示过滤到的类将不被注册为Spring管理Bean,它比<context:include-filter>具有更高优先级;
    • type:表示过滤器类型,目前支持注解类型、类类型、正则表达式、aspectj表达式过滤器,当然也可以自定义自己的过滤器,实现org.springframework.core.type.filter.TypeFilter即可;
    • expression:表示过滤器表达式。

  • 相关阅读:
    Linux 共享库
    使用Visual Studio(VS)开发Qt程序代码提示功能的实现(转)
    ZOJ 3469 Food Delivery(区间DP)
    POJ 2955 Brackets (区间DP)
    HDU 3555 Bomb(数位DP)
    HDU 2089 不要62(数位DP)
    UESTC 1307 windy数(数位DP)
    HDU 4352 XHXJ's LIS(数位DP)
    POJ 3252 Round Numbers(数位DP)
    HDU 2476 String painter (区间DP)
  • 原文地址:https://www.cnblogs.com/zhanghaiyang/p/7213563.html
Copyright © 2011-2022 走看看