Spring 核心控制器DispatcherServlet(二)

23.3.1  模型视图类ModelAndView


从名字上看ModelAndView中的Model代表模型,View代表视图,这个名字就很好地解释了该类的作用。业务处理器调用模型层处理完用户请求后,把结果数据存储在该类的model属性中,把要返回的视图信息存储在该类的view属性中,然后让该ModelAndView返回该Spring MVC框架。框架通过调用配置文件中定义的视图解析器,对该对象进行解析,最后把结果数据显示在指定的页面上。


【示例23-7】下面看ModelAndView.Java的代码,如下所示。


package org.springframework.web.servlet;  

import Java.util.HashMap;  

import Java.util.Map;  

publicclass ModelAndView {  

private Object view;                    //该

属性用来存储返回的视图信息

private Map model;                      //该属

性用来存储处理后的结果数据

//下面是一些参数不同的构造函数,用来创建ModelAndView实例

public ModelAndView() {  

   }  

public ModelAndView(View view) {  

this.view = view;  

   }  

public ModelAndView(String viewName) {  

this.view = viewName;  

   }  

public ModelAndView(View view, Map model) {  

this.view = view;  

this.model = model;  

   }  

public ModelAndView(String viewName, Map model) {  

this.view = viewName;  

this.model = model;  

   }  

public ModelAndView(View view, String modelName,

Object modelObject) {  

this.view = view;  

       addObject(modelName, modelObject);  

   }  

public ModelAndView(String viewName, String modelName,

Object modelObject)  {  

this.view = viewName;  

       addObject(modelName, modelObject);  

   }  

//view属性的getter、setter方法,可以看出view属性可以是一个View类的实例,也可

   以是一个String  

publicvoid setView(View view) {  

this.view = view;  

   }  

public View getView() {  

return (this.view instanceof View ? (View) this.view : null);  

   }  

publicvoid setViewName(String viewName) {  

this.view = viewName;  

   }  

public String getViewName() {  

return (this.view instanceof String ? (String) this.view : null);  

   }  

publicboolean isReference() {  

return (this.view instanceof String);  

   }  

//下面的方法用来给model属性赋值、或者获取该属性的值,model是一个Map类型的属性

protected Map getModelInternal() {  

returnthis.model;  

   }  

public Map getModel() {  

if (this.model == null) {  

this.model = new HashMap(1);  

       }  

returnthis.model;  

   }  

public ModelAndView addObject(String modelName, Object modelObject) {  

       getModel().put(modelName, modelObject);  

returnthis;  

   }  

public ModelAndView addAllObjects(Map modelMap) {  

       getModel().putAll(modelMap);  

returnthis;  

   }  

   ...  

}

通过上面的代码可以看出,ModelAndView创建了两个属性view和model,并且创建了获取这两个属性以及给这两个属性赋值的一些方法。view属性可以是View接口的一个实例,也可以是一个String类型的值,所有的视图类都必须实现View接口,在后面的内容中将会讲到;model属性是一个Map类型的值,所以其每一个元素都是一个Key-Value对。


说明:回忆Struts2框架,当该框架的业务处理器处理完用户请求后,将数据存储在ActionContext的实例中,在试图层通过OGNL表达式获取该实例中的结果数据。而Spring MVC框架是将结果数据存在ModelAndView类实例的model属性中。


Spring MVC支持不同的视图技术,如JSP/Servlet、Jstl、Velocity等。每种视图技术对应一个XXXView.Java类,它们都要实现View接口。例如,JSP/Servlet技术对应的类是InternalResourceView.Java;Jstl技术对应的类是JstlView.Java;Velocity技术对应的类是VelocityView.Java。


【示例23-8】要想在一个工程中使用不同的视图技术,需要在配置文件中进行配置,下面是配置代码:


<?xml version="1.0" encoding="UTF-8" ?>  

<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.  

org/dtd/spring-beans.dtd">  

