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

写给和我一样的懒人。

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



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

二、目的:
去掉.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 !

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

1. 新建一个web工程

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

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

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的文档,我这里就不再赘述了

四、分析
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";
		}


五、总结一下

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

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:[email protected]
QQ:376665005
原帖地址: http://huxiao.iteye.com/blog/604053



你可能感兴趣的:(apache,html,qq,fckeditor,tapestry)