懒人策略之:Tapestry4中,去掉累赘的.page文件

写给和我一样的懒人。


/**
*
* 原帖地址:http://huxiao.iteye.com/blog/604053
* 转载请您保留作者信息
*
* @author 杨凯 http://www.blogjava.net/ycyk168
* @author 胡晓 http://huxiao.iteye.com kskr@qq.com QQ:376665005
*
* @Time 2010-02-26 23:44
*
* 祝大家新年、元宵快乐,祝凯哥步步高升,祝自己工作顺利
*
*/



[size=large][color=blue]一、起因:[/color][/size]
tapestry中,一般我们在.html中写展示程序,用BasePage的子类处理程序,用.page关联.html和Page类。
因为.page的存在,Page类不管放在哪,只要路径记录在.page文件中,都可以找得到Page类,这样有很大的灵活性,但是同时也造成每次都要写一个.page文件的麻烦。
实际开发中,考虑到团队合作、开发效率、规范性、条理性等因素,作为一个正常人我们都不会把那些Page类胡乱摆放,通常的做法是路径一一对应,文件名一一对应,这么看起来条理很清晰,时间久了,就寻思,既然路径有了规则,还要.page文件做什么呢?

[size=large][color=blue]二、目的:[/color][/size]
去掉.page文件,让tapestry根据我们定义的规则去寻找相应的Page类

我这里就实现一种一一对应的关系,如:
/admin/test.html 对应 com.djwl.pages.admin.test.java
/aaa/bbb/ccc/Tapestry.html 对应 com.djwl.pages.aaa.bbb.ccc.Tapestry.java

说干就干,Go !

