本文来自:曹胜欢博客专栏。转载请注明出处:http://blog.csdn.net/csh624366188
一:获取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类来得到这三个对象。ActionContext是Action执行的上下文,保存了很多对象如parameters、request、session、application和locale等。通过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实例注入request、session和application对象。这种方式也就是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的方式基本都差不多,通常我们建议大家采用第一种方式来获取HttpServletRequest和ServletContext对象,这样简单而又清晰,并且降低了和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,一个对应表单提交的数据,另一个用与持久层。