Jdon框架使用开发指南

来源:百度文库 编辑:神马文学网 时间:2024/06/03 07:38:35
板桥里人(banq J道http://www.jdon.com)
1.2.2版本: 2005年7月14日
JdonFramework下载地址:http://sourceforge.net/projects/jdon/
技术支持论坛:http://www.jdon.com/jive/forum.jsp?forum=61&thRange=30
快速J2EE开发工具 JdonFramework开发指南
专业创新来自Jdon.com
技术背景
J2EE基本概念
J2EE可以说指Java在数据库信息系统上实现,数据库信息系统从早期的dBase、到Delphi/VB等C/S结构,发展到B/S(Browser浏览器/Server服务器)结构,而J2EE主要是指B/S结构的实现。
J2EE又是一种框架和标准,框架类似API、库的概念,但是要超出它们。
J2EE是一个虚的大的概念,J2EE标准主要有三种子技术标准:WEB技术、EJB技术和JMS,谈到J2EE应该说最终要落实到这三个子概念上。
这三种技术的每个技术在应用时都涉及两个部分:容器部分和应用部分,Web容器也是指Jsp/Servlet容器,你如果要开发一个Web应用,无论是编译或运行,都必须要有Jsp/Servlet库或API支持(除了JDK/J2SE以外)。
Web技术中除了Jsp/Servlet技术外,还需要JavaBeans或Java Class实现一些功能或者包装携带数据,所以Web技术最初裸体简称为Jsp/Servlet+JavaBeans系统。
谈到JavaBeans技术,就涉及到组件构件技术(component),这是Java的核心基础部分,很多软件设计概念(设计模式)都是通过JavaBeans实现的。
JavaBeans不属于J2EE概念范畴中,如果一个JavaBeans对象被Web技术(也就是Jsp/Servlet)调用,那么JavaBeans就运行在J2EE的Web容器中;如果它被EJB调用,它就运行在EJB容器中。
EJB(企业JavaBeans)是普通JavaBeans的一种提升和规范,因为企业信息系统开发中需要一个可伸缩的性能和事务、安全机制,这样能保证企业系统平滑发展,而不是发展到一种规模重新更换一套软件系统。
至此,JavaBeans组件发展到EJB后,并不是说以前的那种JavaBeans形式就消失了,这就自然形成了两种JavaBeans技术:EJB和POJO,POJO完全不同于EJB概念,指的是普通JavaBeans,而且这个JavaBeans不依附某种框架,或者干脆可以说:这个JavaBeans是你为这个应用程序单独开发创建的。
J2EE应用系统开发工具有很多:如JBuilder、Eclipse等,这些IDE首先是Java开发工具,也就是说,它们首要基本功能是可以开发出JavaBeans或Java class,但是如果要开发出J2EE系统,就要落实到要么是Web技术或EJB技术,那么就有可能要一些专门模块功能,最重要的是,因为J2EE系统区分为容器和应用两个部分,所以,在任何开发工具中开发J2EE都需要指定J2EE容器。
J2EE容器分为WEB容器和EJB容器,Tomcat/Resin是Web容器;JBoss是EJB容器+Web容器等,其中Web容器直接使用Tomcat实现的。所以你开发的Web应用程序可以在上面两种容器运行,而你开发的Web+EJB应用则只可以在JBoss服务器上运行,商业产品Websphere/Weblogic等和JBoss属于同一种性质。
J2EE容器也称为J2EE服务器,大部分时它们概念是一致的。
如果你的J2EE应用系统的数据库连接是通过JNDI获得,也就是说是从容器中获得,那么你的J2EE应用系统基本与数据库无关,如果你在你的J2EE应用系统耦合了数据库JDBC驱动的配置,那么你的J2EE应用系统就有数据库概念色彩,作为一个成熟需要推广的J2EE应用系统,不推荐和具体数据库耦合,当然这其中如何保证J2EE应用系统运行性能又是体现你的设计水平了。 快速J2EE开发工具 JdonFramework开发指南
专业创新来自Jdon.com
如何开发一个高质量的J2EE系统
衡量J2EE应用系统设计开发水平高低的标准就是:解耦性;你的应用系统各个功能是否能够彻底脱离?是否不相互依赖,也只有这样,才能体现可维护性、可拓展性的软件设计目标。
为了达到这个目的,诞生各种框架概念,J2EE框架标准将一个系统划分为WEB和EJB主要部分,当然我们有时不是以这个具体技术区分,而是从设计上抽象为表现层、服务层和持久层,这三个层次从一个高度将J2EE分离开来,实现解耦目的。
因此,我们实际编程中,也要将自己的功能向这三个层次上靠,做到大方向清楚,泾渭分明,但是没有技术上约束限制要做到这点是很不容易的,因此我们还是必须借助J2EE具体技术来实现,这时,你可以使用EJB规范实现服务层和持久层,Web技术实现表现层;
EJB为什么能将服务层从Jsp/Servlet手中分离出来,因为它对JavaBeans编码有强制的约束,现在有一种对JavaBeans弱约束,使用Ioc模式实现的(当然EJB 3.0也采取这种方式),在Ioc模式诞生前,一般都是通过工厂模式来对JavaBeans约束,形成一个服务层,这也是是Jive这样开源论坛设计原理之一。
由此,将服务层从表现层中分离出来目前有两种可选架构选择:管理普通JavaBeans(POJO)框架(如Spring、JdonFramework)以及管理EJB的EJB框架,因为EJB不只是框架,还是标准,而标准可以扩展发展,所以,这两种区别将来是可能模糊,被纳入同一个标准了。
但是,通常标准制定是为某个目的服务的,总要牺牲一些换取另外一些,所以,这两种架构会长时间并存。
前面谈了服务层框架,使用服务层框架可以将JavaBeans从Jsp/Servlet中分离出来,而使用表现层框架则可以将Jsp中剩余的JavaBeans完全分离,这部分JavaBeans主要负责显示相关,一般是通过标签库(taglib)实现,不同框架有不同自己的标签库,Struts是应用比较广泛的一种表现层框架。
这样,表现层和服务层的分离是通过两种框架达到目的,剩余的就是持久层框架了,通过持久层的框架将数据库存储从服务层中分离出来是其目的,持久层框架有两种方向:直接自己编写JDBC等SQL语句(如iBatis);使用O/R Mapping技术实现的Hibernate和JDO技术;当然还有EJB中的实体Bean技术。
持久层框架目前呈现百花齐放,各有优缺点的现状,所以正如表现层框架一样,目前没有一个框架被指定为标准框架,当然,表现层框架现在又出来了一个JSF,它代表的页面组件概念是一个新的发展方向,但是复杂的实现让人有些忘而却步。
最后,你的J2EE应用系统如果采取上面提到的表现层、服务层和持久层的框架实现,基本可以在无需深刻掌握设计模式的情况下开发出一个高质量的应用系统了。
还要注意的是: 开发出一个高质量的J2EE系统还需要正确的业务需求理解,那么域建模提供了一种比较切实可行的正确理解业务需求的方法,相关详细知识可从UML角度结合理解。
当然,如果你想设计自己的行业框架,那么第一步从设计模式开始吧,因为设计模式提供你一个实现JavaBeans或类之间解耦参考实现方法,当你学会了系统基本单元JavaBeans或类之间解耦时,那么系统模块之间的解耦你就可能掌握,进而你就可以实现行业框架的提炼了,这又是另外一个发展方向了。
以上理念可以总结为一句话:
J2EE开发三件宝: Domain Model(域建模)、patterns(模式)和framework(框架)。 快速J2EE开发工具 JdonFramework开发指南
专业创新来自Jdon.com
域驱动开发框架
Ruby on Rails已经受到越来越多的重视,更多文章开始关注,Rolling with Ruby on Rails一文比较详细,也有国人做了翻译,按这里。这里我们不过多讨论ROR(Ruby on Rails),而是探讨如何以一个正确的方式快速开发J2EE。
现在的问题
现在我们J2EE开发碰到了什么问题呢? 让我们想象一下使用Spring和Hibernate开发一个J2EE WEB应用是什么样的:我们需要增加一个新的域对象类型为Person,下面主要的开发步骤:
1.创建Person类.
2.创建PersonDAO类.
3.创建Person数据表.
4.定义PersonDAO在Spring的application context XML文件.
5.创建Person page页面和action类.
6.增加Person页面到web框架(如struts)XML配置文件中.
7.创建personList页面来显示Person实例.
8.创建personEdit页面来编辑Person实例.
你会确实感慨:真是需要很多步骤啊。
如何解决?
关键问题是我们开发时不能重复一些步骤,因此必须尽量减少步骤,如果只减少步骤到:
1.创建Person类?
是否只需要第一个步骤就可以?在第一步时,我们花费更多时间精力进行域建模,确定域模型的属性行为等。其他步骤我们会发现下面的规律:
1. 对于每个实体,我们需要完成应用的基本功能,如 create, retrieve, update, and delete (CRUD).
2.我们需要每个实体持久化到数据库.
3.我们需要数据库为每个实体创建数据表。
4.我们需要安排实体之间的关系。
当然,在复杂应用中,不会只是这些功能,但是如果我们将这些功能通过框架实现,将大大提高我们的开发效率。
域驱动开发
域驱动开发(domain driven development framework ),简称DDD是一种最新的OO设计概念,它是由ROR和Naked Object组织提出的。
所谓naked Object是指一个复杂的域对象,这个Object是一个POJO,但是不是一个傻傻的完全是属性的POJO,而是封装了业务逻辑的POJO,注意这里是最大的区别,一般业务逻辑我们是通过另外的Service类来实现,然后在Service中封装的transaction script, 见Martin Fowler的PoEEA. ,而 naked Object则是合并起来的(有的类似回归传统了),个人感觉整个OO软件好像一直在玩0或1的游戏,不过也许最复杂的体系就是来自最简单的抉择,如股票/汇市等投资领域也如此。 快速J2EE开发工具 JdonFramework开发指南
专业创新来自Jdon.com
naked Object提出现在J2EE开发和裸体对象DDD开发下的图:
通过这张图我们可以看到,以前方式造成J2EE开发层次之间调用混乱,修改和拓展非常不方便,而在右边的DDD开发方式下,界面(边界)对象就是域对象就是持久化的实体,没有多余的Contorller或Action了。
现在怎么办?
ROR提倡的DDD方式引起了众多J2EE开发者的兴趣,在各大Java媒体正在引起广泛的讨论,但是ROR不是Java的,那么有无基于Java的DDD开发框架呢?
目前有不少DDD开发框架正在诞生中,Jdon框架正是在ROR这种精神指引下的一款快速开源开发框架,Jdon框架1.2.2版本虽然不是一个完全意义上的Naked Object,但是已经初步具备上图右边开发流程,具体可参考Jdon Sample的开发流程
使用Jdon框架开发J2EE应用系统,最重要的一个前提是:设计好你的域对象,然后在将域对象复制到表现层,变成表现层的ActionForm/ModelForm;将域对象直接在持久层使用Hibernate/iBatis等持久化到数据库;如果使用EJB的实体Bean持久化技术,将无需实现建立数据表;应用系统部署时,J2EE容器将直接根据实体建立数据表,也可节省前面步骤中两个步骤。
当然,目前Jdon框架是采取分离手法,遵循桥模式,将抽象和行为分离,每个域对象对应一个操作它的服务类或DAO类,服务类主要用来封装业务逻辑层,然后将业务Service作为一个业务组件暴露给表现层的Controller/Action类,而Controller/Action则无需代码,只要通过如下配置即可完成:

//指定边界类

//指定某个业务接口

//业务接口的新增方法
//业务接口的修改方法 快速J2EE开发工具 JdonFramework开发指南
专业创新来自Jdon.com




通过上述配置,净化了上图中应用控制层(Application or Controller layer)和Domain Layer之间对应关系,变得有条理而且明晰。
随着Naked Object 被越来越多人认识和应用成熟,Jdon框架也将转向支持Naked Object。
Ioc框架
代码案例
假设有调用者B和被调用者A代码如下:
调用者B类
package test;
public class B{
AInfterface a;
public B(AInfterface a){
this.a = a
}
public void invoke(){
a.myMethod();
}
}
被调用者A类:
package test;
public class A implements AInfterface {
public void myMethod(){
System.out.println("hello");
}
}
生成B类实例代码如下:
B b = new B(new A());
创建B的实例要逐个照顾到B类中涉及到所有其他类(如A类)的实例化,给编程者带来代码编写的琐碎工作,无法提高效率。
使用Jdon框架的Ioc模式后,B类生成实例代码如下:
B b = (B) WebAppUtil.getService(“b”);
b. invoke();
无需首先照顾其他类如A类的实例生成。B的实例生成再也与其他类如A类没有任何关系了,实现松耦合。
实现上述调用效果,需另外实现jdonframework.xml配置如下:



快速J2EE开发工具 JdonFramework开发指南
专业创新来自Jdon.com


……

革命性优点
Java编程中类创建成实例的过程简化:(Class -> Instance)
使用Jdon等Ioc框架前: 编程者需要自己逐个解决这个Class相关涉及的其他Class的实例化。
使用Jdon等Ioc框架后:
无需编程者自己实现这种级联式、琐碎的实例化过程。
松耦合;更换各种子类方便。上例中,如果Ainterface有另外一个实现子类AA类,只要将jdonframework.xml中:

更换为:

快速J2EE开发工具 JdonFramework开发指南
专业创新来自Jdon.com
Jdon框架的特点?
Jdon框架是一个真正轻量级别的开发框架,设计简单巧妙,适合快速开发各种架构的J2EE应用系统。它是一套符合当前国际水平的、面向构件开发的、国人拥有自主产权的中间件产品。
在J2EE应用开发中的主要优点
Jdon框架给J2EE应用开发带来主要的好处是:
当你的项目刚开始时,它可能是一个小项目,实现一些简单功能,这时你可能只需要使用普通JavaBeans(POJO)实现数据库操作业务,这个POJO一般一次请求生成一个实例(使用new)。
当访问量逐渐增加,这种POJO每次请求生成和销毁都会耗费性能,你的J2EE应用系统可能出现性能降低缓慢等现象,这时我们就需要池(Pool)和缓存(Cache)来优化。
下面我们从J2EE应用系统运行原理开始简单分析:
如果在某个时刻有两个以上用户同时访问你的系统,也就是说同时发出请求(例如刷新页面),因为J2EE应用系统是运行在J2EE容器中(Tomcat JBoss),而J2EE容器(如Tomcat)等前端有线程池支持;後端有数据库连接池支持,这些虽然提升了你的系统性能,但是因为你的代码最重的POJO是每次请求创建,这实际是整个系统的性能瓶颈。
用对象池优化你的POJO 服务类;用缓存优化你的数据类。这就是使用Jdon框架带给你性能上的跳跃。
使用Jdon框架后,你的J2EE应用系统性能提升不少,但是访问量还是不断上升,尽管优化了其他该优化的:JVM;J2EE服务器;数据库等,系统性能还是碰到了天花板。
使用EJB的多服务器集群分布式计算特性吧,只要增加服务器就可以提升性能。
这时,你可能用EJB将你的POJO封装起来,经过这样架构升级,如果你不使用Jdon框架,你的表现层(Struts)中原来调用POJO的代码需要修改,这是一个存在相当风险的大手术,你可能要全部重新测试;但是使用了Jdon框架,你做的只是在Jdonframework.xml配置中修改一下即可,整个系统代码无需更改。(其他框架升级到EJB时,需要你的EJB继承原来的接口,且需要EJB配置,不方便)
从你的项目一开始就使用Jdon框架,它带给了你方便的可伸缩的解决方案。也就是说:你的系统在规模很小时运行良好;在规模迅速扩张时,无需更改代码;带给你方便的架构更换。
Jdon框架帮助你实现架构设计的可伸缩性。
相比其他框架,Jdon框架实现了对POJO Service和EJB之间无缝支持。
Jdon框架特点
Jdon框架是根据最新设计思想Ioc/AOP构建的一个源框架(Meta Framework),随着时间推移,它将不断增加入新的设计概念和功能(如MDA或工作流引擎等)。
Jdon框架给你的J2EE应用系统带来完整的高质量解决方案:
Transparency(透明性):框架配置修改维护方便,Jdon框架配置划分三种:基础配
快速J2EE开发工具 JdonFramework开发指南
专业创新来自Jdon.com
置组件、AOP相关配置和应用服务配置,将经常需要修改与应用相关的配置单独出来,可分别修改和拓展。
Scalability(可伸缩性):使用本框架,可以开发出两种系统:真正轻量的Web应用系统或Web+EJB应用系统;无缝同时支持两种服务架构:EJB Session Bean和POJO Service(Web应用),在不改变代码的情况下,可以很方便地将一个Web系统升级到Web+EJB系统。
Performance(良好的性能):Jdon框架提供强大缓存功能,无需编程,在自己的系统中加入Jdon框架后,自动提升了每个应用系统的运行性能,特别是批量查询性能。对Model数据通过缓存拦截器提升性能;对POJO无态服务使用对象池拦截器;也实现了POJO的有态服务拦截器。
High-Availability(高可用性):在J2EE多层分离完全解耦的前提下,提供了数据增删改查(crud)快速开发方式,程序员需要编写的crud代码很少,表现层crud功能实现通常只需要配置就可以实现,No Code。如果持久层开放辅助以其他快速开发工具,可以迅速地提高J2EE开发速度。
Extendable(扩展性):Jdon框架是可伸缩的、动态配置的,应用者可以将自己系统中的通用功能从具体系统中抽象出来,加入Jdon框架,从而逐步形成自己的行业专业开发框架。
Loose coupling(松耦合):使用Jdon框架可以完全解耦J2EE多层之间的耦合,从而实现应用系统稳定的健壮性、方便容易的可维护性。从而也使得J2EE表现层开发和业务层开发可以完全分离、各自独立同时进行,提高了开发效率。
Jdon框架区别于其他同类框架的独特特点:
Lightweight:真正轻量化,代码精简巧妙,是复杂庞大的Spring框架替代品。
Auto Injection:自适应的构造器注射,Jdon框架是基于PicoContainer为其微容器核心,PicoContainer主要是构造器注射,而且它拥有强大的自适应注射;其它类似框架则要在配置文件中逐个明确指定注射对象,配置烦琐。
EJB服务平滑方便支持,Jdon框架支持EJB服务就如同支持一般POJO服务一样,方便直接,无需EJB服务继承特定接口;而其它类似框架(如Spring)则需要所有EJB服务继承特定接口。
Jdon框架的AOP功能是可分解的,通过缓存优化了动态代理实现,提高了运行性能,Jdon框架AOP支持所有遵循继承Aopalliance(如Spring)的拦截器。
Jdon框架目前使用流行的Struts作为其主要表现层框架支持。
Jdon框架与J2EE架构
目前在J2EE架构设计中主要分两大流派:EJB和POJO(普通的Javabeans);以EJB为代表的流派主流架构是:Struts/JSF+EJB或者Struts/JSF+Session Bean+hibernate/JDO;以POJO为代表的轻量流派则是:Struts/JSF+Hibernate/JDO。
Jdon框架对这两种流派都有良好方便的支持,也可以在同一个系统混合这两种架构,所以可以形成:Struts/JSF+Jdon_EJB或者Struts/JSF+Jdon+Hibernate/JDO等架构。
随着新技术诞生,目前它们组件形式无非是EJB或POJO两种,因此Jdon框架可适应这些未来的新技术实现。 快速J2EE开发工具 JdonFramework开发指南
专业创新来自Jdon.com
适合哪些人
Jdon框架主要是面向J2EE程序员,对于程序员要求并不很高,只要具备以下技术背景之一就可以尝试学习使用Jdon框架:
拥有Jsp/Servlet JavaBeans J2EE的Web编程经验的程序员
拥有EJB简单编程的程序员
最好有一些Struts感性认识和少量编程经验。
快速J2EE开发工具 JdonFramework开发指南
专业创新来自Jdon.com
Jdon框架安装说明
在Jdon框架源码包中的dist目录下,有下列几个包:
jdonFramework.jar
Jdon框架核心包
必须需要
aopalliance.jar
AOPAlliance包
必须需要
jdom.jar
读取XML的JDOM包
必须需要
picocontainer-1.1.jar
Picocontainer包
必须需要
commons-pool-1.2.jar
Apache对象池包
必须需要
log4j.jar
Log4j 调试记录跟踪包
可选
log4j.properties
Log4j配置文件
可选
struts.jar …
struts驱动包,支持struts1.2
可选
Jdon框架在JBoss中安装
安装步骤:
1. 确保已经安装J2SE 1.4 以上版本,然后设置操作系统的环境变量 JAVA_HOME=你的J2SE目录
2. 下载JBoss 3.X/JBoss 4.x
3. 安装Jdon框架驱动包:将Jdon框架源码包中的dist目录下除log4j.jar和log4j.properties以外的包拷贝到jboss/server/default/lib目录下。
4. 安装struts驱动包,下载struts 1.2,将jar包拷贝到jboss/server/default/lib。
或者使用Jdon框架例程samples中SimpleJdonFrameworkTest项目的lib目录。将该目录下jar包拷贝到jboss/server/default/lib
对于具体J2EE应用系统,需要配置Jboss的数据库连接池JNDI:
1.配置JBoss的数据库连接:
将数据库驱动包如MYSQL的mysql-connector-java-3.0.14-production-bin.jar或Oracle的class12.jar拷贝到jboss/server/default/lib。
2. 选择JBoss的数据库JNDI定义文件:
在jboss的docs目录下寻找你的数据库的配置文件,如果是MySQL,则是mysql-ds.xml;如果是Oracle;则是oracle-ds.xml。下面以MySQL为例子。
将mysql-ds.xml拷贝到jboss/server/default/deploy目录下。
3. 修改配置数据库定义文件:
打开jboss/server/default/deploy/mysql-ds.xml,如下:


DefaultDS
jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=UTF-8
快速J2EE开发工具 JdonFramework开发指南
专业创新来自Jdon.com

com.mysql.jdbc.Driver
root



4.启动JBoss
打开jboss/server/default/log/server.log 如果没有错误,一切OK,一般可能是数据库连接错误,检查mysql-ds.xml配置,查取相关资料,弄懂每行意义。
至此,可以将基于Jdon框架开发的J2EE应用程序部署到JBoss中。
一般是将*.ear或*.war拷贝到jboss/server/default/deploy目录下即可。
Jdon框架在其他商业服务器中安装
只要将Jdon框架包和struts 1.2包安装到服务器的库目录下即可,或者配置在系统的classpath中即可。如果你的服务器没有log4j包,那么还需要log4j.jar,并将log4j.properties放置在系统classpath中。
Jdon框架在Tomcat中安装
Jdon框架在Tomcat下安装主要问题是log4j问题,下面是安装步骤:
1. 将struts驱动包和Jdon框架包(包括log4j.jar)拷贝到tomcat/ common/lib目录下。
2. 将Jdon框架源码包dist目录下的log4j. properties拷贝到tomcat/common/classes目录。
3. 配置Tomcat中运行log4j的关键是:检查commons-logging.jar 和 log4j.jar 文件在common/lib 目录,struts驱动包中已经包含commons-logging.jar包。
4. 上面步骤都正常了,可以启动Tomcat,但是你会发现tomcat/logs下没有输出记录,因为我们已经使用新的log4j,所以为了使得tomcat运行信息输出文件便于调试,编辑/common/classes下log4j. properties:
将#log4j.appender.R.File=D:/javaserver/jakarta-tomcat-5.0.28/logs/tomcat.log一行前面的#删除,文件目录是绝对路径,更改为你自己的目录和文件。
同时将log4j.rootLogger=INFO, A1一行前面加#注释。 重新启动Tomcat,这时可以从tomcat.log看到输出记录。
5. 配置Jdon框架运行过程输出,在log4j. properties中下面一行:
log4j.logger.com.jdon=DEBUG
该配置将会显示Jdon框架的主要运行信息,如果你要关闭,只要更改如下:
log4j.logger.com.jdon=ERROR
快速J2EE开发工具 JdonFramework开发指南
专业创新来自Jdon.com
基本概念和配置篇
本章节讲述Jdon框架的基本配置和一些基本设计,是使用Jdon框架之前需要了解的章节,通过这些基本概念说明,可以让程序员对Jdon框架有一个大体概念上的轮廓。
本章节对于初学者不是必读,其中很多用法可在后面案例中学习,因此,本章节也可以直接跳过去,进入下一个章节阅读。
可彻底分离的组件管理
Jdon框架可以实现几乎所有组件可配置、可分离的管理,这主要得益于Ioc模式的实现,Jdon框可以说是一个组件(JavaBeans)管理的微容器。
在Jdon框架中,有三种性质的组件(JavaBeans):框架基础组件;AOP拦截器组件和应用服务组件。三种性质的组件都是通过配置文件实现可配置、可管理的,框架应用者替换这三种性质组件的任何一个。
框架基础组件是Jdon框架最基本的组件,是实现框架基本功能的组件,如果框架应用者对Jdon框架提供的功能不满意或有意替换,可以编写自己的基础功能组件替代,从而实现框架的可彻底分离或管理。
应用服务组件是框架应用者针对具体项目设计的组件,如一个新闻系统,用户管理UserDao、新闻管理NewsManager都属于应用服务组件。
AOP拦截器组件主要是指一些应用相关的通用基础功能组件,如缓存组件、对象池组件等。相当于应用服务组件前的过滤器(Filter),在客户端访问应用服务组件之前,必须首先访问的组件功能。
这三种性质组件基本函括了应用系统开发大部分组件,应用服务组件是应用系统相关组件,基本和数据库实现相关,明显特征是一个DAO类;当应用服务组件比较复杂时,我们就可以从中重整Refactoring出一些通用功能,这些功能可以上升为框架基础组件,也可以抽象为AOP拦截器组件,主要取决于它们运行时和应用服务组件的关系。当然这三种性质框架组件之间可以相互引用(以构造方法参数形式),因为它们注册在同一个微容器中。
使用Jdon框架,为应用系统开发者提炼行业框架提供了方便,框架应用者可以在Jdon框架基本功能基础上,添加很多自己行业特征的组件,从而实现了框架再生产,提高应用系统的开发效率。 快速J2EE开发工具 JdonFramework开发指南
专业创新来自Jdon.com
Jdon框架应用第一步
在JdonFramework中,所有组件都是在配置文件中配置的,框架的组件是在container.xml和aspect.xml中配置,应用系统组件是在jdonframework.xml中配置,应用系统组件和框架内部或外部相关组件都是在应用系统启动时自动装载入J2EE应用服务器中,它们可以相互引用(以构造器参数引用,只要自己编写的普通JavaBeans属于构造器注射类型的类就可以),好似是配置在一个配置文件中一样。
因此,组件配置主要有三个配置文件:应用服务组件配置container.xml、AOP拦截器组件aspect.xml和应用服务组件配置jdonframework.xml
初次使用Jdon框架时,需要在应用系统中指定自己定义的配置文件,分两步:
1. 定义自己的jdonframework.xml配置文件,这是必须的步骤。
目前Jdon框架中整合了Struts前台表现层技术,因此可以通过Struts的Plugin实现jdonframework.xml启动,这样做的好处,可以实现Struts多模块开发,一个Struts项目中可能有多个功能模块,每个功能模块涉及从页面表现(struts配置和tiles配置)、模型设计以及后台持久化等横向一系列组件,Jdon框架通过Plugin启动jdonframework.xml可以支持这种多模块开发方式。
Jdon框架已经提供一个Plugin缺省实现子类:com.jdon.strutsutil. InitPlugIn,你可以根据自己要求实现自己的Plugin实现子类。在struts-config.xml(或其它struts模块配置文件如struts-config-admin.xml等等)中配置Plugin实现子类:



InitPlugIn主要实现从struts-config.xml中读取modelmapping-config的值,modelmapping-config的值是你自己定义的jdonframework应用配置文件,文件名不一定是
快速J2EE开发工具 JdonFramework开发指南
专业创新来自Jdon.com
jdonframework.xml,可以任意指定,例如news.jdonframework.xml表示jdonframework.xml在包名news下, com.jdon.app.myframework.xml表示myframework.xml在包名com.jdon.app下。
2. Jdon框架启动时将使用jdonframework.jar中META-INF目录下缺省的container.xml和aspect.xml。如果你需要拓展Jdon框架,如自己开发了一些小零碎组件,如计算公式、报表组件等或者行业软件的一些通用功能,这些组件可能需要启动时就载入,或者希望它们实现可配置可替换,那么就将它们整合入Jdon框架中,有两种加入自己配置的方式(该步骤不是必需的):
第一.定义自己的组件配置文件和拦截器组件配置,文件名必须为mycontainer.xml和myaspect.xml,这两个文件必须放置在系统的classpath路径中,或者必须在你自己的jar包,这个jar包可以和Jdon框架jar包一起部署。
第二.可以在应用系统的web.xml中定义,可以在web.xml中加入自己定义这两种配置:

containerConfigure
/WEB-INF/mycontainer.xml
aspectConfigure
/WEB-INF/myaspect.xml


com.jdon.container.startup.ServletContainerListener

那么你的mycontainer.xml和myaspect.xml必须放置在Web项目的WEB-INF目录下,当然这两个文件名可以自己任意取名。
用户自己定义的配置文件中可以覆盖缺省的container.xml或aspect.xml相应的配置。只要取相同的name值就可以。
应用服务组件配置
jdonframework.xml是应用服务组件配置文件,文件名可自己自由定义,jdonframework.xml中主要是定义Model(模型)和Service(服务)两大要素。
jdonframework.xml最新定义由http://www.jdon.com/jdonframework.dtd规定。
段落是定义应用系统的建模,一个应用系统有哪些详细具体的模型,可由Domain Model分析设计而来。中的详细配置说明可见 数据模型增、删、改、查章节。
段落是定义服务组件的配置,目前有两种主要服务组件配置:EJB和POJO。
EJB服务组件配置如下:




每个ejbService组件有一个全局唯一的名字,如newsManager,有两个必须子定义:该EJB的JNDI名称和其Local或remote接口类。
POJO服务组件配置如下:


快速J2EE开发工具 JdonFramework开发指南
专业创新来自Jdon.com

POJO服务也必须有一个全局唯一名称,如userJdbcDao,以及它的class类定义。如果该POJO构造器有字符串变量,可在这里定义其变量的值,目前Jdon框架只支持构造器字符串变量注射。
如果该POJO服务需要引用其它服务,例如UserPrincipalImp类的构造器如下:
public UserPrincipalImp(UserDao userDao){
this.userDao = userDao;
} ……
UserPrincipalImp构造器需要引用UserDao子类实现,只需在jdonframework.xml中同时配置这两个服务组件即可,Jdon框架会自动配置它们之间的关系:




上面配置中news.container.UserJdbcDao是接口UserDao的子类实现,这样,直接通过userPrincipal这个名称可获得UserPrincipalImp的实例。
基础组件配置说明
container.xml是Jdon框架基础组件配置文件,container.xml中包含的组件将由Jdon框架在启动时向微容器(PicoContainer)中注册,至于这些组件之间的依赖关系由微容器解决,称为Ioc模式。
container.xml内容主要由每行配置组成,每行格式如下:



代表组件com.jdon.model.handler.XmlModelHandler,其名称为modelHandler,如果需要在程序中调用XmlModelHandle实例,只需要以modelHandler为名称从微容器中获取即可。
组件配置也可以带有参数,例如下行:



而LRUCache的类代码如下:
public class LRUCache implements Cache {
public LRUCache(String configFileName) {
PropsUtil propsUtil = new PropsUtil(configFileName);
cache = new UtilCache(propsUtil);
}
……..
}
这样LRUCache 中的configFileName值就是cache.xml,在cache.xml中定义了有关缓存的一些设置参数。目前Jdon框架只支持构造器是纯字符串型,可多个字符串变量,但不能字类型和其它类型混淆在一起作为一个构造器的构造参数。如果需要多个类型作为构造参数,可新建一个包含字符串配置的类,这个类就可和其它类型一起作为一个构造器的构造参
快速J2EE开发工具 JdonFramework开发指南
专业创新来自Jdon.com
数了。
一般在container.xml中的组件是框架基本功能的类,不涉及到具体应用系统。
拦截器组件配置说明
aspect.xml是关于拦截器组件配置,有两个方面:advice(拦截器Interceptor)和pointcut(切入点)两个方面配置,有关AOP的基本概念可见:http://www.jdon.com/AOPdesign/aspectJ.htm。
Jdon AOP的设计目前功能比较简单,不包括标准AOP中的Mixin和Introduction等功能;Pointcut不是针对每个class和方法,而是针对一一系列class,拦截粒度最粗,与许多复杂完整的AOP框架(如AspectJ、Spring)不同的是:Jdon AOP在粒度方面是最粗的,AspectJ最细,Spring中等,如果你需要粒度细腻的AOP功能,还是推荐使用Spring或AspectJ。目前这样设计是主要有两个原因:
每个类在运行时刻都实现动态拦截,在性能上有所损失,这如同职责链模式缺点一样。
在实际应用中,可以通过代理模式Proxy、装饰模式Decorator实现一些细腻度拦截,结合容器的Ioc特性,这两个代理模式使用起来非常方便,运行性能有一定提高。
Jdon AOP主要针对拦截器Interceptor设计,它可以为所有jdonframework.xml中定义的Service提供拦截器;所有的拦截器被放在一个拦截器链InterceptorsChain中。
Jdon AOP并没有为每个目标实例都提供拦截器配置的功能,在JdonAOP中,目标对象是以组为单位,而非每个实例,类似Cache/Pool等这些通用拦截器都是服务于所有目标对象。
JdonAOP拦截器目标对象组有三种:全部目标服务;EJB服务;POJO服务(EJB服务和POJO服务是在JdonFramework.xml中定义的ejbService和pojoService)。从而也决定了Pointcut非常简单。以下是aspect.xml中的配置:
class="com.jdon.aop.interceptor.CacheInterceptor"
pointcut="services" />
其中pointcut有三种配置可选:services ; pojoServices和ejbServices
拦截器配置也可以如组件配置一样,带有constructor参数,以便指定有关拦截器设置的配置文件名。
拦截器的加入不只是通过配置自己的aspect.xml可以加入,也可以通过程序实现,调用WebAppUtil的addInterceptor方法即可,该方法只要执行一次即可。
如何实现自己的拦截器?
以对象池拦截器PoolInterceptor为例,对象池是使用Aapche Commons Pool开源产品,对象池主要是为了提高POJO Service的运行性能,在没有对象池的情形下,POJO Service每次被访问时,要产生一个新的实例,如果并发访问用户量很大,JVM将会频繁创建和销毁大量对象实例,这无疑是耗费性能的。
使用对象池则可以重复使用一个先前以前生成的POJO Service实例,这也是Flyweight模式一个应用。
对象池如何加入到Jdon框架中呢?有两种方式:1.替代原来的POJO Service实例创建方式,属于PojoServiceFactory实现(Spring中TargetSource实现);2.通过拦截器,拦截在原来的PojoServiceFactory实现之前发生作用,同时屏蔽了原来的PojoServiceFactory实现。
快速J2EE开发工具 JdonFramework开发指南
专业创新来自Jdon.com
Jdon框架采取的这一种方式。
首先,为拦截器准备好基础组件。 对象池拦截器有两个:对象池com.jdon.controller.pool.CommonsPoolAdapter和对象池工厂com.jdon.controller.pool.CommonsPoolFactory,这两个实现是安装Apache Pool要求实现的。
第二步,需要确定拦截器的Pointcut范围。前面已经说明,在Jdon框架中有三个Pointcut范围:所有服务、所有EJB服务和所有POJO服务,这种划分目标的粒度很粗糙,我们有时希望为一些服务群指定一个统一的拦截器,例如,我们不想为所有POJO服务提供对象池,想为指定的一些目标服务(如访问量大,且没有状态需要保存的)提供对象池,那么如何实现呢?这实际是如何自由划分我们自己的目标群(或单个目标实例)的问题
我们只要制作一个空接口Poolable,其中无任何方法,只要将我们需要对象池的目标类实现这个接口即可,例如com.jdon.security.web.AbstractUserPrincipal多继承一个接口Poolable,那么AbstractUserPrincipal所有的子类都将被赋予对象池功能,所有子类实例获得是从对象池中借入,然后自动返回。
这种通过编程而不是配置实现的Pointcut可灵活实现单个目标实例拦截或一组目标实例拦截,可由程序员自由指定划分,非常灵活,节省了琐碎的配置,至于Pointcut详细到类方法适配,可在拦截器中代码指定,如缓存com.jdon.aop.interceptor.CacheInterceptor只拦截目标服务类中的getXXXX方法,并且该方法的返回结果类型属于Model子类,为了提高性能,CacheInterceptor将符合条件的所有方法在第一次检查合格后缓存起来,这样,下次无需再次检查,省却每次检查。
第三步,确定拦截器在整个拦截器链条中的位置。这要根据不同拦截器功能决定,对象池拦截器由于是决定目标服务实例产生方式,因此,它应该最后终点,也就是在拦截器链中最后一个执行,aspect.xml中配置拦截器是有先后的:
class="com.jdon.aop.interceptor.CacheInterceptor" pointcut="services" />
class="com.jdon.aop.interceptor.PoolInterceptor" pointcut="pojoServices" />
拦截器链中排序是根据Interceptor的name值排序的,cacheInterceptor第一个字母是c,而poolInterceptor第一个字母是p,按照字母排列顺序,cacheIntercepotr排在poolInterceptor之前,在运行中cacheInterceptor首先运行,在以后增加新拦截器时,要注意将poolInterceptor排在最后一个,name值是可以任意指定的,如为了使PoolInterceptor排在最后一个,可命名为zpoolInterceptor,前面带一个z字母。
第四步,确定拦截器激活行为是在拦截点之前还是之后,或者前后兼顾,这就是advice的三种性质:Before、After或Around,这分别是针对具体拦截点jointcut位置而言。
虽然Jdon框架没有象Spring那样提供具体的BeforeAdvice和AfterReturningAdvice等接口,其实这些都可以有程序员自己直接实现。
Jdon框架的拦截器和Spring等遵循aopalliance的AOP框架继承同一个接口MethodInterceptor,也就是说,一个拦截器在这些不同AOP框架之间可以通用,具体实现方式不同而已。
例如,一个AroundAdvice实现如下代码,它们都只需要完成invoke方法内尔:
public class AroundAdvice implements MethodInterceptor
{
public Object invoke( MethodInvocation invocation) throws Throwable
{
System.out.println("Hello world! (by " + this.getClass().getName() + ")");
快速J2EE开发工具 JdonFramework开发指南
专业创新来自Jdon.com
invocation.proceed();
System.out.println("Goodbye! (by " + this.getClass().getName() + ")");
return null;
}
}
beforeAdvice实现代码如下:
public class BeforeAdvice implements MethodInterceptor
{
public Object invoke( MethodInvocation invocation) throws Throwable
{
System.out.println("Hello world! (by " + this.getClass().getName() + ")");
invocation.proceed();
}
}
由此可以注意到,invocation.proceed()类似一个joincut点,这个方法类似400米接力比赛中的传接力棒,将接力棒传到下一个拦截器中,非常类似与servletFilter中的chain.doFilter(request, response);拦截器一些更详细说明可参考Spring相关文档,如下面网址:http://www.onjava.com/pub/a/onjava/2004/10/20/springaop2.html,和Jdon拦截器原理基本一致。
考察对象池拦截器功能,它实际是一个around advice,在joincut之前需要从对象池借用一个目标服务实例,然后需要返回对象池。com.jdon.aop.interceptor.PoolInterceptor主要核心代码如下:
Pool pool = commonsPoolFactory.getPool(); //获得对象池
Object poa = null;
Object result = null;
try {
poa = pool.acquirePoolable(); //借用一个服务对象
Debug.logVerbose(" borrow a object:" + targetMetaDef.getClassName()
+ " from pool", module);
//set the object that borrowed from pool to MethodInvocation
//so later other Interceptors or MethodInvocation can use it!
proxyMethodInvocation.setThis(poa); //放入invocation,以便供使用
result = invocation.proceed();
} catch (Exception ex) {
Debug.logError(ex, module);
} finally {
if (poa != null) {
pool.releasePoolable(poa); //将服务对象归还对象池
Debug.logVerbose(" realease a object:" + targetMetaDef.getClassName()
+ " to pool", module);
}
}
return result;
经过上述四步考虑,我们基本可以在Jdon框架动态plugin自己的拦截器,关于对象池拦截器有一点需要说明的,首先EJB服务因为有EJB容器提供对象池功能,因此不需要对象池了,在POJO服务中,如果你的POJO服务设计成有状态的,或者你想让其成为单例,就不能使用对象池,只要你这个POJO服务类不继承Poolable,它的获得是通过组件实例方式获得,参考后面“组件实例和服务实例”章节。 快速J2EE开发工具 JdonFramework开发指南
专业创新来自Jdon.com
POJO应用篇
本章节主要讲述如何在应用系统中应用Jdon框架,技术架构主要是基于J2EE的Web结构,使用普通JavaBeans实现服务功能情况下的Jdon框架使用,POJO应用架构适合Struts+Jdon+Hibernate等架构。
组件实例和服务实例
在Jdon框架中, POJO实例分为两种性质:组件实例和服务实例。
组件实例是指那些注册在容器中的普通Javabeans,它们是单例的,也就是你每次获得的组件实例都是同一个实例,也就是说是单态的。
组件POJO获得方法是通过WebAppUtil的getComponentInstance方法,例如在container.xml中有如下组件定义:

在程序中如果要获得ModelManagerImp组件实例的方法是:
ModelManager modelManager =
(ModelManager)WebAppUtil.getComponentInstance(“modelManager”, sc);
组件实例获得的原理实际是直接在微容器中寻找以前注册过的那些POJO,相当于直接从XML配置中直接读取组件实例配置。
服务实例是指在jdonframework.xml中定义的ejbService和pojoService,当然它们也可以组件实例方式获得,但是如果以组件实例方式获得,AOP功能将失效;而以服务实例方式获得的话,在aspect.xml中定义的拦截器功能将激活。
EJB服务一定是通过服务实例方式获得,只有普通JavaBeans(POJO)才可能有这两种方式。
因此,除非特殊需要,一般推荐在应用系统中,通过获得服务实例方式来获得jdonframework.xml中定义的服务实例。
如在jdonframework.xml中有如下服务组件定:



获得服务实例的方法代码如下:
UserDao ud = (UserDao)WebAppUtil.getService(“userJdbcDao”, request);
UserDao是UserJdbcDao的接口。注意,这里必须getService结果必须下塑为接口类型,不能是抽象类或普通类,这也是与getComponentInstance不同所在。
如果你在aspect.xml将pojoServices都配置以对象池Pool拦截器,那么上面代码将是从对象池中获取一个已经事先生成的实例。
如何获得一个POJO实例?
在上面章节说明了服务实例和组件实例的区别,在jdonframework.xml中配置POJO既可以组件实例获得,也可以服务实例获得。
首先,我们需要在jdonframework.xml中定义自己的POJO服务,例如在
快速J2EE开发工具 JdonFramework开发指南
专业创新来自Jdon.com
jdonframework.xml有如下一行定义:

那么在应用程序中需要访问UserJdbcDao 实例有以下两种方式:
第一. 通过WebAppUtil工具类的getService方法获得服务实例,如:
UserDao ud = (UserDao)WebAppUtil.getService(“userJdbcDao”, request);
getService方法每次返回的一个新的服务实例对象,相当于new一对象。如果对象池拦截器被配置,那么这里返回的就是从对象池中借用的一个对象。
第二. 通过WebAppUtil工具类的getComponentInstance方法获得组件实例,这也是获得UserJdbcDao一个实例,与服务实例不同的是,每次获得组件实例是同一个对象,因此,如果这个服务中如果包含上次访问的状态或数据,下次访问必须使用到这些状态和数据,那么就必须使用getComponentInstance方法获得服务实例。
注意,以上方式是假定你获得一个POJO实例,是为了使用它,也就是说,是为了访问它的方法,如访问userJdbcDao的getName方法,就要使用上述方式。
如果你不是为了使用它,而是作为别的POJO服务的输入参数,如构造器的输入参数,那么完全不必通过上述方式,你只要直接使用上述方式获得那个POJO服务的实例就可以,因为容器自动完成它们的匹配。
还有一点要求注意的是:使用getService获得服务实例,必须该服务类有一个接口,这样才能将getService downcasting下塑为其接口,否则只能是一个普通Object,你获得后无法使用它。当然getComponentInstance没有这样限制。
如何编写一个POJO 类?
既然在Jdon框架中获得POJO服务这么方便,那么POJO服务类的编写是否有特殊规定,回答是没有,就是一个普通的Java类,当然如果你需要在这个类引用其他类,最好将其他类作为构造器参数,如A类中引用B类,A类的写法如下:
class A {
private B b;
public A(B b){
this.b = b;
}
….
}
这样,在jdonframework.xml中配置如下两行:


当然,你也可以使用下面一种方式:
class A {
private B b = new B();
….
}
这样就无需配置jdonframework.xml,但是这样做的缺点是不够灵活,万一B类改名或更改后就需要修改A类代码,带来强烈的耦合性,这也是为什么使用Ioc模式的原因。
如果你希望你的POJO服务能够以对象池形式被访问,那么你的类需要implements
快速J2EE开发工具 JdonFramework开发指南
专业创新来自Jdon.com
com.jdon.controller.pool.Poolable
如何获得一个POJO服务的运行结果
在应用系统中,我们不但可以通过上面方式获得一个POJO实例,然后在通过代码调用其方法,获得其运行结果,例如写入代码:
userJdbcDao.getName();
可以获得getName方法的运行结果。
除此之外,Jdon框架还可以直接获得POJO服务的运行结果,只要你告诉它POJO类名、需要调用的方法名和相关方法参数类型和值,借用Java Method Relection机制,Jdon框架可以直接获得运行结果。
实现这个功能,只要和接口com.jdon.controller.service.Service打交道即可:
public interface Service {
public Object execute(String name,
MethodMetaArgs methodMetaArgs,
HttpServletRequest request) throws Exception;
public Object execute(TargetMetaDef targetMetaDef,
MethodMetaArgs methodMetaArgs,
HttpServletRequest request) throws Exception;
}
Service提供了两种获得某个POJO服务运行结果的方法。一个是以Jdonframework.xml中配置的POJO服务名称为主要参数,这是经常使用的一个情况。
MethodMetaArgs是包含调用的方法名、方法参数类型和方法参数值。
这种调用方式适合于POJO服务配置式调用,也就是说,通过编写自己的XML配置文件也可以实现如同写代码一样的服务调用和运行。 快速J2EE开发工具 JdonFramework开发指南
专业创新来自Jdon.com
EJB应用篇
Jdon框架不只可以支持普通JavaBeans,也就是POJO结构,也支持EJB架构,使用EJB的好处是能够获得可伸缩的、分布式的强大计算性能,当然EJB的开发需要借助JBuilder之类商业开发工具的图形开发功能才能方便快速实现。
使用Jdon框架可以开发出基于Struts+Jdon+EJB的标准J2EE架构系统,Jdon框架在标准的J2EE架构中所处位置如下图的红色标记:
从上图底部向上看,是J2EE架构从抽象到具体技术的演变划分,J2EE是一个中间件系统,那么中间层包括哪些部分呢?主要区分Web层和EJB层,Web层主要是完成用户交互操作功能,Jdon框架主要部分就位于这个Web层最後端,直接和EJB层打交道;同时Jdon框架有一部分JdonDAO运行在EJB容器中。
如何获得一个EJB服务实例?
如何在表现层如Struts中获得一个EJB实例呢?
如同获得POJO实例一样,也是通过WebAppUtil工具类的getService方法获得,如果在jdonframework.xml中有如下配置:




那么,通过下面代码就可以访问NewsManagerLocal: 快速J2EE开发工具 JdonFramework开发指南
专业创新来自Jdon.com
NewsManagerLocal nm = (NewsManagerLocal)WebAppUtil.getService(“newsManager”, request);
这样就访问nm这个EJB对象的方法了。
如何编写一个EJB类?
Jdon框架对于一个EJB类的编写没有任何约束和规定。
但是,如果你原来使用POJO服务实现你的服务层,想无缝迁移到EJB服务,那么此时你的Session Bean需要继承implements 原来POJO服务的接口,同时在jdonframework.xml的ejbService加入interface配置,指定原来POJO服务接口类,这样才能保证原来代码中通过getService方法获得服务实例的调用代码无需改变:





如何获得EJB服务运行结果?
使用方式和获得POJO服务一样。
这种方式运行原理简要如下:当知道一个EJB/POJO的接口,通过Proxy.newProxyInstance生成一个动态代理实例(InvocationHandler的实现子类)即可,以后对EJB/POJO的调用,实际由这个动态代理实例的invoke自动激活,从而使用Method Reflection实现EJB/POJO的调用。
快速J2EE开发工具 JdonFramework开发指南
专业创新来自Jdon.com
增删改查(crud)快速开发
Jdon框架提供了基于Struts的快速开发,可以快速开发出数据的增删改查功能(crud),以及批量查询等功能。
本章是与Jdon框架其它功能分离的,你可以只使用Jdon框架的组件管理和AOP功能,而不必使用基于Struts的快速开发功能,随着新的表现层技术出现,Jdon框架将推出基于的技术如JSF的快速开发框架。
关于为什么使用jdon框架的crud功能,除了在开发速度上有极大提高以外,在设计上也有很多优点:
边界类,控制类和业务接口的关系:
http://www.jdon.com/jive/article.jsp?forum=91&thread=21245
J2EE中几种业务代理模式的实现和比较:
http://www.jdon.com/artichect/businessproxy.htm
struts基础
Jdon框架的crud功能是基于struts实现,主要工作是表现层的配置,那么是否需要程序员熟悉struts呢?其实完全不必。
但是,需要了解struts的一些基础:struts主要有一个配置文件,在Web目录的WEB-INF下struts-config.xml,其中主要有两部分配置:ActionForm和Action。
ActionForm配置:主要是配置你设计的ActionForm子类;例如:


type="com.jdon.framework.samples.jpetstore.presentation.form.AccountForm"/>

…..

Action配置:相当于一个servlet,也需要在struts-config.xm中action-mappings l配置,例如:

type="com.jdon.strutsutil.ModelViewAction" scope="request">



action中配置简要说明:
name是ActionForm名称,是前面ActionForm名称;
path是该action在浏览器中调用的url名称,使用http://xxxx/web名称/shop/newAccountForm.do就可以调用这个action,在path值后面加一个.do即可,.do是在web.xml中配置的:

快速J2EE开发工具 JdonFramework开发指南
专业创新来自Jdon.com
action
*.do

你可以改变*.do配置,改为*.shtml,那么上面action(Servlet)调用变成http://xxxx/web名称/shop/newAccountForm.shtml
type是你继承struts的Action实现子类:如com.jdon.strutsutil.ModelViewAction是Jdon框架的实现子类。
Scope是值你的ActionForm生存周期,一般有request或session,相当于Jsp中useBean中的scope,表示ActionForm实例有效范围,request表示用户发出一个请求周期;session表示是从用户登陆到退出为止,这个ActionForm实例一直存在,你可以引用同一个实例。

这表示Action执行完成后,需要推出的Jsp页面,name值是你在Action执行子类中定义的名称;path是你的jsp页面相对路径。
所以,编写一个传统意义上的Jsp页面,在struts变成下面几步:
1. 编写一个ActionForm实现子类
2. 编写一个Action实现子类,完成其中方法execute内容。
3. 配置struts-config.xml
4. 使用struts标签编写Jsp页面,这时的Jsp页面和传统的Jsp相比:已经没有Java代码,完全Html语法。
由以上可见,struts在实现设计优化目的同时,实际增加编程复杂性,Jdon框架试图保证struts优点的前提下,简化标程复杂性,crud的功能对上述步骤优化如下:
1. ActionForm是Model代码复制;
2. 无需编写Action实现子类;
3. 模板化配置struts-config.xml,参考别的配置,稍微修改即可。
4. 简化Jsp页面编写数量;四个功能只需编写一个Jsp。
strtus学习资源:http://www.jdon.com/idea/strutsapp/04005.htm
crud设计起源
一般情况下,一个数据的增加修改的Struts实现流程如下:
(1)一般情况下,一个系统的操作用户(以下简称用户)新增或修改数据,首先要推送给他一个Jsp页面,如果是新增页面,就是一个空白表单的Jsp页面;如果是修改页面,则先到数据库中查询获得原来的数据,然后推出一个有数据表单的Jsp页面,用户才能在原来的数据上修改或编辑。
由于在MVC模式中,Jsp页面只是一个页面输出,或者说不能有任何Java功能实现,因此上面修改页面推出前需要查询数据库这个需要Java实现的功能不能在Jsp页面中实现,只能在Jsp页面前加一个Action,这样,修改页面的推出流程变为不是直接调用Jsp页面,而是:action.do ---> Jsp页面,首先调用Action;然后才由Action推出Jsp页面。
这个Action实现我们称为ViewAction,专门用于控制输出Jsp界面,新增Jsp页面的推出前我们也加上这个ViewAction,这样无论是新增Jsp页面或修改Jsp页面,都是由ViewAction推出,那么到底是推出新增Jsp页面还是修改Jsp页面呢?
关键是ViewAction的输入参数Action的值,根据Action的值来判断是新增还是修改。如果设置Action值如为空或为create值(如http://xxx/viewAction.do?action=create),则直接输出新增性质的JSP页面;而Action值如为edit(如http://xxx/viewAction.do?action=edit),
快速J2EE开发工具 JdonFramework开发指南
专业创新来自Jdon.com
则是要求输出进行编辑页面,根据ID查询数据库获得存在的数据,然后输出编辑修改性质的JSP页面。
当然,在ViewAction中还有一些具体参数的检查,如果是编辑,则关键字ID不能为空,因为后台要根据此ID为主键,查询数据库其相应的记录,如果数据库未查询到该主键的记录,则需要显示无此记录等信息。
(2)创建有关该数据的JSP页面,既用于新增页面,也用于修改页面。将该Jsp页面作为ViewAction的输出页面。该Jsp页面结构如下:



…….

当用户填写完该Jsp页面中的表单数据将提交给一个新Action子类实现:专门用于接受表单数据并保存持久化它们。
(3) 创建SaveAction,它用来接受提交表单的数据,不同于ViewAction专门用于输出Jsp表单页面,该Action专门用于接受Jsp表单页面提交的数据。
SaveAction中主要是调用业务层Service实现数据持久化操作,调用Service之前,需要将表单的数据ActionForm转为一个对象(DTO对象),然后作为方法参数传送给Service具体方法, Service处理完成后,返回结果对象,SaveAction还需要检查Service是否操作成功等。
总结:一个数据新增删除修改流程需要创建两个Action,一个Jsp页面;当然爱Struts 1.2中已经通过DispatchAction解决了需要创建两个Action问题,只需要一个Action,但是使用Jdon框架的Struts配置,一般情况下都不需要Action,只要配置一下JdonFramework.xml如下即可:











在以后章节将详细说明上面配置的语法。
crud原则:一个数据表对应一个模型Model;一个Model对应四个功能:新增;删除;修改和查询。
crud设计重要角色
这里首先介绍一下Jdon框架在上面思路上延伸抽象设计的思路:
一个数据的增删改查流程有上面总结的流程组成:
ViewAction -?? 表单Jsp 页面 -?? SaveAction --?? 结果Jsp页面
这样我们就可以将这个流程固定抽象到Jdon框架中,不同的是在其中流转的数据,既
快速J2EE开发工具 JdonFramework开发指南
专业创新来自Jdon.com
然抽象出共性了,那么如何处理异性呢?
将这个流程中的异性使用继承实现,如下图:
ViewAction和SaveAction中有关具体每个数据有不同的操作,都委托给ModelHandler实现,用户使用框架时,只要继承实现ModelHandler,填补异性方法就可以。
以上是Jdon框架对crud流程的功能类设计规划,那么对于数据的抽象是如何设计的?
Jdon框架以com.jdon.controller.model.Model抽象代表数据模型,在简单系统中,Model又作为数据传送对象(DTO),在表现层和服务层以及持久层之间来回传递,由于表现层和持久层框架产品不同,Model可能根据不同的表现层框架产品进行映射,如果表现层使用Struts,那么显然ActionForm实际就是Model在表现层的映射或代名词,或者说是共同数据载体。
crud规则一:Model和ModelForm
Model是域模型,是采取领域模型分析法从系统需求分析中获得的,反映了应用系统的本质,Model是一个简单的POJO,属于数据类型的POJO,区别于POJO服务。从传统意义上理解,Model设计相当数据表设计,在传统的过程化编程中,一个数据库信息系统设计之前我们总是从数据表设计开始,而现在我们提倡是从域模型提炼开始。
Jdon框架对模型对象建立有两个要求,继承com.jdon.controller.model.Model,每个Model有一个主键。
每个Model需要一个主键
每个Model必须有一个主键,就象每个数据表设计都有主键一样,如果你的Model没有主键怎么办?那么使用强制给它一个Object ID,这可以由一个专门序列器产生。
上述Account这个Model中主键是username,这是依据Account对应的数据表account确定的,account数据表设计中,username是主键。
Model配置
Model在jdonframework.xml中配置,如下:

…..

key的值就是指定Model的主键,这里是username,class是Account的类,这样就定义了一个Model。
Model配置除定义Model自身属性以外,还需要定义了子属性:actionForm和handler,这两个定义在下面章节描述。 快速J2EE开发工具 JdonFramework开发指南
专业创新来自Jdon.com
ModelForm是Model的映射
Jdon框架通过映射设计,保证表现层、Jdon框架、服务层和持久层之间能够解耦独立,相互可插拔、脱离或组合。例如下图展示了表现层使用Struts;服务层/持久层使用了EJB的映射设计:
Web层 Strut应用框架ActionForm(代表JSP界面表单)
Model和ModelForm映射是通过相同字段拷贝实现的,也就是这两个类之间有相同的字段属性,那么字段属性的值可以在他们之间拷贝转移。例如Model Account的代码:
public class Account extends Model {
private String username;
private String password;
private String email;
private String firstName;
private String lastName;
……
}
那么其ModelForm的类代码如下:
public class AccountForm extends ModelForm {
private String username;
private String password;
private String email;
private String firstName;
private String lastName;
……
}
这样两者代码保证它们之间的映射,这是使用Jdon框架的规则之一。
ModelForm配置
ModelForm就是ActionForm,因此只需要在struts-config.xml的中定义ActionForm就可以,如下: 快速J2EE开发工具 JdonFramework开发指南
专业创新来自Jdon.com


type="com.jdon.framework.samples.jpetstore.presentation.form.AccountForm"/>


注意ModelForm的名字是accountFrom,因为Model和ModelForm是映射对应关系,我们需要告诉Jdon框架这种对应关系。
那么,拓展前面的Model配置,在jdonframework.xml配置如下:


……

ModelViewAction原理
Jdon框架中的com.jdon.strutsutil.ModelViewAction是实现前面ViewAction原理的类,主要是实现在推出让用户新增数据或编辑数据的Jsp页面的之前的准备工作。它主要是做Jsp页面推出前的准备工作,然后推出不同的Jsp页面。
ModelViewAction根据调用参数action的值,判断用户发出的是新增或编辑命令。
Action有两个规定值create 或edit,也就是说,只有三种URL调用形式:
新增URL:http://xxxx/XXXModelViewAction.do?action=create
新增URL:http://xxxx/XXXModelViewAction.do
编辑URL:http://xxxx/XXXModelViewAction.do?action=edit
如果用户在浏览器发出这三种URL调用,将激活ModelViewAction,ModelViewAction将根据action的值分别推出用于创建性或编辑性的页面。下面分别详细说明这两种流程:
1.在创建性Jsp页面推出之前,要做一些准备工作,由于Jsp页面中表单数据(
之间数据)是由ModelForm(ActionForm)概括的,所以,创建性Jsp页面推出前的准备工作主要是ModelForm的初始化创建,ModelForm是Struts的ActionForm继承者,ModelForm是抽象页面表单数据,它是Model在表现层的映射影子。
ModelForm的初始化创建很重要,它决定了推出给用户创建性页面的内容,需要程序员介入度很高, Jdon框架提供有几种初始化ModelForm方法:
首先检查ModelHanlder的initForm方法,检查用户有无自己实现初始化ModelForm实现;如果无,则用Struts的自动创建ActionForm(ModelForm),这时的ModelForm实例是空对象,这时还有另外一种方式提供用户初始化ModelForm实例的方法:调用Modelhandler的initModel方法,将该方法返回的Model对象拷贝到ModelForm中,这符合Model和ModelForm相互映射的关系。下面章节将描述ModelHandler的initForm和initModel方法有何区别。
2.推出编辑性页面之前的准备工作主要是:根据主键查询从服务层获得一个已经存在的数据模型,简单地说:根据主键从数据库查询已经存在的一个记录,这样,推出的Jsp页面中包括数据的表单,用户可修改编辑。
这个工作涉及两部分:ModelForm创建;然后从服务层获得一个有数据的Model,将Model数据拷贝到ModelForm实例中。 快速J2EE开发工具 JdonFramework开发指南
专业创新来自Jdon.com
crud规则二:配置ModelViewAction
根据不同域模型,我们有相应的不同的配置,例如:有一个Model为A,那么为了推出A新增或修改或删除的页面,我们需要在struts-config.xml配置如下:




这是一个标准的Action配置,A的ActionForm为aActionForm,新增修改删除A的Jsp页面为a.jsp,如上面配置,这样,如果用户从浏览器网址如下调用:
http://xxxxx/aAction.do?action=create
这样调用将推出a.jsp用于新增;
http://xxxxx/aAction.do?action=edit
这样调用将推出a.jsp用于修改删除。
再举例,如果Model为B,B的ActionForm为bActionForm,Jsp页面为b.jsp,那么Struts-config.xml只要如下配置:




如果Model为c,那么struts-config.xml配置如下:




总结上面配置,配置具备模板化编程的特点,拷贝粘贴然后修改,修改且有规律。
ModelSaveAction原理
ModelSaveAction是前面的SaveAction实现,它是真正的核心Action实现,专门将用户新增或修改后的数据提交到服务层,实现持久化如保存到数据库中。
ModelSaveAction主要是委托ModelHandler实现数据提交和服务激活,这个过程比较单一有规律,用户可介入程度低,因此可以使用配置来代替代码实现。
我们看看ModelSaveAction是如何将Jsp页面提交的数据传递给后台,ModelSaveAction从ModelForm(ActionForm)中获得用户提交的数据,将其拷贝到相应的Model实例,然后将Model对象打包到EventModel对象中,将EventModel作为方法参数,调用ModelHandler的serviceAction方法,ModelHandler的serviceAction则是调用服务层相应Service的对应的create/update/delete方法,所以ModelSaveAction主要工作委托给ModelHandler实现,我们将在下面研究重要的ModelHandler类。
crud规则三:配置ModelSaveAction
ModeSaveAction和ModelViewAction类似,根据Model不同,在struts-config.xml中配置如下: 快速J2EE开发工具 JdonFramework开发指南
专业创新来自Jdon.com




如果Model为B,那么struts-config.xml的配置是:




关于ModeSaveAction使用是在Jsp页面中赋值给action:


….

该Jsp页面是新增或修改性质的页面,例如可以是前面介绍的ModelViewAction推出的a.jsp或b.jsp。
至此,ModelViewAction -?? jsp ??ModelSaveAction实现了首尾衔接,实现了一个crud操作流程。
通过配置使用Jdon框架中的ModelViewAction和ModelSaveAction,程序员避免了象开发普通Struts应用系统那样建立至少一个Action子类。
在这个流程中,需要和服务层服务交互,这需要由程序员根据具体程序定制,下面介绍程序员可介入定制的重要类ModelHandler。
规则四:配置/代码实现ModelHandler类
ModelHandler是前面ModelViewAction和ModelSaveAction的委托者,程序员需要定制实现的部分由继承ModelHandler实现。
目前,Jdon框架提供两种Modelhandler的实现:编码和配置。
ModelHandler几个重要方法是总结了表现层和服务层三种交互操作,如果你的应用不属于这三种交互操作,那么可能就无法使用Jdon框架的crud功能,直接使用Struts实现。
ModelHandler有几个重要方法是 :initForm方法、initModel方法、findModelByKey方法和serviceAction方法。前面两个方法是和页面初始化有关,页面初始化有可能和服务层交互访问;findModelByKey是根据主键查询数据库存在的记录,这是和编辑页面相关,编辑之前需要先查询,这个方法也需要和服务层交互访问;serviceAction则是将用户对数据的新增修改删除决定通知服务层进一步处理,这是和服务层主要交互访问。
这几种主要交互操作详细描述如下:
规则四.一:页面初始化
推出页面需要初始化,推出新增性和编辑性页面的初始化工作是不一样的。我们知道,Jsp页面的初始化其实是ActionForm(ModelForm)的初始化,我们分别从新增和编辑页面的初始化两条线路说明。
如果你需要给ModelForm中属性字段简单初始化一些常量,那么很显然这些在ModelForm的构造方法中实现,如:
public class AccountForm extends ModelForm{ 快速J2EE开发工具 JdonFramework开发指南
专业创新来自Jdon.com
private List languages;
public AccountForm() {
languages = new ArrayList();
languages.add("english");
languages .add("japanese");
}
}
上面代码需要给languages加入一些初始值,这样,推出页面时,用户可以进行多项选择(使用html的select/options multibox等实现)。
如果初始化这些属性值需要从数据库中获得,那么就需要和服务层实现交互了,那么就要考虑继承实现ModelHandler了。
ModelForm创建可由通过服务层后台交互实现,这种交互实现也有两种方式:
1. 通过代码实现:ModelHandler提供的initForm方法, initForm程序员只要继承ModelHandler,在initForm方法中调用服务层服务,获得初始化值,再赋值入ModelForm,如下代码:
public class NewsHandler extends ModelHandler {
public ModelForm initForm(HttpServletRequest request) throws Exception {
Debug.logVerbose("enter iniForm .", module);
NewsActionForm nf = new NewsActionForm(); //创建ModelForm
NewsManagerLocal newsManager = (NewsManagerLocal)
WebAppUtil.getEJBService("newsManager", request);
PageIterator pi = newsManager.getNewsTypePage(0, 50);
Collection newsTypes = new ArrayList();
while (pi.hasNext()) {
String id = (String) pi.next();
NewsType newsType = newsManager.getNewsType(id);
newsTypes.add(newsType);
}
nf.setNewsTypes(newsTypes); //将新闻类型列表赋值到新闻这个ModelForm
return nf;
}
……
}
还需要配置一下,告诉Jdon框架你的ModelHandler实现:



以上是代码实现直接初始化ModelForm,还有一种初始化ModelForm,思路是:初始化Model,然后将Model值拷贝到Model中,这是通过ModelHandler的initModel实现的。不过initModel和initForm只能选择其一实现,initModel还可以通过配置实现:
2. 通过配置实现,配置主要是通过initModel方法实现,在jdonframework.xml中配置如下:



快速J2EE开发工具 JdonFramework开发指南
专业创新来自Jdon.com
…..



上面配置initMethod方法是访问accountService的initAccount方法,也就是说,ModelForm的初始化推给Model初始化,而Model初始化则由accountService的initAccount实现,程序员必须实现的accountService的initAccount方法。如:
public class AccountServiceImp implements AccountService{
private ProductManager productManager;
public AccountServiceImp(ProductManager productManager){
this.productManager = productManager;
}
public Account initAccount(){
Account account = new Account();
account.setCategories(productManager.getCategoryList());
return account;
}
….
这两种页面初始化选用依据,是根据用来初始化的数据来自何处?是来自后台或服务层,那么选择第二个方案;如果来自request相关的例如HttpSession,则选用第一个方案。
规则四.二:编辑之前的数据查询
前面是实现页面初始化,对于推出的编辑性页面,查询获得已经存在的数据也属于一种初始化工作,这是通过ModelHandler的findModelByKey实现的,这也有两种实现方法:
1. 代码实现,继承ModelHandler实现,如果你的Model查询不是从数据库获得,如是从HttpSession获得的,那么你要继承实现自己的方法findModelByKey。代码实现后,记住配置jdonframework.xml:



一般情况下,是通过服务层查询数据库获得的,那么涉及到服务层交互,可以采取配置实现。
2. 配置实现,在jdonframework.xml配置如下:




…..



那么,要求你的服务层服务有相应的方法如下:
public class AccountServiceImp implements AccountService{
private ProductManager productManager;
public AccountServiceImp(ProductManager productManager){
this.productManager = productManager;
快速J2EE开发工具 JdonFramework开发指南
专业创新来自Jdon.com
}
public Account getAccount(String username) {
Account account = null;
try{
account = accountDao.getAccount(username);
account.setCategories(productManager.getCategoryList());
//other business logic
}catch(DaoException daoe){
Debug.logError(" Dao error : " + daoe, module);
}
return account;
}
….
}
注意,在此配置中,对服务层的服务方法有一个规定:getAccount的方法参数类型必须是String型的,getAccount返回必须是Model类型,上面Account类是继承Model类的。
规则四.三:数据提交保存
用户在Jsp页面填写或修改数据后,将实现保存提交,或实现删除提交,这时Jsp页面的action是提交给ModelSaveAction的,ModelSaveAction委托ModelHandler的serviceAction实现结果保存或删除。serviceAction有两种实现:
1.代码实现,这部分工作主要是实现传递工作,代码比较有规律,一般使用配置实现,如果你需要在提交之前实现一些其他特殊实现,那么使用代码实现如下:
public void serviceAction(EventModel em, HttpServletRequest request) throws
java.lang.Exception {
try { //从Httpsession获得User,保存到News中
User user = (User) ContainerUtil.getUserModelAfterLogin(request);
if (user != null) {
News news = (News) em.getModel();
news.setUser(user);
}
NewsManagerLocal newsManager = (NewsManagerLocal) WebAppUtil.
getEJBService("newsManager", request);
switch (em.getActionType()) {
case Event.CREATE:
newsManager.createNews(em); //递交service创建方法
break;
case Event.EDIT:
newsManager.updateNews(em);//递交service修改方法
break;
case Event.DELETE:
newsManager.deleteNews(em);//递交service删除方法
break;
}
} 快速J2EE开发工具 JdonFramework开发指南
专业创新来自Jdon.com
catch (Exception ex) {
throw new Exception(" serviceAction Error:" + ex);
}
}
还需要配置jdonframework.xml你的ModelHandler代码实现:



2.配置实现,如果在递交服务处理之前没有特殊工作实现,可使用配置,如下:






…..



那么,要求你的Service有insertAccount 和 updateAccount或deleteAccount方法,而且这三个方法参数必须是EventModel类型,当然,方法名是可以根据你的service具体方法不同,在配置中更改,例如你的Service有insert方法,那么配置就是:

方法名可以变化,任意取,但是方法参数必须是EventModel类型的。如:
public interface AccountService {
Account initAccount(); //初始化
Account getAccount(String username); //查询获得存在的Model
void insertAccount(EventModel em); //新增方法
void updateAccount(EventModel em); //修改方法
}
当然,如果你使用代码实现,就没有这些对Service类的编程规定了。
这里稍微谈一下上面配置实现代码功能原理:上面配置在Jdon框架中是自动调用com.jdon.model.handler.XmlModelhandler作为代码实现,也就是说,XmlModelhandler根据上面配置自动生成前面的代码,属于一种简单的代码自动生成。
注意:有时我们可能需要代码配置混合实现,ModelHandler几个方法中只代码实现一个方法,其他都可以配置实现,这也是可以的,只是这时你的代码不是继承ModelHandler,而是XmlModelHandler了,同时配置要写明你的子类实现:






…..



快速J2EE开发工具 JdonFramework开发指南
专业创新来自Jdon.com
struts-config.xml配置技巧
crud功能实现标准配置
参考前面的ModelViewAction和ModelSaveAction配置章节,一个数据Model的标准crud功能实现无需编写任何Action,只要有ActionForm(还是Model的影子),通过struts-config.xml配置就可以实现:
1.推出创建性或编辑性页面:




约束:
(1)type必须是com.jdon.strutsutil.ModelViewAction
(2)forward的name值必须是create或edit
2.接受数据提交数据并递交Service服务层处理。




约束:
type必须是com.jdon.strutsutil.ModelSaveAction
forward的name值必须是success或failure
crud功能实现分离配置
crud可以通过两行action配置ModelViewAction ModelSaveAction合并完成,也可以将新增和修改分开配置,例如用户注册功能,用户注册(用户创建)和用户资料修改属于不同的需要分离开的两个过程,用户注册功能是针对新用户,或者说是非注册用户;而用户资料修改是针对注册用户,两者服务对象角色是不一样的,因此,不能象其他Model那样将crud合并在一起。
1. 推出创建性页面的配置:
type="com.jdon.strutsutil.ModelViewAction" scope="session">


将forward的edit值为空,没有这一行。
2. 接受创建数据并并递交Service服务层处理
type="com.jdon.strutsutil.ModelSaveAction" scope="session"
validate="true" input="/account/NewAccountForm.jsp">



3. 推出编辑性页面的配置
专业创新来自Jdon.com
type="com.jdon.strutsutil.ModelViewAction" scope="session">


将forward的create值为空,没有这一行,不过调用/shop/editAccountForm.shtml形式还是必须要有参数的:/shop/editAccountForm.shtml?action=edit&username=XXX
4. 接受修改后的数据并并递交Service服务层处理
与第2条类似,不同的是path和jsp因为编辑页面不一样而不同:
type="com.jdon.strutsutil.ModelSaveAction" scope="session"
validate="true" input="/account/EditAccountForm.jsp">



单纯输出Jsp页面
如果有时只是为了输出一个Jsp页面而做一个Action比较麻烦,Jdon框架实现了一个缺省的转发Action,可以简单重用在很多这样场合。只要在struts-config.xml配置如下:
name="XXX" scope="request" validate="false">


上述配置有三点要求如下:
type必须是com.jdon.strutsutil.ForwardAction
forward的name值是forward
Jdon框架的出错信息
在服务层一旦实现数据库操作出错,Jdon框架通过EventModel的setErrors方法向表现层传递出错信息,因此,只要在你的服务层Service实现方法的出错代码调用该方法即可,如:
public void insertOrder(EventModel em) {
Order order = (Order)em.getModel();
try{
orderDao.insertOrder(order);
}catch(Exception daoe){
Debug.logError(" Dao error : " + daoe, module);
em.setErrors("db.error");
}
}
当存储orderDao调用出错,将db.error保存到EventModel中,而db.error是struts的Application.properties中定义的,,需要在你的应用系统中定义,Jdon框架应用到的信息如下:
id.required = you must input id
id.notfound = sorry, not found
db.error=sorry, database operation failure!
快速J2EE开发工具 JdonFramework开发指南
专业创新来自Jdon.com
system.error=sorry, the operation failure cause of the system errors.
maxLengthExceeded=The maximum upload length has been exceeded by the client.
notImage=this is not Image.
主要有id.required id.notfound db.error等几个,需要在J2EE应用系统中定义。
快速J2EE开发工具 JdonFramework开发指南
专业创新来自Jdon.com
批量分页查询快速开发
原理
批量查询由于是频繁操作,对系统性能设计要求很高,而且批量查询几乎存在每个数据库信息系统,因此,这个功能具有可重用性,Jdon框架根据Jive等传统成熟系统批量查询设计原理,总结多个应用系统,在不破坏多层情况下,抽象出批量查询子框架。
批量查询设计两个宗旨:
1. 尽量减少数据库访问,减少数据库负载和I/O性能损耗。
2. 由于J2EE是一个多层结构,尽量减少在多个层次之间传送的数据量。减少传送损耗。
因此,批量查询设计将涉及到两个层面:表现层和持久层,Jdon框架提供了持久层下JDBC查询操作模板(JdbcDao),这个JdbcDao可以被服务层不同服务类型(POJO Service或Session Bean)调用。
根据批量查询设计宗旨,有下面实现要点:
1. 使用缓存减少数据库访问
2. 持久层不能将满足查询条件的每页所有完整数据记录传送到表现层,试想一下,如果一条数据记录有1000个字节,那么一页显示20条,那么就有2万个字节传送,目前很多批量查询设计都是这样做,存在浪费内存和性能消耗大的隐患。
3. 缓存越靠近用户界面端,性能越好,因此,持久层如果只传送满足查询条件的数据记录主键(一个字段)集合,那么表现层可以优先从缓存中根据主键获得该数据完整数据,查询越频繁使用,缓存击中率越高,各方面消耗就越小。
根据以上设计原则,Jdon框架的批量查询设计方案如下:
对于持久层:
• 获取满足查询条件的所有数据表记录总个数。
• 获取当前页面所有数据记录主键集合(ID集合)。
• 将上述两种数据打包成一个对象(PageIterator)传送到前台
对于表现层:
• 获得后台满足查询条件的PageIterator对象。
• 遍历PageIterator对象中的主键集合,根据主键再查询后台获取完整数据对象(Model),先查询缓冲,如果没有,再访问数据库。
• 将获取的完整数据对象(Model)封装成一个集合,打包成ModelListForm对象。
• Jsp页面再展开ModelListForm对象中Model数据集合,逐条显示出来。
批量查询的主要示意图: 快速J2EE开发工具 JdonFramework开发指南
专业创新来自Jdon.com
图中解释:后台将满足查询条件的数据记录的主键集合(ID集合)包装成PageIterator对象,然后由服务层返回到表现层(通过ModelListAction调用服务EJBService或POJO Service的getPageIterator方法),表现层的ModelListAction再遍历这个ID集合,根据每个ID查询获得完整Model数据,再打包到ModelListForm对象中。
批量查询框架基于一个前提是:每个Model有一个主键,就象每个数据表设计都有主键一样,如果你的Model没有主键怎么办?那么使用强制给它一个Object ID,这可以由一个专门序列器产生。这与前面crud实现的前提是一致的。
重要对象PageIterator
从前面批量查询原理中看出,PageIterator实际是一个在多层之间穿梭的数据载体,前后台关系只是靠PageIterator来实现消息交互。
因此,无论后台持久层使用什么技术(JDBC、实体Bean、Hibernate或iBatis),只要能提供一个查询结果对象PageIterator给前台就可以。
考虑到持久层技术发展极快,Jdon框架并没有在持久层的缺省实现,但提供了JDBC实现的帮助API(见包:com.jdon.model.query),具体可参考Jdon框架源码包Samples目录下JdonNews的news.ejb.dao. JdbcDao。这个JdbcDao虽然为EJB Session Bean调用,也可以为普通的POJO服务调用,JdbcDao开发也有模板化,一般是一个Model完成三个方法,具体可见“在JBuilder下开发Struts+Jdon+EJB”章节的“批量查询持久层实现”。
在Jdon-Jpetstore中,批量查询持久层实现是使用iBatis,然后在服务层将iBatis的PaginatedList类转换成Jdon框架的PageIterator即可。
com.jdon.controller.model.PageIterator类代码如下:
public class PageIterator implements Iterator, Serializable {
public final static Object[] EMPTY = new Object[0];
private int allCount = 0; //符合查询条件的所有Model总数
private Object[] keys; //符合查询条件的当前页面的Model的ID集合
private int currentIndex = -1; //遍历当前页面时的记录指针
private Object nextElement = null; //下一个记录
private int start; //当前页面在所有符合查询条件中开始点
private boolean hasNext; //是否有下一页
//以上两个参数是有关页为单位的信息 快速J2EE开发工具 JdonFramework开发指南
专业创新来自Jdon.com
/**
* 完整的构造器
* allCount 是PageIterator重要参数
*/
public PageIterator(int allCount, Object[] keys, int start, boolean hasNext) {
this.allCount = allCount;
this.keys = keys;
this.start = start;
this.hasNext = hasNext;
}
/**
* 当前页面的构造器
* allCount 是PageIterator重要参数,也需要赋值,
* 因为其值对于每个页面是一样的,所以可使用本构造方法构造后,再从缓存
* 中读取allCount, 使用setAllCount延迟赋值
*/
public PageIterator(Object[] keys, int start, boolean hasNext) {
this.allCount = 0;
this.keys = keys;
this.start = start;
this.hasNext = hasNext;
}
/**
* 空构造器
* 使用本构造器可防止没有符合查询条件时,Jsp页面报出nullException讨厌错误。
*/
public PageIterator() {
this.allCount = 0;
this.keys = EMPTY;
this.start = 0;
this.hasNext = false;
}
…..
}
持久层参考API:PageIteratorSolver
Jdon框架提供了PageIterator创建的持久层创建API:com.jdon.model.query.PageIteratorSolver,这是一个基于SQL的JDBC实现,当然你完全可以其他持久层技术实现创建PageIterator。
使用Jdon框架的PageIteratorSolver提供JDBC模板操作,在查询功能上几乎无需写JDBC语句。使用PageIteratorSolver可以书写很少代码就实现一种Model的批量分页查询功能,PageIteratorSolver可以被Session Bean调用,或者作为普通POJO被调用,缺省情况下PageIteratorSolver不会配置在Jdon框架容器中。 快速J2EE开发工具 JdonFramework开发指南
专业创新来自Jdon.com
PageIteratorSolver创建有两种方式:
第一种:直接new,PageIteratorSolver提供两种构造器,具体参考其API,一种构造器可以更换缓存器,另外一种是缺省的;这两种构造器都需要DataSource作为构造参数。如下:
ServiceLocator sl = new ServiceLocator(); //Jdon框架中的ServiceLocator
DataSource dataSource = (DataSource) sl.getDataSource("java:/NewsDS");
PageIteratorSolver pageIteratorSolverOfType = new PageIteratorSolver(dataSource);
因为PageIteratorSolver中包含缓存器,因此,你可以为每个Model建立一个对应的PageIteratorSolver对象,这样,该Model更新时,只要刷新这个Model相关的缓存,具体可见下面缓存清除方法。
第二种,将PageIteratorSolver作为POJO Service配置在jdonframework,xml,然后通过组件实例方式获得(如果它的调用者是以组件服务方式获得)。
创建PageIterator
首先,根据批量查询原理,首先要查询符合查询条件的记录总数和当前页面的所有Model的ID集合,这是通过PageIterator的创建实现。
PageIteratorSolver 中重要方法getPageIterator是为了创建一个PageIterator对象,需要的输入参数相当比较复杂,这里详细说明一下:
getPageIterator方法如下:
public PageIterator getPageIterator(String sqlqueryAllCount,
String sqlquery, String queryParam, int start, int count) throws Exception
注意:PageIteratorSolver的getDatas与getPageIterator实则一样,getPageIterator名称或参数都显得易懂正规一些。
public PageIterator getDatas(String queryParam, String sqlqueryAllCount,
String sqlquery, int start,
int count) throws Exception
首先从String sqlqueryAllCount参数解释:
sqlqueryAllCount参数其实是一个sql语句,该sql语句用来实现查询满足条件的所有记录的总数。例如查询某个表myTable的sql语句如下:
String sqlqueryAllCount = "select count(1) from myTable ";
当然这是一个标准sql语句,后面可写入where等查询条件
sqlquery参数则是关键的查询语句,主要实现批量查询中查询符合条件的数据记录(Model)的主键ID集合。例如表myTable的主键是id,则
String sqlquery = select id from T_ myTable where categoryId = ?
queryParam参数则是和sqlquery参数相关,如果sqlquery语句中有查询条件“?”,如上句categoryId = ?中?的值就是queryParam参数,这样sqlquery和queryParam联合起来形成一个查询语句。如果没有查询条件,那么queryParam就赋值为null或""。
注意,如果查询条件有多个“?”,那么就需要将这些queryParam打包成一个Collection,然后调用方法:
public PageIterator getPageIterator(String sqlqueryAllCount,
String sqlquery, Collection queryParams, int start, int count) throws Exception
这个方法与前面的getDatas区别就是方法参数由String queryParam变成集合 queryParams,queryParams是多个查询条件“?”的集合,注意,查询条件目前Jdon框架支
快速J2EE开发工具 JdonFramework开发指南
专业创新来自Jdon.com
持常用的几种查询参数类型:String Integer Float 或 Long,例如,如果你的查询条件如下:
String sqlquery = select id from T_ myTable where categoryId = ? and name = ?
这里有两个查询参数,那么将这两个查询参数打包到数组中即可,注意顺序和问号的顺序是一样,如果问号对应的变量categoryIdValue和nameValue,则如下:
Collection queryParams = new ArrayList();
queryParams.add(categoryIdValue);
queryParams.add(nameValue);
参数start和count则是有表现层传入的,start表示当前页面是从所有符合查询条件记录中第几个开始的,而count则是从start开始查询多少条记录个数,也就是一页显示的记录数目。
单个Model查询
前面已经获得PageIterator对象,Jdon框架会在ModelListAction中遍历这个PageIterator对象中ID集合,然后根据ID先从缓存中获取完整Model数据,如果没有则从数据库获得,那么我们在持久层还需要提供单个Model查询的实现。
Jdon框架已经内置查询模板,无需使用者自己实现JDBC语句操作,直接使用模板即可,以JdonNews中Model :NewsType查询一个实例为例子:
首先确定sql语句写法,如下:
String GET_TYPE = "select * from T_NEWS_TYPE where typeid = ?";
其中typeid参数值由外界传入,因此写方法如下:
public NewsType getNewsType(String Id) throws Exception {
String GET_TYPE = "select * from T_NEWS_TYPE where typeid = ?";
List queryParams = new ArrayList();
queryParams.add(Id); //Id是GET_TYPE sql语句中的?的值
…..
}
上述代码准备了JDBC模板查询两个参数:GET_TYPE和queryParams,
调用PageIteratorSolver中的两个Model查询方法:
//适合查询返回结果是单个字段,如:
// select name from user where id=?
public Object querySingleObject(Collection queryParams, String sqlquery) throws Exception ;
//适合查询返回结果是多个字段,而且有多项记录
//例如:select id, name, password from user where id = ?
public List queryMultiObject(Collection queryParams, String sqlquery) throws Exception ;
这两个方法适合不同的查询语句,当你的查询sql语句返回不只一条数据记录,而且每条数据记录不只是一个字段,而是多个字段,使用queryMultiObject,queryMultiObject经常使用。下面是获得一个Model查询的完整写法:
public NewsType getNewsType(String Id) throws Exception {
String GET_TYPE = "select * from T_NEWS_TYPE where typeid = ?";
List queryParams = new ArrayList();
queryParams.add(Id);
NewsType ret = null;
try { 快速J2EE开发工具 JdonFramework开发指南
专业创新来自Jdon.com
List list = pageIteratorSolverOfType.queryMultiObject(queryParams, GET_TYPE);
Iterator iter = list.iterator();
if (iter.hasNext()) {//遍历查询结果,根据你的判断,决定这里使用if或while
Map map = (Map)iter.next(); //List中每个对象是一个Map
ret = new NewsType();
ret.setTypeName((String)map.get("typename")); //根据字段名称从Map中获取其相应值
ret.setTypeId(Id);
}
}
catch (Exception se) {
throw new Exception("SQLException: " + se.getMessage());
}
return ret;
}
缓存清除
由于PageIteratorSolver内置了缓存,缓存两种情况:
1. 符合查询条件的所有记录总数,这个总数第一次使用后将被缓存,不必每次都执行select count的数据操作,这是很耗费数据性能的。
2. 当前页面中所有Model的ID集合,在没有新的Model被新增或删除情况下,这个ID集合几乎是不变的,因此其结果在第一次被使用后将被缓存。
但是,某个Model发生新增或删除情况下,我们要主要清除这些缓存,否则新增或删除的Model将不会出现在Model列表中,PageIteratorSolver缓存设计前提是Model的新增和删除不会很频繁,至少没有查询频繁。
PageIteratorSolver的clearCache()提供主动缓存清除。
我们需要在Model的新增方法或删除方法中,在这些方法成功后,主动调用PageIteratorSolver的clearCache方法。
因为Model的新增或删除方法是由程序员自己实现,如使用CMP或Hibernate等实现,因此需要注意加入clearCache方法。
我们建议PageIteratorSolver创建依据每个Model有一个对应的PageIteratorSolver对象,这样,这个Model变动只会清除它的有关缓存;如果所有Model共用一个PageIteratorSolver对象,一旦一个Model变动将引起所有缓存清除,降低缓存效益。
其他形式PageIterator创建
上面是使用Jdon框架的JDBC模板实现PageIterator创建,你有可能使用其他技术实现持久层,这里提供两种PageIterator创建的参考代码:
例如,productDao是封装了别的持久层技术(iBatis或Hibernate)。要求两步就可以完成PageIterator创建:
第一步. productDao返回符合条件的当前页面ID集合:
List list = productDao.getProductIDsListByCategory(categoryId, start, count);
第二步. productDao返回符合条件的总数:
int allCount = productDao.getProductIDsListByCategoryCount(categoryId);
创建PageIterator代码如下:
int currentCount = start + list.size();//计算到当前页面已经显示记录总数 快速J2EE开发工具 JdonFramework开发指南
专业创新来自Jdon.com
PageIterator pageIterator = new PageIterator(allCount, list.toArray(), start,
(currentCount < allCount)?true:false);
以上代码是在服务层实现,一旦服务层能够返回一个PageIterator实例,就可以按照下面表现层实现完成批量分页查询功能。
另外一个PageIterator创建实现,假设所有数据ID或数据模型包装在一个List中,从List中创建PageIterator的代码如下:
//计算未显示记录个数
int offset = itemList.size() - start;
int pageCount = (count < offset)?count:offset;
//生成当前页面的List
List pageList = new ArrayList(pageCount);
//从start开始遍历,遍历次数是一页显示个数
//将当前页面要显示的数据取出来放在pageList中
for(int i=start; i< pageCount + start;i++){
pageList.add(itemList.get(i));
}
int allCount = itemList.size();
int currentCount = start + pageCount;
PageIterator pageIterator = new PageIterator(allCount, pageList.toArray(), start,
(currentCount < allCount)?true:false);
表现层实现:
前面章节主要谈论了批量查询的持久层实现,Jdon框架的批量查询主要在表现层封装了主要设计原理,让我们看看表现层Struts的实现步骤:
ModelListForm
这里以查询商品类别(Category)下所有商品(Product)列表为需求,演示批量的查询的开发步骤。
使用Jdon框架实现批量查询时,无需使用代码创建ModelForm(ActionForm),Jdon框架提供一个批量查询的缺省实现:com.jdon.strutsutil.ModelListForm
只要在struts-config.xml中配置这个ActionForm就可以:

习惯地,我们将这个ActionForm命名为:
model名称+ListForm
例如,查询商品列表是显示一个商品Prodcut数据,那么这个ActionForm的名称就是productListForm
ModelListForm是一个普通的JavaBeans,主要属性如下:
public class ModelListForm extends ActionForm{
private int allCount = 0; //符合查询条件的所有记录总数
private int start = 0; //当前页面的开始
private int count = 20; //当前页面的可显示的记录数
private boolean hasNextPage = false; //是否有下一页 快速J2EE开发工具 JdonFramework开发指南
专业创新来自Jdon.com
/**
* 父Model
*/
private Model oneModel = null;
/**
* 批量显示的Model集合
* 这是本类主要的属性
*/
private Collection list = new ArrayList();
……
}
抽象类ModelListAction
com.jdon.strutsutil.ModelListAction.ModelListAction是一个struts的标准Action,它主要实现如下功能:
1. 从服务层获得符合查询条件PageIterator,这是需要通过getPageIterator方法实现;
2. 展开遍历PageIterator,根据ID,首先从缓存中查询是否有其Model(完整数据记录),如无,则调用服务层从持久层数据库获得,这是需要通过findModelByKey方法实现。
3. 将获得的Model集合封装在ModelListForm的list字段中。
所以,ModelListAction实际是将ID集合换算成Model集合,然后交给ModelListForm,Jsp页面的显示只和ModelListForm有关。
public abstract class ModelListAction extends Action {
private ModelManager modelManager;
//struts action的缺省execute方法
public ActionForward execute(ActionMapping actionMapping,
ActionForm actionForm,
HttpServletRequest request,
HttpServletResponse response) throws Exception {
……
//从服务层获得符合查询条件的PageIterator
PageIterator pageIterator = getPageIterator(request, start, count);
if (pageIterator == null) {
throw new Exception(
"getPageIterator‘s result is null, check your ModelListAction subclass");
}
//获得ModelListForm实例
ModelListForm listForm = getModelListForm(actionMapping, actionForm,
request, pageIterator);
//赋值页面起始数等值到ModelListForm
listForm.setStart(start);
listForm.setCount(count);
快速J2EE开发工具 JdonFramework开发指南
专业创新来自Jdon.com
listForm.setAllCount(pageIterator.getAllCount());
listForm.setHasNextPage(pageIterator.isNextPageAvailable());
//根据pageIterator获得Model集合
Collection c = getModelList(request, pageIterator);
Debug.logVerbose(" listForm ‘s property: getList size is " + c.size(), module);
//将Model集合赋值到ModelListForm的list字段
listForm.setList(c);
//设置其他Model到ModelListForm,以便jsp页面能显示其他Model
listForm.setOneModel(setupOneModel(request));
//程序员自己定义的优化ModelListForm其他动作,供实现者自己优化。
customizeListForm(actionMapping, actionForm, request, listForm);
……
}
ModelListAction是一个抽象类,它必须有一个实现子类需要有两个方法必须实现:
1. 获得服务层的Pageiterator,也就是getPageIterator方法
2. 根据key或ID获得单个Model的方法,findModelByKey方法。
其他可选实现的方法有:
1. 是否激活缓存方法 protected boolean isEnableCache() 有时,批量查询实现可能不需要缓存,获得每个Model都一定要执行findModelByKey方法。通过覆盖isEnableCache方法实现。
2. 批量查询的Jsp页面,可能不只是需要显示某一个Model的很多实例列表;还可能需要其他类型Model实例显示,可以覆盖方法:
protected Model setupOneModel(HttpServletRequest request)
3. 自己再优化加工ModelListForm方法,覆盖方法:
public void customizeListForm(ActionMapping actionMapping,
ActionForm actionForm,
HttpServletRequest request,
ModelListForm modelListForm ) throws Exception
通过以上方法覆盖实现,基本使用ModelListAction可以实现批量查询的各种复杂功能实现,如果你觉得还不行,那么干脆自己模仿ModelListAction自己实现一个标准的struts的Action子类,只要ActionForm还是ModelListForm,页面显示还是可以使用Jdon框架的页面标签库。
编写ModelListAction子类
com.jdon.strutsutil.ModelListAction.ModelListAction有两个方法需要实现:
第一.继承getPageIterator方法
public PageIterator getPageIterator(HttpServletRequest request, int start, int count)
这是从服务层获得满足查询条件的当前页面所有Model的ID(主键)集合,当前页面是通过start 和 count两个变量说明,前者表示从第几个开始;后者表示开始后需要显示多少个Model。
以查询商品为例子,ProductListAction继承ModelListAction,详细方法内容如下:
public class ProductListAction extends ModelListAction {
public PageIterator getPageIterator(HttpServletRequest request, int start,
快速J2EE开发工具 JdonFramework开发指南
专业创新来自Jdon.com
int count) {
//获得一个服务
ProductManager productManager = (ProductManager) WebAppUtil.getService(
"productManager", request);
//从查询参数中获得categoryId
String categoryId = request.getParameter("categoryId");
//调用服务的方法
return productManager.getProductListByCategory(categoryId);
}
public Model findModelByKey(HttpServletRequest request, Object key) {
……

}
所以,关键在于服务层的服务需要有个返回参数类型是方法,这是对服务层服务方法的约束要求,这个服务方法如何实现在后面章节介绍。
第二. 继承findModelByKey方法
这个方法主要也是从服务层获得一个Model实例,ProductListAction的该方法实现如下:
public Model findModelByKey(HttpServletRequest request, Object key) {
//获得一个服务
ProductManager productManager = (ProductManager) WebAppUtil.getService(
"productManager", request);
return productManager. getProduct ((String)key);
}
非常需要注意的是:需要搞清楚这里获得Model实例是哪个?
是商品类别Category 还是商品Product?
这主要取决于你的批量查询显示的是什么,如果希望显示的是商品Product列表,当前这个案例是需要查询某个商品类别Category下的所有商品Product列表,那么这里的Model类型就是Product。
搞清楚Model类型是Product后,那么我们可能对findModelByKey方法参数Object类型的key有些疑问,其实这个key就是com.jdon.controller.model. PageIterator中重要属性keys(满足查询条件的ID集合)中的一个元素,这里根据key查询获得一个完整Model,所以♂知道,PageIterator中封装的不是普通ID,而是以后需要根据这些ID能够获得唯一一个Model,所以key其实是Model主键,也是数据表的主键。
以上述案例为例:
productManager. getProduct ((String)key);
这是将key类型Object强制转换为String类型,因为PageIterator中keys(ID集合)都是String类型。所以, key的类型其实是由你后台创建PageIterator时决定的。
第三其他方法:
以上面例子为例,商品批量查询除了显示多个商品Product信息外,还需要显示商品目录Category,也就是说在一个页面中,不但显示多个商品,也要显示商品目录的名称,比如商品目录名是“电器”,其下具体商品列表很多。
在一个页面中装入一个以上Model有两种方法:
1. 配置另外一个需要装入Model的ActionForm,例如Category对应的ActionForm是CategoryForm,只要在struts-config.xml中配置CategoryForm的scope为session,同时注意CategoryForm在操作流程中要预先赋值。 快速J2EE开发工具 JdonFramework开发指南
专业创新来自Jdon.com
2. 由于批量查询的ActionForm是ModelListForm,我们可以向这个ModelListForm加入一个Model,实现ModelListAction的customizeListForm方法, 在这个方法中,实现将Category加入的语法::
public void customizeListForm(ActionMapping actionMapping,
ActionForm actionForm,
HttpServletRequest request,
ModelListForm modelListForm ) throws Exception{
ModelListForm listForm = (ModelListForm) actionForm;
ProductManager productManager = (ProductManager) WebAppUtil.getService(
"productManager", request);
String categoryId = request.getParameter("categoryId");
Category category = productManager.getCategory(categoryId);
listForm.setOneModel(category);
}
如果加入的布置一个Model,那么可以使用DynamicModel将那些Model装载进来,然后在Jsp页面再一个个取出来。
struts-config.xml配置
批量查询与jdonframework.xml配置无关,也就是说jdonframework.xml中没有与批量查询实现相关的配置,简化了工作,这点与crud实现相反,crud更多的是jdonframework.xml配置(当然都少不了struts-config.xml配置),crud缺省情况下除了Model和ModelForm以外可以没有表现层编码,而批量查询主要是表现层编码,由于查询条件多种多样,只有靠更多编码才能实现查询的灵活性。
上例子中,商品查询的ModelListForm配置如同一般ActionForm一样配置:


……

ProductListAction配置如同一般Action一样:

type="com.jdon.framework.samples.jpetstore.presentation.action.ProductListAction"
name="productListForm" scope="request"
validate="false" >


……

Jsp MultiPages标签
剩余最后工作就是Jsp标签使用,通过使用struts的logic:iterator将前面配置的productListForm中的Model集合遍历出来。遍历标签语法如下:



快速J2EE开发工具 JdonFramework开发指南
专业创新来自Jdon.com

上述语法将逐个输出每行记录,如果配合Html的table语法,输出效果如下:
批量查询输出还有一个重要功能:页面显示,如上图的“前页”和“后页”显示,这是使用Jdon框架的标签库实现的。
只要在Jsp页面中你希望显示“前页”和“后页”部位贴入下面代码即可:
<%@ taglib uri="/WEB-INF/MultiPages.tld" prefix="MultiPages" %>
…..

Prev

Next

这是一个MultiPages标签,注意标签使用方法前提:
1. 在Jsp页面头部需要声明该标签:
<%@ taglib uri="/WEB-INF/MultiPages.tld" prefix="MultiPages" %>
2. 在web.xml中有声明解释该标签库文件所在位置:

/WEB-INF/MultiPages.tld
/WEB-INF/MultiPages.tld

这表示Jsp中的uri定义/WEB-INF/MultiPages.tld实际是执行本地文件/WEB-INF/ MultiPages.tld,那么在你的Web项目的WEB-INF下必须要有MultiPages.tld文件,可以从Jdon框架源码包目录src\META-INF\tlds下将MultiPages.tld拷贝过去即可,有些开发工具如JBuilder在配置了框架功能后,会自动在建立Web项目时拷贝这些标签库的。
下面说明一下批量查询的MultiPages标签使用方法:

其中actionFormName是你在struts-config.xml中配置的ModelListForm名称,也就是这段配置中的name值:

MultiPages中的page是指当前页面是通过哪个action调用产生的,这样,在显示多页其它页面也可以调用这个action,这里是/userListAction.do,很显然,这个/userListAction.do是你的struts-config.xml中的action配置中的path值,如下:
scope="request">


上述配置中的type值com.jdon.framework.test.web.UserListAction是你的ModelListAction实现子类。如果你的查询是条件的,也就是说/userListAction.do后面有查询参数,那么你也需要写在MultiPages后面,如:
快速J2EE开发工具 JdonFramework开发指南
专业创新来自Jdon.com
paramId="catId" paramName="catId">
MultiPages标签的其它配置很简单,无需变化,如下:
Prev

Next
MultiPages:prev是显示“前页”的标签,Prev是显示“Prev”,你可以使用图标或汉字来替代Prev。
是自动显示 1 2 3 …多个数字。目前只提供数字显示。
MultiPages:next和MultiPages:prev类似,显示“后页”字样。
MultiPages:prev和 MultiPages:next还有另外一种写法,如下:

使用了name属性,这样个语法的好处是:当前页面如果没有超过一页,将没有”Prev”或”Next”字样出现,只有超过一个页面时,才会有相应的字样出现,显示智能化。
快速J2EE开发工具 JdonFramework开发指南
专业创新来自Jdon.com
开发一个简单Jdon应用系统
开发环境:JBuilder /eclipse+ JBoss(演示版本是JBuilder 2005和JBoss 3.2X)。
以下以一个简单需求说明Struts+Jdon+EJB/POJO的开发,源码见Jdon框架源码包中的SimpleJdonFrameworkTest/JdonFrameworkTest项目,更复杂一些的案例可见源码包中的另外一个JdonNews,简单需求如下:
实现User模型的数据新增、修改、删除和批量查询。
User模型有两个字段userId和name。
在JBuilder配置JdonFramework为IDE驱动包
如果你使用JBuilder开发工具,按下列步骤:
将JdonFramework导入项目库,或者成为整个配置库。如图:
同时,配置Framework配置,如下图: 快速J2EE开发工具 JdonFramework开发指南
专业创新来自Jdon.com
该配置是为了激活项目Web开发支持JdonFramework的标签库taglib,使用JdonFramework开发应用系统时,自动支持多页批量查询和树形结构显示,这两种功能需要在Jsp页面导入特定的标签库,该设置就是为实现此功能。
注意:由于JBuilder的部分BUG,最好将JdonFramework.jar的MEAT-INF/tlds目录下的MultiPages.tld等文件解压出,形成单独文件,在这里导入这些单独文件。
配置WEB模块时,需选中Web框架,如下图:
如果你使用的是Eclipse,在将JdomFramework.jar作为一般的JAR导入项目。
建模
开始开发程序, 建立Model实现UserTest
快速J2EE开发工具 JdonFramework开发指南
专业创新来自Jdon.com
建立Model对应的ModelForm:UserActionForm是继承ModelForm,而ModelForm实际也是继承Struts的ActionForm。
将UserTest中方法和字段拷贝到UserActionForm,原则上这两者内容是一致的,JdonFramework将自动在这两个对象之间拷贝相同方法的值,但是它们服务的对象不一样,UserTest是为了中间层或后台业务层服务的;而UserActionForm是为了界面服务的,两者分离是为了使得我们的业务层和界面分离,这样,当有新的可替代Struts界面框架出现,我们可以不修改业务层代码下更换界面框架。
业务层开发
Jdon框架支持Web应用和EJB应用,下面两种服务实现任意选一种。
POJO服务实现
POJO服务只要编写一个TestService接口和一个实现TestServicePOJOImp即可,在实现子类中调用持久层jdbcDao.
EJB服务实现
这里使用EJB实现业务层功能,建立一个无态Session Beans
为TestEJB,主要负责UserTest的数据库增删改查等功能,这部分开发不在JdonFramework规定之类,而要遵循EJB相关规定。这部分开发细节可参见有关JBuilder开发EJB教程。
建立一个实体Bean名为User:首先需要将JBuilder配置成能够直接访问数据库,我们使用MySQL数据库,配置后,重新启动JBuilder。
配置JBoss中的数据源,数据源JNDI名称是DefaultDS。在代码中调用JBoss的JNDI,JBoss有特定规定,应该是java:/DefaultDS。
创建CMP时有两种方式:一种直接创建新的CMP,当该J2EE部署时,将自动创建CMP对应的数据表,或者先创建数据表,由数据表导入CMP。本演示采取后者方式。
完成Session Bean TestEJB的新增、修改和删除方法,下面完成查询和批量查询方法,查询方法建议使用DAO+JDBC实现。
持久层:增删改查crud实现
持久层的增删改查功能可使用Jdon框架的Jdbc模板实现:
新增操作
public void insert(UserTest userTest) throws Exception{
String sql = "INSERT INTO testuser (userId , name) "+
"VALUES (?, ?)";
List queryParams = new ArrayList();
queryParams.add(userTest.getUserId());
queryParams.add(userTest.getName());
jdbcTemp.operate(queryParams,sql);
clearCacheOfItem();
} 快速J2EE开发工具 JdonFramework开发指南
专业创新来自Jdon.com
修改操作:
public void update(UserTest userTest) throws Exception{
String sql = "update testuser set name=? where userId=?";
List queryParams = new ArrayList();
queryParams.add(userTest.getName());
queryParams.add(userTest.getUserId());
jdbcTemp.operate(queryParams,sql);
clearCacheOfItem();
}
删除操作:
public void delete(UserTest userTest) throws Exception{
String sql = "delete from testuser where userId=?";
List queryParams = new ArrayList();
queryParams.add(userTest.getUserId());
jdbcTemp.operate(queryParams,sql);
clearCacheOfItem();
}
查询操作:
public UserTest getUser(String Id) {
String GET_FIELD = "select * from testuser where userId = ?";
List queryParams = new ArrayList();
queryParams.add(Id);
UserTest ret = null;
try {
List list = pageIteratorSolverOfUser.queryMultiObject(queryParams,
GET_FIELD);
Iterator iter = list.iterator();
if (iter.hasNext()) {
Map map = (Map) iter.next();
ret = new UserTest();
ret.setName((String) map.get("name"));
ret.setUserId((String) map.get("userId"));
}
} catch (Exception se) {
}
return ret;
}
持久层:批量查询实现
这里是为了实现批量查询,如果不需要批量查询,可跳过此步:
持久层需要向前台提供数据库等持久层数据的查询和获得,一般可通过Jdon框架的简单JDBC模板实现,我们创建一个JdbcDao类。 快速J2EE开发工具 JdonFramework开发指南
专业创新来自Jdon.com
PageIterator对象创建
首先为每个Model引入分页对象;
//通过JNDI获得Datasource
dataSource = (DataSource) sl.getDataSource(JNDINames.DATASOURCE);
//框架使用 为每个Model建立一个分页对象。
pageIteratorSolverOfUser = new PageIteratorSolver(dataSource);
三个方法实现
JdbcDao有三个缺省方法必须才能完成从数据库中获得所要求的数据,这三个方法分别是:
User getUsers(String Id); //查询获得单个Model实例
PageIterator getUsers(int start, int count); //查询某页面的所有Model的ID集合
public void clearCacheOfUser() ; //清除某个Model的缓存。
上述三个方法中,只有一个方法需要编写代码,其它可委托JdonFramework提供的工具API方法完成。
第一个方法完成: 编写获得单个Usertest的数据库查询方法:从现成的示例拷贝如下:
public UserTest getUser(String Id) {
String GET_FIELD = "select * from testuser where userId = ?";
List queryParams = new ArrayList();
queryParams.add(Id);
UserTest ret = null;
try {
List list = pageIteratorSolverOfUser.queryMultiObject(queryParams,
GET_FIELD);
Iterator iter = list.iterator();
if (iter.hasNext()) {
Map map = (Map) iter.next();
ret = new UserTest();
ret.setName((String) map.get("name"));
ret.setUserId((String) map.get("userId"));
}
} catch (Exception se) {
}
return ret;
}
第二个方法实现:编写分页查询SQL实现方法,直接使用框架内部的方法如下:
String GET_ALL_ITEMS_ALLCOUNT = "select count(1) from testuser ";
String GET_ALL_ITEMS = "select userId from testuser ";
return pageIteratorSolverOfUser.getDatas("", GET_ALL_ITEMS_ALLCOUNT,
GET_ALL_ITEMS, start, count);
注意getDatas的方法参数使用,参考批量查询章节
第三个方法实现.:编写缓存清除方法:该方法是在该Model被新增或删除等情况下调用。 这样,当前台页面新增新的数据后,批量查询能够立即显示新的数据,否则将是缓存
快速J2EE开发工具 JdonFramework开发指南
专业创新来自Jdon.com
中老的数据集合,无法立即更新。
public void clearCacheOfUser() {
pageIteratorSolverOfUser.clearCache();
}
JdbcDAO模板化编程到此结束。 下一步,使用EJB的Session Bean包装这个JdbcDao,如果服务层不是使用EJB实现,直接通过一般的POJO服务包装JdbcDao。
JdonFramework.xml配置
根据http://www.jdon.com/ jdonframework.dtd导出XML:
增删改查crud配置
















…..

注意Handler配置有两种:缺省是上述配置,如果情景复杂,需要代码完成,则编写一个Hamdler类继承ModelHandler实现即可,然后在上述handler段配置如下:

POJO服务配置实现






上面定义了一个POJO服务:com.jdon.framework.test.service.TestServicePOJOImp,因为TestServicePOJOImp中使用了JdbcDAO,这里配置了com.jdon.framework.test.dao.JdbcDAO
快速J2EE开发工具 JdonFramework开发指南
专业创新来自Jdon.com
实现,如果你使用其他持久层技术,可以在此更换另外一个JdbcDao。
这里的jdbcDAO需要数据库连接,数据库连接参数不是在应用程序中配置,而是使用J2EE服务器已经配置成功的JNDI名称,Tomcat中配置JNDI数据库连接池可见:http://www.7880.com/Info/Article-37f05fa0.html;JBoss的JND配置见本站相关文章。
java:/DefaultDS是JBoss的数据库连接池JNDI写法,如果你使用其他服务器,参考他们的JNDI要求写法。
如果服务配置中存在ejbService,如下:





那么可以在ejbService和pojoService之间更换,将pojoService和ejbService的name值对换一下即可, 将更换为,将表现层:增删改查crud配置
crud功能无需编制代码,代码功能已经在上节的jdonframework.xml中配置完成,这里只要配置struts的页面流程struts-config.xml配置和编写Jsp两步即可。
(1) 按照前面章节规则二:配置ModelViewAction介绍的。实现新增或编辑视图输出的Action:




上述配置中,需要修改的跟Model名称相关的配置。
(2)创建user.jsp页面。
在user.jsp中要增加一行关键语句:
用来标识新增 修改等动作。
(3) 配置userSaveAction实现数据真正操作。
type="com.jdon.strutsutil.ModelSaveAction">



(4) 创建数据操作结果页面result.jsp, 上述配置修改不大,需要修改的跟Model名称相关的配置。
表现层:批量查询实现
crud功能只需配置和Jsp实现就可以,而批量查询表现层需要进简单编码,然后再需要
快速J2EE开发工具 JdonFramework开发指南
专业创新来自Jdon.com
配置和Jsp完成。还有一个区别是:前者无需编码,替代的是需要配置jdonframework.xml;而后者因为编码实现;无需配置jdonframework.xml了。
编码:UserListAction
UserListAction继承ModelListAction实现,按照前面批量查询章节,这里完成两个方法:getPageIterator和findModelByKey:
getPageIterator方法如下:
public PageIterator getPageIterator(HttpServletRequest request, int start,
int count) {
PageIterator pageIterator = null;
try {
TestService testService = (TestService) WebAppUtil.getService("testService", request);
pageIterator = testService.getAllUsers(start, count);
} catch (Exception ex) {
logger.error(ex);
}
return pageIterator;
}
findModelByKey方法:
public Model findModelByKey(HttpServletRequest request, Object key) {
Model model = null;
try {
TestService testService = (TestService) WebAppUtil.getService("testService", request);
model = testService.getUser( (String) key);
} catch (Exception ex) {
logger.error(ex);
}
return model;
}
代码中:
TestService testService = (TestService) WebAppUtil.getService("testService", request);
其中testService是jdonframework.xml配置中 中名称。
Struts-config.xml配置
(1)配置一个批量查询的ActionForm: 首先在struts-config.xml创建一个用于批量分页查询的ActionForm,固定推荐名称为listForm。

(2)配置UserListAction,按照普通Struts 的action配置



(4)编写userList.jsp,用于分页显示查询结果。
在userList.jsp中使用到分页的标签库:
<%@ taglib uri="/WEB-INF/MultiPages.tld" prefix="MultiPages" %>
快速J2EE开发工具 JdonFramework开发指南
专业创新来自Jdon.com

前页

后页

我们将userList.jsp作为本演示首页,将新增 修改和删除等操作的执行集中在这个页面实现,使用JavaScript来实现。
基本全部完成。
配置启动Jdon框架
(1)配置jdonframework.xml在struts-config.xml中。



(2)将jdonframework.xml打包到你的Web项目。
否则,后台控制台会出现:
[InitPlugIn] looking up a config: jdonframework.xml
[InitPlugIn] cannot locate the config:: jdonframework.xml
打包部署
通过JBuilder的new创建,建立一个ear应用系统。
注意,将Web项目中去除任何包,也就是说WEB-INF/lib下没有任何包,Struts和JdonFramework包都放置在JBoss的server/default/lib下。
如果出现与tld相关的错误, 如:
File "/WEB-INF/MultiPages.tld" not found
解决:在web.xml中应该有下面两行tld配置:

/WEB-INF/MultiPages.tld
/WEB-INF/MultiPages.tld


/WEB-INF/TreeView.tld
/WEB-INF/TreeView.tld

并且确保/WEB-INF/MultiPages.tld和/WEB-INF/TreeView.tld下文件存在,这两个MultiPages.tld和TreeView.tl在JdonFramework.jar中的META-INF/tlds中,可手工拷贝过去。
更多详细错误,打开JBoss日志文件:server/default/log/server.log
快速J2EE开发工具 JdonFramework开发指南
专业创新来自Jdon.com
运行案例
打开浏览器,键入http://localhost:8080/testWeb/index.jsp
我们将index.jsp导向真正执行/userListAction.do
网上实时演示网址:http://www.jdon.com:8080/testWeb/
本章节源码在Jdon框架源码包的samples中SimpleJdonFrameworkTest/JdonFrameworkTest
快速J2EE开发工具 JdonFramework开发指南
专业创新来自Jdon.com
J2EE应用系统Jdon JPetstore
以IBatis.com的iBATIS-Jpetstore为例,我们使用Jdon框架对其重构成为Jdon-JPetstore,本章开发环境是Eclipse(本章案也适用其他开发工具),部署运行环境是JBoss。
Eclipse是一个非常不错的开源开发工具,使用Eclipse开发和使用JBuilder将有完全不同的开发方式。我们使用Eclipse基于Jdon框架开发一个完全Web应用,或者可以说,开发一个轻量(lightweight)的J2EE应用系统。
通过这个轻量系统开发,说明Jdon框架对完全POJO架构的支持,因为EJB分布式集群计算能力,随着访问量提升,可能需要引入EJB架构,这时只要使用EJB session Bean包装POJO服务则可以无缝升级到EJB。使用Jdon框架可实现方便简单地架构升迁。
Eclipse安装简要说明
1.下载Eclipse:在http://www.eclipse.org 的下载点中选择tds ISP 比较快。
2.安装免费插件:
编辑Jsp需要lomboz :http://www.objectlearn.com/projects/download.jsp
注意对应的Eclipse版本。
编辑XML,使用Xmlbuddy:http://xmlbuddy.com/
基本上这两个插件就够了。
如果希望开发 Hibernate,插件:
http://www.binamics.com/hibernatesync
代码折叠
http://www.coffee-bytes.com/eclipse/update-site/site.xml
3. 关键学习ant的编写build.xml,在build.xml将你的jsp javaclass打包成war或jar或ear就可以。都可以使用ant的jar打包,build.xml只要参考一个模板就可以:SimpleJdonFrameworkTest.rar 有一个现成的,可拷贝到其它项目后修改后就可用.
然后在这个模板上修改,参考 ant的命令参考:
http://ant.apache.org/manual/tasksoverview.html
网上有中文版的ant参考,在google搜索就能找到。
关键是学习ant的build.xml编辑,SimpleJdonFrameworkTest.rar 有一个现成的,可拷贝到其它项目后修改后就可用.
用ant编译替代Eclipse的缺省编译:选择项目属性-->Builders ---> new --> Ant Builder --->选择本项目的build.xml workspace 选择本项目
eclipse开发就这些,非常简单,不象Jbuilder那样智能化,导致项目目录很大。eclipse只负责源码开发,其它都由ant负责 快速J2EE开发工具 JdonFramework开发指南
专业创新来自Jdon.com
架构设计要点
Jdon-JPetstore除了保留iBATIS-JPetstore 4.0.5的域模型、持久层ibatis实现以及Jsp页面外,其余部分因为使用了Jdon框架而和其有所不同。
保留域模型和Jsp页面主要是在不更改系统需求的前提下,重构其架构实现为Jdon框架,通过对比其原来的实现或Spring的JPetstore实现,可以发现Jdon框架的使用特点。
在原来jpetstore iBatis包会延伸到表现层,例如它的分页查询PaginatedList,iBatis只是持久层框架,它的作用范围应该只限定在持久层,这是它的专业范围,如果超过范围,显得 ….。所以,在Jdon-Jpetstore中将iBatis封装在持久层(砍掉PaginatedList这只太长的手),Jdon框架是一种中间层框架,联系前后台的工作应该由Jdon这样的中间层框架完成。
在iBatis 4.0.5版本中,它使用了一个利用方法映射Reflection的小框架,这样,将原来需要在Action实现方法整入了ActionForm中实现,ActionForm变成了一个复杂的对象:页面表单抽象以及与后台Service交互,在ActionForm中调用后台服务是通过单态模式实现,这是在一般J2EE开发中忌讳的一点,关于单态模式的讨论可见:http://www.jdon.com/jive/article.jsp?forum=91&thread=17578
Jdon框架使用了微容器替代单态,消除了Jpetstore的单态隐患,而且也简化了ActionForm和服务层的交互动作(通过配置实现)。
用户注册登陆模块实现
用户域建模(Model)
首先,我们需要从域建模开始,建立正确的领域模型,以用户账号为例,根据业务需求我们确立用户账号的域模型Account,该模型需要继承Jdon框架中的com.jdon.controller.model.Model。
public class Account extends Model {
private String username;
private String password;
private String email;
private String firstName;
private String lastName;
……
}
username是主键。
域模型建立好之后,就可以花开两朵各表一支,表现层和持久层可以同时开发,先谈谈持久层关于用户模型的crud功能实现。
持久层Account crud实现
主要是用户的新增和修改,主要用于注册新用户和用户资料修改。
public interface AccountDao {
Account getAccount(String username); //获得一个Account
快速J2EE开发工具 JdonFramework开发指南
专业创新来自Jdon.com
void insertAccount(Account account); //新增
void updateAccount(Account account); //修改
}
持久层可以使用多种技术实现,例如Jdon框架的JdbcTemp代码实现比较方便,如果你的sql语句可能经常改动,使用iBatis的sql语句XML定义有一定好处,本例程使用Jpetstore原来的持久层实现iBatis。见源码包中的Account.xml
表现层Account表单创建(ModelForm)
这是在Domain Model建立后最重要的一步,是前台表现层Struts开发的起步,表单创建有以下注意点:
表单类必须继承com.jdon.model.ModelForm
表单类基本是Domain Model的影子,每一个Model对应一个ModelForm实例,所谓对应:就是字段名称一致。ModelForm实例是由Model实例复制获得的。
public class AccountForm extends ModelForm {
private String username;
private String password;
private String email;
private String firstName;
private String lastName;
……
}
当然AccountForm可能有一些与显示有关的字段,例如注册时有英文和中文选择,以及类别的选择,那么增加两个字段在AccountForm中:
private List languages;
private List categories;
这两个字段需要初始化值的,因为在AccountForm对应的Jsp的页面中要显示出来,这样用户才可能进行选择。选择后的值将放置在专门的字段中。
有两种方式初始化这两个字段:
1. 在AccountForm构造方法中初始化,前提是:这些初始化值是常量,如:
public AccountForm() {
languages = new ArrayList();
languages.add("english");
languages .add("japanese");
}
2.如果初始化值是必须从数据库中获取,那么采取前面章节介绍的使用ModelHandler来实现,这部分又涉及配置和代码实现,缺省时我们考虑通过jdonframework.xml配置实现。
Account crud的struts-config.xml的配置
第一步配置ActionForm:
上节编写了ModelForm代码,ModelForm也就是struts的ActionForm,在struts-config.xml
快速J2EE开发工具 JdonFramework开发指南
专业创新来自Jdon.com
中配置ActionForm如下:

第二步配置Action:
这需要根据你的crud功能实现需求配置,例如本例中用户注册和用户修改分开,这样,配置两套ModelViewAction和ModelSaveAction,具体配置见源码包中的struts-config-security.xml,这里将struts-config.xml根据模块划分成相应的模块配置,实现多模块开发,本模块是用户注册登陆系统,因此取名struts-config-security.xml。
Account crud的Jdonframework.xml配置












.其中有一个initMethod主要用于AccuntForm对象的初始化。其他都是增删改查的常规实现。
Account crud 的Jsp页面实现
在编辑页面EditAccountForm.jsp中加入:

在新增页面NewAccountForm.jsp加入:

所有的字段都是直接来自accountFrom。
整理模块配置
商品模块功能完成,struts提供了多模块开发,因此我们可以将这一模块单独保存在一个配置中:/WEB-INF/struts-config-security.xml,这样以后扩展修改起来方便。
商品查询模块实现
在iBATIS-JPetstore中没有单独的CategoryForm,而是将三个Model:Category、Product、,Item合并在一个CatalogBean中,这样做的缺点是拓展性不强,将来这三个Model也许需要单独的ActionForm。
由于我们使用Jdon框架的crud功能配置实现,因此,不怕细分这三个Model带来代码
快速J2EE开发工具 JdonFramework开发指南
专业创新来自Jdon.com
复杂和琐碎。
由于原来的Jpetstore“偷懒”,没有实现Category Product等的crud功能,只实现它们的查询功能,因此,我们使用Jdon框架的批量查询来实现查询。
持久层 Product批量查询实现
商品查询主要有两种批量查询,根据其类别ID:CategoryId查询所有该商品目录下所有的商品;根据关键字搜索符合条件的所有商品,下面以前一个功能为例子:
iBatis-jpetstore使用PaginatedList作为分页的主要对象,该对象需要保存到HttpSession中,然后使用PaginatedList的NextPage等直接遍历,这种方法只适合在小数据量合适,J2EE编程中不推荐向HttpSession放入大量数据,不利于cluster。
根据Jdon批量查询的持久层要求,批量查询需要两种SQL语句实现:符合条件的ID集合和符合条件的总数:以及单个Model查询。
//获得ID集合
List getProductIDsListByCategory(String categoryId, int pagessize);
//获得总数
int getProductIDsListByCategoryCount(String categoryId);
//单个Model查询
Product getProduct(String productId) ;
这里我们需要更改一下iBatis原来的Product.xml配置,原来,它设计返回的是符合条件的所有Product集合,而我们要求是Product ID集合。
修改Product.xml如下:





ProductDao是IBatis DAO实现,读取Product.xml中配置:
public List getProductIDsListByCategory(String categoryId, int start, int pagessize) {
return sqlMapDaoTemplate.queryForList(
"getProductListByCategory", categoryId, start, pagessize);
}
public int getProductIDsListByCategoryCount(String categoryId){
Integer countI = (Integer)sqlMapDaoTemplate.queryForObject(
"getProductListByCategoryCount", categoryId);
return countI.intValue();
}
这样,结合配置的iBatis DAO和Jdon框架批量查询,在ProductManagerImp中创建PageIterator,当然这部分代码也可以在ProductDao实现,创建PageIterator代码如下:
public PageIterator getProductIDsListByCategory(String categoryId, int start, int count)
{ 快速J2EE开发工具 JdonFramework开发指南
专业创新来自Jdon.com
PageIterator pageIterator = null;
try {
List list = productDao.getProductIDsListByCategory(categoryId, start, count);
int allCount = productDao.getProductIDsListByCategoryCount(categoryId);
int currentCount = start + list.size();
pageIterator = new PageIterator(allCount, list.toArray(), start,
(currentCount < allCount)?true:false);
} catch (DaoException daoe) {
Debug.logError(" Dao error : " + daoe, module);
}
return pageIterator;
表现层Product批量查询实现
根据批量查询的编程步骤,在表现层主要是实现ModelListAction继承、配置和Jsp编写,下面分步说:
第一步,创建一个ModelListAction子类ProductListAction,实现两个方法:getPageIterator和findModelByKey,getPageIterator方法如下:
public PageIterator getPageIterator(HttpServletRequest request, int start,
int count) {
ProductManager productManager = (ProductManager) WebAppUtil.getService(
"productManager", request);
String categoryId = request.getParameter("categoryId");
return productManager.getProductIDsListByCategory(categoryId, start, count);
}
findModelByKey方法如下:
public Model findModelByKey(HttpServletRequest request, Object key) {
ProductManager productManager = (ProductManager) WebAppUtil.getService(
"productManager", request);
return productManager.getProduct((String)key);
}
由于我们实现的是查询一个商品目录下所有商品功能,因此,需要显示商品目录名称,而前面操作的都是Product模型,所以在显示页面也要加入商品目录Category模型,我们使用ModelListAction的customizeListForm方法:
public void customizeListForm(ActionMapping actionMapping,
ActionForm actionForm, HttpServletRequest request,
ModelListForm modelListForm) throws Exception {
ModelListForm listForm = (ModelListForm) actionForm;
ProductManager productManager = (ProductManager) WebAppUtil.getService(
"productManager", request);
String categoryId = request.getParameter("categoryId");
Category category = productManager.getCategory(categoryId);
listForm.setOneModel(category);
}
第二步,配置struts-config.xml,配置ActionForm和Action:

快速J2EE开发工具 JdonFramework开发指南
专业创新来自Jdon.com
action配置如下:
type="com.jdon.framework.samples.jpetstore.presentation.action.ProductListAction"
name="productListForm" scope="request"
validate="false" >


第三步,编写Category.jsp
从productListForm中取出我们要显示两个模型,一个是oneModel中的Category;另外一个是Product Model集合list,Jsp语法如下:


我们可以显示商品目录名称如下:


这样我们就可以遍历productList中的Product如下:






加上分页标签库如下:
paramId="categoryId" paramName="category" paramProperty="categoryId">




至此,一个商品目录下的所有商品批量查询功能完成,由于是基于框架的模板化编程,直接上线运行成功率高。
商品搜索批量查询:
参考上面步骤,商品搜索也可以顺利实现,从后台到前台按照批量查询这条线索分别涉及的类有:
持久层实现:ProductDao中的三个方法:
List searchProductIDsList(String keywords, int start, int pagessize); //ID集合
int searchProductIDsListCount(String keywords); //总数
Product getProduct(String productId) ; //单个Model
表现层:建立ProductSearchAction类,配置struts-config.xml如下:
type="com.jdon.framework.samples.jpetstore.presentation.action.ProductSearchAction"
name="productListForm" scope="request"
validate="false"> 快速J2EE开发工具 JdonFramework开发指南
专业创新来自Jdon.com


与前面使用的都是同一个ActionForm:productListForm
编写SearchProducts .jsp,与Category.jsp类似,相同的是ActionForm;不同的是action。
商品条目Item批量查询
条目Item批量实现与Product批量查询类似:
持久层:ItemDao提供三个方法:
List getItemIDsListByProduct(String productId, int start, int pagessize);//ID集合
int getItemIDsListByProductCount(String productId);//总数
Item getItem(String itemId); //单个Model
表现层:创建一个ItemListAction继承ModelListAction:完成getPageIterator和findModelByKey,如下:
public class ItemListAction extends ModelListAction {
public PageIterator getPageIterator(HttpServletRequest request, int start,
int count) {
ProductManager productManager = (ProductManager) WebAppUtil.getService(
"productManager", request);
String productId = request.getParameter("productId");
return productManager.getItemIDsListByProduct(productId, start, count);
}
public Model findModelByKey(HttpServletRequest request, Object key) {
ProductManager productManager = (ProductManager) WebAppUtil.getService(
"productManager", request);
return productManager.getItem((String)key);
}
public void customizeListForm……….
}
与前面的ProductListAction相比,非常类似,不同的是Model名称不一样,一个是Product一个是Item;
struts-config.xml配置如下:

type="com.jdon.framework.samples.jpetstore.presentation.action.ItemListAction"
name="itemtListForm" scope="request"
validate="false">


比较前面product的配置,非常类似,其实itemListForm和productListForm是同一个ModelListForm类型,可以合并起来统一命名为listForm,节省ActionForm的配置。
Product.jsp页面与前面的Category.jsp SearchProdcuts.jsp类似。 快速J2EE开发工具 JdonFramework开发指南
专业创新来自Jdon.com


分页显示:
paramId="productId" paramName="product" paramProperty="productId">
…..
商品条目Item单条查询
单个显示属于crud中的一个查询功能,我们需要建立Model对应的ModelForm,将Item的字段拷贝到ItemForm中。配置这个ActionForm如下:
type="com.jdon.framework.samples.jpetstore.presentation.form.ItemForm"/>
第二步:因为这个功能属于crud一种,无需编程,但是需要配置jdonframework.xml:








配置中只要一个方法getMethod就可以,因为只用到crud中的读取方式。
第三步:配置struts-config.xml如下:
name="itemForm" scope="request"
validate="false">



第四步编辑Item.jsp,现在开始发现一个问题,Item.jsp中不只是显示Item信息,还有Product信息,而前面我们定义的是Item信息,如果使得Item.jsp显示Product信息呢,这就从设计起源Domain Model上考虑,在Item的Model中有Product引用:
private Product product;
public Product getProduct() { return product; }
public void setProduct(Product product) { this.product = product; }
Item和Product的多对一关系其实应该在域建模开始就考虑到了。
那么,我们只要在持久层查询Item时,能够将其中的Product字段查询就可以。在持久层的iBatis的Product.xml实现有下列SQL语句:

这段语法实际在查询Item时,已经将Product查询出来,这样Item Model中已经有Product数据,因为ActionForm是Model映射,因此,前台Jsp也可以显示Product数据。 快速J2EE开发工具 JdonFramework开发指南
专业创新来自Jdon.com
在Item.jsp中,进行下面定义:


将itemForm中product属性定义为product即可;这样不必大幅度修改原来的Item.jsp了。
整理模块配置
商品模块功能完成,struts提供了多模块开发,因此我们可以将这一模块单独保存在一个配置中:/WEB-INF/struts-config-catalog.xml,这样以后扩展修改起来方便。
购物车模块实现
购物车属于一种有状态数据,也就是说,购物车的scope生命周期是用户,除非这个用户离开,否则购物车一直在内存中存在。
有态POJO服务
现在有两种解决方案:
第一,将购物车状态作为数据类,保存到ActionForm中,设置scope为session,这种形式下,对购物车的数据操作如加入条目等实现不很方便,iBatis-jpetstore 4.0.5就采取这个方案,在数据类Cart中存在大量数据操作方法,那么Cart这个类到底属于数据类Model?还是属于处理服务类呢?
在我们J2EE编程中,通常使用两种类来实现功能,一种是数据类,也就是我们设计的Model;一种是服务类,如POJO服务或EJB服务,服务属于一种处理器,处理过程。使用这两种分类比较方便我们来解析业务需求,EJB中实体Bean和Session Bean也是属于这两种类型。
iBatis-jpetstore 4.0.5则是将服务和数据类混合在一个类中,这也属于一种设计,但是我们认为它破坏了解决问题的规律性,而且造成数据和操作行为耦合性很强,在设计模式中我们还使用桥模式来分离抽象和行为,因此这种做法可以说是反模式的。那么我们采取数据类和服务分离的方式方案来试试看:
第二.购物车功能主要是对购物车这个Model的crud,与通常的crud区别是,数据是保存到HttpSession,而不是持久化到数据库中,是数据状态保存不同而已。所以如果我们实现一个CartService,它提供add或update或delete等方法,只不过操作对象不是数据库,而是其属性为购物车Cart,然后将该CarService实例保存到HttpSession,实现每个用户一个CartService实例,这个我们成为有状态的POJO服务。
这种处理方式类似EJB架构处理,如果我们业务服务层使用EJB,那么使用有态会话Bean实现这个功能。
现在问题是,Jdon框架目前好像没有提供有状态POJO服务实例的获得,那么我们自己在WebAppUtil.getService获得实例后,保存到HttpSession中,下次再到HttpSession中获得,这种有状态处理需要表现层更多代码,这就不能使用Jdon框架的crud配置实现了,需要我们代码实现ModelHandler子类。
考虑到可能在其他应用系统还有这种需求,那么能不能将有状态的POJO服务提炼到Jdon框架中呢?关键使用什么方式加入框架,因为这是设计目标服务实例的获得,框架主
快速J2EE开发工具 JdonFramework开发指南
专业创新来自Jdon.com
要流程代码又不能修改,怎么办?
Jdon框架的AOP功能在这里显示了强大灵活性,我们可以将有状态的POJO服务实例获得作为一个拦截器,拦截在原来POJO服务实例获得之前。在Jdon框架设计中,目标服务实例的获得一般只有一次。
创建有状态POJO服务拦截器com.jdon.aop.interceptor. StatefulInterceptor,再创建一个空接口:com.jdon.controller.service.StatefulPOJOService,需要实现有状态实例的POJO类只要继承这个接口就可以。
配置aspect.xml,加入这个拦截器:
pointcut="pojoServices" />
这里需要注意的是:你不能让一个POJO服务类同时继承Poolable,然后又继承Stateful,因为这是两种不同的类型,前者适合无状态POJO;后者适合CartService这样有状态处理;这种选择和EJB的有态/无态选择是一样的。
Model和Service设计
购物车模块主要围绕域模型Cart展开,需要首先明确Cart是一个什么样的业务模型,购物车页面是类似商品条目批量查询页面,不过购物车中显示的不但是商品条目,还有数量,那么我们专门创建一个Model来指代它,取名为CartItem,CartItem是Item父集,多了一个数量。
这样购物车页面就是CartItem的批量查询页面,然后还有CartItem的crud操作,所以购物车功能主要是CartItem的crud和批量查询功能。
iBatis 4.0.5原来设计了专门Cart Model,其实这个Cart主要是一个功能类,因为它的数据项只有一个Map和List,这根本不能代表业务需求中的一个模型。虽然iBatis 4..0.5也可以自圆其说实现了购物车功能,但是这种实现是随心所欲,无规律性可遵循,因而以后维护起来也是困难,维护人员理解困难,修改起来也没有章程可循,甚至乱改一气。
CartItem可以使用iBatis原来的CartItem,这样也可保持Cart.jsp页面修改量降低。删除原来的Cart这个Model,建立对应的CartService,实现原来的Cart一些功能。
public interface CartService {
CartItem getCartItem(String itemId);
void addCartItem(EventModel em);
void updateCartItem(EventModel em);
void deleteCartItem(EventModel em);
PageIterator getCartItems();
}
CartServiceImp是CartService子类,它是一个有状态POJO服务,代码简要如下:
public class CartServiceImp implements CartService, Stateful{
private ProductManager productManager;
//将原来iBatis 中Cart类中两个属性移植到CartServiceImp中
private final Map itemMap = Collections.synchronizedMap(new HashMap());
private List itemList = new ArrayList();
public CartServiceImp(ProductManager productManager) {
super();
this.productManager = productManager;
}
…… 快速J2EE开发工具 JdonFramework开发指南
专业创新来自Jdon.com
}
itemMap是装载CartItem的一个Map,是类属性,由于CartServiceImp是有状态的,每个用户一个实例,那么也就是每个用户有自己的itemMap列表,也就是购物车。
CartServiceImp中的 getCartItemIDs是查询购物车当前页面的购物条目,属于批量分页查询实现,这里有一个需要考量的地方,是getCartItems方法还是getCartItemIDs方法?也就是返回CartIem的实例集合还是CartItem的ItemId集合?按照前面标准的Jdon框架批量分页查询实现,应该返回CartItem的ItemId集合,然后由Jdon框架的ModelListAction根据ItemId首先从缓存中获得CartItem实例,但是本例CartItem本身不是持久化在数据库,而也是内存HttpSession中,所以ModelListAction这种流程似乎没有必要。
如果将来业务需求变化,购物车状态不是保存在内存而是数据库,这样,用户下次登陆时,可以知道他上次购物车里的商品条目,那么采取Jdon框架标准查询方案还是有一定扩展性的。
这里,我们就事论事,采取返回CartIem的实例集合,展示一下如何灵活应用Jdon框架的批量查询功能。下面是CartServiceImp 的getCartItems方法详细代码:
public PageIterator getCartItems(int start, int count) {
int offset = itemList.size() - start; //获得未显示的总个数
int pageCount = (count < offset)?count:offset;
List pageList = new ArrayList(pageCount); //当前页面记录集合
for(int i=start; i< pageCount + start;i++){
pageList.add(itemList.get(i));
}
int allCount = itemList.size();
int currentCount = start + pageCount;
return new PageIterator(allCount, pageList.toArray(new CartItem[0]), start,
(currentCount < allCount)?true:false);
}
getCartItems方法是从购物车所有条目itemList中查询获得当前页面的条目,并创建一个PageIterator。
注意,现在这个PageIterator中keys属性中装载的不是数据ID集合,而是完整的CartItem集合,因为上面代码中pageList中对象是从itemList中获得,而itemList中装载的都是CartItem。
表现层购物车显示功能
由于PageIterator中封装的是完整Model集合,而不是ID集合,所以现在表现层有两种方案,继承框架的ModelListAction;或重新自己实现一个Action,替代ModelListAction。
这里使用继承框架的ModelListAction方案,巧妙地实现我们的目的,省却编码:
public class CartListAction extends ModelListAction {
public PageIterator getPageIterator(HttpServletRequest request, int arg1, int arg2) {
CartService cartService = (CartService)WebAppUtil.getService("cartService", request);
return cartService.getCartItems();
}
public Model findModelByKey(HttpServletRequest arg0, Object key) {
return (Model)key; //因为key不是主键,而是完整的Model,直接返回
} 快速J2EE开发工具 JdonFramework开发指南
专业创新来自Jdon.com
protected boolean isEnableCache(){
return false; //无需缓存,CartItem本身实际是在内存中。
}
}
配置struts-config.xml:




type="com.jdon.framework.samples.jpetstore.presentation.action.CartListAction"
name="listForm" scope="request"
validate="false">


……

上面是购物车显示实现,只要调用/shop/viewCart.shtml就可以显示购物车了。
在Cart.jsp页面插入下面标签:

….

分页显示标签如下:





购物车新增删除条目功能
前面完成了购物车显示功能,下面是设计购物车的新增和删除、修改功能。
参考Jdon框架的crud功能实现,Model是CartItem,配置jdonframework.xml使其完成新增删除功能:
class="com.jdon.framework.samples.jpetstore.domain.CartItem">








在这个配置中,只有新增和删除方法,修改方法没有,因为购物车修改主要是其中商品
快速J2EE开发工具 JdonFramework开发指南
专业创新来自Jdon.com
条目的数量修改,它不是逐条修改,而是一次性批量修改,这里的Model是CartItem,这是购物车里的一个条目,因此如果这里写修改,也只是CartItem一个条目的修改,不符合我们要求。下面专门章节实现这个修改。
表现层主要是配置,没有代码,代码都依靠cartService中的addCartItem和deleteCartItem实现:例如:
public void addCartItem(EventModel em) {
CartItem cartItem = (CartItem) em.getModel();
String workingItemId = cartItem.getWorkingItemId();
……
}
注意addCartItem中从EventModel实例中获取的Model是CartItem,这与我们在jdonframework.xml中上述定义的Model类型是统一的。
Struts-config.xml中定义是crud的标准定义,注意,这里只有ModelSaveAction无需ModelViewAction,因为将商品条目加入或删除购物车这个功能没有专门的显示页面:
name="cartItemForm" scope="request"
validate="false">


name="cartItemForm" scope="request"
validate="false">


注意,调用删除功能时,需要附加action参数:
/shop/removeItemFromCart.shtml?action=delete
而/shop/addItemToCart.shtml是新增属性,缺省后面无需跟参数。
购物车条目批量修改功能
上面基本完成了购物车主要功能;购物车功能一个复杂性在于其显示功能和修改功能合并在一起,修改功能是指修改购物车里所有商品条目的数量。
既然有修改功能,而且这个修改功能比较特殊,我们需要设计一个独立的ActionForm,用来实现商品条目数量的批量修改。
首先设计一个ActionForm(ModelForm),该ModelForm主要用来实现购物车条目数量的更改,取名为CartItemsForm,其内容如下:
public class CartItemsForm extends ModelForm {
private String[] itemId;
private int[]quantity;
private BigDecimal totalCost;
…..
}
itemId和quantity设计成数组,这样,Jsp页面可以一次性提交多个itemId和quantity数值。
现在CartItemsForm已经包含前台jsp输入的数据,我们还是将其传递递交到服务层实现处理。因此建立一个Model,内容与CartItemsForm类似,这里的Model名为CartItems,
快速J2EE开发工具 JdonFramework开发指南
专业创新来自Jdon.com
实际是一个传输对象。
public class CartItems extends Model{
private String[] itemId;
private int[] quantity;
private BigDecimal totalCost;
……
}
表现层在jdonframework.xml定义配置就无需编码,配置如下:
class="com.jdon.framework.samples.jpetstore.domain.CartItems">







上面配置中,Model是CartItems,ActionForm是cartItemsForm,这两个是专门为批量修改设立的。只有一个方法updateMethod。因为在这个更新功能中,没有根据主键从数据库查询Model的功能,因此,这里model的key可以为空值。
服务层CartServiceImp的updateCartItems方法实现购物车条目数量更新:
public void updateCartItems(EventModel em) {
CartItems cartItems = (CartItems) em.getModel();
try {
String[] itemIds = cartItems.getItemId();
int[] qtys = cartItems.getQuantity();
int length = itemIds.length;
for (int i = 0; i < length; i++) {
updateCartItem(itemIds[i], qtys[i]);//逐条更新购物车中的数量
}
} catch (Exception ex) {
logger.error(ex);
}
}
注意updateCartItems中从EventModel取出的是CartItems,和前面addCartItem方法中取出的是CartItem Model类型不一样,这是因为这里我们在jdonframework.xml中定义与updateCartItems相对应的Model是CartItems.
最后一步工作是Cat.jsp中加入CartItemsForm,能够在购物车显示页面有一个表单提交,客户按提交按钮,能够立即实现当前页面购物车数量的批量修改。Cat.jsp加入如下代码:


……
">
property="quantity"/>" />
…….
注意,一定要有action赋值edit这一行,这样提交给updateCartQuantities.shtml实际是
快速J2EE开发工具 JdonFramework开发指南
专业创新来自Jdon.com
ModelSaveAction时,框架才知道操作性质。
购物车总价显示功能
最后,还有一个功能需要完成,在购物车显示时,需要显示当前购物车的总价格,注意不是显示当前页面的总价格,所以无法在Cart.jsp直接实现,必须遍历购物车里所有CartItem计算总数。
该功能是购物车显示时一起实现,购物车显示是通过CartListAction实现的,这个CartListAction实际是生成一个ModelListForm,如果ModelListForm能够增加一个getTotalPrice方法就可以,因此有两种实现方式:继承ModelListForm加入自己的getTotalPrice方法;第二种无需再实现自己的ModelListForm,ModelListForm可以携带一个Model,通过setOneModel即可,这个方法是在ModelListAction的子类CartListAction可以override覆盖实现的,在CartListAction加入下列方法:
protected Model setOneModel(HttpServletRequest request){
CartService cartService = (CartService)WebAppUtil.getService("cartService", request);
CartItems cartItems = new CartItems();
cartItems.setTotalCost(cartService.getSubTotal());
return cartItems;
}
我们使用空的CartItems作为携带价格总数的Model,然后在Cart.jsp中再取出来显示:

Sub Total:
将当前页面listForm中属性oneModel定义为cartItems,它实际是我们定义的CartItems,
下一行取出总价即可。
用户喜欢商品列表功能
在显示购物车时,需要一起显示该用户喜欢的商品列表,很显然这是一个批量分页查询实现,但是它有些特殊,它首先显示的第一页不是由URL调用的,而是嵌入在购物车显示中,那么只能在购物车显示页面的ModellistForm中做文章。
在上节中,在CartListAction中setOneModel方法中,使用CartItems作为价格总数的载体,现在恐怕我们也要将之作为本功能实现载体。
还有一种实现载体,就是其他scope为session的ActionForm,AccountForm很适合做这样的载体,而且和本功能意义非常吻合,所以在AccountForm/Account中增加一个myList字段,在myList字段中,放置的是该用户喜欢的商品Product集合,注意不必放置Product的主键集合,因为我们只要显示用户喜欢商品的第一页,这一页是嵌入购物车显示页面中,所以第一页显示的个数是由程序员可事先在程序中定义。
这样在Account获得时,一起将myList集合值获得。
订单模块实现
我们还是从域模型开始,Order是订单模块的核心实体,其内容可以确定如下:
public class Order extends Model {
/* Private Fields */
快速J2EE开发工具 JdonFramework开发指南
专业创新来自Jdon.com
private int orderId;
private String username;
private Date orderDate;
private String shipAddress1;
private String shipAddress2;
…..
}
第二步,建立与Model对应的ModelForm,我们可以称之为边界模型,代码从Order拷贝过来即可。当然OrderForm还有一些特殊的字段以及初始化:
public class OrderForm extends ModelForm
private boolean shippingAddressRequired;
private boolean confirmed;
static {
List cardList = new ArrayList();
cardList.add("Visa");
cardList.add("MasterCard");
cardList.add("American Express");
CARD_TYPE_LIST = Collections.unmodifiableList(cardList);
}
public OrderForm(){
this.shippingAddressRequired = false;
this.confirmed = false;
}
…..
}
第三步,建立Order Model的业务服务接口,如下:
public interface OrderService {
void insertOrder(Order order);
Order getOrder(int orderId);
List getOrdersByUsername(String username);
}
第四步,实现OrderService的POJO子类:OrderServiceImp。
第五步,表现层实现,本步骤可和第四步同时进行。
OrderService中有订单的插入创建功能,我们使用Jdon框架的crud中create配置实现,配置struts-config.xml和jdonframework.xml:
type="com.jdon.framework.samples.jpetstore.presentation.form.OrderForm"/>
class="com.jdon.framework.samples.jpetstore.domain.Order">







快速J2EE开发工具 JdonFramework开发指南
专业创新来自Jdon.com
第六步:根据逐个实现界面功能,订单的第一个功能创建一个新的订单,在新订单页面NewOrderForm.jsp推出之前,这个页面的ActionForm已经被初始化,是根据购物车等Cart其他Model数据初始化合成的。
新订单页面初始化
根据Jdon框架中crud功能实现,初始化一个ActionForm有两种方法:一继承ModelHandler实现initForm方法;第二通过jdonframework.xml的initMethod方法配置。
这两个方案选择依据是根据用来初始化的数据来源什么地方。
订单表单初始化实际是来自购物车信息或用户账号信息,这两个都事先保存在HttpSession中,购物车信息是通过有态CartService实现的,所以这些数据来源是和request相关,那么我们选择第一个方案。
继承ModelHandler之前,我们可以考虑首先继承ModelHandler的子类XmlModelHandler,只要继承initForm一个方法即可,这样节省其他方法编写实现。
public class OrderHandler extends XmlModelHandler {
public ModelForm initForm(HttpServletRequest request) throws
Exception{
HttpSession session = request.getSession();
AccountForm accountForm = (AccountForm) session.getAttribute("accountForm");
OrderForm orderForm = createOrderForm(accountForm);
CartService cartService = (CartService)WebAppUtil.getService("cartService", request);
orderForm.setTotalPrice(cartService.getSubTotal());
//below can read from the user‘s creditCard service;
orderForm.setCreditCard("999 9999 9999 9999");
orderForm.setExpiryDate("12/03");
orderForm.setCardType("Visa");
orderForm.setCourier("UPS");
orderForm.setLocale("CA");
orderForm.setStatus("P");
Iterator i = cartService.getAllCartItems().iterator();
while (i.hasNext()) {
CartItem cartItem = (CartItem) i.next();
orderForm.addLineItem(cartItem);
}
return orderForm;
}
private OrderForm createOrderForm(AccountForm account){
……
}
}
ModelHandler的initForm继承后,因为这使用了Jdon的crud功能实现,这里我们只使用到crud中的创建功能,因此,findModelBykey方法就无需实现,或者可以在jdonframework.xml中配置该方法实现。 快速J2EE开发工具 JdonFramework开发指南
专业创新来自Jdon.com
考虑到在initForm执行后,需要推出一个NewOrderForm.jsp页面,这是一个新增性质的页面。所以在struts-config.xml
name="orderForm" scope="request" validate="false">


订单确认流程
新的订单页面推出后,用户需要经过两个流程才能确认保存,这两个流程是填写送货地址以及再次完整确认。这两个流程实现的动作非常简单,就是将OrderForm中的shippingAddressRequired字段和confirm字段赋值,相当于简单的开关,这是一个很简单的动作,可以有两种方案:直接在jsp表单中将这两个值赋值;直接使用struts的Action实现。后者需要编码,而且不是非有这个必要,只有第一个方案行不通时才被迫实现。
第一步:填写送货地址
使用Jdon框架的推出纯Jsp功能的Action配置struts-config.xml:
name="orderForm" scope="session" validate="false">


这是实现送货地址页面的填写,使用的还是OrderForm。更改前面流程NewOrderForm.jsp中的表单提交action值为本action path: shippingForm.shtml:

……

在ShippingForm.jsp中增加将shippingAddressRequired赋值的字段:

第二步:确认订单
类似上述步骤,配置struts-config.xml:
name="orderForm" scope="session" validate="false">


将上一步ShippingForm.jsp的表单action改为本action的path: confirmOrderForm.shtml:

修改ConfirmOrder.jsp中提交的表单为最后一步,保存订单newOrder.shtml:

第三步:下面是创建数据保存功能实现:
name="orderForm" scope="session"
validate="true" input="/order/NewOrderForm.jsp">


ModelSaveAction是委托ModelHandler实现的,这里有两种方式:配置方式:在jdonframework.xml中配置了方法插入;第二种是实现代码,这里原本可以使用配置方式实现,但是因为在功能上有要求:在订单保存后,需要清除购物车数据,因此只能使用代码实
快速J2EE开发工具 JdonFramework开发指南
专业创新来自Jdon.com
现方式,在ModelHandler中实现子类方法serviceAction:
public void serviceAction(EventModel em, HttpServletRequest request) throws java.lang.Exception {
try {
CartService cartService = (CartService) WebAppUtil.getService("cartService", request);
cartService.clear(); //清楚购物车数据
OrderService orderService = (OrderService) WebAppUtil.getEJBService("orderService", request);
switch (em.getActionType()) {
case Event.CREATE:
Order order = (Order) em.getModel();
orderService.insertOrder(order);
cartService.clear();
break;
case Event.EDIT:
break;
case Event.DELETE:
break;
}
} catch (Exception ex) {
throw new Exception(" serviceAction Error:" + ex);
}
}
用户订单列表
用户查询自己的订单列表功能很明显可以使用Jdon框架的批量查询事先。
在struts-config.xml中配置ModelListForm如下:

建立继承ModelListAction子类OrderListAction:
public class OrderListAction extends ModelListAction {
public PageIterator getPageIterator(HttpServletRequest request, int start, int count) {
OrderService orderService = (OrderService) WebAppUtil.getService("orderService", request);
HttpSession session = request.getSession();
AccountForm accountForm = (AccountForm) session.getAttribute("accountForm");
if (accountForm == null) return new PageIterator();
return orderService.getOrdersByUsername(accountForm.getUsername(), start, count);
}
public Model findModelByKey(HttpServletRequest request, Object key) {
OrderService orderService = (OrderService) WebAppUtil.getService("orderService", request);
return orderService.getOrder((Integer)key);
}
}
修改OrderService, 将获得Order集合方法改为:
public class OrderService{
PageIterator getOrdersByUsername(String username, int start, int count)
快速J2EE开发工具 JdonFramework开发指南
专业创新来自Jdon.com
….
}
根据Jdon批量查询要求,使用iBatis实现返回ID集合以及符合条件的总数。
最后编写ListOrders.jsp,两个语法:logic:iterator 和MultiPages
配置jdon框架启动
目前我们有四个struts-config.xml,前面每个模块一个配置:
/WEB-INF/struts-config.xml 主配置
/WEB-INF/struts-config-catalog.xml 商品相关配置
/WEB-INF/struts-config-security.xml 用户相关配置
/WEB-INF/struts-config-cart.xml 购物车相关配置
/WEB-INF/struts-config-order.xml 订单相关配置
本项目只有一个jdonframework.xml,当然我们也可以创建多个jdonframework.xml,然后在其struts-config.xml中配置。



修改iBatis的DAO配置
iBatis 4.0.5中原来的配置过于扩张(从持久层扩张到业务层),AccountDao每个实例获得都需要通过daoManager.getDao这样形式,而使用Jdon框架后,AccountDao等DAO实例获得无需特别语句,我们只要在AccountService直接引用AccountDao接口,至于AccountDao的具体实例,通过Ioc注射进入即可。
因此,在jdonframework.xml中有如下配置:
class="com.jdon.framework.samples.jpetstore.persistence.dao.sqlmapdao.AccountSqlMapDao"/>
class="com.jdon.framework.samples.jpetstore.service.bo.AccountServiceImp"/>
class="com.jdon.framework.samples.jpetstore.service.bo.ProductManagerImp"/>
而AccountServiceImp代码如下:
public class AccountServiceImp implements AccountService, Poolable {
private AccountDao accountDao;
private ProductManager productManager;
public AccountServiceImp(AccountDao accountDao,
ProductManager productManager){
this.accountDao = accountDao;
this.productManager = productManager;
}
AccountServiceImp需要两个构造方法实例,这两个中有一个是AccountDao。
按照iBatis原来的AccountDao子类AccountSqlMapDao有一个构造方法参数是
快速J2EE开发工具 JdonFramework开发指南
专业创新来自Jdon.com
DaoManager,但是我们无法生成自己的DaoManager实例,因为DaoManager是由dao.xml配置文件读取后生成的,这是一个动态实例;那只有更改AccountSqlMapDao构造方法了。
根源在于BaseSqlMapDao类,BaseSqlMapDao是一个类似JDBC模板类,每个Dao都继承它,现在我们修改BaseSqlMapDao如下:
public class BaseSqlMapDao extends DaoTemplate implements SqlMapExecutor{
…..
}
BaseSqlMapDao是XML配置和JDBC模板的结合体,在这个类中,这两者搭配在一起,在其中实现SqlMapExecutor各个子方法。
我们再创建一个DaoManagerFactory,专门根据配置文件创建DaoManager实例:
主要方法如下:
Reader reader = Resources.getResourceAsReader(daoResource);
daoManager = DaoManagerBuilder.buildDaoManager(reader);
其中daoResource是dao.xml配置文件,这个配置是在jdonframework.xml中配置:
class="com.jdon.framework.samples.jpetstore.persistence.dao.DaoManagerFactory">
value="com/jdon/framework/samples/jpetstore/persistence/dao/sqlmapdao/sql/dao.xml"/>

这样,我们可以通过改变jdonframework.xml配置改变dao.xml配置。
Dao.xml配置如下:



value="com/jdon/framework/samples/jpetstore/persistence/dao/sqlmapdao/sql/sql-map-config.xml"/>

implementation="com.jdon.framework.samples.jpetstore.persistence.dao.sqlmapdao.BaseSqlMapDao"/>


在dao.xml中,我们只配置一个JDBC模板,而不是将所有的如AccountDao配置其中,因为我们需要iBatis只是它的JDBC模板,实现持久层方便的持久化,仅此而已!
DaoManagerFactory为我们生产了DaoManager实例,那么如何赋值到BaseSqlMapDao中,我们设计一个创建BaseSqlMapDao工厂如下:
public class SqlMapDaoTemplateFactory {
private DaoManagerFactory daoManagerFactory;
public SqlMapDaoTemplateFactory(DaoManagerFactory daoManagerFactory) {
this.daoManagerFactory = daoManagerFactory;
}
public SqlMapExecutor getSqlMapDaoTemp(){
DaoManager daoManager = daoManagerFactory.getDaomanager();
快速J2EE开发工具 JdonFramework开发指南
专业创新来自Jdon.com
return (SqlMapExecutor)daoManager.getDao(SqlMapExecutor.class);
}
}
通过getSqlMapDaoTemp方法,由DaoManager.getDao方法获得BaseSqlMapDao实例,BaseSqlMapDao的接口是SqlMapExecutor,这样我们通过DaoManager获得一个JDBC模板SqlMapExecutor的实例。
这样,在AccountDao各个子类AccountSqlMapDao中,我们只要通过SqlMapDaoTemplateFactory获得SqlMapExecutor实例,委托SqlMapExecutor实现JDBC操作,如下:
public class AccountSqlMapDao implements AccountDao {
//iBatis数据库操作模板
private SqlMapExecutor sqlMapDaoTemplate;
//构造方法
public AccountSqlMapDao(SqlMapDaoTemplateFactory sqlMapDaoTemplateFactory) {
sqlMapDaoTemplate = sqlMapDaoTemplateFactory.getSqlMapDaoTemp();
}
//查询数据库
public Account getAccount(String username) throws SQLException{
return (Account)sqlMapDaoTemplate.queryForObject("getAccountByUsername", username);
}
部署调试
使用Jdon框架开发Jpetstore, 一次性调试通过率高,一般问题都是存在数据库访问是否正常,一旦正常,主要页面就出来了,其中常见问题是jsp页面和ActionForm的字段不对应,如jsp页面显示如下错误:
No getter method available for property creditCardTypes for bean under name orderForm
表示在OrderForm中没有字段creditCardTypes,或者有此字段,但是大小写错误等粗心问题。
如果jsp页面或后台log记录显示:
System error! please call system Admin.java.lang.Exception
一般这是由于前面出错导致,根据记录向前搜索,搜索到第一个出错记录:
2005-07-07 11:55:16,671 [http-8080-Processor25] DEBUG com.jdon.container.pico.PicoContainerWrapper - getComponentClass: name=orderService
2005-07-07 11:55:16,671 [http-8080-Processor25] ERROR com.jdon.aop.reflection.MethodConstructor - no this method name:insertOrder
第一个出错是在MethodConstructor报错,没有insertOrder方法,根据上面一行是orderService,那么检查orderService代码看看有无insertOrder:
public interface OrderService {
void insertOrder(Order order);
Order getOrder(int orderId);
List getOrdersByUsername(String username);
}
快速J2EE开发工具 JdonFramework开发指南
专业创新来自Jdon.com
OrderService接口中是有insertOrder方法,那么为什么报错没有呢?仔细检查一下,是不是insertOrder的方法参数有问题,哦, 因为orderService的调用是通过jdonframework.xml下面配置进行的:
class="com.jdon.framework.samples.jpetstore.domain.Order">







而根据Jdon框架要求,使用配置实现ModelHandler,则要求OrderService的insertOrder方法参数必须是EventModel,更改OrderService的insertOrder方法如下:
public interface OrderService {
void insertOrder(EventModel em);
}
同时,修改OrderService的子类代码OrderServiceImp:
public void insertOrder(EventModel em) {
Order order = (Order)em.getModel();
try{
orderDao.insertOrder(order);
}catch(Exception daoe){
Debug.logError(" Dao error : " + daoe, module);
em.setErrors("db.error");
}
}
注意em.setErrors方法,该方法可向struts页面显示出错信息,db.error是在本项目的application.properties中配置的。
关于本次页面出错问题,还有更深缘由,因为我们在jdonframework.xml中中定义了createMethod,而根据前面已经有ModelHandler子类代码OrderHandler实现,所以,这里不用配置实现,jdonframework.xml的配置应该如下:
class="com.jdon.framework.samples.jpetstore.domain.Order">



直接定义了hanlder的class是OrderHandler,在OrderHandler中的serviceAction我们使用代码调用了OrderService的insertOrder方法,如果使用这样代码调用,无需要求OrderService的insertOrder的参数是EventModel了。
总结
Jpetstore整个开发大部分基于Jdon框架开发,特别是表现层,很少直接接触使用struts
快速J2EE开发工具 JdonFramework开发指南
专业创新来自Jdon.com
原来功能,Jdon框架的表现层架构基本让程序员远离了struts的烦琐开发过程,又保证了struts的MVC实现。
Jpetstore中只有SignonAction这个类是直接继承struts的DispatchAction,这个功能如果使用基于J2EE容器的安全认证实现(见JdonNews),那么Jpetstore全部没有用到struts的Action,无需编写Action代码;ActionForm又都是Model的拷贝,Action和ActionForm是struts编码的两个主要部分,这两个部分被Jdon框架节省后,整个J2EE的Web层开发方便快速,而且容易得多。
这说明Jdon框架确实是一款快速开发J2EE工具,而且是非常轻量的。
纵观Jpetstore系统,主要有三个层的配置文件组成,持久层由iBatis的Product.xml等配置文件组成;服务层由jdon框架的jdonframework.xml组成;表现层由struts的struts-config.xml和jdonframework.xml组成;剩余代码基本是Model之类实现。
快速J2EE开发工具 JdonFramework开发指南
专业创新来自Jdon.com
Jdon框架核心设计架构
架构设计
由于整个框架中中组件基于Ioc实现,组件类之间基本达到完全解耦。
从上图中可以看出,任何JavaBeans组件只要在XML配置文件(container.xml aspect.xml和jdonframework.xml)中配置,由容器装载机制注册到Ioc 容器中,这些组件的客户端,也就是访问者,只要和微容器(或者组件的接口)打交道,就可以实现组件访问。
因此,本框架中每个功能块都可从框架中分解出来,单独使用它们。用户自己的任意功能块也可以加入框架中,Jdon框架成为一种完全开放、伸缩自如的基础平台。
包结构
Jdon框架有下列包名组成:
AOP : 表示AOP相关功能的类,其与bussinessproxy包关系最紧密,两者是系统的核心包。
Bussinessproxy:与动态代理、目标服务相关的类。
Container: 实现将组件装载入容器,这是与Ioc 微容器相关的类,缺省使用了PicoContainer,但是可以替换的。包括容器的组件注册、配置读取、组件访问等功能。以上三个包可从Jdon框架中独立出来。
Controller: 这是一些基础功能类、以及和应用相关类的功能包,该包是Container的客户端。
Model:这是和数据模型增删改查(crud)。批量查询、缓存优化等相关功能的类。
Security:这是和用户注册登陆相关功能类,
Servicelocator:这是和EJB 运行定位相关的类
Strutsutil:与Struts相关类,前面Model包中各种功能必须通过具体表现层技术实现。
Util:一些工具类。 快速J2EE开发工具 JdonFramework开发指南
专业创新来自Jdon.com
container包
Container包主要是负责容器管理方面的功能,其他包中的组件都被写入配置文件container.xml、aspect.xml和jdonframework.xml中,而container包主要负责与这些配置文件打交道,从配置文件中获得其他包的组件,向容器中注册,并启动容器。
主要一个接口是ContainerWrapper, ContainerWrapper有两个主要方法:向容器注册组件;从容器查询获得组件。
ContainerWrapper接口的缺省实现是PicoContainerWrapper和VisitorContainerWrapper两个子类,如下图:
PicoContainerWrapper使用了著名的PicoContainer实现(http://www.picocontainer.org),ContainerWrapper接口主要从其抽象出来。
访问者模式
VisitorContainerWraper是观察者模式的实现,传统观察者模式中,对于Visitor角色一般有下面多个访问不同被访问者的方法:
visitAcomponent();
visitBcomponent();
…..
由于Acomponent、Bcomponent这些类已经注册到容器,因此,通过容器可以直接直接实现不同组件的访问,只需通过下面一个方法实现:
public ComponentVisitor getComponentVisitor(){
return new ComponentOriginalVisitor(this);
}
而访问者Visitor则可以节省为单一方法即可:
visit(XXX xxx);
使用访问者模式的原因:主要为了实现缓存,提高一些组件运行性能,如果一些组件每次访问时,都需要new,例如Proxy.newInstance如果频繁反复运行,将是十分耗费性能的,因此,使用缓存尽量避免每次创建将提高系统的运行性能。
Visitor有两个子类实现:ComponentOriginalVisitor和HttpSessionProxyVisitor,这里又使用了装饰者模式,Decoratee 是ComponentOriginalVisitor;而Decorator是HttpSessionProxyVisitor,HttpSessionProxyVisitor是HttpSessionBindingListener,也就是说,我们使用了HttpSession作为缓存机制,HttpSession的特点是以用户为Key的缓存,符合♂的缓存机制,当然,我们以后也可以使用更好的缓存机制替换HttpSession,替换了HttpSession根本不必涉及到其他类的更好,因为这里使用模式实现了彻底的解耦。
访问者模式另外一个重要角色:Visitable,它则是那些运行结果需要缓存的组件必须继
快速J2EE开发工具 JdonFramework开发指南
专业创新来自Jdon.com
承的,注意,这里有一个重要点:不是那些组件本省生成需要缓存,而是它的运行结果需要缓存的。继承Visitable这些组件事先必须注册在容器中。
目前Visitable有两个子类:
* @see com.jdon.bussinessproxy.dyncproxy.ProxyInstanceFactoryVisitable
* @see com.jdon.bussinessproxy.target.TargetServiceFactoryVisitable
前者主要是动态代理的创建,因为Proxy.newInstance频繁执行比较耗费性能,第一次创建后,将动态代理实例保存在httpSession中,当然每个Service对应一个动态代理实例。
TargetServiceFactoryVisitable主要是为了缓存那些目标服务的实例,目前这个功能没有激活,特殊情况下才推荐激活。
容器的启动
容器启动主要是将配置文件中注册的组件注册到容器中,并启动容器,这涉及到container包下Config和Builder等几个子包。
Config包主要负责从container.xml和aspect.xml读取组件;
Builder包主要负责向ContainerWrapper注册这些组件,注册过程是首先从基础组件(container.xml)开始,然后是拦截器组件(aspect.xml),最后是用户的服务组件(jdonframework.xml)。
容器的生命周期
容器启动后,容器本身实例是放置在Web容器的ServletContext中。
容器启动并不是在应用系统部署到Web容器时就立即启动,而是该应用系统被第一次访问时触发启动,这个行为是由ContainerSetupScript的startup触发的,而startup方法则是由ServletContainerFinder的findContainer触发。
当应用系统从Web容器中销毁或停止,Jdon框架容器也就此销毁,最好将你的组件中一些对象引用释放,只要继承Startable,实现stop方法即可。
容器的使用
客户端访问组件必需通过容器进行,这个过程分两步:
从Web容器中获得框架容器ContainerWrapper实例。
从ContainerWrapper容器中查询获得组件实例,有两者实例方式:单例和多例。
关于具体使用方式可见前面章节“如何获得POJO实例”等。
这个容器使用过程是通过finder包下面的类完成,主要是ServletContainerFinder类,ComponentKeys类保存中一些container.xml中配置的组件名称,这些组件名称可能需要在框架程序中使用到,这就需要引起注意,这个类中涉及的组件名称不要在container.xml中随意改动,当然这个类在以后重整中争取去除。
AOP包
Jdon AOP的设计目前功能比较简单,不包括标准AOP中的Mixin和Introduction等功能;AOP包下有几个子包:Interceptor、joinpoint和reflection,分别涉及拦截器、;拦截器切点和反射机制等功能。 快速J2EE开发工具 JdonFramework开发指南
专业创新来自Jdon.com
bussinessproxy包
Jdon框架从架构角度来说,它是一种业务代理,前台表现层通过业务代理层访问业务服务层,使用AOP实现业务代理是一种新的趋势,因此本包功能是和AOP包功能交互融合的。
bussinessproxy包中的config子包是获取jdonframework.xml中的两种服务EJB或POJO配置,然后生成meta子包中TargetMetaDef定义。
target子包封装了关于这两种服务由元定义实现对象创建的工厂功能,服务对象创建透过访问模式使用了HttpSession作为EJB实例缓存,这个功能主要是为了EJB中有态会话Bean实现,这样,客户端通过getService获得一个有态会话Bean时,无需自行考虑保存这个Bean引用到HttpSession中这一使用细节了。无态会话bean从实战角度发现也可以缓存,提高了性能。
Jdon框架既然是一种业务代理,那么应该服务于各种前台表现层,Jdon框架业务代理还适合客户端为Java客户端情况下的业务服务访问。这部分功能主要是com.jdon.bussinessproxy.remote子包实现的。使用方式参考:http://www.jdon.com/product/ejbinvoker.htm
TargetMetaDef
TargetMetaDef是目标服务的元定义,TargetMetaDef主要实现有两种:EJBTargetMetaDef和POJOTargetMetaDef,分别是EJB服务和POJO服务实现。
所谓目标服务元定义也就是程序员在jdonframework.xml中定义的services。
Jdon框架提供目标服务两种形式:目标服务实例创建和目标服务本身。
ServiceFactory
目标服务创建工厂ServiceFactory可以根据目标服务元定义创建一个目标服务实例,这也是WebAppUtul.getService获得目标服务的原理实现,
com.jdon.controller.service.DefaultServiceFactory是ServiceFactory缺省实现,在DefaultServiceFactory中,主要是通过动态代理获得一个服务实例,使用了DefaultServiceFactory来实现动态代理对象的缓存。不必每次使用Proxy.newinstance第一次执行后,将其结果保存在HttpSession中。
Service
目标服务Service则是通过方法Relection运行目标服务,只要告知Service目标服务的类、类的方法、方法参数类型和方法参数值这些定义,除了方法参数值,其余都是字符串定义,这是Java的另外一种调用方式。
security包
Security包主要应用于JdonSD(一个组件库)产品的基于容器的安全权限认证。
快速J2EE开发工具 JdonFramework开发指南
专业创新来自Jdon.com
strutsutil包
本包封装了Jdon框架基于Struts的表现层实现,Jdon框架的crud功能和批量分页查询以及树形显示功能都是在该包中实现,还有上传图片等功能(待测试)。
controller包
controller包是Jdon框架的整体结构包,主要面向框架使用客户端,其中WebAppUtil是经常被调用的类。
cache子包是框架的缓存机制,这个缓存机制主要面向数据类设计,例如Model类,功能类是通过container实现管理,功能类使用访问者模式实现结合HttpSession实现有状态。
Jdon框架缺省缓存是使用了开源OfBiz的一个缓存,如果你希望使用更好的缓存产品。可以通过继承Cache或LRUCache,然后在container.xml中替代如下配置即可:



cache子包提供的缓存不但为Model服务,还为批量查询的PageIterator创建服务,可参见PageIteratorSolver类;另外也为上传图片缓存服务,以后可拓展为更多数据性缓存服务。
Config子包主要是为读取pojoService和ejbService配置实现XML读取功能。
Events子包包含的是装载数据Model载体在各层之间穿梭,类似信使,通常表现层会将Model封装到EventModel中,传给服务层的服务,服务处理完毕,如果出错将出错信息封装在EventModel对象中,这样表现层可从EventModel中查知后台处理是否出错,并返回出错信息。
EventModel的setErrors方法是用于服务层封装处理出错信息的方法,可直接将”id.notfound”这样约定出错信息赋值进去,前台如struts可根据id.notfound到Application.properties中查找本地化信息提示。
Model子包是封装了框架重要的数据类Model,Model既是域模型中的Model,也是一种DTO,它是在各层之间传送数据的载体,因此Model可以允许嵌套Model,DynamicModel也许更加适合临时组装一个传送对象。
Model是框架的crud功能重要角色;PageIterator是框架批量查询的重要角色。
Pool子包封装了Apache Pool功能,主要用于Pool拦截器,如果你的POJO服务需要池化提升性能,只要让你的POJO服务类继承该包下的Poolable即可。
Service子包封装框架两大有关服务的重要功能:创建一个服务ServiceFactory.create;或直接运行一个服务Service.execute。
model包
model包主要是围绕Model的crud功能实现展开,这部分是中间层,尽量做到和表现层strutsutil包实现解耦,这样如果有新的表现层如JSF可直接和本包发生作用,实现JSF
快速J2EE开发工具 JdonFramework开发指南
专业创新来自Jdon.com
下的crud功能。
ModelManager是面向表现层操作Model的主要客户端类,ModelForm和ModelHandler都是可能需要客户端继承实现的重要类。
Config子包主要是从jdonfamework.xml中获得models相关配置,将这些加载到ModelMapping对象中。
Handler子包是延续config子包,model配置中Modelhandler部分配置被加载到HandlerMetaDef对象中,然后预先实现class.forName运行,HandlerObjectFactory是创建ModelHandler实例工厂,一次性生产20个,如果不够用,还可以再生产20个,因为ModelHandler为struts的action直接调用,而action是多线程,所以使用这种池形式可提高性能,这里使用了自制的池功能,原理简单,池性能好,简单性能就好是Jdon框架设计一个原则。
Query子包主要是为批量查询服务,这些类在持久层被调用。
servicelocator和Util包
这两个包都是工具,大部分类从其它开源借鉴过来,正是因为这些经过实践证明运行良好的基础组件,才使得Jdon框架的整体基础牢固,感谢他们。
站在巨人的基础上发展是Jdon框架设计的另外一个原则。
更详细的描述将在专门Jdon框架设计文档中描述。
技术支持注意
无论Jdon框架本身还是使用Jdon框架开发的任何系统出现问题,都可以在论坛
http://www.jdon.com/jive/forum.jsp?forum=61&thRange=30
发生错误后,请按下面步骤贴出错误:
1. 需要打开Jboss/server/default/log/server.log
2. 键入搜索" ERROR ",注意ERROR两边各有一个空格,找到第一个错误,那是错误起源,贴到论坛中。
我们也提供Jdon框架上门培训和咨询,通过5天培训让企业用户完全掌握使用Jdon框架开发J2EE信息系统。联系banqiao@jdon.com
全文完 快速J2EE开发工具 JdonFramework开发指南
专业创新来自Jdon.com