<beans>  

   <bean id="viewResolver"class="org.springframework.web.servlet.view.  

   ResourceBundleViewResolver">  

       <!--basename 属性指定用views.properties文件统一保存视图信息-->  

       <property name="basename" value="views"/>    

   </bean>  

   <bean id="helloWorldAction"class="com.examp.ch22.HelloWorldAction">  

       <property name="helloWorld" value="HelloWorld"/>  

       <!--下面的JspPage 、JstlPage、VelocityPage都定义在views.properties文  

       件中-->  

       <property name="viewPage1" value="JspPage"/>  

       <property name="viewPage2" value="JstlPage">  

       <property name="viewPage3" value="VelocityPage"/>  

   </bean>  

   ...  

</beans>

可以看出,上面代码在定义viewResolver时,指定<property name="basename" value="views"/>,这表示把关于页面的定义统一编写在一个views.properties文件中,上面代码中的JspPage、JstlPage、VelocityPage都定义在这个文件中,其代码如下:


#定义JspPage  

JspPage.class=org.springframework.web.servlet.

view.InternalResourceView  

JspPage.url=/WEB-INF/JspPage.jsp  

#定义JstlPage  

JstlPage.class=org.springframework.web.servlet.view.JstlView  

JstlPage.url=/WEB-INF/JstlPage.jsp  

#定义VelocityPage  

VelocityPage.class=org.springframework.web.servlet.view.VelocityView  

VelocityPage.url=/WEB-INF/VelocityPage.jsp

上面的代码对JspPage、JstlPage、VelocityPage 3个页面进行了定义,每个要素都包括class和url两个属性,class指定的就是对应技术的类,url是该页面的位置。这样就可以在一个工程中同时使用不同技术实现的页面。


23.4  使用拦截器


和Struts2一样,Spring MVC也可以使用拦截器对请求进行拦截处理,用户可以自定义拦截器来实现特定的功能,自定义的拦截器必须实现HandlerInterceptor接口。


【示例23-9】HandlerInterceptor接口的代码如下:


package org.springframework.web.servlet;  

import Javax.servlet.http.HttpServletRequest;  

import Javax.servlet.http.HttpServletResponse;  

publicinterface HandlerInterceptor {  

// preHandle()方法在业务处理器处理请求之前被调用

boolean preHandle(HttpServletRequest request,

HttpServletResponse response,  

   Object handler)  

throws Exception;  

// postHandle()方法在业务处理器处理请求之后被调用

void postHandle(  

           HttpServletRequest request, HttpServletResponse

response, Object  

           handler, ModelAndView modelAndView)  

throws Exception;  

// afterCompletion()方法在DispatcherServlet完全处理完请求后被调用

void afterCompletion(  

           HttpServletRequest request, HttpServletResponse

response, Object  

           handler, Exception ex)  

throws Exception;  

}

下面对代码中的三个方法进行解释。


preHandle():这个方法在业务处理器处理请求之前被调用,在该方法中对用户请求request进行处理。如果程序员决定该拦截器对请求进行拦截处理后还要调用其他的拦截器,或者是业务处理器去进行处理,则返回true;如果程序员决定不需要再调用其他的组件去处理请求,则返回false。


postHandle():这个方法在业务处理器处理完请求后,但是DispatcherServlet向客户端返回请求前被调用,在该方法中对用户请求request进行处理。


afterCompletion():这个方法在DispatcherServlet完全处理完请求后被调用,可以在该方法中进行一些资源清理的操作。


下面通过一个例子来说明如何使用Spring MVC框架的拦截器。


【示例23-10】要求编写一个拦截器,拦截所有不在工作时间的请求,把这些请求转发到一个特定的静态页面,而不对它们的请求进行处理。


首先编写TimeInterceptor.Java,代码如下:




package com.examp.ch23;  

import Java.util.Calendar;  

import Javax.servlet.http.HttpServletRequest;  

import Javax.servlet.http.HttpServletResponse;  

import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;  

