本文来自:曹胜欢博客专栏。转载请注明出处: 

 

一:获取servletAPI的三种方法

       在传统的Web开发中,经常会用到Servlet API中的HttpServletRequest、HttpSession和ServletContext。Struts 2框架让我们可以直接访问和设置action及模型对象的数据,这降低了对HttpServletRequest对象的使用需求,同时降低了对servletAPI的依赖性,从而降低了与servletAPI的耦合度。但在某些应用中,我们可 能会需要在action中去访问HttpServletRequest等对象,所以有时候我们不得不拉近struts2和servletAPI的关系,但struts2也有尽量减少耦合度的方法,下面我们就一起具体看一下在struts2中获得ServletAPI的三种方法:

 

1.ServletAPI解藕方式(一)获取Map对象:

    为了避免与Servlet API耦合在一起,方便Action类做单元测试,Struts 2对HttpServletRequest、HttpSession和ServletContext进行了封装,构造了三个Map对象来替代这三种对象,在Action中,直接使用HttpServletRequest、HttpSession和ServletContext对应的Map对象来保存和读取 数据。可以通过com.opensymphony.xwork2.ActionContext类来得到这三个对象。ActionContextAction执行的上下文,保存了很多对象如parametersrequestsessionapplicationlocale等。通过ActionContext类获取Map对象的方法为:

ActionContextcontext=ActionContext.getContext(); --得到Action执行的上下文    Maprequest=(Map)context.get("request");--得到HttpServletRequest的Map对象    Mapsession=context.getSession();--得到HttpSession的Map对象    Mapapplication=context.getApplication();--得到ServletContext的Map对象

          ActionContext中保存的数据能够从请求对象中得到,其中的奥妙就在于Struts 2中的org.apache.struts2.dispatcher.StrutsRequestWrapper类,这个类是 HttpServletRequest的包装类,它重写了getAttribute()方法(在页面中获取request对象的属性就要调用这个方法), 在这个方法中,它首先在请求对象中查找属性,如果没有找到(如果你在ActionContext中保存数据,当然就找不到了),则到 ActionContext中去查找。这就是为什么在ActionContext中保存的数据能够从请求对象中得到的原因。

2.IOC(控制反转)获取servletAPI

     Action类还有另一种获得ServletAPI的解耦方式,这就是我们可以让他实现某些特定的接口,让Struts2框架在运行时向Action实例注入requestsessionapplication对象。这种方式也就是IOC(控制反转)方式,与之对应的三个接口和它们的方法如下所示:

public class SampleAction implementsAction,  RequestAware, SessionAware, ApplicationAware  {  private Map request;  private Map session;  private Map application;    @Override  public void setRequest(Map request)  {this.request = request;}    @Override  public void setSession(Map session)  {this.session = session;}    @Override  public void setApplication(Map application)  {this.application = application;}    }

ServletRequestAware接口和ServletContextAware接口不属于同一个包,前者在org.apache.struts2.interceptor包中,后者在org.apache.struts2.util包中,这很让人迷惑。

 

3.与Servlet API耦合的访问方式

    直接访问Servlet API将使你的Action与Servlet环境耦合在一起,我们知道对于HttpServletRequest、 HttpServletResponse和ServletContext这些对象,它们都是由Servlet容器来构造的,与这些对象绑定在一起,测试时就需要有Servlet容器,不便于Action的单元测试。但有时候,我们又确实需要直接访问这些对象,那么当然是以完成任务需求为主。要直接获取HttpServletRequest和ServletContext对象,可以使用org.apache.struts2. ServletActionContext类,该类是ActionContext的子类,在这个类中定义下面两个静态方法:

1.得到HttpServletRequest对象:

public static HttpServletRequestgetRequest()

2.得到ServletContext对象:

public static ServletContextgetServletContext()

此外,ServletActionContext类还给出了获取HttpServletResponse对象的方法,如下:

public static HttpServletResponsegetResponse()

ServletActionContext类并没有给出直接得到HttpSession对象的方法,HttpSession对象可以通过HttpServletRequest对象来得到。

除了上述的方法调用得到HttpServletRequest和ServletContext对象外,还可以调用ActionContext对象的 get()方法,传递ServletActionContext.HTTP_REQUEST和 ServletActionContext.SERVLET_CONTEXT键值来得到HttpServletRequest和 ServletContext对象同样的,也可以向ActionContext的get()方法传递ServletActionContext.HTTP_ RESPONSE键值来得到HttpServletResponse对象

 

       总结:通过上面三种方式的讲解我们可以看出,三种获得servletAPI的方式基本都差不多,通常我们建议大家采用第一种方式来获取HttpServletRequestServletContext对象,这样简单而又清晰,并且降低了和servletAPI的耦合度,这样也方便进行单元测试

二:struts2封装请求参数三种方式

     在struts2开发应用中,我们可能经常要求获得视图层传过来的很多数据,一般都是一个实体类的n多属性,很多时候实体类的属性特别多,这时候如果还是按以前的方式在action里面一个个的定义出这些属性的私有变量,然后在提供set、get方法的话,这样就会使整个action太臃肿,严重妨碍了代码的可阅读性,并且也违背了代码的可复用性,这时我们就需要对这些请求参数进行封装,提高代码的可复用性,下面我们就一起来具体看一下三种封装请求参数的方法:

