??xml version="1.0" encoding="utf-8" standalone="yes"?> 它会整?/span>action中的变量转化?/span>JSON数据(根对象在JSON中数据添加一?/span>”root”标识)。如果要使用它,Action必须遵@以下几点Q?/span> 1. q回的页面类型中”content-type”必须?/span>”application/json”.(q个已经Internet Community采用). 2. JSON内容必须是符合格式要求的. 3. Action?/span>field必须?/span>public?/span>setҎ.(是不是没?/span>setҎ׃会将fieldd?/span>JSON数据中,有待验证). 4. 它支持的cd?/span>: 基本cd(int,long...String), Date, List, Map, Primitive Arrays, 其它class, 对象数组. 5. ?/span>JSON中Q何的Object会被装?/span>list?/span>map中,数据会被装E?/span>LongQ如果是含有的数据则会被装E?/span>DoubleQ数l会被封装程List. 下面l出JSON的数据格?/span>: { "doubleValue": 10.10, "nestedBean": { "name": "Mr Bean" }, "list": ["A", 10, 20.20, { "firstName": "El Zorro" }], "array": [10, 20] } 说明: a. q个插g支持以下几个注释: 注释?/span> ?/span> 默认?/span> 序列?/span> 反序列化 name 配置JSON?/span>name empty yes no serialize ?/span>serialization?/span> true yes no deserialize ?/span>deserialization?/span> true no yes format 格式?/span>Date字段 "yyyy-MM-dd'T'HH:mm:ss" yes yes 可以通过配置来显C指攑֜JSON?/span>fieldQ其中有个自q验证规则需要研I?/span>. <!-- Result fragment --> <result type="json"> <param name="excludeProperties"> login.password, studentList.*".sin </param> </result> <!-- Interceptor fragment --> <interceptor-ref name="json"> <param name="enableSMD">true</param> <param name="excludeProperties"> login.password, studentList.*".sin </param> </interceptor-ref> b. 根对?/span> <result type="json"> <param name="root"> person.job </param> </result> 也可以用拦截器配置操作父对?/span> <interceptor-ref name="json"> <param name="root">bean1.bean2</param> </interceptor-ref> c. ?/span>JSON数据用注释封?/span> 如果wrapWithComments讄?/span>true(默认gؓfalse)Q则生成?/span>JSON数据会变成这P /* { "doubleVal": 10.10, "nestedBean": { "name": "Mr Bean" }, "list": ["A", 10, 20.20, { "firstName": "El Zorro" }], "array": [10, 20] } */ q样做可以避?/span>js中一些潜在的风险Q用时需?/span>: Var responseObject = eval("("+data.substring(data.indexOf(""/"*")+2, data.lastIndexOf(""*"/"))+")"); d. 父类 “root”对象中父cȝfield不会默认存放?/span>JSON数据中,如果不想q样做,需要在配置时指?/span>ignoreHierarchy?/span>false: <result type="json"> <param name="ignoreHierarchy">false</param> </result> e. 枚Dcd 默认处理枚DcdӞ会被处理?/span>JSON数据?/span>name{于枚D?/span>value?/span>value{于枚D?/span>name. public enum AnEnum { ValueA, ValueB } JSON: "myEnum":"ValueA" 如果在处理枚丄型时Q在xml中配|了enumAsBeanQ则会被当作一?/span>Bean处理Q在JSON数据中会有一个特别的属?/span>”_name”gؓname().q个枚D中的所有属性都会被处理. public enum AnEnum { ValueA("A"), ValueB("B"); private String val; public AnEnum(val) { this.val = val; } public getVal() { return val; } } JSON: myEnum: { "_name": "ValueA", "val": "A" } Xml中配|?/span>: <result type="json"> <param name="enumAsBean">true</param> </result> f. 例子 a) Action import java.util.HashMap; import java.util.Map; import com.opensymphony.xwork2.Action; public class JSONExample { private String field1 = "str"; private int[] ints = {10, 20}; private Map map = new HashMap(); private String customName = "custom"; //'transient' fields are not serialized private transient String field2; //fields without getter method are not serialized private String field3; public String execute() { map.put("John", "Galt"); return Action.SUCCESS; } public String getField1() { return field1; } public void setField1(String field1) { this.field1 = field1; } public int[] getInts() { return ints; } public void setInts(int[] ints) { this.ints = ints; } public Map getMap() { return map; } public void setMap(Map map) { this.map = map; } @JSON(name="newName") public String getCustomName() { return this.customName; } } b) Xml配置 <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN" "http://struts.apache.org/dtds/struts-2.0.dtd"> <struts> <package name="example" extends="json-default"> <action name="JSONExample" class="example.JSONExample"> <result type="json"/> </action> </package> </struts> q里有两个地斚w要注?/span>: 1) 需要?/span>json-default?/span> 2) <result>{定义 c) JSON数据 { "field1" : "str", "ints": [10, 20], "map": { "John":"Galt" }, "newName": "custom" } d) JSON RPC JSON插g可以?/span>js中调?/span>actionҎQ返回执行结果。这个已l在dojo中有了实玎ͼ可以?/span>Simple Method Definition调用q程服务。来一L看下面的例子Q?/span> 首先写一?/span>ActionQ?/span> package smd; import com.googlecode.jsonplugin.annotations.SMDMethod; import com.opensymphony.xwork2.Action; public class SMDAction { public String smd() { return Action.SUCCESS; } @SMDMethod public Bean doSomething(Bean bean, int quantity) { bean.setPrice(quantity * 10); return bean; } } e) Ҏ必须?/span>SMDMethod加上注解Q这h能被q程调用Qؓ了安全因素。这个方法会产生一?/span>bean对象Q实C改h格的功能?/span>Action被添加上SMD注解会生成一?/span>SMDQ同时参C会被加上SMDMethodParameter注解。像你所看到的,Action中定义了一个空ҎQ?/span>smd。这个方法是作ؓSimple Method Definition (定义class中提供的服务)Q在struts.xml配置<result>时?/span>type属性gؓ”json”?/span> 下面?/span>bean的定义: package smd; public class Bean { private String type; private int price; public String getType() { return type; } public void setType(String type) { this.type = type; } public int getPrice() { return price; } public void setPrice(int price) { this.price = price; } } Xml文g: <package name="RPC" namespace="/nodecorate" extends="json-default"> <action name="SMDAction" class="smd.SMDAction" method="smd"> <interceptor-ref name="json"> <param name="enableSMD">true</param> </interceptor-ref> <result type="json"> <param name="enableSMD">true</param> </result> </action> </package> q里需要注意一?/span>:” enableSMD”q个必须?/span>interceptor?/span>result都要配置. Js代码: <s:url id="smdUrl" namespace="/nodecorate" action="SMDAction" /> <script type="text/javascript"> //load dojo RPC dojo.require("dojo.rpc.*"); //create service object(proxy) using SMD (generated by the json result) var service = new dojo.rpc.JsonService("${smdUrl}"); //function called when remote method returns var callback = function(bean) { alert("Price for " + bean.name + " is " + bean.price); }; //parameter var bean = {name: "Mocca"}; //execute remote method var defered = service.doSomething(bean, 5); //attach callback to defered object defered.addCallback(callback); </script> JsonService会发Z个请求到action加蝲SMDQ同时远E方法会q回一?/span>JSON对象Q这个过E是Dojol?/span>action中的Ҏ创徏了一?/span>Proxy。因是异步调用过E,当远E方法执行的时候,它会q回一个对象到callbackҎ中?/span> f) 代理的对?/span> 当用的注解不是l承?/span>JavaQ可能你使用代理会出C些问题。比如:当你使用aop拦截你的action的时候。在q种情况下,q个插g不会自动发现注解的方法。ؓ了避免这U情况发生,你需要在xml中配|?/span>ignoreInterfaces?/span>falseQ这h件会自己查找注解的所有接口和父类?/span> 注意Q这个参数只有在Action执行的过E是通过注解来运行的时候才应该设ؓfalse?/span> <action name="contact" class="package.ContactAction" method="smd"> <interceptor-ref name="json"> <param name="enableSMD">true</param> <param name="ignoreInterfaces">false</param> </interceptor-ref> <result type="json"> <param name="enableSMD">true</param> <param name="ignoreInterfaces">false</param> </result> <interceptor-ref name="default"/> </action>
]]>
]]>
一、从零开?/h2>
q里Q我徏立一个新的示例项目,作ؓ讲解的v炏V我使用JDK 6、Maven 2、Eclipse 3.3来徏立这个示例,如果读者对Maven2不熟也没关系Q这只是个示例?br />
首先Q运行下边的命oQ?br />
mvn archetype:create -DgroupId=demo.struts -DartifactId=demo-struts-coc -DarchetypeArtifactId=maven-archetype-webapp
q会建立如下的目录结构:
|- POM.xml
|- src
|- main
|- resources
|- webapp
|- index.jsp
|- WEB-INF
|- web.xml
然后我们在src/main目录下新Z个名为java的目录,用来攄java代码。在src下徏立test目录Qƈ在test目录下徏立java目录Q用来放|测试代码。另外,我这个示例不想用JSPQ所以我src/main/webapp目录下的index.jsp改ؓindex.html?br />
现在Q需要配|该目要用到哪些lib。在POM.xml中加入struts2-coreQ?br />
另外Q我惛_Eclipse里用jetty来启动项目ƈq行试Q所以在POM.xml中再加入jetty、jetty-util、servlet-api{的依赖Q详情见附g?br />
我希望用Eclipse来作个项目的IDEQ所以,我在命o行状态下Q进入这个项目所在的目录Q运行:
mvn eclipse:eclipse
然后使用Eclipse导入q个目。如果你是第一ơ用Eclipse导入用Maven生成的项目,那你需要在Eclipse里配|一个名叫M2_REPO的VariableQ指向你的Maven 2的repository目录。缺省情况下Q它应该位于${user.home}/.m2/repository?br />
OKQ现在我们已l可以在Eclipse中进行工作了?br />
修改src/main/webapp/WEB-INF/web.xmlQ加入struts2的FilterDispatcherq设|filter-mapping。在q个CZ中我url-pattern设ؓ"/app/*"Q也是_url的匹配是Z路径来做的。这只是我的个h喜好而已Q你也可以将它设?*"?br />
既然是在讲struts2的零配置Q当然是可以不要M配置文g的。但是ؓ了更好地q行“配置”Q我q是建立了struts.xml文gQ在src/main/resources目录下)。我不喜Ƣurl最后都有个action后缀Q现在,我在struts.xml中配|struts.action.extensionQ将q个后缀LQ?br />
然后我在src/test/java下徏立demo/RunJetty.java文gQmainҎ如下Q?br />
现在Q在Eclipse里运行或调试q个RunJetty.javaQ用览器打开http://localhost:8080/看看吧。如果不出问题,应该可以讉K到webapp目录下的index.html了。有了JettyQ你q在用MyEclipse或其它插件么Q?br />
二、零配置
首先要澄清一点,q里说的雉|ƈ不是一炚w|都没有Q只是说配置很少而已?br />
Struts2Q我只用qStruts 2.0.6?.0.9Q不清楚其它版本是否支持雉|)引入了零配置的新Ҏ,元数据可以通过规则和注解来表达QA "Zero Configuration" Struts application or plugin uses no additional XML or properties files. Metadata is expressed through convention and annotation.
目前Q这个新Ҏ还在测试阶D,但经q一D|间的使用Q我觉得q个Ҏ已l可用。下面我讲一下如何用它?br />
1. Actions的定?br />
以前需要在xml配置文g中配|Action的name和classQ如果用零配置Q所带来的一个问题就是如何定位这些Action。我们需要在web.xml中找到struts2的filter的配|,增加一个名为actionPackages的init-paramQ它的值是一个以逗号分隔的Java包名列表Q比如:demo.actions1,demo.actions2。struts2会扫描q些包(包括q些包下边的子包Q,在这些包下,所有实CAction接口的或者是cd?#8220;Action”l尾的类都会被检查到Qƈ被当做Action?br />
以前Q我们写Action必须要实现Action接口或者承ActionSupport。但是,上面提到的类名以"Action"l尾的类q不需要这样做Q它可以是一个POJOQStruts2支持POJO ActionQ?br />
下面是actionPackages的配|示例:
2. CZ
现在我们建立demo.actions1.app.person和demo.actions2.app.group两个包,在demo.actions1.app.person包下建立ListPeopleAction.javaQ在demo.actions2.app.group下徏立ListGroupAction.java。作为示例,q两个类只是包含一个executeҎQ返?success"?error"Q其它什么都不做Q?br />
在Filter的配|中Q我指定actionPackages为demo.actions1,demo.actions2Q当pȝ启动ӞStruts2׃在这两个包下扫描到demo.actions1.app.person.ListPeopleAction和demo.actions2.app.group.ListGroupAction?br />
3. Action and Package name
Struts2扫描到Action后,从actionPackages指定的包开始,子包名会成ؓq个Action的namespaceQ而Action的name则由q个Action的类名决定。将cd首字母小写,如果cd以Actionl尾Q则L"Action"后缀QŞ成的名字是q个Action的名字。在如上所q的CZ中,actionPackages指定为demo.actions1,demo.actions2Q那么你可以q样讉Kdemo.actions1.app.person.ListPeopleActionQ?br />
http://localhost:8080/app/person/listPeople
4. Results
Struts2是通过"Result"?Results"两个cȝ别的annotations来指定Results的?br />
作ؓCZQ我们在webapp目录下徏两个html文gQsuccess.html和error.htmlQ随便写点什么内定w可以。现在假设我们访?app/person/listPeopleӞ或Actionq回successp{到success.html面Q若是errorp{到error.html面Q这只需要在ListPeopleActioncM加上一D|解就可以了:
同上Q我们给ListGroupAction也加上注解?br />
现在Q我们已l完成了一个零配置的示例。我们ƈ没有在xml文g里配|ListPeopleAction和ListGroupActionQ但它们已经可以工作了!
用Eclipseq行RunJettyQ然后用览器访问http://localhost:8080/app/person/listPeople和http://localhost:8080/app/group/listGroup看看Q是不是正是success.htmlQ或error.htmlQ的内容Q?br />
5. Namespaces
如上所qͼnamespace由包名所形成Q但我们可以使用"Namespace"注解来自己指定namespace?br />
6. Parent Package
q个配置用得较少。Struts2提供一?ParentPackage"注解来标识Action应该是属于哪个package?br />
三、用COC
如上所qͼStruts2用注解来实现雉|。然而,q不是我喜欢的方式。在我看来,q不q是配|从XML格式换成了注解方式,q不是真的零配置。而且Q这U方式也未必比XML形式的配|更好。另外,对元数据的修改必然会D目的重新编译和部v。还有,现在的Struts2版本g对Result注解中的params的处理有些问题?br />
其实QStruts2的actionPackages配置已经使用了COCQ那Z么不能ؓResults也实现COCQ从而去除这些每个Action都要写的注解Q?br />
在严谨的目中,package、action的名U和面的\径、名UC定存在着某种关系。比如,面的\径可能和package是对应的Q页面的名称可能和action的名U是对应的,或是Ҏ某种法则q算得到。我们知道webwork2和struts2有个配置叫global-results。我们ؓ什么不能根据这些对应规则写个ResultQ将它配到global-results中,从而真正免去result的配|?
事实上,我推荐Struts2的用者只用Struts2输出XML或JSONQ放弃UIQ页面这层还是用标准的HTML、CSS和一些JSlg来展现。许多h反映Struts2慢,实QStruts2是慢Q很慢!慢在哪儿Q很大一部分因素是UIq层引v的,特别是用了q多的Struts2的tagQƈ使用了ajax theme。但是,如果我们攑ּ了Struts2的笨拙的UIQResult只输出XML或JSONQUI则用标准的HTML+CSSQ用JSlgQDOJO、Adobe Spry Framework、YUI-Ext{)来操作Struts2的输出数据,情况会如何Q我们会得到一个高性能、高可配的、UI和应用服务器的职责分割更为明、合理的、更易于静态化部v的开发组合?br />
q似乎是阉割了Struts2Q但是这样阉割过的Struts2摆脱了性能低下的包袱,更轻、更C化?br />
有些扯远了,a归正传,不管是让Struts2输出XML或JSONQ还是输出页面,我们都有办法Ҏ目的规则写一个ResultQ将它配到global-results中,从而大大减Result的配|?br />
假设我们让Struts2只输出JSONQ有个jsonplugin可以做这件事。用JsonResultӞ不再需要知道页面的位置、名U等信息Q它仅仅是数据输出,那么我们可以将q个Result配成全局的,大部分Action不再需要Result的配|?br />
作ؓCZQ我假设我的例子中输出的两个html面Qsuccess.html和error.htmlQ是JSONQ我们看看怎么免去我例子中的两个Action的Result注解?br />
首先Q我们删去ListPeopleAction和ListGroupAction两个Action的注解,q修改struts.xml文gQ加入:
误住这只是一个示例,Z方便Q我没在目中加入jsonplugin来作真实的演C,我只是假设这个success是json输出Q读者可以自行用jsonplugin来作实验?/font>
现在Q离成功不远了,但是目仍然不能正常q行。我们的Actionq回successQ但q不会匹配到global-results中配|。ؓ什么呢Q因为,我们q里是把global-results配置?demo-default"q个package下的Q而Struts2ҎactionPackages扑ֈ的Action不会匚w到这个package上。解军_法也很简单,q记得上面讲到的Parent Package吧?lAction加个注解Q指定ParentPackage?demo-default"。但q样可不是我喜欢的,其实有更好的办法Q我们在struts.xml中加个constant好了:
现在Q大功告成!q行RunJetty来测试下吧!你可以访?app/person/listPeopleQ可以访?app/group/listGroupQ而所有的配置仅仅是web.xml和struts.xml中的几行Q我们的Java代码中也没有加注解。如果再加上几百个Action呢?配置仍然p几行?br />
可是Q某些Action实需要配|怎么办?对这些ActionQ你可以加注解,也可以针对这些Action来写些XML配置。一个项目中Q大部分Action的配|是可以遵从一定规则的Q可以用规则来化配|,只有部分需要配|,q就是COC?br />
]]>
用过struts1.x的h都知道,标签库有html、bean、logic、tilesQ?br />
而struts2.0里的标签却没有分c,只用在jsp头文件加?br />
<%@ taglib prefix="s" uri="/struts-tags" %>
p使用struts2.0的标{ֺ
下面׃l下每个标签的用法(有错h正)Q?/p>
AQ?/p>
<s:a href=""></s:a>-----链接,cM于html里的<a></a>
<s:action name=""></s:action>-----执行一个view里面的一个action
<s:actionerror/>-----如果action的errors有值那么显C出?br />
<s:actionmessage/>-----如果action的message有值那么显C出?br />
<s:append></s:append>-----d一个值到listQ类glist.add();
<s:autocompleter></s:autocompleter>-----自动完成<s:combobox>标签的内容,q个是ajax
BQ?br /> <s:bean name=""></s:bean>-----cM于struts1.x中的QJavaBean的?/p>
CQ?br />
<s:checkbox></s:checkbox>-----复选框
<s:checkboxlist list=""></s:checkboxlist>-----多选框
<s:combobox list=""></s:combobox>-----下拉?br />
<s:component></s:component>-----囑փW号
DQ?br />
<s:date/>-----获取日期格式
<s:datetimepicker></s:datetimepicker>-----日期输入?br />
<s:debug></s:debug>-----昄错误信息
<s:div></s:div>-----表示一个块Q类ghtml?lt;div></div>
<s:doubleselect list="" doubleName="" doubleList=""></s:doubleselect>-----双下拉框
EQ?br />
<s:if test=""></s:if>
<s:elseif test=""></s:elseif>
<s:else></s:else>-----q?个标{一起用,表示条g判断
FQ?br />
<s:fielderror></s:fielderror>-----昄文g错误信息
<s:file></s:file>-----文g上传
<s:form action=""></s:form>-----获取相应form的?/p>
GQ?br /> <s:generator separator="" val=""></s:generator>----?lt;s:iterator>标签一起?/p>
HQ?br /> <s:head/>-----?lt;head></head>里用,表示头文件结?br /> <s:hidden></s:hidden>-----隐藏?/p>
IQ?br />
<s:i18n name=""></s:i18n>-----加蝲资源包到值堆?br />
<s:include value=""></s:include>-----包含一个输出,servlet或jsp面
<s:inputtransferselect list=""></s:inputtransferselect>-----获取form的一个输?br />
<s:iterator></s:iterator>-----用于遍历集合
LQ?br /> <s:label></s:label>-----只读的标{?/p>
MQ?br /> <s:merge></s:merge>-----合ƈ遍历集合出来的?/p>
OQ?br /> <s:optgroup></s:optgroup>-----获取标签l?br /> <s:optiontransferselect doubleList="" list="" doubleName=""></s:optiontransferselect>-----左右选择?/p>
PQ?br /> <s:param></s:param>-----为其他标{提供参?br /> <s:password></s:password>-----密码输入?br /> <s:property/>-----得到'value'的属?br /> <s:push value=""></s:push>-----value的值push到栈?从而property标签的能够获取value的属?/p>
RQ?/p>
<s:radio list=""></s:radio>-----单选按?br /> <s:reset></s:reset>-----重置按钮
SQ?br />
<s:select list=""></s:select>-----单选框
<s:set name=""></s:set>-----赋予变量一个特定范围内的?br />
<s:sort comparator=""></s:sort>-----通过属性给list分类
<s:submit></s:submit>-----提交按钮
<s:subset></s:subset>-----为遍历集合输出子?/p>
TQ?br />
<s:tabbedPanel id=""></s:tabbedPanel>-----表格?br />
<s:table></s:table>-----表格
<s:text name=""></s:text>-----I18n文本信息
<s:textarea></s:textarea>-----文本域输入框
<s:textfield></s:textfield>-----文本输入?br />
<s:token></s:token>-----拦截?br />
<s:tree></s:tree>-----?br />
<s:treenode label=""></s:treenode>-----树的l构
UQ?br />
<s:updownselect list=""></s:updownselect>-----多选择?br />
<s:url></s:url>-----创徏url
|
|
|