publicclass TimeInterceptor extends HandlerInterceptorAdapter {  

//继承HandlerInterceptorAdapter类

privateint openingTime;            //openingTime 属性指定上班时间

privateint closingTime;            //closingTime属性指定下班时间

private String outsideOfficeHoursPage;  

//outsideOfficeHoursPage属性指定错误

                                         提示页面的URL  

publicvoid setOpeningTime(int openingTime) {  

this.openingTime = openingTime;  

   }  

publicvoid setClosingTime(int closingTime) {  

this.closingTime = closingTime;  

   }  

publicvoid setOutsideOfficeHoursPage(String outsideOfficeHoursPage) {  

this.outsideOfficeHoursPage = outsideOfficeHoursPage;  

   }  

//重写 preHandle()方法,在业务处理器处理请求之前对该请求进行拦截处理

publicboolean preHandle(  

           HttpServletRequest request,  

           HttpServletResponse response,  

           Object handler)  

throws Exception {  

       Calendar cal = Calendar.getInstance();  

int hour = cal.get(Calendar.HOUR_OF_DAY);       //获取当前时间

if (openingTime<=hour && hour<closingTime) {    //判断当前是否处于工作

                                                         时间段内  

returntrue;  

       } else {  

           response.sendRedirect(outsideOfficeHoursPage);  //返回提示页面

returnfalse;  

       }  

   }  

}

可以看出,上面的代码重载了preHandle()方法,该方法在业务处理器处理请求之前被调用。在该方法中,首先获得当前的时间,判断其是否在openingTime和closingTime之间,如果在,返回true,这样才会调用业务控制器去处理该请求;否则直接转向一个静态页面,返回false,这样该请求就不会被处理。


下面是在dispatcherServlet-servlet.xml中对拦截器进行的配置,代码如下:


<bean id="urlMapping"class="org.springframework.web.servlet.handler.Simple-  

UrlHandlerMapping">  

       <property name="mappings">  

           <props>  

               <prop key="helloWorld.do">helloWorldAction</prop>  

               <prop key="login.do">loginController</prop>  

           </props>  

       </property>  

       <property name="interceptors">  

                               <!--在interceptors 属性中定义所有的拦截器-->  

           <list>  

               <ref bean="officeHoursInterceptor"/>  

                               <!--引用officeHoursInterceptor 拦截器-->  

           </list>  

       </property>  

</bean>  

<!--定义TimeInterceptor拦截器,id为officeHoursInterceptor -->  

<bean id="officeHoursInterceptor"

class="com.examp.ch23.TimeInterceptor">  

   <!--openingTime 属性指定上班时间-->  

   <property name="openingTime"><value>9</value></property>  

   <!--closingTime属性指定下班时间-->  

   <property name="closingTime"><value>18</value></property>  

    <!--outsideOfficeHoursPage属性指定提示页面的URL-->  

   <property name="outsideOfficeHoursPage"><value>http://localhost:8080/

   ch23/outsideOfficeHours.html</value></property>  

</bean>

可以看出,上面代码用bean标签去定义TimeInterceptor,令其id为officeHoursInterceptor,并给它的3个属性赋值。在urlMapping中通过<property name="interceptors">去指定officeHoursInterceptor为一个拦截器,读者可以在<list>和</list>之间定义多个拦截器。


outsideOfficeHours.html的代码很简单,只是输出一句提示语。


运行程序,在浏览器中随便访问一个页面,如果请求的时间在9点~18点之间,则该请求可以被处理;否则,返回一句提示语,如图23-5所示。



(点击查看大图)图23-5  请求被拦截效果图

说明:在第22章中介绍过控制反转是Spring框架的核心思想,即用一个接口去定义一些操作,在接口的实现类中去重写这些操作,然后在Spring的配置文件中去把该接口的实现类注入到应有框架中,这样就可以通过调用接口去调用接口的实现类。本节讲的拦截器就体现了这种思想,即实现HandlerInterceptorAdapter接口,重写preHandle()方法并在配置文件中实现TimeInterceptor的注入。这样当框架调用HandlerInterceptorAdapter时,就可以调用到TimeInterceptor类的preHandle()方法。


你可能感兴趣的:(object,配置文件,package,private,处理器)