- 浏览: 1068035 次
- 性别:
- 来自: 长沙
文章分类
- 全部博客 (639)
- 服务器配置篇 (58)
- hibernate篇 (14)
- spring篇 (33)
- struts篇 (28)
- JS篇 (46)
- 其他技术篇 (46)
- 数据库集群配置 (6)
- JAVA基础相关 (48)
- 分布式框架HadHoop的应用 (2)
- FLEX篇 (8)
- SQLSERVER技术 (32)
- Android学习 (13)
- amchart学习笔记 (1)
- openfire+smark搭建即时通讯 (9)
- Linux学习 (18)
- Oracle数据库 (15)
- 网站优化技术 (12)
- mysql数据库 (2)
- 项目学习总结 (18)
- 工具类(JAVA) (12)
- 工具类(JS) (2)
- 设计模式 (10)
- Lucene学习 (24)
- EJB3学习 (6)
- Sphinx搜索引擎 (3)
- 工作中用到的软件小工具 (5)
- .NET (49)
- JAVA 连接SQLSERVER2008步骤 (1)
- MongoDB (19)
- Android手机开发 (3)
- Maven (6)
- vue (9)
- Shiro (4)
- mybatis (3)
- netty框架 (1)
- SpringCloud (3)
- spring-cloud (7)
- Git (1)
- dubbo (2)
- springboot (13)
- rocketmq (1)
- git学习 (2)
- kafka服务器 (2)
- linux (10)
- WEB系统辅助项目 (1)
- jenkins (2)
- docker (4)
- influxdb (3)
- python (2)
- nginx (1)
最新评论
-
jiangfuofu555:
这样数据量大,效率怎么样?
sqlserver 实现分页的前台代码 以及后台的sqlserver语句 -
w156445045:
博主请问下,如何做到实时的刷新呢,
另外我后台是Java 谢谢 ...
web 版本的汽车仪表盘,非常好看。还有各种图形 -
jackyin5918:
<transportConnector name=&qu ...
ActiveMQ的activemq.xml详细配置讲解 -
握着橄榄枝的人:
你这个不是spring1.x的吧
spring1.x使用AOP实例 -
xiaophai:
全乱套了!
openfire+spark搭建完美的及时通讯
OGNL —— 完美的催化剂
为了解决数据从View层传递到Controller层时的不匹配性,Struts2采纳了XWork的OGNL方案。并且在OGNL的基础上,构建了OGNLValueStack的机制,从而比较完美的解决了数据流转中的不匹配性。
OGNL(Object Graph Navigation Language),是一种表达式语言。使用这种表达式语言,你可以通过某种表达式语法,存取Java对象树中的任意属性、调用Java对象树的方法、同时能够自动实现必要的类型转化。如果我们把表达式看做是一个带有语义的字符串,那么OGNL无疑成为了这个语义字符串与Java对象之间沟通的桥梁。
如何使用OGNL
让我们先研究一下OGNL的API,他来自于Ognl的静态方法:
- /**
- * Evaluates the given OGNL expression tree to extract a value from the given root
- * object. The default context is set for the given context and root via
- * <CODE>addDefaultContext()</CODE>.
- *
- * @param tree the OGNL expression tree to evaluate, as returned by parseExpression()
- * @param context the naming context for the evaluation
- * @param root the root object for the OGNL expression
- * @return the result of evaluating the expression
- * @throws MethodFailedException if the expression called a method which failed
- * @throws NoSuchPropertyException if the expression referred to a nonexistent property
- * @throws InappropriateExpressionException if the expression can't be used in this context
- * @throws OgnlException if there is a pathological environmental problem
- */
- public static Object getValue( Object tree, Map context, Object root ) throws OgnlException;
- /**
- * Evaluates the given OGNL expression tree to insert a value into the object graph
- * rooted at the given root object. The default context is set for the given
- * context and root via <CODE>addDefaultContext()</CODE>.
- *
- * @param tree the OGNL expression tree to evaluate, as returned by parseExpression()
- * @param context the naming context for the evaluation
- * @param root the root object for the OGNL expression
- * @param value the value to insert into the object graph
- * @throws MethodFailedException if the expression called a method which failed
- * @throws NoSuchPropertyException if the expression referred to a nonexistent property
- * @throws InappropriateExpressionException if the expression can't be used in this context
- * @throws OgnlException if there is a pathological environmental problem
- */
- public static void setValue( Object tree, Map context, Object root, Object value ) throws OgnlException
/** * Evaluates the given OGNL expression tree to extract a value from the given root * object. The default context is set for the given context and root via * <CODE>addDefaultContext()</CODE>. * * @param tree the OGNL expression tree to evaluate, as returned by parseExpression() * @param context the naming context for the evaluation * @param root the root object for the OGNL expression * @return the result of evaluating the expression * @throws MethodFailedException if the expression called a method which failed * @throws NoSuchPropertyException if the expression referred to a nonexistent property * @throws InappropriateExpressionException if the expression can't be used in this context * @throws OgnlException if there is a pathological environmental problem */ public static Object getValue( Object tree, Map context, Object root ) throws OgnlException; /** * Evaluates the given OGNL expression tree to insert a value into the object graph * rooted at the given root object. The default context is set for the given * context and root via <CODE>addDefaultContext()</CODE>. * * @param tree the OGNL expression tree to evaluate, as returned by parseExpression() * @param context the naming context for the evaluation * @param root the root object for the OGNL expression * @param value the value to insert into the object graph * @throws MethodFailedException if the expression called a method which failed * @throws NoSuchPropertyException if the expression referred to a nonexistent property * @throws InappropriateExpressionException if the expression can't be used in this context * @throws OgnlException if there is a pathological environmental problem */ public static void setValue( Object tree, Map context, Object root, Object value ) throws OgnlException
我们可以看到,OGNL的API其实相当简单,你可以通过传递三个参数来实现OGNL的一切操作。而这三个参数,被我称为OGNL的三要素。
那么运用这个API,我们能干点什么呢?跑个测试看看结果:
- /**
- * @author Downpour
- */
- public class User {
- private Integer id;
- private String name;
- private Department department = new Department();
- public User() {
- }
- // setter and getters
- }
- //=========================================================================
- /**
- * @author Downpour
- */
- public class Department {
- private Integer id;
- private String name;
- public Department() {
- }
- // setter and getters
- }
- //=========================================================================
- /**
- * @author Downpour
- */
- public class OGNLTestCase extends TestCase {
- /**
- *
- * @throws Exception
- */
- @SuppressWarnings("unchecked")
- @Test
- public void testGetValue() throws Exception {
- // Create root object
- User user = new User();
- user.setId(1);
- user.setName("downpour");
- // Create context
- Map context = new HashMap();
- context.put("introduction","My name is ");
- // Test to directly get value from root object, with no context
- Object name = Ognl.getValue(Ognl.parseExpression("name"), user);
- assertEquals("downpour",name);
- // Test to get value(parameter) from context
- Object contextValue = Ognl.getValue(Ognl.parseExpression("#introduction"), context, user);
- assertEquals("My name is ", contextValue);
- // Test to get value and parameter from root object and context
- Object hello = Ognl.getValue(Ognl.parseExpression("#introduction + name"), context, user);
- assertEquals("My name is downpour",hello);
- }
- /**
- *
- * @throws Exception
- */
- @SuppressWarnings("unchecked")
- @Test
- public void testSetValue() throws Exception {
- // Create root object
- User user = new User();
- user.setId(1);
- user.setName("downpour");
- // Set value according to the expression
- Ognl.setValue("department.name", user, "dev");
- assertEquals("dev", user.getDepartment().getName());
- }
- }
/** * @author Downpour */ public class User { private Integer id; private String name; private Department department = new Department(); public User() { } // setter and getters } //========================================================================= /** * @author Downpour */ public class Department { private Integer id; private String name; public Department() { } // setter and getters } //========================================================================= /** * @author Downpour */ public class OGNLTestCase extends TestCase { /** * * @throws Exception */ @SuppressWarnings("unchecked") @Test public void testGetValue() throws Exception { // Create root object User user = new User(); user.setId(1); user.setName("downpour"); // Create context Map context = new HashMap(); context.put("introduction","My name is "); // Test to directly get value from root object, with no context Object name = Ognl.getValue(Ognl.parseExpression("name"), user); assertEquals("downpour",name); // Test to get value(parameter) from context Object contextValue = Ognl.getValue(Ognl.parseExpression("#introduction"), context, user); assertEquals("My name is ", contextValue); // Test to get value and parameter from root object and context Object hello = Ognl.getValue(Ognl.parseExpression("#introduction + name"), context, user); assertEquals("My name is downpour",hello); } /** * * @throws Exception */ @SuppressWarnings("unchecked") @Test public void testSetValue() throws Exception { // Create root object User user = new User(); user.setId(1); user.setName("downpour"); // Set value according to the expression Ognl.setValue("department.name", user, "dev"); assertEquals("dev", user.getDepartment().getName()); } }
我们可以看到,简单的API,就已经能够完成对各种对象树的读取和设值工作了。这也体现出OGNL的学习成本非常低。
在上面的测试用例中,需要特别强调进行区分的,是在针对不同内容进行取值或者设值时,OGNL表达式的不同。
上面这段内容摘自Struts2的Reference,我把这段话总结为以下2条规则:
A) 针对根对象(Root Object)的操作,表达式是自根对象到被访问对象的某个链式操作的字符串表示。
B) 针对上下文环境(Context)的操作,表达式是自上下文环境(Context)到被访问对象的某个链式操作的字符串表示,但是必须在这个字符串的前面加上#符号,以表示与访问根对象的区别。
上面的这点区别咋看起来非常容易理解,不过一旦放到特定的环境中,就会显示出其重要性,它可以解释很多Struts2在页面展示上取值的各种复杂的表达式的现象。这一点在下一篇文章中会进行具体的分析。
OGNL三要素
我把传入OGNL的API的三个参数,称之为OGNL的三要素。OGNL的操作实际上就是围绕着这三个参数而进行的。
1. 表达式(Expression)
表达式是整个OGNL的核心,所有的OGNL操作都是针对表达式的解析后进行的。表达式会规定此次OGNL操作到底要干什么。
我们可以看到,在上面的测试中,name、department.name等都是表达式,表示取name或者department中的name的值。OGNL支持很多类型的表达式,之后我们会看到更多。
2. 根对象(Root Object)
根对象可以理解为OGNL的操作对象。在表达式规定了“干什么”以后,你还需要指定到底“对谁干”。
在上面的测试代码中,user就是根对象。这就意味着,我们需要对user这个对象去取name这个属性的值(对user这个对象去设置其中的department中的name属性值)。
3. 上下文环境(Context)
有了表达式和根对象,我们实际上已经可以使用OGNL的基本功能。例如,根据表达式对根对象进行取值或者设值工作。
不过实际上,在OGNL的内部,所有的操作都会在一个特定的环境中运行,这个环境就是OGNL的上下文环境(Context)。说得再明白一些,就是这个上下文环境(Context),将规定OGNL的操作“在哪里干”。
OGNL的上下文环境是一个Map结构,称之为OgnlContext。上面我们提到的根对象(Root Object),事实上也会被加入到上下文环境中去,并且这将作为一个特殊的变量进行处理,具体就表现为针对根对象(Root Object)的存取操作的表达式是不需要增加#符号进行区分的。
OgnlContext不仅提供了OGNL的运行环境。在这其中,我们还能设置一些自定义的parameter到Context中,以便我们在进行OGNL操作的时候能够方便的使用这些parameter。不过正如我们上面反复强调的,我们在访问这些parameter时,需要使用#作为前缀才能进行。
OGNL与模板
我们在尝试了OGNL的基本操作并了解了OGNL的三要素之后,或许很容易把OGNL的操作与模板联系起来进行比较。在很多方面,他们也的确有着相似之处。
对于模板,会有一些普通的输出元素,也有一些模板语言特殊的符号构成的元素,这些元素一旦与具体的Java对象融合起来,就会得到我们需要的输出结果。
而OGNL看起来也是非常的类似,OGNL中的表达式就雷同于模板语言的特殊符号,目的是针对某些Java对象进行存取。而OGNL与模板都将数据与展现分开,将数据放到某个特定的地方,具体来说,就是Java对象。只是OGNL与模板的语法结构不完全相同而已。
深入浅出OGNL
在了解了OGNL的API和基本操作以后,我们来深入到OGNL的内部来看看,挖掘一些更加深入的知识。
OGNL表达式
OGNL支持各种纷繁复杂的表达式。但是最最基本的表达式的原型,是将对象的引用值用点串联起来,从左到右,每一次表达式计算返回的结果成为当前对象,后面部分接着在当前对象上进行计算,一直到全部表达式计算完成,返回最后得到的对象。OGNL则针对这条基本原则进行不断的扩充,从而使之支持对象树、数组、容器的访问,甚至是类似SQL中的投影选择等操作。
接下来我们就来看看一些常用的OGNL表达式:
1. 基本对象树的访问
对象树的访问就是通过使用点号将对象的引用串联起来进行。
例如:name,department.name,user.department.factory.manager.name
2. 对容器变量的访问
对容器变量的访问,通过#符号加上表达式进行。
例如:#name,#department.name,#user.department.factory.manager.name
3. 使用操作符号
OGNL表达式中能使用的操作符基本跟Java里的操作符一样,除了能使用 +, -, *, /, ++, --, ==, !=, = 等操作符之外,还能使用 mod, in, not in等。
4. 容器、数组、对象
OGNL支持对数组和ArrayList等容器的顺序访问:
例如:group.users[0]
同时,OGNL支持对Map的按键值查找:
例如:#session['mySessionPropKey']
不仅如此,OGNL还支持容器的构造的表达式:
例如:{"green", "red", "blue"}构造一个List,#{"key1" : "value1", "key2" : "value2", "key3" : "value3"}构造一个Map
你也可以通过任意类对象的构造函数进行对象新建:
例如:new java.net.URL("http://localhost/")
5. 对静态方法或变量的访问
要引用类的静态方法和字段,他们的表达方式是一样的@class@member或者@class@method(args):
例如:@com.javaeye.core.Resource@ENABLE,@com.javaeye.core.Resource@getAllResources
6. 方法调用
直接通过类似Java的方法调用方式进行,你甚至可以传递参数:
例如:user.getName(),group.users.size(),group.containsUser(#requestUser)
7. 投影和选择
OGNL支持类似数据库中的投影(projection) 和选择(selection)。
投影就是选出集合中每个元素的相同属性组成新的集合,类似于关系数据库的字段操作。投影操作语法为 collection.{XXX},其中XXX 是这个集合中每个元素的公共属性。
例如:group.userList.{username}将获得某个group中的所有user的name的列表。
选择就是过滤满足selection 条件的集合元素,类似于关系数据库的纪录操作。选择操作的语法为:collection.{X YYY},其中X 是一个选择操作符,后面则是选择用的逻辑表达式。而选择操作符有三种:
? 选择满足条件的所有元素
^ 选择满足条件的第一个元素
$ 选择满足条件的最后一个元素
例如:group.userList.{? #this.name != null}将获得某个group中user的name不为空的user的列表。
上述的所有的表达式,只是对OGNL所有表达式的大概的一个概括,除此之外,OGNL还有更多的表达式,例如lamba表达式等等。最具体的表达式的文档,大家可以参考OGNL自带的文档:
http://www.ognl.org/2.6.9/Documentation/html/LanguageGuide/apa.html
在撰写时,我也参考了potain同学的XWork教程以及一些网络上的一些文章,特此列出:
http://www.lifevv.com/java/doc/20071018173750030.html
http://blog.csdn.net/ice_fire2008/archive/2008/05/12/2438817.aspx
OGNLContext
OGNLContext就是OGNL的运行上下文环境。OGNLContext其实是一个Map结构,如果查看一下它的源码,就会发现,它其实实现了java.utils.Map的接口。当你在调用OGNL的取值或者设值的方法时,你可能会自己定义一个Context,并且将它传递给方法。事实上,你所传递进去的这个Context,会在OGNL内部被转化成OGNLContext,而你传递进去的所有的键值对,也会被OGNLContext接管维护,这里有点类似一个装饰器,向你屏蔽了一些其内部的实现机理。
在OGNLContext的内部维护的东西很多,其中,我挑选2个比较重要的提一下。一个是你在调用方法时传入的Context,它会被维护在OGNL内部,并且作为存取变量的基础依据。另外一个,是在Context内部维护了一个key为root的值,它将规定在OGNLContext进行计算时,哪个元素被指定为根对象。其在进行存取时,将会被特殊对待。
this指针
我们知道,OGNL表达式是以点进行串联的一个字符串链式表达式。而这个表达式在进行计算的时候,从左到右,每一次表达式计算返回的结果成为当前对象,并继续进行计算,直到得到计算结果。每次计算的中间对象都会放在一个叫做this的变量里面这个this变量就称之为this指针。
例如:group.userList.size().(#this+1).toString()
在这个例子中,#this其实就是group.userList.size()的计算结构。
使用this指针,我们就可以在OGNL表达式中进行一些简单的计算,从而完成我们的计算逻辑,而this指针在lamba表达式的引用中尤为广泛,有兴趣的读者可以深入研究OGNL自带的文档中lamba表达式的章节。
默认行为和类型转化
在我们所讲述的所有的OGNL的操作中,实际上,全部都忽略了OGNL内部帮助你完成的很多默认行为和类型转化方面的工作。
我们来看一下OGNL在进行操作初始化时候的一个函数签名:
- /**
- * Appends the standard naming context for evaluating an OGNL expression
- * into the context given so that cached maps can be used as a context.
- *
- * @param root the root of the object graph
- * @param context the context to which OGNL context will be added.
- * @return Context Map with the keys <CODE>root</CODE> and <CODE>context</CODE>
- * set appropriately
- */
- public static Map addDefaultContext( Object root, ClassResolver classResolver, TypeConverter converter, MemberAccess memberAccess, Map context );
/** * Appends the standard naming context for evaluating an OGNL expression * into the context given so that cached maps can be used as a context. * * @param root the root of the object graph * @param context the context to which OGNL context will be added. * @return Context Map with the keys <CODE>root</CODE> and <CODE>context</CODE> * set appropriately */ public static Map addDefaultContext( Object root, ClassResolver classResolver, TypeConverter converter, MemberAccess memberAccess, Map context );
可以看到,在初始化时,OGNL还需要额外初始化一个类型转化的接口和一些其他的信息。只不过这些默认行为,由OGNL的内部屏蔽了。
一旦需要自己定义针对某个特定类型的类型转化方式,你就需要实现TypeConverter接口,并且在OGNL中进行注册。
同时,如果需要对OGNL的许多默认行为做出改变,则需要通过设置OGNL的全局环境变量进行。
发表评论
-
struts2 之命名空间
2014-11-21 10:15 840把namespace单独拉出来讲一方面是因为它实际上不是一个 ... -
struts2 annotation 验证采用自定义的跳转
2014-11-19 11:22 977对于methodName 和resultName ... -
Struts2 annotation方法验证
2014-11-18 11:58 837struts2的验证分为分编程式验证、声明式验证、注解式验证 ... -
struts2 设置全局404页面
2014-11-04 10:38 1053目前在做一个网络应用程序,struts2 + spring ... -
struts2 整合json 出现no default param json type问题解决
2014-11-04 09:35 622服务器配置json后 我开始配置采用的是、<resu ... -
struts2 默认命名空间默认提示问题
2014-11-03 17:13 699不知道大家有没有遇到在使用表单的时候,有时候会出一个莫名其妙 ... -
struts2 配置下载文件解决中文文件名的问题
2012-04-05 17:01 1608我们的项目名为 struts2hello ,所使用的开发环境 ... -
struts2 下载文件报错的问题
2012-04-05 17:00 12202010-1-22 9:45:03 org.apache.st ... -
struts2 文件下载配置
2011-11-08 09:55 1663有些应用我们要下载文件,而不是直接在浏览器中打开,我们在STU ... -
使用struts2加ztree加jquery生成树形结构
2011-06-11 23:22 7148首先下载ztree,然后引入 ... -
struts2.1.8.1+jquery1.4.2返回json数据
2011-05-26 06:57 12141、引入包(本文中的包全部引自struts-2.1.8.1\l ... -
解决response.sendRedirect(url); 的URL中包含中文的问题
2010-09-10 08:54 3316今天在项目中,发现在response.sendRedirect ... -
请求转发和请求重定向
2010-09-08 10:00 1916请求转发请求转发允许把请求转发给同一应用程序中的其他Web组件 ... -
struts2中提交的中文乱码解决办法
2010-09-08 09:22 2185jsp文件中使用struts2 <s:form>表 ... -
struts2中select的用法
2010-09-07 17:27 1041<s:select name="disabl ... -
对于struts2中textfiled标签每次自己添加了tr,td等代码的处理
2010-09-07 16:47 1945初学struts2,在头脑中一直在想一个问题,就是对于stru ... -
如何给struts2的redirect的result传递多个参数
2010-09-06 16:24 3194如何给struts2的redirect的result传递多个参 ... -
struts2.0学习笔记九(struts2.0中的拦截器的使用)
2009-03-27 15:26 1159struts2.0中基本拦截器的配置采用的是可插拔的方式 1. ... -
struts2.0学习笔记八(struts2.0中的验证方式)
2009-03-15 10:13 1128在struts2.0中也是通过采用validate方法来验证客 ... -
struts2.0学习笔记七(struts2.0action中植的传递)
2009-03-13 15:40 1358当我们在程序中,从一个ACTION到另一个ACTION的跳转, ...
相关推荐
本学习笔记主要涵盖了Struts2.0的基础概念、核心组件、配置方法以及实际代码示例,旨在帮助开发者更好地理解和运用这个框架。 一、Struts2.0基础 Struts2.0是Apache软件基金会的开源项目,它是Struts1.x的升级版本...
通过阅读Struts2.0中文手册和学习笔记,以及动手实践教程中的示例,可以全面掌握Struts2.0的用法和精髓。在实际项目中,熟练运用Struts2.0能提高开发效率,降低维护成本。同时,了解并熟悉相关的社区资源和最佳实践...
这个压缩包包含的资源是作者学习Struts2.0过程中的笔记和示例,分为两个文档:struts2.0文档1-2.doc和struts2.0文档3-4-5.doc,涵盖了从基础到进阶的内容。 在"struts2.0文档1-2.doc"中,可能包括了以下知识点: 1...
OGNL是Struts2中用于对象属性访问和表达式语言,它允许在Action、ValueStack和视图层之间传递数据。ValueStack是一个对象栈,它持有ActionContext上下文中的所有对象,包括Action实例、ActionForm、Session、Request...
OGNL是Struts2的表达式语言,用于在Action和视图之间传递数据。它能直接访问Java对象的属性,支持表达式计算,简化了视图层的数据绑定。 六、插件和国际化 Struts2有丰富的插件库,如Tiles、Freemarker、JSON等,...
### Struts2 学习重点知识点总结 #### 一、Struts2 概念与架构 **1.1 Struts2 简介** - **定义**:Struts2 是 Apache 组织提供的一个基于 MVC 架构模式的开源 Web 应用框架。 - **核心**:Struts2 的核心其实是 ...
Struts2 OGNL语言学习笔记 OGNL(Object-Graph Navigation Language)是 Struts 2 中的一种表达式语言,主要用于简化 JSP 页面中的编码,使页面与后台代码分离。下面是 OGNL 语言的主要特点和用法: 1. 支持对象...
### Struts2学习笔记知识点详解 #### 一、准备工作及实例 **1. 解压struts-2.1.6-all.zip** - **Apps目录**:包含struts2自带的例子程序,适合初学者通过实际案例了解框架的基本用法。 - **Docs目录**:官方文档...
13. ** strut2四天笔记**:这份学习笔记可能涵盖了以上所有知识点,包括如何创建Action,配置struts.xml,使用OGNL表达式,处理异常,以及实践中的各种技巧和最佳实践。 在四天的学习过程中,你应该通过实践和理解...
本学习笔记将对Struts1和Struts2进行详细解析。 **Struts1简介** Struts1是早期流行的MVC框架,它的核心是ActionServlet,负责处理HTTP请求,并通过ActionForm对象收集表单数据,然后调用Action类的方法进行业务...
在示例代码中,`<s:property>`标签使用OGNL表达式`#request.req`、`#session.sess`和`#application.app`分别获取了Request、Session和Application范围内的属性值。这展示了OGNL如何灵活地访问不同级别的数据。 其次...
4. ValueStack:值栈是Struts2的数据容器,它将Action对象和OGNL表达式结合,方便在视图层获取和设置模型数据。 5. OGNL(Object-Graph Navigation Language):OGNL是Struts2中的表达式语言,用于在Action对象和...
#### OGNL表达式语言与Struts标签库 OGNL(Object-Graph Navigation Language)是Struts2中用于访问和操作对象属性的强大表达式语言。Struts标签库则提供了一系列用于页面渲染和交互的HTML标签,如`<s:property>`、...
6. **OGNL(Object-Graph Navigation Language)**:Struts 2默认使用OGNL作为表达式语言,用于在Action与JSP页面之间传递数据。OGNL允许动态访问对象属性,提供了丰富的表达式支持。 7. **国际化和本地化**:...