Struts2
OGNL表达式
概述
OGNL是Object Graphic Navigation Language(对象图导航语言)的缩写,他是一个开源项目。Struts框架使用OGNL作为默认的表达式语言。
所谓表达式,就是用一定的格式字符串访问数据或方法。
表达式显示的内容如果脱离了解析引擎是毫无意义的。
OGNL提供了这种字符串解析的引擎
开发人员可以把重点放在如何写字符串上就可以达到调用方法或属性的功能
OGNL环境
Jar包
ognl-xxx.jar
,javassist-xxx.jar
ognl:ognl的核心环境
javassist是ognl的依赖,提供字节码修改能力。
OGNL提供的功能
- 支持对象方法的调用
- 支持对象对象属性访问(getter方法属性化的转换)
- 支持静态方法的调用
- 支持集合对象操作
OGNL API使用
核心类和元素
- 表达式:expression
- 根对象:root
- 上下文: OgnlContext
- Ognl工具类: Ognl
表达式与根对象
访问根对象的属性
// 根对象 User root = new User(); root.setName("张三疯"); root.setAge(19); // 表达式 String expression="name"; //通过Ognl方式获得根对象中的表达式结果 Object value = Ognl.getValue(expression, root);
Ognl在根对象中以表达式的方式取出根对象的属性
访问根对象的方法
// 根对象 User root = new User(); root.setName("张三疯"); root.setAge(19); // 表达式 String expression="getName()"; //通过Ognl方式获得根对象中的表达式结果 Object value = Ognl.getValue(expression, root);
表达式与上下文
通过
#+key
修饰符的方式访问上下文中的对象// 上下文对象 Map<Object, Object> context = new HashMap<Object, Object>(); context.put("aaa", "小a"); context.put("bbb", "小b"); // 根对象 Object root = null; // 表达式 String expression = "#aaa"; Object value = Ognl.getValue(expression, context, root);
表达式,根对象,上下文
根对象与上下文结合
// 上下文对象 Map<Object, Object> context = new HashMap<Object, Object>(); context.put("aaa", "小a"); context.put("bbb", "小b"); // 根对象 User root = new User(); root.setName("小a"); root.setAge(19); // 表达式 String expression = "name.equals(#aaa)"; // 获取ognl结果 Object value = Ognl.getValue(expression, context, root);
静态方法调用
// Integer.valueOf("123");
String expression = "@java.lang.Integer@valueOf(\"123\")";
Object value = Ognl.getValue(expression, null);
// Runtime.getRuntime().exec("calc.exe");
String expression = "@java.lang.Runtime@getRuntime().exec(\"calc.exe\")";
Object value = Ognl.getValue(expression, null);
静态方法调用中:
- 类名必须使用全路径
- 类前加
@
修饰- 调用的静态方法的
点
用@
替代
ValueStack值栈
值栈的概念
ValueStack是一个接口,实现类是OgnlValueStack。值栈就是一个存取空间,理解为是一个数据的中转站。客户端发送一个请求到Action,服务器就会创建一个Action的实例。(Action是多例的,不会出现线程安全的问题。Servlet是单例的,会出现线程安全的问题。),只要服务器有一个Action的实例,就会创建一个值栈为这个Action服务。值栈对象贯穿了整个的Action的生命周期。值栈被Struts2保存到了request域中一份。
- 值栈是数据的中转站
- MVC模型中,Action负责Controller,在Action中将model数据存储到值栈中
- MVC模型中,JSP负责View,view通过值栈去取数据
ActionContext和ValueStack的关系
每次请求中,都会在这个请求的过程中,创建一个ActionContext。ActionContext创建的同时,会创建一个ValueStack。ValueStack中的的上下文对象会传递给ActionContext,作为ActionContext的上下文。ValueStack中的上下文同时引用了自身ValueStack。
public ActionContext createActionContext(HttpServletRequest request, HttpServletResponse response) {
ActionContext ctx;
Integer counter = 1;
Integer oldCounter = (Integer) request.getAttribute(CLEANUP_RECURSION_COUNTER);
if (oldCounter != null) {
counter = oldCounter + 1;
}
ActionContext oldContext = ActionContext.getContext();
if (oldContext != null) {
// detected existing context, so we are probably in a forward
ctx = new ActionContext(new HashMap<String, Object>(oldContext.getContextMap()));
} else {
ValueStack stack = dispatcher.getContainer().getInstance(ValueStackFactory.class).createValueStack();
stack.getContext().putAll(dispatcher.createContextMap(request, response, null));
ctx = new ActionContext(stack.getContext());
}
request.setAttribute(CLEANUP_RECURSION_COUNTER, counter);
ActionContext.setContext(ctx);
return ctx;
}
ValueStack获取方式
ActionContext方式获取
ValueStack valueStack = ActionContext.getContext().getValueStack();
值栈是存储在自己内部的map中,自己的map又传递交给了ActionContext。
HttpServletRequest方式获取ValueStack
ValueStack valueStack = ServletActionContext.getValueStack(ServletActionContext.getRequest());
值栈也存储了一份到request中。
值栈的结构组成
root 区
根对象。
具体类型为CompoundRoot。
CompoundRoot实现了List接口,其实就是一个list集合。context 区
上下文对象。
具体类型为OgnlContext。
OgnlContext内部操控的是一个Map集合,其实context区可以理解为一个Map集合。
值栈的debug查询展示
在jsp页面中,引入struts的标签库
<%@ taglib uri="/struts-tags" prefix="s"%>
- 通过
<s:debug/>
标签可以在jsp中查看值栈的结构
上下文区存储的数据
- request对象数据
- response对象数据
- application对象数据
- parameters数据
- attr数据:request,session,application中的atrribute数据。
值栈存储数据方式
值栈存储数据主要指的是往根对象区存储数据。根对象是一个List集合类型,存储着对象。
其中根对象还维护了一个Map负责存储key-value类型的数据
默认的值栈数据
- DefaultTextProvider
- 当前的action对象实例
push方式存取
存储
ValueStack valueStack = ActionContext.getContext().getValueStack(); User user = new User(); user.setName("李四"); user.setAge(100); valueStack.push(user);
获取
<s:property value="name" />
set方式存取
存储
ValueStack valueStack = ActionContext.getContext().getValueStack(); User user = new User(); user.setName("李四"); user.setAge(100); valueStack.set("user", user);
获取
<s:property value="user.name" />
属性驱动方式
- 存储:自动通过valueStack存储到当前action的类中
获取
<s:property value="name" />
模型驱动方式
- 存储:自动存储到valuestack的root区中
- 获取
<s:property value="name" />
EL 如何获得值栈中数据
- struts在前端控制器中对请求进行了包装,包装为了StrutsRequestWrapper
- EL表达式的核心内容其实就是对request的attribute属性进行访问,即request.getAttribute()方法
StrutsRequestWrapper对getAttribute方法进行了重写
public Object getAttribute(String key) { if (key == null) { throw new NullPointerException("You must specify a key value"); } if (disableRequestAttributeValueStackLookup || key.startsWith("javax.servlet")) { // don't bother with the standard javax.servlet attributes, we can short-circuit this // see WW-953 and the forums post linked in that issue for more info return super.getAttribute(key); } ActionContext ctx = ActionContext.getContext(); Object attribute = super.getAttribute(key); if (ctx != null && attribute == null) { boolean alreadyIn = isTrue((Boolean) ctx.get(REQUEST_WRAPPER_GET_ATTRIBUTE)); // note: we don't let # come through or else a request for // #attr.foo or #request.foo could cause an endless loop if (!alreadyIn && !key.contains("#")) { try { // If not found, then try the ValueStack ctx.put(REQUEST_WRAPPER_GET_ATTRIBUTE, Boolean.TRUE); ValueStack stack = ctx.getValueStack(); if (stack != null) { attribute = stack.findValue(key); } } finally { ctx.put(REQUEST_WRAPPER_GET_ATTRIBUTE, Boolean.FALSE); } } } return attribute; }
通过struts的源码可以看出,getAttribute方法是先在request的attribuite中找,找不到就到ValueStack中去找
获取值栈上下文中的数据
<s:property value="#request.name"/>
<s:property value="#session.name"/>
<s:property value="#application.name"/>
<s:property value="#parameters.id"/>
<s:property value="#parameters.aaa"/>
<s:property value="#attr.name"/>
OGNL中的符号
#号的作用
获取Context中的数据
<s:property value="#request.name"/> <s:property value="#session.name"/>
构建一个Map集合
<s:iterator var="entry" value="#{ 'aa':'11','bb':'22','cc':'33' }"> <s:property value="key"/>--<s:property value="value"/><br/> <s:property value="#entry.key"/>--<s:property value="#entry.value"/><br/> </s:iterator>
%号的作用
强制解析OGNL表达式
<s:textfield value="%{#request.name}" name="name"/>
强制不解析OGNL表达式
<s:property value="%{'#request.name'}"/>
$号的作用
```
中文 :login.welcome=您好:{#session.user.username}
英文 :login.welcome=Hello:{#session.user.username}
“`