一、Struts2的MVC实现
Figure 1 Struts2的MVC实现
l 模型(Model):表示一个应用程序的数据并且包含访问和管理这些数据的业务逻辑。通常由JavaBean和EJB组件实现。
l 视图(View):由JSP页面、HTML页面等多种视图组成,用于表示模板的状态。
l 控制器(Controller):由核心控制器StrutsPrepareAndExecuteFilter与众多控制器Action构成,用于获取并映射用户的输入动作并由模型执行。
二、Struts2的基本组成
Figure 2 Struts2的基本组成
Struts2 主要由充当中央控制器的核心过滤器StrutsExecuteFilter、充当线程清洁工的StrutsPrepareFilter过滤器、决定是否需要调用自定义控制器Action的Action映射器、根据配置文件创建Action执行环境的Action代理、Action的执行环境以及Struts2标签库在内的Struts2视图组件等构成。
1.StrutsExecuteFilter包括框架内部的控制流程和处理机制,在web.xml中配置如下:
<filter> <filter-name>struts2</filter-name> <filter-class> org.apache.struts2.dispatcher.ng.filter.StrutsExecuteFilter </filter-class> </filter> <filter-mapping> <filter-name>struts-execute</filter-name> <url-pattern>/*</url-pattern> </filter-mapping></web-app> |
2.StrutsPrepareFilter用于清理线程的ActionContext和Dispatcher,在web.xml中配置如下:
<filter> <filter-name>struts2</filter-name> <filter-class> org.apache.struts2.dispatcher.ng.filter.StrutsPrepareFilter </filter-class> </filter> <filter-mapping> <filter-name>struts2</filter-name> <url-pattern>/*</url-pattern> </filter-mapping></web-app> |
为简化配置,StrutsPrepareFilter和StrutsExecuteFilter整合成StrutsPrepareAndExecuteFilter,其在web.xml中的配置如下:
<filter> <filter-name>struts2</filter-name> <filter-class> org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter </filter-class> </filter> <filter-mapping> <filter-name>struts2</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> |
3.Struts2的配置文件包括struts.xml和struts.properties组成。Struts.xml用于配置Action映射、全局Result、异常映射及拦截器等,struts.properties用于Struts2的框架属性设置,可以在struts.xml中一并设置。
4.业务控制器Action是由用户自定义的Java类,在Action执行前后可以使用拦截器进行特殊处理。
三、Struts2常用类
l Action接口:通过Action接口可以创建业务控制Action类。其中,具体的业务在execute()方法中编写,调用ActionContext.getContext()获取ServletContext的引用。Action处理结果Result返回的常量有5个:ERROR、SUCCESS、NONE、INPUT、LOGIN,用于规范返回结果。
l ActionSupport类:该类同时实现了Action接口和Validateable接口。具体的业务在execute()中编写,数据验证在validate()中处理。
l ActionContext类:该类是用于访问Servlet(如HttpServletRequest、HttpSession和ServletContext)的API,也是Struts2中默认的Action类。
l ServletActionContext类:该类是直接访问ServletContext的API。
l ModelDriven接口:当Action采用模型驱动,必须实现此接口和其getModel()方法。
四、Action的编写与配置
Action的编写要实现属性的SETXXX和GETXXX方法并在struts.xml中进行配置。配置形式如下:
<package name=”” extends=””> <action name=”” class=””> <result name=”success”>/admin/index.jsp</result> <result name=”login”>/admin/login.jsp</result> </action> </package> |
此外,还可以使用通配符简化配置。
Action的编写可以实现Action接口,继承ActionSupport类或者采用模型驱动的方法进行编写。模型驱动的方法需要继承ModelDriven类并实现getModel方法。
五、Struts2的处理结果Result
Struts的处理结果类型:
返回的处理结果需要在struts.xml中配置。其中<global-result>用于定义全局结果,对所有Action有效;在特定Action中定义的结果仅对该Action有效。配置如下:
<!—全局--> <global-results> <result name=”exception”>/exception.jsp</result> <param name=””>value</param><!—用于传递参数值 --> … </global-results> |
<!—局部--> <Action name=”” class=”’> <result name=”exception”>/exception.jsp</result> … </global-results> |
六、OGNL语言
OGNL(Object Graphic Navigation Language)可通过简单的表达式访问Java对象。访问的方法如下:
七、Struts2流程分析
1. 用户通过浏览器提交访问请求
2. web.xml中的StrutsPrepareAndExecuteFilter拦截请求并根据welcome-file导向欢迎页面。
3. 用户在网页中输入信息,通过submit等提交给处理Action,例如sayHello.action
4. sayHello.action被StrutsPrepareAndExecuteFilter拦截,首先清除当前线程的ActionContext和Dispatcher,然后通过ActionMapper决定调用哪个Action。由于sayHelllo.action是Struts默认的扩展名.action,因此StrutsPrepareAndExecuteFilter将该请求交给ActionProxy处理。
5. ActionProxy通过struts.xml找到需要调用的类。
6. Action首先调用一系列的拦截器(系统的或者用户的),然后调用validate方法验证数据的正确性,验证出错可以通过addFielderError方法添加错误字段,然后跳转到相应的页面中。之后执行execute方法,进行业务处理。对于复杂的业务逻辑,建议在专门的业务逻辑组件中处理,简单的可以直接放在Action中处理。
7. 通过struts.xml中命名视图无物理视图的映射,决定下一个需要跳转的页面。
八、Struts2的异常处理
Struts2通过在struts中配置exception-mapping元素实现异常处理。异常分为全局异常(放置在globa-exception-mapping,对所有Action都有效)和局部异常(放置在Action中,仅对该Action有效)。配置exception-mapping是需要配置两个参数:exception用于指向具体的异常类型;result用于指向命名视图。
在负责异常处理的页面,通过<s:property value=”exception.message”/>标签输出异常信息;通过<s:property value=”exceptionStack”/>输出异常堆栈信息。
九、Struts2的国际化
创建不同语言的资源文件用以实现Struts2的国际化。资源文件的命名包括:基本名(如messageResource)、语言代号(如en、zh)、国家代号(如US、CN)和扩展名“.properties”构成。
在sturts.xml中,通过如下配置引用资源文件。通过如下两句即可引用资源文件messageResource_zh_CN.properties。
<constant name=”struts.local” value=”zh-CN”> <constant name=”struts.custom.i18n.resource” value=”messageResource”> |
此外,可以通过两种方法切换国际化语言。一种是通过Action类中的ActionContext.getContext().setLocal(Local.US)方法完成,此方法较为常用。另一种是通过struts默认带的拦截器i18n,因为该拦截器在Action调用之前首先会检查Action的请求参数,如果请求参数制定了Local对象,那么就调用相应的Local,如果没有指定,就调用默认的Local。但是,这种方法需要在Action的请求后带上request_Local参数,例如:http://localhost:8080/hello.action?request_locale=en-US。
十、Struts2的校验框架
数据校验可以位于客户端,也可以位于服务端。客户端校验的好处是降低服务器的处理压力,但是可能会遭到恶意的的提交表单,安全性差。服务端校验的好处是安全性强,缺点是增加了服务器的处理压力。在Struts中校验有两种方法,一种是自动校验,一种是Action手动校验。
手动校验是在Action中重载validate方法并判断数据的正确性。需要注意的是,每一用到用户提交数据的方法都要编写相应的验证方法。如该方法为xxx,则验证方法为validatexxx()。通过调用addFieldError方法将相应的错误信息爆粗难道fieldError中并返回input视图。然后在input视图中,通过hasFieldError判断是否存在输入错误,调用<s:fielderror/>标签输出具体的错误信息。
自动校验是指通过编写校验的xml文件完成校验。校验配置文件的命名为:Action类名-Action别名-valdiation.xml。其中Action别名是该Action在struts.xml中的name属性名。
Struts2内建校验器如下:
1、类型转换检验器:
(1)非字段校验:
<validator type="conversion">
<param name="fieldName">myField</param>
<message>类型转换错误</message>
<param name ="repopulateField">true</param>
</validator>
(2)字段校验:
<field name="myField">
<field-validator type="conversion">
<message>类型转换错误</message>
<param name ="repopulateField">true</param>
</field-validator>
</field>
fieldName:该参数指定检查是否存在转换异常的字段名称,如果是字段校验,则不用指定该参数。
repopulateField:该参数指定当类型转换失败后,返回input页面时,类型转换失败的表单是否保留原来的错误输入。true为保留,false为不保留。
2、日期校验器:
(1)非字段校验:
<validator type="date">
<param name="fieldName">birthday</param>
<param name="min">1990-01-02</param>
<param name="max">2010-07-28</param>
<message>生日数据错误</message>
</validator>
(2)字段校验:
<field name="birthday">
<field-validator type="date">
<param name="min">1990-01-01</param>
<param name="max">2010-07-28</param>
<message key="error.birthday"></message>
</field-validator>
</field>
min:指定字段日期值的最小值,该参数为可选参数。
max:指定字段日期值的最大值,该参数为可选参数。
3、浮点数值校验器:
(1)非字段校验:
<validator type="double">
<param name="fieldName">percentage</param>
<param name="minInclusive">20.1</param>
<param name="maxInclusive">50.1</param>
<message>生日数据错误</message>
</validator>
(2)字段校验:
<field name="percentage">
<field-validator type="double">
<param name="minInclusive">20.1</param>
<param name="maxInclusive">50.1</param>
<message key="error.percentage"></message>
</field-validator>
</field>
minInclusive|minExclusive:指定字段的最小值,包含该值|不包含该值。
maxInclusive|maxExclusive:指定字段的最大值, 包含该值|不包含该值。
4、邮件地址校验器:
(1)非字段校验:
<validator type="email">
<param name="fieldName">MyEmail</param>
<message>非法的邮件地址</message>
</validator>
(2)字段校验:
<field name="MyEmail">
<field-validator type="email">
<message>非法的邮件地址</message>
</field-validator>
</field>
5、表达式校验器:
<validator type="expression">
<param name="expression">.......</param>
<message>Failed to meet Ognl Expression...</message>
</validator>
expression:该参数为一个逻辑表达式,该参数使用OGNL表达式,并基于值栈计算,返回一个Boolean类型值。
6、字段表达式校验器:
(1)非字段校验:
<validator type="fieldexpression">
<param name="fieldName">myField</param>
<param name="expression"><![CDATA[#myCreditLimit > #myGirfriendCreditLimit]]></param>
<message>My credit limit should be MORE than my girlfriend</message>
</validator>
(2)字段校验:
<field name="myField">
<field-validator type="fieldexpression">
<param name="expression"><![CDATA[#myCreditLimit > #myGirfriendCreditLimit]]></param>
<message>My credit limit should be MORE than my girlfriend</message>
</field-validator>
</field>
7、整数校验器:
(1)非字段校验:
<validator type="int">
<param name="fieldName">age</param>
<param name="min">10</param>
<param name="max">100</param>
<message>年龄必须在在${min}到${max}之间</message>
</validator>
(2)字段校验:
<field name="age">
<field-validator type="int">
<param name="min">10</param>
<param name="max">100</param>
<message>年龄必须在在${min}到${max}之间</message>
</field-validator>
</field>
8、正则表达式校验器:
(1)非字段校验:
<validator type="regex">
<param name="fieldName">myStrangePostcode</param>
<param name="expression"><![CDATA[([aAbBcCdD][123][eEfFgG][456])]></param>
</validator>
(2)字段校验:
<field name="myStrangePostcode">
<field-validator type="regex">
<param name="expression"><![CDATA[#myCreditLimit > #myGirfriendCreditLimit]]></param>
<message>My credit limit should be MORE than my girlfriend</message>
</field-validator>
</field>
expression:为必选参数,指定匹配有的表达式。
caseSensitive:指明进行匹配时,是否区分大小写,为可选参数,默认为true。
9、必填校验器:
(1)非字段校验:
<validator type="required">
<param name="fieldName">username</param>
<message>用户名不能为空</message>
</validator>
(2)字段校验:
<field name="username">
<field-validator type="required">
<message>用户名不能为空</message>
</field-validator>
</field>
10、必填字符串校验器:
(1)非字段校验:
<validator type="requiredstring">
<param name="fieldName">username</param>
<param name="trim">true</param>
<message>用户名不能为空</message>
</validator>
(2)字段校验:
<field name="username">
<field-validator type="requiredstring">
<param name="trim">true</param>
<message>用户名不能为空</message>
</field-validator>
</field>
trim:可选参数,用于指定是否在校验之前对字符串进行整理,默许为true。
11、字符串长度校验器:
(1)非字段校验:
<validator type="stringlength">
<param name="fieldName">username</param>
<param name="minLength">4</param>
<param name="maxLength">10</param>
<message>用户名长度在${minLength}到${maxLength}之间</message>
</validator>
(2)字段校验:
<field name="username">
<field-validator type="stringlength">
<param name="minLength">4</param>
<param name="maxLength">10</param>
<param name="trim">true</param>
<message key="error.length.username"></message>
</field-validator>
</field>
12、网址校验器:
(1)非字段校验:
<validator type="url">
<param name="fieldName">myHomePage</param>
<message>Invalid homepage url</message>
</validator>
(2)字段校验:
<field name="myHomePage">
<field-validator type="url">
<message>Invalid homepage url</message>
</field-validator>
</field>
13、visitor校验器:
该校验器名称为:visitor,用来校验Action中定义的复合类型属性,支持简单的复合类型、数组类型、Map等集合类型。
(1)非字段校验:
<validator type="visitor">
<param name="fieldName">user</param>
<param name="context">myContext</param>
<param name="appendPrefix">true</param>
</validator>
(2)字段校验:
<field name="user">
<field-validator type="visitor">
<param name="context">myContext</param>
<param name="appendPrefix">true</param>
</field-validator>
</field>
如果想自定义一个自己的校验器,可以在Web工程的WEB-LIB\lib目录下增加一个validators.xml配置文件,在该文件中定义自己的校验器。
注意:如果使用validators.xml配置文件,则系统默认的default.xml配置文件不会加载,所以需要将default.xml的内容复制到validators.xml配置文件中。否则不能使用系统内建的校验器
十一、Struts2拦截器
Struts2的拦截器采用典型的AOP思想。AOP为Aspect Oriented Programming的缩写,意为:面向切面编程(也叫面向方面),可以通过预编译方式和运行期动态代理实现在不修改源代码的情况下给程序动态统一添加功能的一种技术。AOP实际是GoF设计模式的延续,设计模式孜孜不倦追求的是调用者和被调用者之间的解耦,AOP可以说也是这种目标的一种实现。
Struts2拦截器的作用是在Action被调用之前,调用拦截器执行一些列的拦截工作。Struts2内建很多拦截器,存在于struts-default.xml中。我们可以将这些拦截器重新定义成拦截器栈成组的调用各拦截器。我们使用的Action只需要继承自默认拦截器的包,那么该默认拦截器就能在这个Action中使用。Struts2内建拦截器如下:
Struts2(XWork)提供的拦截器的功能说明:
拦截器 | 名字 | 说明 |
Alias Interceptor | alias | 在不同请求之间将请求参数在不同名字件转换,请求内容不变 |
Chaining Interceptor | chain | 让前一个Action的属性可以被后一个Action访问,现在和chain类型的result(<result type=”chain”>)结合使用。 |
Checkbox Interceptor | checkbox | 添加了checkbox自动处理代码,将没有选中的checkbox的内容设定为false,而html默认情况下不提交没有选中的checkbox。 |
Cookies Interceptor | cookies | 使用配置的name,value来是指cookies |
Conversion Error Interceptor | conversionError | 将错误从ActionContext中添加到Action的属性字段中。 |
Create Session Interceptor | createSession | 自动的创建HttpSession,用来为需要使用到HttpSession的拦截器服务。 |
Debugging Interceptor | debugging | 提供不同的调试用的页面来展现内部的数据状况。 |
Execute and Wait Interceptor | execAndWait | 在后台执行Action,同时将用户带到一个中间的等待页面。 |
Exception Interceptor | exception | 将异常定位到一个画面 |
File Upload Interceptor | fileUpload | 提供文件上传功能 |
I18n Interceptor | i18n | 记录用户选择的locale |
Logger Interceptor | logger | 输出Action的名字 |
Message Store Interceptor | store | 存储或者访问实现ValidationAware接口的Action类出现的消息,错误,字段错误等。 |
Model Driven Interceptor | model-driven | 如果一个类实现了ModelDriven,将getModel得到的结果放在Value Stack中。 |
Scoped Model Driven | scoped-model-driven | 如果一个Action实现了ScopedModelDriven,则这个拦截器会从相应的Scope中取出model调用Action的setModel方法将其放入Action内部。 |
Parameters Interceptor | params | 将请求中的参数设置到Action中去。 |
Prepare Interceptor | prepare | 如果Acton实现了Preparable,则该拦截器调用Action类的prepare方法。 |
Scope Interceptor | scope | 将Action状态存入session和application的简单方法。 |
Servlet Config Interceptor | servletConfig | 提供访问HttpServletRequest和HttpServletResponse的方法,以Map的方式访问。 |
Static Parameters Interceptor | staticParams | 从struts.xml文件中将<action>中的<param>中的内容设置到对应的Action中。 |
Roles Interceptor | roles | 确定用户是否具有JAAS指定的Role,否则不予执行。 |
Timer Interceptor | timer | 输出Action执行的时间 |
Token Interceptor | token | 通过Token来避免双击 |
Token Session Interceptor | tokenSession | 和Token Interceptor一样,不过双击的时候把请求的数据存储在Session中 |
Validation Interceptor | validation | 使用action-validation.xml文件中定义的内容校验提交的数据。 |
Workflow Interceptor | workflow | 调用Action的validate方法,一旦有错误返回,重新定位到INPUT画面 |
Parameter Filter Interceptor | N/A | 从参数列表中删除不必要的参数 |
Profiling Interceptor | profiling | 通过参数激活profile |
自定义一个拦截器需要三步:
1.自定义一个实现Interceptor接口(或者继承自AbstractInterceptor)的类。
2.在strutx.xml中注册上一步中定义的拦截器。
3.在需要使用的Action中引用上述定义的拦截器,为了方便也可将拦截器定义为默认的拦截器,这样在不加特殊声明的情况下所有的Action都被这个拦截器拦截。
Interceptor接口声明了三个方法:
public interface Interceptor extends Serializable {
void destroy();
void init();
String intercept(ActionInvocation invocation) throws Exception;
}
Init方法在拦截器类被创建之后,在对Action镜像拦截之前调用,相当于一个post-constructor方法,使用这个方法可以给拦截器类做必要的初始话操作。
Destroy方法在拦截器被垃圾回收之前调用,用来回收init方法初始化的资源。
Intercept是拦截器的主要拦截方法,如果需要调用后续的Action或者拦截器,只需要在该方法中调用invocation.invoke()方法即可,在该方法调用的前后可以插入Action调用前后拦截器需要做的方法。
如果不需要调用后续的方法,则返回一个String类型的对象即可,例如Action.SUCCESS。
一、Struts2的主题与模板
Struts模板加载时搜索的位置有两种,Web应用路径下和CLASSPATH路径下。例如,调用select标签,该标签属于xhtml主题,则搜索该模板的路径如下:
1. Web应用路径/template/xhtml/select.ftl
2. CLASSPTH路径/template/xhtml/select.ftl
模板文件的扩展名为ftl,它是FreeMarker的模板文件。此外,还可以使用诸如Velocity和JSP等模板文件。
Struts提供四种主题:
1. simple:最基本的主题,每个标签只生成一个html元素,不会生成多余的元素。
2. xhtml:simple的扩展。
3. css_xhtml:xhtml的扩展,增加css控制样式。
4. AJAX:xhtml的扩展,增加对AJAX的支持。
二、Struts2的控制标签
控制标签用于实现诸如分支、循环等流程控制。主要的控制标签如下:
1. if/elseif/else
<s:if test=”逻辑表达式”> 标签体 </s:if> |
属性test用于存放条件判断的逻辑表达式 |
2. iterator:用于迭代输出Collection集合类的元素
<s:iterator value=”集合对象” id=”每次迭代的引用” status=”迭代状态实例”> 标签体 <s:iterator> |
Value用于指定迭代对象; Id指定每次迭代的引用; Status是生成的迭代实例,提供诸如getCount等方法返回元素信息。 |
3. append:用于拼接Collection对象
<s:append id=”拼接后得到的新集合的对象名” > <s:param value=”需要拼接的集合对象”> … <s:append> |
4. generator用于将字符串按照指定的分隔符拆分成字符串集合,并存放在ValueStack栈顶,供iterator标签迭代输出。
5. merge用法和作用同append,不同的是:append是将后一个拼接在尾部,merge则是两个带拼接的集合中的元素一个接着一个的排列在心生成的集合中。
6. subset用于截取源集合的一个子集置于ValueStack栈顶,供iterator标签调用输出。
7. sort用于将源集合按照指定的排序规则排序后置于ValueStack栈顶,供iterator标签迭代输出。
三、Struts的数据标签
(一)action: 该标签用于JSP页面直接调用一个Action,通过指定executeResult参数,还可以将该Action的处理结果包含到本页面中来;
(1) id :这是一个可选属性,该属性将会作为该Action的引用ID。
(2)name :这是一个必填属性,通过该属性指定该标签调用哪个Action。
(3)namespace :这是一个可选属性,该属性指定该标签调用的Action所在的Action。
(4)executeResult :这是一个可选属性,该属性指定是否要将Action的处理结果包含到本页面。
该属性默认值是false,即不包含。
(5)ignoreContentParams :这是一个可选属性,他指定该页面的请求参数,是否需要传入调用的Action,该参数默认值为false,即将参数可以传入Action;
<!--将结果返回到actionName.action的页面中,但参数传递不进Action -->
<s:action name="actionName" executeResult="true" ignoreContentParams="true" />
(二)bean:该标签用于创建一个JavaBean实例,如果指定了id属性,则可以将创建的JavaBean实例放入 Stack Content中;
(1) id :这是一个可选属性,如果指定了id属性,则该JavaBean实例会被放入 Stack Content
中(并不是ValueStack),从而允许直接通过id属性来访问该JavaBean实例。
(2)name :这是一个必填属性,该属性指定要实例化的JavaBean的实现类。
注意:
在bean标签的标签体内,bean标签创建的JavaBean实例位于ValueStack的顶端;但一旦该bean
标签一结束了,该bean标签创建的JavaBean实例将被移除ValueStack,除非指定了id属性(就可以
在Stack Context中访问该实例),否则将无法访问该JavaBean实例。
<!--创建bean时未使用id属性-->
<s:bean name="lee.Person">
<s:param name="name" value=" 'zhengyong' "/>
<s:param name="age" value=" '22' "/>
<!--因为该bean的lee.Person实例位于ValueStack的栈顶,在bean的标签内,故可以直接访问-->
<s:property value="name" />
<s:property value="age" />
</s:bean>
<!--创建bean时指定了id属性-->
<s:bean name="lee.Person" id="p">
<s:param name="name" value=" 'zhengyong' "/>
<s:param name="age" value=" '22' "/>
</s:bean>
<!--根据该JavaBean的实例指定的id来访问-->
<s:property value="#p.name" />
<s:property value="#p.age" />
(三)date:除了可以用于格式化输出一个日期;还可以计算指定日期和当前时刻之间的时差。
(1)format :这是一个可选属性,如果指定了该属性,将根据该属性指定的格式来格式化日期;
(2) nice : 这是一个可选属性,该属性只能为true,或者false.他用于指定是否输出指定日期
和当前时刻之间的时差。该属性默认是false,即表示不输出时差。
(3) name :这是一个必填属性,该属性指定要格式化的日期值。
(4) id :这是一个可选属性,如果指定了引用该元素的id值。
<!--如果没有指定nice,则按format进行,如果2种均指定了则按nice进行-->
<s:date name="#attr.nowTime" format="dd/MM/yyyy" nice="false" />
(四)push :用于将某个值放入ValueStack的栈顶;
(1)value :这是一个必填属性,该属性指定需要放到Valuestack栈顶的值;
(2)id :这是一个可选属性,如果指定了引用该元素的id值。
<!--创建bean时指定了id属性-->
<s:bean name="lee.Person" id="p">
<s:param name="name" value=" 'zhengyong' "/>
<s:param name="age" value=" '22' "/>
</s:bean>
<!--使用push标签把Stack Context中的p实例放入Valuestack栈顶->
<s:push value="#p" />
<s:property value="name"/><br/>
</s:push>
(五)set :用于设置一个新变量,并可以将新变量放入指定的范围内;
(1)name : 这是一个必填属性,重新生成的新变量的名字;
(2)scope :这是一个可选属性,指定新变量被放置的范围,该属性可以接受application、session、request、page、action 5 个值,如果没有指定,默认放于 Stack Context 中。
(3)value : 这是一个可选属性,指定将赋给变量的值。如果没有指定该属性,则将ValueStack栈顶的值赋给新变量。
(4)id: 这是一个可选属性,如果指定了引用该元素的ID。
<!-- 将 Stack Context 中的P值放入默认范围内 -->
<s:set value="#p" name="xxx" />
四、Struts2的表单标签
详见:http://blog.sina.com.cn/s/blog_8417657f01011cid.html