【Struts2】剖析Struts2中的反射技术 ValueStack(值栈)
1,Struts2框架主要组件的处理流程
在说ValueStack之前,笔者先说一说Struts2中常用的组件,struts2中常用组件有strutsPrepareAndExecuteExceptionn,以及一般执行流程:
- 请求来进入 Filter 控制器
- Filter 控制器创建 ValueStack 对象并初始化
- Filter 控制器根据 struts.xml 调用 defaultStack 拦截器栈
- Filter 控制器根据 struts.xml 调用 Action 处理
- Filter 控制器会根据 Action 返回值 +struts.xml 调用 Result 处理
- Filter 控制器销毁 ValueStack 对象信息
- Filter 控制器将 Result 生成的响应信息输出
想必搭建过Struts2框架的读者都知道上面的过程的类几乎都是需要我们在搭建的时候就进行配置。但是ValueStack对象的配置我们并没有显示指定出来,为了研究struts2中反射,我们打开struts-default.xml文件,找到里面的params拦截器(这是struts2框架中所有默认执行拦截器的一种,主要完成对参数的赋值和取值功能),找到它关联的类(ParametersInterceptor)。打开ParametersInterceptor的原码后我们可以在里面看到ValueStack。ValueStack其实是一个接口,其实现子类只有一个OgnlValueStack类。再继续剖析OgnlValueStack类,我们可以看出这个类其实是对OGNL工具包的包装。OGNL是一个封装了反射操作的工具包,因此Struts2底层参数赋值的反射操作是通过使用OGNL这个工具包来完成的。
Struts2中的ValueStack不仅仅用于参数方面,它是用于存储request,session,application,action,parameters等请求相关的数据对象。
下面通过一张图片了解一下ValueStack看一看:
通过这张图片,我们可以非常清楚的了解到valueStack是基于OGNL工具包的,并且valueStack存储数据的形式分为两种root好context。root是ArrayList栈结构,并且Action对象是最后压入栈的,所以Action对象永远在栈的最顶部。另一种存储数据的结构是Context,它和root不同,context是一种map结构。
2,OGNL工具的使用
OGNL是Object Graphics Navigation Language 的简写,是一种对象图导航语言。
上面介绍了ValueStack是基于OGNL工具的,如果说我们也想使用OGNL做一些封装,那么首先应该导入ognl.jar包和javassist-GA.jar包。
Ognl.getValue("OGNL 表达式 ",context,root) Ognl.getValue("OGNL 表达式",context); Ognl.getValue("OGNL 表达式",root); Ognl.setValue("OGNL 表达式 ",context,root,value) Ognl.setValue("OGNL 表达式",context,value); Ognl.setValue("OGNL 表达式",root,value);
在有context和root的语句中,可以使用OGNL表达式加以区分操作的对象,如果以#开头表示在context中操作数据,如果不以#开头表示在root中操作数据。
User.java文件
package cn.ongl.test; public class User { private int id; private String userName; private String userPassword; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getUserName() { return userName; } public void setUserName(String userName) { this.userName = userName; } public String getUserPassword() { return userPassword; } public void setUserPassword(String userPassword) { this.userPassword = userPassword; } }
接下来看一看测试:
@Test public void testName() throws OgnlException{ User user=new User(); user.setId(1); user.setUserName("jame"); user.setUserPassword("123456"); Object root=user; Map<String,Object> context=new HashMap<String,Object>(); context.put("userName","brace"); //设置为userName,表示将root中userName属性值设置为lucy Ognl.setValue("userName",context,root, "lucy"); //从root中获取userName字段的值 String uname= (String)Ognl.getValue("userName",context,root); System.out.println(uname);//lucy //从context中获取userName的值 uname=(String)Ognl.getValue("#userName", context, root); System.out.println(uname);//brace //设置为#userName,表示将context中userName属性值设置steven Ognl.setValue("userName",context,"steven"); //从root中获取userName属性字段的值 uname=(String)Ognl.getValue("userName", context, root); System.out.println(uname);//lucy //从context中获取userName字段的值 uname=(String)Ognl.getValue("#userName", context,root); System.out.println(uname);//steven }
接下来模拟一下请求参数给struts2中Action对象赋值的过程:
Enumeration<String> parameterNames = request.getParameterNames(); //将所有的参数和值封装到parameter中去 Map<String,String> parameters = new HashMap<String,String>(); String value; String nextElement; /* * 循环遍历,将请求的参数以key-value的形式封装到Map集合 */ while(parameterNames.hasMoreElements()){ nextElement = parameterNames.nextElement(); value=request.getParameter(nextElement); parameters.put(nextElement, value); } //循环访问请求参数,给Action对象中同名的属性赋值 User root = new User(); Set<String> keys = parameters.keySet(); for(String key:keys){ try { Ognl.setValue(key, root, parameters.get(key)); } catch (OgnlException e) { e.printStackTrace(); } } //打印root对象值 System.out.println(root.getId()+" " +root.getUserName()+" "+root.getUserPassword());
在上面的请求中,我们只需要得到request,我们也可以完成一个简单的框架自动赋值映射的功能了。
Ognl.getValue("OGNL 表达式",context);