1.利用实体类封装参数

        这种方式是封装参数最简单的方法,一般也比较常用,因为在我们的struts应用程序中,我们一般会根据数据库的信息写出对应的实体类,所以这正好使我们可以利用的,下面我们看一下具体操作:

1.创建实体类user(包括用户名和密码属性),这里比较简单,我们就不贴出代码了。

2.创建action,这里我们主要是来看一下action接收数据的属性这个地方,我们就不是在一一定义这些属性的私有变量了,我们直接定义一个对应实体类的私有对象就可以了,代码如下:

package com.bzu.action;    publicclass LoginAction extends ActionSupport {        private User user;        public User getUser() {            returnuser;        }        publicvoid setUser(User user) {            this.user = user;        }        public String execute(){        if(user.getUsername().equals("admin")&&user.getPassword().equals("123456"))                return"success";            return"fail";        }    }

3.定义表单,这里我们需要注意一下,这里表单里面的控件的name属性定义有一定的要求,定义name时我们应该定义为:对象.属性的形式,示例代码:

4.配置struts.xml,这里配置和平常一样,这里就不再重复了

至此,我们简单的实体类封装请求参数就完成了,我相信大家一定也会感觉很简单吧

 

2.模型驱动封装请求参数

      模型驱动是指使用JavaBean来封装来回请求的参数.这种方式的好处就是减少了action的压力。既用于封装来回请求的参数,也保护了控制逻辑,使它的结构清晰.这就是模型驱动的优势.

下面我们具体来看一下模型驱动的具体实现:

模型驱动的实现主要是体现在action上

1.首先建立一个实体,比较简单,这里就不再写了。

2.建立action类,继承自ActionSupport,实现ModelDriven接口,这个接口定义了一个getModel()方法,用于返回定义的Model,然后调用set方法,进行赋值。代码示例:

publicclass LoginAction3 extends ActionSupport implementsModelDriven
{ private User user=new User();//这里记住要实例化 private LoginService loginService=new LoginServiceImpl();//这里是调用登录的业务处理逻辑 @Override public User getModel() { //TODOAuto-generated method stub return user; } public String execute() { System.out.println(user.getUsername()); System.out.println(user.getPassword()); if(loginService.isLogin(user.getUsername(),user.getPassword())) { return SUCCESS; } return INPUT; } }

在com.opensymphony.xwork2.ModelDriven接口源代码中有一段很重要的说明,现抄录如下

ModelDriven Actions provide a model object to bepushed onto the ValueStack in additionto the Action itself,allowing a FormBeantype approach like Struts
翻译:模型驱动的Action。将模型对象以及Action对象都放到ValueStack里面,允许像Struts一样的FormBean方式
也即:一个Action要想成为模型驱动的话,就必须实现ModelDriven接
口,而我们之前所一直继承的ActionSupport类并没有实现ModelDriven接口

ModelDrivenAction类的执行流程是:首先调用getModel()方法得到User对象,接着根据JavaBean的原则将客户端传过来的属性,一个一个的set到User对象的属性中,将属性全部set完之后,再执行execute()方法。对于模型驱动,只要了解这些就足够了

扩展:模型驱动的底层实现机制

这里用到了defaultStack拦截器栈中的modelDriven拦截器
它对应com.opensymphony.xwork2.interceptor.ModelDrivenInterceptor类,其API描述如下
public class ModelDrivenInterceptor extends AbstractInterceptor
Watches for ModelDriven actions and adds the action`s model on to the valuestack.
翻译:观察模型驱动的Action,并将这个Action的模型【这里指User对象】放到值栈中
Note:The ModelDrivenInterceptor must come before the bothStaticParametersInterceptor and ParametersInterceptor if you want theparameters to be applied to the model.
翻译:若希望将表单提交过来的参数应用到模型里面,那么ModelDrivenInterceptor拦截器就必须位于StaticParametersInterceptor和ParametersInterceptor拦截器前面。

实际上struts-default.xml已完成这个工作了。可以在defaultStack拦截器栈中查看三者位置,所以对于采用模型驱动的方式的话,在struts.xml中只需要指定模型驱动的类就可以了,其它的都不需要我们手工修改

3,属性驱动接收参数

这种方式应该不算是参数封装的方式,但我们很多情况下都用属性驱动的方式接收参数,因为这种方式方便,简洁,易控制。属性驱动在Action中提供与表单字段一一对应的属性,然后一一set赋值,采用属性驱动的方式时,是由每个属性来承载表单的字段值,运转在MVC流程里面。由于这种方式比较简单,这里就不在赘述了。

到底是用属性驱动和是模型驱动呢?

1)统一整个系统中的Action使用的驱动模型,即要么都是用属性驱动,要么都是用模型驱动。

2)如果你的DB中的持久层的对象与表单中的属性都是一一对应的话,那么就使用模型驱动吧,毕竟看起来代码要整洁得多。

3)如果表单的属性不是一一对应的话,那么就应该使用属性驱动,否则,你的系统就必须提供两个Bean,一个对应表单提交的数据,另一个用与持久层。