上一個項目使用的是spring MVC; 客戶需要做Ajax應用;所以就找了一些資料研究了一下,比如DWR,dojo, prototype,JSON-RPC, trimpath 等等,發現很多不適合我們,比如DWR要生成客戶端js,服務器端還要部署,麻煩;dojo又太慢了;經過一輪淘汰剩下了prototype和trimpath;所以最終就選這2個了;
Prototype在書寫普通的js時候,有很多好處,比如簡單,實用的很多函數;比如$()系列;
Trimpath提供一個客戶端的js模板,如果從服務器回來的數據很復雜,要動態改變Html元素是比較費力的事情;用trimpath就方便許多;
在模板語言的世界里,總有2個東西:模板和模板中的數據;trimpath的模板接受的數據是javascript object,模板則定義在一個不顯示的textarea里面;
所以有個問題就是:怎么讓ajax調用返回一個javascript對象?
后來,我終于發現了(想起了劉若英)JSON;發現json是個好東東;比xml輕量級,又可以很容易的轉換為javascript對象,而且還有java api;唉,開源的世界多美妙;
所以解決方案就是,在springmvc框架中,用response返回json string,給ajax 客戶端,然后生成javascript對象,然后,調用trimpath模板,然后,動態修改頁面。
代碼片段:
public ModelAndView getClient(HttpServletRequest request, HttpServletResponse response) throws Exception {
JSONObject jsonObject = new JSONObject();
Client client=clientMgr.getClientByPk(Long.parseLong(request.getParameter("clientId")));
jsonObject.add("client", client);
return ajaxResponse(jsonObject, response);
}
protected ModelAndView ajaxResponse(JSONObject jsonObject, HttpServletResponse response) throws Exception {
response.setContentType("application/x-json;charset=UTF-8");
response.getWriter().print(jsonObject);
return null;
}
利用xpath,freemarker等技術,使得用戶通過xml配置文件,模板等簡單的方式,快速開發代碼和根據需求調整模板;
Dom4j對實現了xpath 1.0, 非常的好用;
所需jar :dom4j-1.6.1.jar, jaxen-1.1-beta-7.jar
Code:
import junit.framework.TestCase;
import org.dom4j.*;
import org.dom4j.io.SAXReader;
import java.util.List;
import java.util.Iterator;
import java.io.File;
/**
* Created by IntelliJ IDEA.
* User: duanbin
* Date: 2007-8-15
* Time: 9:47:17
* To change this template use File | Settings | File Templates.
*/
public class XPathTest extends TestCase {
public void testXPathViaDom4jXpathV1() {
printSelectedNodeValue("D:""xpath""src""test.xml");
}
/**
* 利用XPath操作XML文件,打印指定節點或者屬性的值, using xpath 1.0
*
* @param filename String 待操作的XML文件(相對路徑或者絕對路徑)
*/
public void printSelectedNodeValue(String filename) {
try {
SAXReader saxReader = new SAXReader();
Document document = saxReader.read(new File(filename));
List list;
list = document.selectNodes("http://qn1:college/@leader[.!='leader1']");
for (Object aList : list) {
Attribute attribute = (Attribute) aList;
System.out.println("http://qn1:college/@leader: " + attribute.getValue());
}
//打印所有student節點的屬性age值,如果有的話
// list = document.selectNodes("/students/student/@age");
// for (Object aList : list) {
// Attribute attribute = (Attribute) aList;
// System.out.println("/students/student/@age:" + attribute.getValue());
// }
//打印所有college節點值,如果有的話
// list = document.selectNodes("/students/student");
// for (Object aList1 : list) {
// Element bookElement = (Element) aList1;
// Iterator iterator = bookElement.elementIterator("college");
// while (iterator.hasNext()) {
// Element titleElement = (Element) iterator.next();
// System.out.println("/students/student/college:" + titleElement.getText());
// }
// }
//測試節點的一些方法
// list = document.selectNodes("http://city");
// for (Object aList2 : list) {
// Element titleElement = (Element) aList2;
// System.out.print("http://telephone:getName:" + titleElement.getName());
// System.out.print(" ##getNodeType:" + titleElement.getNodeType());
// System.out.print(" ##getTextTrim:" + titleElement.getTextTrim());
// System.out.print(" ##getNamespaceURI:" + titleElement.getNamespaceURI());
// System.out.print(" ##getNodeTypeName:" + titleElement.getNodeTypeName());
// System.out.print(" ##getQualifiedName:" + titleElement.getQualifiedName());
// System.out.print(" ##getUniquePath:" + titleElement.getUniquePath());
// System.out.println(" ##getPath:" + titleElement.getPath());
// }
//打印所有name節點值,如果有的話,與上面college的取法不一樣
// list = document.selectNodes("/students/student/name");
// Iterator iter = list.iterator();
// for (Object aList3 : list) {
// Element titleElement = (Element) aList3;
// System.out.println("/students/student/name:" + titleElement.getText());
// }
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
Test.xml:
<?xml version="1.0" encoding="UTF-8"?>
<students xmlns:qn1="http://qn1.com">
<student age="259911911911"><!--如果沒有age屬性,默認的為20-->
<qn1:name>崔衛兵</qn1:name>
<college>PC學院</college>
<telephone>62354666</telephone>
<notes>男,1982年生,碩士,現就讀于北京郵電大學</notes>
<addr><city>Beijing</city></addr>
</student>
<student>
<name>cwb</name>
<qn1:college leader="學院領導">PC學院</qn1:college><!--如果沒有leader屬性,默認的為leader-->
<telephone>62358888</telephone>
<notes>男,1987年生,碩士,現就讀于中國農業大學</notes>
</student>
<student>
<name>xxxxx</name>
<college leader="">xxx學院</college>
<telephone>66666666</telephone>
<notes>注視中,注釋中</notes>
</student>
<student age="9911911911">
<name>yyyyyy</name>
<qn1:college leader="leader1">yyyy學院</qn1:college>
<telephone>88888888</telephone>
<notes>注視中111,注釋中222</notes>
</student>
</students>
Freemarker基本示例:
import freemarker.template.Configuration;
import freemarker.template.DefaultObjectWrapper;
import freemarker.template.Template;
import java.io.File;
import java.io.Writer;
import java.io.OutputStreamWriter;
import java.io.FileOutputStream;
import java.util.Map;
import java.util.HashMap;
import org.apache.log4j.Logger;
/**
* Created by IntelliJ IDEA.
* User: duanbin
* Date: 2007-8-21
* Time: 22:30:13
* To change this template use File | Settings | File Templates.
*/
public abstract class BaseGenerator {
protected Configuration cfg;
protected final Logger logger = Logger.getLogger(this.getClass());
public BaseGenerator() {
try {
cfg = getConfiguration(CodeEngineConfig.getTemplateDir());
} catch (Exception e) {
e.printStackTrace();
}
}
public abstract void generate();
protected void generateFile(String template, Map root, String fileName){
try {
File dist = new File(fileName);
FileOutputStream fos = new FileOutputStream(dist);
Writer out = new OutputStreamWriter(fos);
// Writer out2 = new OutputStreamWriter(System.out);
cfg.getTemplate(template).process(root, out);
out.flush();
out.close();
logger.info("Generated File: " + fileName);
} catch (Exception e) {
e.printStackTrace();
}
}
protected Configuration getConfiguration(String tempDir) throws Exception {
Configuration cfg = new Configuration();
// Specify the data source where the template files come from.
// Here I set a file directory for it:
cfg.setDirectoryForTemplateLoading(
new File(tempDir));
// Specify how templates will see the data model. This is an advanced topic...
// but just use this:
cfg.setObjectWrapper(new DefaultObjectWrapper());
return cfg;
}
}
模板文件示例:
package ${package}.application.web;
import biz.web.framework.web.BaseController;
<#list managers! as mgr>
import ${package}.application.manager.${mgr};
</#list>
<#list services! as svc>
import ${package}.application.service.${svc};
</#list>
/**
* Created by IntelliJ IDEA.
* User: ${author!'admin'} "${r"${build.dir}"} "${'$'}{cfg.startDate}
* Date: Sep 1, 2006
* Time: 9:19:17 AM
* To change this template use File | Settings | File Templates.
*/
public class ${name}Controller extends BaseController {
<#list managers! as mgr>
protected ${mgr} ${mgr?uncap_first};
</#list>
<#list services! as svc>
protected ${svc} ${svc?uncap_first};
</#list>
<#list managers! as mgr>
public void set${mgr}(${mgr} ${mgr?uncap_first}) {
this.${mgr?uncap_first} = ${mgr?uncap_first};
}
</#list>
<#list services! as svc>
public void set${svc}(${svc} ${svc?uncap_first}) {
this.${svc?uncap_first} = ${svc?uncap_first};
}
</#list>
public ModelAndView list${name}(HttpServletRequest request, HttpServletResponse response) throws Exception {
ModelAndView mav = new ModelAndView("update_${name?uncap_first}");
Long user = getUserId();
mav.addObject("userId", user);
return mav;
}
}
springside的文檔中有aop的配置;http://wiki.springside.org.cn/display/springside/Spring+Aop
里面有關于pointcut 表達式語言的表述;
里面也有官方文檔中文版的鏈接:http://www.redsaga.com/spring_ref/2.0/html/aop.html
我們用scheme-based aop 配置方式:
<aop:config> ...... </aop:config>
Advisor方式:
假設我們有一個MethodBeforeAdvice 叫TestAdvice
;用于打印將要執行的方面名;
public class TestAdvice implements MethodBeforeAdvice {
public void before(Method method, Object[] objects, Object
object)
throws Throwable {
System.out.println("method.getName() = "
+ method.getName());
System.out.println("TestAdvice
testing..." );
}
}
配置如下:
<bean id="testAdvice"
class="biz.pxzit.application.service.TestAdvice"/>
<aop:config>
<aop:advisor pointcut="execution(*
biz..*Mgr.save*(..))"
advice-ref="testAdvice" order="1"/>
......
</aop:config>
order是可選的,指定執行的次序;上面的配置語意是,在執行biz開頭的package下面任意以Mgr結尾的manager的save開頭的方法時候,執行testAdvice,因為是MethodBeforeAdvice
所以在save開頭方法執行前執行;
Aspect方式:
public class TestAdvice2 {
public void goAfter(JoinPoint joinPoint) throws Throwable {
System.out.println("TestAdvice2.goAfter
testing..."+joinPoint.getTarget());
System.out.println("TestAdvice2.goAfter
testing..." );
}
}
配置如下:
<bean id="testAdvice2"
class="biz.pxzit.application.service.TestAdvice2"/>
<aop:config>
......
<aop:aspect
id="ddAspect" ref="testAdvice2">
<aop:after
method="goAfter"
pointcut="execution(* biz..*Mgr.*(..))"
/>
.......
</aop:aspect>
</aop:config>
注意TestAdvice2的方法參數,這里用的是JoinPoint
joinPoint,還有很多細節,具體可以看文檔;
要使用<aop:aspect >節點,還需asm.jar
(3個,在springframework的lib下有)否則會報noclassfound
exception;
注意我之前配置的pointcut表達式是錯的,正確的是execution(*
biz..*Mgr.*(..))";