[size=large][color=blue]三、搭建一个最基本的支持tapestry的项目[/color][/size]
为了方便演示,我这里新建一个web项目,等下我就用tapestry Quick Start(http://tapestry.apache.org/tapestry4.1/quickstart/helloworld.html)里面的一个例子给大家做例子。

1. 新建一个web工程

2. 下载tapestry v4.1.6,解压,jar包copy到lib目录下,编译后如下图所示

[img]http://dl.iteye.com/upload/attachment/209846/de105073-a7a5-316b-8342-eec1433fe291.jpg[/img]

3. web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.4"
xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">

<servlet>
<servlet-name>app</servlet-name>
<servlet-class>org.apache.tapestry.ApplicationServlet</servlet-class>
<load-on-startup>0</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>app</servlet-name>
<url-pattern>/app</url-pattern>
</servlet-mapping>

</web-app>



4. 在根目录下新建Home.html文件,里面输出一个new java.util.Date().toLocaleString(),

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>Home.html</title>

<meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
<meta http-equiv="description" content="this is my page">
<meta http-equiv="content-type" content="text/html; charset=gbk">

</head>

<body>
<span jwcid="@Insert" value="ognl:new java.util.Date().toLocaleString()"></span>
</body>
</html>



至此,如果不出什么意外,运行项目,在地址栏中输入地址(如http://localhost:8080/app)应该就可以访问了,这一步如果你没看懂,可以去看一下tapestry的文档,我这里就不再赘述了

[size=large][color=blue]四、分析[/color][/size]
ps:这一部分是分析,讲述代码一步一步怎么来的,如果你着急也可以跳过此步直接看下一步总结出来的代码,但是分析过程还是很重要的,就像谈恋爱一样,过程大于结果嘛

1. 寻找突破口
细心的朋友可能已经注意到,我上面的Home.html其实并没有.page文件,纯粹的html文件肯定是不能解析new java.util.Date().toLocaleString()这段程序的,所以说Home.html实际上肯定对应了某个Page类,当然并不是我们新建的,我们上面并没有新建任何类,说到这里其实已经很明了了,这个类就是我们新建Page类要继承的org.apache.tapestry.html.BasePage.java

也就是说,当系统找不到.page文件的时候,他会用BasePage作为该html的对应Page类,那么我们能不能照葫芦画瓢,当没有.page文件的时候,让系统根据html的文件名和路径去找与之对应的Page类呢?

2. 初试牛刀
经过一番测试,发现完全模仿一边tapestry的思路并不容易,按照我们上面的思路,需要判断.page死否存在,如果存在则如何,如果不存在又如何,这个判断做起来相对复杂。但是我们有一个简单的路可走。既然tapestry已经判断好了,那么我们何必还要去判断呢?

打个简单的比喻吧,暑假期间,你去美国找你的朋友小明玩儿。一天小明和你一起去菜市场买猪肉,A和B在卖肉(.page存在和不存在),甲上前问价钱,如果A便宜就买A的(如果.page存在就根据.page找),否则买B的(否则使用BasePage类)。这时你也想买肉,但是英语不好不好问屠夫价钱(就是我们上面的问题,不好判断.page是否存在)。试问,如果遇到这种情况你会怎么办呢?

我觉得你会问小明,对吗?回到我们这上面来,既然tapestry判断并知道了.page是否存在,那么我们问问tapestry不就ok了吗?

tapestry这个小明出国多年,汉语讲得很一般,我跟他交流了半天,他才告诉我,存在不存在不知道,但是我准备买B的肉(使用BasePage类)

就是在这里,他告诉我的,org.apache.tapestry.pageload.PageLoader.java中的instantiatePage方法:


private IPage instantiatePage(String name, INamespace namespace, IComponentSpecification spec) {
Location location = spec.getLocation();
ComponentClassProviderContext context = new ComponentClassProviderContext(name, spec, namespace);

// 这个className,就是他要使用的Page类的路径
String className = _pageClassProvider.provideComponentClassName(context);


// 这个if是我加的,其他的代码都是原来的
// 那么我们就可以判断,如果他使用BasePage,说明.page不存在,那么我们就去找我们对应的Page类啦!!
if (StringUtils.equals(className, "org.apache.tapestry.html.BasePage")) {
String fullName = namespace.constructQualifiedName(name);
className = "com.djwl.pages." + fullName.replaceAll("/", ".");
}

Class pageClass = _classResolver.findClass(className);

if (!IPage.class.isAssignableFrom(pageClass)) {
throw new ApplicationRuntimeException(PageloadMessages.classNotPage(pageClass), location, null);
}

String pageName = namespace.constructQualifiedName(name);

ComponentConstructor cc = _componentConstructorFactory.getComponentConstructor(spec, className);

IPage result = (IPage) cc.newInstance();

result.setNamespace(namespace);
result.setPageName(pageName);
result.setPage(result);
result.setLocale(_locale);
result.setLocation(location);

return result;
}


改好了这个之后,我们新建com.djwl.pages.Home.java文件试一下:

package com.djwl.pages;

import org.apache.tapestry.html.BasePage;


public abstract class Home extends BasePage {

}



重启,浏览,没有问题

作为一个态度严谨的程序员-_-!! 为了进一步证实他对应的真的是我们的Home.java而不是BasePage,我们还是再做一个测试吧,在Home.java里面定义一个变量在前台输出出来:

Home.java

package com.djwl.pages;

import org.apache.tapestry.html.BasePage;

public abstract class Home extends BasePage {

private String info = "hello, huxiao";

public String getInfo() {

return info;
}

}




Home.html

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>Home.html</title>

<meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
<meta http-equiv="description" content="this is my page">
<meta http-equiv="content-type" content="text/html; charset=gbk">

</head>

<body>
<span jwcid="@Insert" value="ognl:new java.util.Date().toLocaleString()"></span>
<span jwcid="@Insert" value="ognl:info"></span>
</body>
</html>



哇塞,居然有成功了!!我们就是天才啊,这程序,太完美了!!

3. 问题又来了

然而我们高兴的还太早,也许当前你还没有发现问题,但是当你使用过一段时间之后,你就会发现,经过我们上面的修改,我们以后的工作更累啦-_-!!程序员就是代码机器啊代码机器……

原因是,对于Home.html,他就去找Home.java。有一天你或者你的美工朋友写一个纯静态的test.html,发现页面上豁然写着“An exception has occurred. ”,原因很明显啊,tapestry发现没有.page文件,去找 BasePage,半路被我们截住,去找test.java,问题来了,没人写过test.java啊,当然找不到了,当然报错了。

ok,我们强忍了,写一个空的类test.java,继承一下BasePage,算是ok了,但是有一天你发现,你的美工朋友引用了一个叫fckeditor的东西(当然也可能是别的),里面有几十上百个html文件。。。编码机器啊……

4. 解决问题

我曾一度写了n个空的Page类(n约等于30~50,大虾们不要笑俺,俺一直在努力),终于有一天美工朋友新添加的几十个html文件把我敲醒了,愣了半响,发现原来是一个多么easy的问题,哎,白写了这么多多余的Page类

还是刚才那个PageLoader类,在if代码段,下面加上这个try,catch,意思是,如果类不存在,再去找BasePage去,呵呵,又绕回去了

/**
* 经过上面的修改,似乎大功告成了,但是问题来了,凡是没有.page文件的html,
* tapestry会去寻找对应的Page类,如果这个Page类不存在,就会出错,
* 这意味着你必须为每个html文件写一个对应的Page类,哪怕是一个纯静态文件,你也要写一个空的Page与之对应
*
* 解决办法是还原tapestry的处理方式,没有.page是吧,那么都交给一个默认的类去处理,
* 我这里就交给默认的那个BasePage了,你也可以交给你自定义的Page类
* 到这里就搞定啦
*/
try {
Class c = Class.forName(className);
Object.class.isAssignableFrom(c);
} catch (Exception e) {
System.out.println("Class \"" + className + "\" not found.We will use \"org.apache.tapestry.html.BasePage\" instead.");
className = "org.apache.tapestry.html.BasePage";
}


[size=large][color=blue]五、总结一下[/color][/size]

说了这么多,我们实际上就修改了一个方法的几句话,如下:

org.apache.tapestry.pageload.PageLoader.instantiatePage(String, INamespace, IComponentSpecification):



private IPage instantiatePage(String name, INamespace namespace, IComponentSpecification spec) {
Location location = spec.getLocation();
ComponentClassProviderContext context = new ComponentClassProviderContext(name, spec, namespace);

String className = _pageClassProvider.provideComponentClassName(context);

/**
* 如果没有.page文件,那么我们就去寻找自定义的类,当然要找哪个类,规则是你自己定义的
*
* 我这里的规则是,root下的html文件和com.djwl.pages包下面的类文件一一对应
* 如"/admin/test.html" --> "com.djwl.pages.admin.testPage.java"
*/
if (StringUtils.equals(className, "org.apache.tapestry.html.BasePage")) {
String fullName = namespace.constructQualifiedName(name);
className = "com.djwl.pages." + fullName.replaceAll("/", ".");
}

/**
* 经过上面的修改,似乎大功告成了,但是问题来了,凡是没有.page文件的html,
* tapestry会去寻找对应的Page类,如果这个Page类不存在,就会出错,
* 这意味着你必须为每个html文件写一个对应的Page类,哪怕是一个纯静态文件,你也要写一个空的Page与之对应
*
* 解决办法是还原tapestry的处理方式,没有.page是吧,那么都交给一个默认的类去处理,
* 我这里就交给默认的那个BasePage了,你也可以交给你自定义的Page类
* 到这里就搞定啦
*/
try {
Class c = Class.forName(className);
Object.class.isAssignableFrom(c);
} catch (Exception e) {
System.out.println("Class \"" + className + "\" not found.We will use \"org.apache.tapestry.html.BasePage\" instead.");
className = "org.apache.tapestry.html.BasePage";
}

Class pageClass = _classResolver.findClass(className);

if (!IPage.class.isAssignableFrom(pageClass)) {
throw new ApplicationRuntimeException(PageloadMessages.classNotPage(pageClass), location, null);
}

String pageName = namespace.constructQualifiedName(name);

ComponentConstructor cc = _componentConstructorFactory.getComponentConstructor(spec, className);

IPage result = (IPage) cc.newInstance();

result.setNamespace(namespace);
result.setPageName(pageName);
result.setPage(result);
result.setLocale(_locale);
result.setLocation(location);

return result;
}


这样就实现了我们的目的,如果是带程序的html,会去找对应的Page类,如果不是,则去找BasePage

这只是一种解决方式,不是最好的,但是可以解决。重要的思考的过程。大家有好的点子也给我分享一下。
Email:kskr@qq.com
QQ:376665005
原帖地址:[url]http://huxiao.iteye.com/blog/604053[/url]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值