Struts2 的参数封装理解

 昨天开发项目的时候一直纠结Struts2使用的对象驱动和模型驱动有什么区别,今天抽空看了网上的资料,结合自己的理解,发现这里面细节还是挺多的...

 总的来说,Struts2是使用Ognl表达式将页面传递的值封装到对象或属性的,大致如下图所示。页面携带参数发送请求后,被

struts2的拦截器,拦截器使用ognl找到ValueStack中对应对应的属性进行赋值,完成参数传递。

struts_ognl

 一共有属性驱动、对象驱动和模型驱动三种情况。虽然从名字的匹配格式上来看属性驱动和模型驱动是一样,但是实际实现原理来讲,属性驱动和对象驱动才是一样,而模型驱动则稍有不用。下面分别讨论。

1.属性驱动
在action中使用属性驱动接收,等价于ongl中的赋值,直接在ValueStack的Root中找到username属性并赋值,所以页面表
单输入框名字要和action中属性名对应一致。
Ognl.setValue("username='akmissxt'",context, root);

下面通过创建自定义拦截器模拟一下struts2的封装过程验证一下猜想,action中添加一个名为"username"的String属性:

public class HelloAction {
	private String username;
	
	public String getUsername() {
		return username;
	}
	public void setUsername(String username) {
		this.username = username;
	}

 
	public String execute(){
		System.out.println(username);
		return "success";
	}
}
页面加个表单,不需要创建输入框,提交地址设置一下:
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
	<form action="${pageContext.request.contextPath }/hello_execute">
		<input type="submit">
	</form>
</body>
</html>
 然后,创建一个自定义拦截器,在拦截器中将{"username":"哈哈哈"}键值对放入ValueStack中:

public class HelloInterceptor extends MethodFilterInterceptor{
	@Override
	protected String doIntercept(ActionInvocation invocation) throws Exception {

		//前处理
		System.out.println("自定义拦截器生效,偷偷放入pinyin的值");
		//在ValueStack中添加username
		ActionContext.getContext().getValueStack().setValue("username", "哈哈哈");
		//放行
		invocation.invoke();
		//后处理
		return null;
	}
}
 最后,将创建的action和拦截器配置好:
		<package name="action" namespace="/" extends="struts-default">
			<!-- 配置拦截器 -->
			<interceptors	>
				<!-- 注册拦截器 -->
				<interceptor name="myInterceptor" class="com.dengtuzi.action.HelloInterceptor">       </interceptor>
				<!-- 自定义拦截器栈 -->
				<interceptor-stack name="mystack">
					<interceptor-ref name="myInterceptor"></interceptor-ref>
					<interceptor-ref name="defaultStack"></interceptor-ref>
				</interceptor-stack>
			</interceptors>
			<!-- 设置默认拦截器栈 -->
			<default-interceptor-ref name="mystack"></default-interceptor-ref>
			<action name="hello_*" method="{1}" class="com.dengtuzi.action.HelloAction">
				<result name="hello">/hello.jsp</result>
				<result name="none"></result>
				<result name="success">/hello.jsp</result>
			</action>
		</package>
 根据我们猜想,虽然提交的表单中没有名为"username"的项,但是由于我们的自定义拦截器在默认拦截器链前把username键 值对已经放在ValueStack中,所以struts2可以使用Ognl在ValueStack中找到username封装给action对象。

 运行,访问页面,查看控制台,和预期结果一致:property1


2.对象驱动
在action中设置对象驱动接收,和属性驱动原理一样需要设置set和get方法,前端页面输入框名字格式为“objectname.propertyname”
,等效语句 如下:
Ognl.setValue("user.username='akmissxt'",context, root);

3.模型驱动
使用模型驱动,命名规则和属性驱动一致,Ognl等效语句也一致,但是实现原理不太一样。
Struts2提供了ModelDriven<T>,需要我们实现一个getModel()方法,并且将要接收参数的对象实例化而不需要设置set和get 方法。
为了搞清楚ModelDriven为我们干了什么,打开struts2的拦截器,找到拦截器链中的ModelDrivenInterceptor,里面的过滤逻辑代码
如下:
@Override
public String intercept(ActionInvocation invocation) throws Exception {
    Object action = invocation.getAction();//获得action实例


    if (action instanceof ModelDriven) {//判断action是否实现ModelDriven接口
        ModelDriven modelDriven = (ModelDriven) action;
        ValueStack stack = invocation.getStack();
        //获得模型实例
        Object model = modelDriven.getModel();
        if (model !=  null) {
        	//如果模型实例不是null,将它压入ValueStack栈顶
        	stack.push(model);
        }
        if (refreshModelBeforeResult) {
            invocation.addPreResultListener(new RefreshModelBeforeResult(modelDriven, model));
        }
    }
    //继续往下执行拦截器链
    return invocation.invoke();
}
 我在里面加了注释,这个拦截器大体逻辑就是如果action实现了ModelDriven接口,且接收参数的实例化对象model不为null
的话,将model压入栈顶,这样本来在栈顶位置的action实例就被压下去了,所以这种情况下,Ognl寻找匹配的属性名赋值的
时候就会先从栈顶的model中开始匹配赋值。
下面通过不实现ModelDriven接口自己将接收参数的对象实例压栈模拟一下Struts2的实现过程。
首先创建action,使用Struts2提供的Preparable接口在参数封装前进行压栈操作:
public class HelloAction implements Preparable{
	private User user = new User(); 
	public String execute(){
		//打印username判断是否封装成功
		System.out.println(user.getUsername());
		return "success";
	}
	
	@Override
	//在prepare拦截器中执行
	public void prepare() throws Exception {
		//获得值栈
		ValueStack valueStack = ActionContext.getContext().getValueStack();
		//将接收参数对象压栈
		valueStack.push(user);
	}
}

页面表单中加入对应输入框:
<form action="${pageContext.request.contextPath }/hello_execute">
		<input name="username" type="text">
		<input type="submit">
     </form>
 将action加入到配置文件中,运行测试;
valueStack3

 和预期结果一致,说明模型驱动和对象驱动的区别在于模型驱动接收的时候Struts2帮我们把接收参数的对象压入ValueStack栈中

这一步操作。

最后附上三种情况的ValueStack内容对比:valueStack1

valueStack2

valueStack3




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值