Spring MVC(上)

Spring Web MVC是一种基于Java的实现了Web MVC设计模式的请求驱动类型的轻量级Web框架,即使用了MVC架构模式的思想,将web层进行职责解耦,基于请求驱动指的就是使用请求-响应模型,框架的目的就是帮助我们简化开发,Spring Web MVC也是要简化我们日常Web开发的。


SpringMVC 与 Struts2 的区别

  • Struts2是类级别的拦截, 一个类对应一个request上下文,SpringMVC是方法级别的拦截,一个方法对应一个request上下文,而方法同时又跟一个url对应,所以说从架构本身上SpringMVC就容易实现restful url,而struts2的架构实现起来要费劲,因为Struts2中Action的一个方法可以对应一个url,而其类属性却被所有方法共享,这也就无法用注解或其他方式标识其所属方法了。
  • SpringMVC的方法之间基本上独立的,独享request response数据,请求数据通过参数获取,处理结果通过ModelMap交回给框架,方法之间不共享变量,而Struts2搞的就比较乱,虽然方法之间也是独立的,但其所有Action变量是共享的,这不会影响程序运行,却给我们编码 读程序时带来麻烦,每次来了请求就创建一个Action,一个Action对象对应一个request上下文。
  • 由于Struts2需要针对每个request进行封装,把request,session等servlet生命周期的变量封装成一个一个Map,供给每个Action使用,并保证线程安全,所以在原则上,是比较耗费内存的。
  • 拦截器实现机制上,Struts2有以自己的interceptor机制,SpringMVC用的是独立的AOP方式,这样导致Struts2的配置文件量还是比SpringMVC大。
  • SpringMVC的入口是servlet,而Struts2是filter(这里要指出,filter和servlet是不同的。以前认为filter是servlet的一种特殊),这就导致了二者的机制不同,这里就牵涉到servlet和filter的区别了。
  • SpringMVC集成了Ajax,使用非常方便,只需一个注解@ResponseBody就可以实现,然后直接返回响应文本即可,而Struts2拦截器集成了Ajax,在Action中处理时一般必须安装插件或者自己写代码集成进去,使用起来也相对不方便。
  • SpringMVC验证支持JSR303,处理起来相对更加灵活方便,而Struts2验证比较繁琐,感觉太烦乱。
  • spring MVC和Spring是无缝的。从这个项目的管理和安全上也比Struts2高(当然Struts2也可以通过不同的目录结构和相关配置做到SpringMVC一样的效果,但是需要xml配置的地方不少)。
  • 设计思想上,Struts2更加符合OOP的编程思想, SpringMVC就比较谨慎,在servlet上扩展。

Spring MVC处理请求的流程


客户端的所有请求都交给前端控制器DispatcherServlet来处理,它会负责调用系统的其他模块来真正处理用户的请求。
DispatcherServlet收到请求后,将根据请求的信息(包括URL、HTTP协议方法、请求头、请求参数、Cookie等)以及HandlerMapping的配置找到处理该请求的Handler(任何一个对象都可以作为请求的Handler)。
在这个地方Spring会通过HandlerAdapter对该处理器进行封装。
HandlerAdapter是一个适配器,它用统一的接口对各种Handler中的方法进行调用。
Handler完成对用户请求的处理后,会返回一个ModelAndView对象给DispatcherServlet,ModelAndView顾名思义,包含了数据模型以及相应的视图的信息。
ModelAndView的视图是逻辑视图,DispatcherServlet还要借助ViewResolver完成从逻辑视图到真实视图对象的解析工作。
当得到真正的视图对象后,DispatcherServlet会利用视图对象对模型数据进行渲染。
客户端得到响应,可能是一个普通的HTML页面,也可以是XML或JSON字符串,还可以是一张图片或者一个PDF文件。

Spring MVC 的配置

web.xml
<servlet>  
    <servlet-name>chapter2</servlet-name>  
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>  
    <load-on-startup>1</load-on-startup>  
</servlet>  
<servlet-mapping>  
    <servlet-name>chapter2</servlet-name>  
    <url-pattern>/</url-pattern>  
</servlet-mapping>  
POST中文乱码解决方案
<filter>  
    <filter-name>CharacterEncodingFilter</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>  
</filter>  
<filter-mapping>  
    <filter-name>CharacterEncodingFilter</filter-name>  
    <url-pattern>/*</url-pattern>  
</filter-mapping> 
spring_mvc.xml
<!-- HandlerMapping -->  
<bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/>  
<!-- HandlerAdapter -->  
<bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"/> 
<!-- BeanNameUrlHandlerMapping:表示将请求的URL和Bean名字映射,如URL为 “上下文/hello”,则Spring配置文件必须有一个名字为“/hello”的Bean,上下文默认忽略。
SimpleControllerHandlerAdapter:表示所有实现了org.springframework.web.servlet.mvc.Controller接口的Bean可以作为Spring Web MVC中的处理器。如果需要其他类型的处理器可以通过实现HadlerAdapter来解决。 -->   
<!-- ViewResolver -->  

<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">  
    <property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>  
    <property name="prefix" value="/WEB-INF/jsp/"/>  
    <property name="suffix" value=".jsp"/>  
</bean>  
<!--InternalResourceViewResolver:用于支持Servlet、JSP视图解析;
    viewClass:JstlView表示JSP模板页面需要使用JSTL标签库,classpath中必须包含jstl的相关jar包;
    prefix和suffix:查找视图页面的前缀和后缀(前缀[逻辑视图名]后缀),比如传进来的逻辑视图名为hello,则该该jsp视图页面应该存放在“WEB-INF/jsp/hello.jsp”;-->
访问静态资源
<!-- 对静态资源文件的访问 -->    
<mvc:resources mapping="/images/**" location="/images/" />  
配置国际化资源文件路径
<bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
        <property name="basename">
            <!-- 定义消息资源文件的相对路径 -->
            <value>i18n/language</value>
        </property>
    </bean>
基于Cookie的本地化解析器
<bean id="localeResolver" class="org.springframework.web.servlet.i18n.CookieLocaleResolver">
       <property name="cookieMaxAge" value="604800"/>
       <property name="defaultLocale" value="zh_CN"/>
       <property name="cookieName" value="Language"></property>
     </bean>
JSON数据处理 高效
<mvc:annotation-driven>
        <mvc:message-converters register-defaults="true">
            <!-- 配置Fastjson支持 -->
            <bean class="com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter">
                <property name="supportedMediaTypes">
                    <list>
                        <value>application/json;charset=UTF-8</value>
                        <value>application/javascript;charset=UTF-8</value>
                    </list>
                </property>
                <property name="features">
                    <list>
                        <value>WriteMapNullValue</value>
                        <value>WriteDateUseDateFormat</value>
                    </list>
                </property>
            </bean>
        </mvc:message-converters>
    </mvc:annotation-driven> 
视图模式配置
<!-- Velocity视图解析器    默认视图  -->
    <bean id="velocityViewResolver" class="org.springframework.web.servlet.view.velocity.VelocityViewResolver">
        <property name="contentType" value="text/html;charset=UTF-8" />
        <property name="viewNames" value="*.html" />
        <property name="suffix" value=""/>
        <property name="dateToolAttribute" value="date" />
        <property name="numberToolAttribute" value="number" /> 
        <property name="requestContextAttribute" value="rc"/>
        <property name="order" value="0"/>
    </bean>
    <bean id="velocityConfigurer" class="org.springframework.web.servlet.view.velocity.VelocityConfigurer">
        <property name="resourceLoaderPath" value="/WEB-INF/view/" />
        <property name="velocityProperties">
          <props>
            <prop key="input.encoding">UTF-8</prop>
            <prop key="output.encoding">UTF-8</prop>
            <prop key="contentType">text/html;charset=UTF-8</prop>
          </props>
        </property>
    </bean>
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">    
        <!-- 这里的配置我的理解是自动给后面action的方法return的字符串加上前缀和后缀,变成一个 可用的url地址 -->  
        <property name="prefix" value="/WEB-INF/view/" />    
        <property name="suffix" value=".jsp" />    
    </bean> 
让SPRING MVC支持文件上传
<bean id="multipartResolver"
        class="org.springframework.web.multipart.commons.CommonsMultipartResolver"
        p:defaultEncoding="utf-8">
        <property name="maxInMemorySize" value="2048"></property>
        <!-- 以字节为单位的最大上传文件的大小 -->
        <property name="maxUploadSize" value="20000000" />
        <!-- <property name="uploadTempDir" value="tmoDir"></property> -->
    </bean> 
开发处理器/页面控制器
import javax.servlet.http.HttpServletRequest;  
import javax.servlet.http.HttpServletResponse;  
import org.springframework.web.servlet.ModelAndView;  
import org.springframework.web.servlet.mvc.Controller;  
@Controller
public class HelloWorldController implements Controller {  
    @Override  
    public ModelAndView handleRequest(HttpServletRequest req, HttpServletResponse resp) throws Exception {  
       //1、收集参数、验证参数  
       //2、绑定参数到命令对象  
       //3、将命令对象传入业务对象进行业务处理  
       //4、选择下一个页面  
       ModelAndView mv = new ModelAndView();  
       //添加模型数据 可以是任意的POJO对象  
       mv.addObject("message", "Hello World!");  
       //设置逻辑视图名,视图解析器会根据该名字解析到具体的视图页面  
       mv.setViewName("hello");  
       return mv;  
    }  
}  

SpringMVC 核心类与接口

DispatcherServlet (前置控制器)作用

1、文件上传解析,如果请求类型是multipart将通过MultipartResolver进行文件上传解析;
2、通过HandlerMapping,将请求映射到处理器(返回一个HandlerExecutionChain,它包括一个处理器、多个HandlerInterceptor拦截器);
3、通过HandlerAdapter支持多种类型的处理器(HandlerExecutionChain中的处理器);
4、通过ViewResolver解析逻辑视图名到具体视图实现;
5、本地化解析;
6、渲染具体的视图等;
7、如果执行过程中遇到异常将交给HandlerExceptionResolver来解析。

WebApplicationContext(父子上下文)中特殊的Bean
  • Controller : 处理器/页面控制器,做的是MVC中的C的事情,但控制逻辑转移到前端控制器了,用于对请求进行处理;
  • HandlerMapping : 请求到处理器的映射,如果映射成功返回一个HandlerExecutionChain对象(包含一个Handler处理器(页面控制器)对象、多个HandlerInterceptor拦截器)对象;如BeanNameUrlHandlerMapping将URL与Bean名字映射,映射成功的Bean就是此处的处理器;
  • HandlerAdapter : HandlerAdapter将会把处理器包装为适配器,从而支持多种类型的处理器,即适配器设计模式的应用,从而很容易支持很多类型的处理器;如SimpleControllerHandlerAdapter将对实现了Controller接口的Bean进行适配,并且掉处理器的handleRequest方法进行功能处理;
  • ViewResolver :ViewResolver将把逻辑视图名解析为具体的View,通过这种策略模式,很容易更换其他视图技术;如InternalResourceViewResolver将逻辑视图名映射为jsp视图;
  • LocalResover : 本地化解析,因为Spring支持国际化,因此LocalResover解析客户端的Locale信息从而方便进行国际化;
  • ThemeResovler : 主题解析,通过它来实现一个页面多套风格,即常见的类似于软件皮肤效果;
  • MultipartResolver : 文件上传解析,用于支持文件上传;
  • HandlerExceptionResolver : 处理器异常解析,可以将异常映射到相应的统一错误界面,从而显示用户友好的界面(而不是给用户看到具体的错误信息);
  • RequestToViewNameTranslator : 当处理器没有返回逻辑视图名等相关信息时,自动将请求URL映射为逻辑视图名;
  • FlashMapManager :用于管理FlashMap的策略接口,FlashMap用于存储一个请求的输出,当进入另一个请求时作为该请求的输入,通常用于重定向场景。

SpringMVC 拦截器

应用场景

1、日志记录:记录请求信息的日志,以便进行信息监控、信息统计、计算PV(Page View)等。
2、权限检查:如登录检测,进入处理器检测检测是否登录,如果没有直接返回到登录页面;
3、性能监控:有时候系统在某段时间莫名其妙的慢,可以通过拦截器在进入处理器之前记录开始时间,在处理完后记录结束时间,从而得到该请求的处理时间(如果有反向代理,如apache可以自动记录);
4、通用行为:读取cookie得到用户信息并将用户对象放入请求,从而方便后续流程使用,还有如提取Locale、Theme信息等,只要是多个处理器都需要的即可使用拦截器实现。
5、OpenSessionInView:如Hibernate,在进入处理器打开Session,在完成后关闭Session。

拦截器和过滤器比较

1、拦截器是基于Java的反射机制的,而过滤器是基于函数回调。
2、拦截器不依赖与servlet容器,过滤器依赖与servlet容器。
3、拦截器只能对action请求起作用,而过滤器则可以对几乎所有的请求起作用。
4、拦截器可以访问action上下文、值栈里的对象,而过滤器不能访问。
5、在action的生命周期中,拦截器可以多次被调用,而过滤器只能在容器初始化时被调用一次。
6、拦截器可以获取IOC容器中的各个bean,而过滤器就不行,这点很重要,在拦截器里注入一个service,可以调用业务逻辑

运行流程图

拦截器的实现方式
  1. 定义的Interceptor类要实现了Spring的HandlerInterceptor 接口

  2. 继承实现了HandlerInterceptor接口的类,比如Spring已经提供的实现了HandlerInterceptor接口的抽象类HandlerInterceptorAdapter

拦截器接口
package org.springframework.web.servlet;  
public interface HandlerInterceptor {  
    boolean preHandle(  
            HttpServletRequest request, HttpServletResponse response,   
            Object handler)   
            throws Exception;  
    void postHandle(  
            HttpServletRequest request, HttpServletResponse response,   
            Object handler, ModelAndView modelAndView)   
            throws Exception;  
    void afterCompletion(  
            HttpServletRequest request, HttpServletResponse response,   
            Object handler, Exception ex)  
            throws Exception;  
}   

preHandle:预处理回调方法,实现处理器的预处理(如登录检查),第三个参数为响应的处理器.返回值:
true表示继续流程(如调用下一个拦截器或处理器);
false表示流程中断(如登录检查失败),不会继续调用其他的拦截器或处理器,此时我们需要通过response来产生响应;
postHandle:后处理回调方法,实现处理器的后处理(但在渲染视图之前),此时我们可以通过modelAndView(模型和视图对象)对模型数据进行处理或对视图进行处理,modelAndView也可能为null。
afterCompletion:整个请求处理完毕回调方法,即在视图渲染完毕时回调,如性能监控中我们可以在此记录结束时间并输出消耗时间,还可以进行一些资源清理,类似于try-catch-finally中的finally,但仅调用处理器执行链中preHandle返回true的拦截器的afterCompletion。

实现流程

1、拦截器实现

//直接在类名称的上端写入即可,value中指定要引入的拦截器的名称即可
public class TestHandlerInterceptor implements HandlerInterceptor {
    /** 
     * 在业务处理器处理请求之前被调用 
     * 如果返回false 
     *     从当前的拦截器往回执行所有拦截器的afterCompletion(),再退出拦截器链
     * 如果返回true 
     *    执行下一个拦截器,直到所有的拦截器都执行完毕 
     *    再执行被拦截的Controller 
     *    然后进入拦截器链, 
     *    从最后一个拦截器往回执行所有的postHandle() 
     *    接着再从最后一个拦截器往回执行所有的afterCompletion() 
     */  
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("--------1  HandlerInterceptor  preHandle----------");
        System.out.println("request--->" + request);
        System.out.println("response--->" + response);
        System.out.println("className--->" + handler);
        return true;
    }
    /**
     * 在业务处理器处理请求执行完成后,生成视图之前执行的动作   
     * 可在modelAndView中加入数据,比如当前时间
     */
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { 
        System.out.println("----------2  HandlerInterceptor  postHandle---------");
    }
    /** 
     * 在DispatcherServlet完全处理完请求后被调用,可用于清理资源等   
     * 当有拦截器抛出异常时,会从当前拦截器往回执行所有的拦截器的afterCompletion() 
     */  
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("---------3  HandlerInterceptor  afterCompletion-----------");
    }
}

2、控制器

@RequestMapping("/index")
    public String index(Map<String,Object> map) {
        System.out.println("===Controller.springMVC===");
        map.put("message", "Hello World!");
        return "springMVC.html";
    }  

3、spring配置文件chapter5-servlet.xml

<!-- 自定义的拦截器类处理类 -->
<mvc:interceptors>
    <mvc:interceptor>
      <mvc:mapping path="/**/**"/>
         <bean class="com.heqing.ssm.interceptor.TestHandlerInterceptor"></bean>
    </mvc:interceptor> 
    <mvc:interceptor>
         <mvc:mapping path="/**/**"/>
         <bean class="com.heqing.ssm.interceptor.TestWebRequestInterceptor"></bean>
     </mvc:interceptor>        
</mvc:interceptors>
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值