第三只眼看Jdon-一个让很多人抓狂框架
1、Jdon为什么会让我们抓狂?平安夜的狂欢过后,Jdon已经三岁了,已经到了5.1版本,作为中国的第一个java开源框架,相比很多胎死腹中的畸形胎儿(比如G-Roller等),可以算是非常健康长寿了,值得所有热爱并从事着开源的人们学习、反思。
不知道为了什么?直到现在仍然还有人对他以及他的创始人抱有深深的偏见或者嫉恨。一个能让这么多人抓狂的框架,如果你对他不了解,那真是一个遗憾。毕竟不是任何东西都能让人抓狂的,而且是让很多“牛”人们集体抓狂,更是不容易。是因为他确实太滥或者又他太优秀?是因为他侵犯了大家的利益还是他在给我们宣扬错误的思想及理念?或者都是,又或者又都不是,也许只有身在其中的人们才知道。
通过两天对Jdon框架的应用体验及源码分析,今天从第三方的角度来审视Jdon,根据我所了解的Jdon的框架,谈谈我的认识及感受,希望能让你对Jdon有一个全新的认识,又或者能为Jdon的发展做出一些小小的贡献。
2、Jdon是什么
Jdon是什么?回答这个问题可以直接借助他的创始人板桥写在其官方网站上的介绍:Jdon Framework(简称JF)是一套适合开发中小型J2EE/JavaEE应用系统的轻量Web框架(Lightweight Java Web Framework)。JF诞生于2004年底,是国人独立开发的中国人自己的框架产品,2005年入选全球SUN公司网站java.net正式企业应用目录。经过多年发展和众多用户的使用及其完善,JF已经发展成为一套成熟的、面向对象的、基于构件(基于组件)的轻量快速开发框架。Jdon是严格坚持分层架构(表现层、业务层和持久层)下的快速开发。JF是快速性和灵活性综合平衡的产物,它带来了多快好省的简单的解决之道(simplify the best):对于小项目,使用Jdon框架可以开发出高质量可扩展的好的系统;对于大项目,使用Jdon框架可以更快地开发出系统。
也许现在看来这些特性都没什么,但如果在当时,确是非常具有革命性的,用板桥的话:“颠覆对象使用之前必须创建的基本定律,正象无需关心对象销毁一样,您可以无需关心对象创建。”,“面向对象编程之父Grady Booch 说:对象最伟大处在于其可被替代。而Jdon框架伟大之处是帮助你替代这些对象,甚至包括Jdon框本身。”
板桥一下强调:“Jdon框架不是面向数据库的;而是面向模型分析设计(OOA/OOD)。”
当然,孩子永远是自家的好,我们不能只听板桥说他如何我们就认为他怎样?究竟这个框架是好还是差,耳听为虚,眼见为实。既然都是开源项目,我们只需要下载他的源代码看看,并跟同类的开源比较一下就知道。如果你跟我一样,通过看他的文档,阅读他的代码,你才会真正了解Jdon究竟是什么。
一个IOC框架
Jdon是一个IOC容器(板桥也算是最早把IOC的概念引入国内的少数牛人之一,并且能通过集成到jdon中向大家展示IoC的使用),这个IOC容器能实现自动按构造子注入组件所依赖的对象。这个Ioc需要配置的东西不多,比如:你只需要在一个类似jdonframework.xml的xml文件中写类似下面的配置信息:
<
services
>
< pojoService name ="testService" class ="com.jdon.framework.test.service.TestServicePOJOImp" />
</ services >
< pojoService name ="testService" class ="com.jdon.framework.test.service.TestServicePOJOImp" />
</ services >
你就能在你的Action中使用下面的代码访问这个testService
TestService testService
=
(TestService) WebAppUtil.getService(
"
testService
"
, request);
testService.createUser(em);
testService.createUser(em);
当然,你必须要搞清楚的是,Jdon的Ioc实现并不是从头开始实现了一个IoC,他是通过在picocontainer基础上进行封装实现的。
简单的AOP及拦截器实现
基于面向切面的编程,可以把面向对象的方法不能很好解决的问题很轻松的解决掉。Jdon在aopalliance定义的API基础上,实现了一个非常简单的AOP,其实就是几个类,使用代理实现了对方法的环绕拦截。虽然没有AspejctJ及Spring的AOP强大,但对于应用80%的AOP需求,已经足够。
Jdon立足中大型应用需求,充分发挥Java在企业应用开发中的优势,考虑到性能等很多因素,Jdon框架还通过成功应用拦截器,实现了对象Cache、对象池等功能。你可以在一个像aspect.xml这样的文件中配置你的拦截器,实现对一组一组的对象进行拦截:
<
interceptor
name
="cacheInterceptor"
class ="com.jdon.aop.interceptor.CacheInterceptor"
pointcut ="services" />
class ="com.jdon.aop.interceptor.CacheInterceptor"
pointcut ="services" />
JdonAOP拦截器目标对象组有三种:全部目标服务;EJB服务;POJO服务(EJB服务和POJO服务是在JdonFramework.xml中定义的ejbService和pojoService)
针对访问EJB业务组件的封装
Jdon充分考虑到在大型的分布式应用中,一些功能仍然必须依靠EJB这种重量级的业务组件来完成的现状。相对于其它一些同类框架,从框架底层到最终应用都考虑到对EJB支持及简化。比如针对EJB2的一个业务组件,可以直接配置成下面的形式:
<
ejbService
name
="testService"
>
< jndi name ="TestEJB" />
< ejbHomeObject class ="com.jdon.framework.test.ejb.TestEJBHome" />
< ejbRemoteObject class ="com.jdon.framework.test.ejb.TestEJBRemote" />
< interface class ="com.jdon.framework.test.service.TestService" />
</ ejbService >
< jndi name ="TestEJB" />
< ejbHomeObject class ="com.jdon.framework.test.ejb.TestEJBHome" />
< ejbRemoteObject class ="com.jdon.framework.test.ejb.TestEJBRemote" />
< interface class ="com.jdon.framework.test.service.TestService" />
</ ejbService >
而在Action中使用的时候,还是像使用普通的pojoService一样,WebAppUtil.getService方法即可获得。
持久层数据访问封装
企业级应用80%都会涉及到数据库的持久化。因此,Jdon提供了对持久层数据访问的简单封装,提供了一个JdbcTemp类来执行数据库操作,另外还提供了数据访问层的DaoCRUD接口,以及一个Hibernate的实现,访问我们使用Hibernate来进行对象持久化。
如下面的代码使用Jdon中提供的jdbc模板类来操作数据库:
//
适合查询返回结果是单个字段,如:
// select name from user where id=?
public Object querySingleObject(Collection queryParams, String sqlquery) throws Exception {
return jdbcTemp.querySingleObject(queryParams, sqlquery);
}
// select name from user where id=?
public Object querySingleObject(Collection queryParams, String sqlquery) throws Exception {
return jdbcTemp.querySingleObject(queryParams, sqlquery);
}
Struts1.x工具集合
Jdon在我看来有一点感觉是为使用Struts1.x开的人用的,因为他诞生的时候,struts1.x正处于鼎盛时期。因此,框架内部集成了很多开发struts1.x应用的实用工具,全部放在strutsutil及下面的包中,包含基于Struts1.x的通用业务处理如ModelDispAction、ModelListActionAction等,文件上传封装,基于Struts1.x的树形组件以及自定义的分页标签等。如下图所示:
一个围绕Struts1.x+POJO(EJB) Service+Hibernate的快速开发框架
说到快速开发框架,我想大家都不陌生,JavaEE领由于涉及到的技术及选择性太多,导致现在的快速开发框架可以说是五化八门,国际上比较知名的有appfuse,国内比较有名的有springside及easyjf等。而jdon在做那么多基础性功能的情况下,也实现了一个快速开发框架。也许他的核心及目标也就是这个快速开发框架,他是最终能让这个框架立竿见影的重要部分。
使用Jdon的快速开发框架,可以使用对象驱动的方式开发j2ee应用。Jdon框架中花了大量的精力及资源来解决模型CRUD的问题,整个框架中差不多一半的代码都是为实现这个灵活、快速、基于对象驱动的快速框架而写的。为了一个添删改查,你只需要一个模型、一个业务组件、一个Form、两个配置文件,就能实现一个非常简单的添删改查找应用。比如针对留言Message进行添删改查,需要下面的内容:
域模型
public class Message extends Model {
private String messageId;
private String name;
public String getName() { return name; }
public void setName(String name) { this .name = name; }
}
业务逻辑层
public interface MessageService {
public void createMessage(EventModel em);
public void updateMessage(EventModel em);
public void deleteMessage(EventModel em);
public Message getMessage(String messageId);
}
实现及DAO层的代码这里省略。
一个Form(Struts1.x的产物):
public class MessageForm extends ModelForm {
private String messageId;
private String name;
public String getName() { return name; }
public void setName(String name) { this .name = name; }
}
public class Message extends Model {
private String messageId;
private String name;
public String getName() { return name; }
public void setName(String name) { this .name = name; }
}
业务逻辑层
public interface MessageService {
public void createMessage(EventModel em);
public void updateMessage(EventModel em);
public void deleteMessage(EventModel em);
public Message getMessage(String messageId);
}
实现及DAO层的代码这里省略。
一个Form(Struts1.x的产物):
public class MessageForm extends ModelForm {
private String messageId;
private String name;
public String getName() { return name; }
public void setName(String name) { this .name = name; }
}
业务层配置文件jdonframework.xml
< models >
<!-- 配置模型的类是Message,其主键是messageId -->
< model key ="messageId" class ="sample.model.Message" >
<!-- 下行是配置界面模型MessageForm -->
< actionForm name ="messageForm" />
< handler >
<!-- 以下配置MessageService -->
< service ref ="messageService" >
< getMethod name ="getMessage" />
< createMethod name ="createMessage" />
< updateMethod name ="updateMessage" />
< deleteMethod name ="deleteMessage" />
</ service >
</ handler >
</ model >
</ models >
< services >
<!-- 以下配置MessageService -->
< pojoService name ="messageService" class ="sample.service.MessageServiceImp" />
</ services >
Struts-config.xml配置文件
< struts-config >
< form-beans >
< form-bean name ="messageForm" type ="sample.web.MessageForm" />
……
</ form-beans >
…..
< plug-in className ="com.jdon.strutsutil.InitPlugIn" >
< set-property property ="modelmapping-config" value ="jdonframework.xml" />
</ plug-in >
< action name ="messageForm" path ="/messageAction" type ="com.jdon.strutsutil.ModelViewAction"
scope ="request" validate ="false" >
< forward name ="create" path ="/message.jsp" />
< forward name ="edit" path ="/message.jsp" />
</ action >
< action name ="messageForm" path ="/messageSaveAction" type ="com.jdon.strutsutil.ModelSaveAction"
scope ="request" validate ="true" input ="/message.jsp" >
< forward name ="success" path ="/result.jsp" />
< forward name ="failure" path ="/result.jsp" />
</ action >
</ struts-config >
< models >
<!-- 配置模型的类是Message,其主键是messageId -->
< model key ="messageId" class ="sample.model.Message" >
<!-- 下行是配置界面模型MessageForm -->
< actionForm name ="messageForm" />
< handler >
<!-- 以下配置MessageService -->
< service ref ="messageService" >
< getMethod name ="getMessage" />
< createMethod name ="createMessage" />
< updateMethod name ="updateMessage" />
< deleteMethod name ="deleteMessage" />
</ service >
</ handler >
</ model >
</ models >
< services >
<!-- 以下配置MessageService -->
< pojoService name ="messageService" class ="sample.service.MessageServiceImp" />
</ services >
Struts-config.xml配置文件
< struts-config >
< form-beans >
< form-bean name ="messageForm" type ="sample.web.MessageForm" />
……
</ form-beans >
…..
< plug-in className ="com.jdon.strutsutil.InitPlugIn" >
< set-property property ="modelmapping-config" value ="jdonframework.xml" />
</ plug-in >
< action name ="messageForm" path ="/messageAction" type ="com.jdon.strutsutil.ModelViewAction"
scope ="request" validate ="false" >
< forward name ="create" path ="/message.jsp" />
< forward name ="edit" path ="/message.jsp" />
</ action >
< action name ="messageForm" path ="/messageSaveAction" type ="com.jdon.strutsutil.ModelSaveAction"
scope ="request" validate ="true" input ="/message.jsp" >
< forward name ="success" path ="/result.jsp" />
< forward name ="failure" path ="/result.jsp" />
</ action >
</ struts-config >
3、Jdon给了我们什么
看了上面的功能介绍,也许确实没给你带来更多的惊喜。但是想想如果你是三年前的今天看到这么多的介绍会是怎样?也许那时候你才开始学hibernate,或者刚使用struts没多久,又或者还在为什么是model2的web应用而困惑,更别说什么是IoC或AOP了。如果那时你知道国人有这么一个框架,使用了这么多先进的理念、引入了很多先进的技术、并且可以让我们开发Struts应用基本上不用写太多的java代码,你会是什么样的感受?
其实,也许就算你现在不用Jdon框架来做项目,作为一个开源爱好者,我仍然强烈推荐你去了解这个框架。因为他曾经带给我们的是很多革命性的东西。而且,就算三年后的今天,也许你已经从一个java菜鸟变成了java小牛,但回头了解Jdon框架中的很多东西,你会发现很多与你的代码有着似曾相识的感觉。
Jdon框架中大量应用了设计模式,这是面向对象编程人员必须掌握的一门内功。通过ServiceFactory、ServiceFacade、ContainerDirector、ComponentVisitor、HttpSessionProxyVisitor、CommonsPoolAdapter、CommonsPoolFactory、WebServiceDecorator这些名字,一个一个的去了解、挖掘,一定会非常受益。
4、Jdon的代码质量分析
Jdon的代码质量总的来说是非常高的,不管是命名规范、代码注释或者是代码结构等。整个框架根据要实现的功能模块分成aop、businessproxy、controller、container、model、strutsutil、persistence、security等大包,框架的核心部件如容器ContainerWrapper、ContainerBuilder、组件描述TargetMetaDef、动作事件 Event、模型ModelIF等都是针对接口编程,具有一定的可扩展性。另外整个设计中使用到大量的设计模式,可读性也非常强,想进一步提高OO水平的java开发人员值得进一步学习研究。
代码的注释有的是英文,有的是中文,这种中英文混合的方式有好处有坏处。好处是国人或老外都能看懂一点,坏处是不管对国人还是老外,都没法让他们彻底地搞懂。比如下面是ModelIF的代码及注释:
/**
* Base domain model it can be DTO or nested Model. it is the important message
* between business layer and view layer. in view layer, it is created by form
* object(such as ActionForm object);in business layer, it is created by
* business components(such as session bean).
*
* thi class can be cached, and setModified is important, this method can be
* used to refresh the cache.
*
* because setModified function ,so the class is designed for a class, but not a
* interface.
* the difference with setModified and setCacheable;
* setCacheable to false, the model will never exist in the cache.
* setModified to true, if the model exists in the cache, the client will not
* get it from cache, it is same as being deleted from cache .
* deleting the model from cache must have a condition that the deleting operator
* can access the cache of the container, if it cann't access the container,
* it cann't delete the model from cache. such it is EJB.
* @author banq
*/
public interface ModelIF extends Cacheable, Cloneable, Serializable {
/**
* in the past version, this method name is isCacheble,
* now change it after 1.3 !
*/
public boolean isCacheable();
/**
* in the past version, this method name is setCacheble,
* now change it after 1.3 !
*/
public void setCacheable( boolean cacheable);
public boolean isModified();
/**
* set the property has been modified such as : setName(String name){
* this.name = name; setModified(true); }
*
*/
public void setModified( boolean modified) ;
}
* Base domain model it can be DTO or nested Model. it is the important message
* between business layer and view layer. in view layer, it is created by form
* object(such as ActionForm object);in business layer, it is created by
* business components(such as session bean).
*
* thi class can be cached, and setModified is important, this method can be
* used to refresh the cache.
*
* because setModified function ,so the class is designed for a class, but not a
* interface.
* the difference with setModified and setCacheable;
* setCacheable to false, the model will never exist in the cache.
* setModified to true, if the model exists in the cache, the client will not
* get it from cache, it is same as being deleted from cache .
* deleting the model from cache must have a condition that the deleting operator
* can access the cache of the container, if it cann't access the container,
* it cann't delete the model from cache. such it is EJB.
* @author banq
*/
public interface ModelIF extends Cacheable, Cloneable, Serializable {
/**
* in the past version, this method name is isCacheble,
* now change it after 1.3 !
*/
public boolean isCacheable();
/**
* in the past version, this method name is setCacheble,
* now change it after 1.3 !
*/
public void setCacheable( boolean cacheable);
public boolean isModified();
/**
* set the property has been modified such as : setName(String name){
* this.name = name; setModified(true); }
*
*/
public void setModified( boolean modified) ;
}
当然,既然是中国人,那么跟所有其它的中国人一样,一个人写这么多的代码,难免也会出现一些小小的Bug及错误,特别是一些错别字。比如说:ModelManager接口中的borrowtHandlerObject方法,又或者isCacheable写成isCacheble,或者Jdbc操作模板类名称叫JdbcTemp而不叫JdbcTemplate等;另外还有把test代码没有跟框架代码分离,而是跟着框架核心代码放在一起,比如ContainerDirectorTest等;还有很多核心的接口缺少注释,比如ServiceAccessor等。
如果不是为了鸡蛋里面挑骨头,但我想这些不是应该成为这个框架不好的原因,毕竟这些问题我想Jdon并不是无法改进。
Jdon的代码存放在sourceforge上面,使用cvs管理,任何人都可以从这个cvs上check out他的源代码进行学习及改进。
5、Jdon还缺哪些?
前面已经说了很多Jdon的优点,但同样是应用程序框架,Jdon为什么没有能像Spring那样火呢?为什么市场上关于Jdon的书基本上为零呢?为什么Jdon没有得到企业的认可,并大量应用呢?
因此,Jdon应该是缺少一些东西,或者说是关键的东西。为什么同样是开源,spring能得到那么多用户的反馈、并不断改进,而针对jdon的bug建议、用户反馈却寥寥无几呢?是技术不行还是市场推广不行?Jdon虽然发布到了5.0,但除了前面的一两个版本,后续的版本都没有带来什么变革性的东西,难道是后劲不足?其实这些问题都值得我们每一个开源爱好者思考。本文只谈谈我对技术上觉得现在Jdon的存在的不足,以供大家参考。
设计上的问题
技术的进步是永无止尽的。Jdon框架的设计仍然还存在着很多的问题。比如,要求用户领域模型继承Jdon的Model类或实现ModelIF接口,添删改查的业务实现类中要包含类似createXxx(EventModel em)这样的方法,导致整个框架对业务层的东西侵入性太严重,不符合实际应用。另外,过多与Struts1.x紧密绑定的设计也不可取。
容器的改进
picocontainer有很多局限性,最要命的是不支持setter方法注入,这个通过实践证明是用得最多的。picocontainer的自动注入应该是按类型注入的,在实际应用中也存在很多限制。比如下面的EmailDefine这样的构造子可阅读性是非常差的,如果存在多个这样的构造子,稍不注意就会搞错:
<
component name
=
"
emailDefine
"
class = " com.jdon.jivejdon.service.imp.account.EmailDefine " >
< constructor value = " J道:用户名和密码 " />
< constructor value = " 您好:您索要的jdon.com网站登陆用户和密码如下: " />
< constructor value = " Jdon.com --- 解惑授道 专业的解决之道 " />
< constructor value = " Jdon.com " />
< constructor value = " [email protected] " />
</ component >
public class EmailDefine{
public EmailDefine(String s1, String s2, String s3, String s4, String s5){
}
}
class = " com.jdon.jivejdon.service.imp.account.EmailDefine " >
< constructor value = " J道:用户名和密码 " />
< constructor value = " 您好:您索要的jdon.com网站登陆用户和密码如下: " />
< constructor value = " Jdon.com --- 解惑授道 专业的解决之道 " />
< constructor value = " Jdon.com " />
< constructor value = " [email protected] " />
</ component >
public class EmailDefine{
public EmailDefine(String s1, String s2, String s3, String s4, String s5){
}
}
因此,Jdon的IoC部份,需要增加针对setter方法注入的支持才行。
统一业务组件
Jdon中的业务组件包括“普通类服务pojoService”、“普通类component”、“EJB服务ejbService”等,不同的业务组件配置的方法还不一致,有的使用<pojoService>、有的使用<component>、有的使用<ejbService>,其实既然是松耦合,就应该是一致的。在一个应用中,又如何能完全区分出谁该扮演component、谁该叫service呢?另外拦截器还要在单独的配置文件中进行配置,这样会增加了配置及处理的复杂程度。下面是当前jdon中的一些配置片断:
<
pojoService
name
="testService"
class
="com.jdon.framework.test.service.TestServicePOJOImp"
/>
< component name ="jdbcDAO" class ="com.jdon.framework.test.dao.JdbcDAO" >
< constructor value ="java:/TestDS" />
</ component >
< context-param >
< param-name > containerConfigure </ param-name >
< param-value > WEB-INF/mycontainer.xml </ param-value >
</ context-param >
……
< context-param >
< param-name > aspectConfigure </ param-name >
< param-value > WEB-INF/myaspect.xml </ param-value >
</ context-param >
EJB服务
< ejbService name ="testService" >
< jndi name ="TestEJB" />
< ejbLocalObject class ="com.jdon.framework.test.ejb.TestEJBLocal" />
</ ejbService >
< ejbService name ="calculator" >
< jndi name ="CalculatorEJB3" />
< interface class ="com.jdon.framework.test.service.Calculator" />
</ ejbService >
< component name ="jdbcDAO" class ="com.jdon.framework.test.dao.JdbcDAO" >
< constructor value ="java:/TestDS" />
</ component >
< context-param >
< param-name > containerConfigure </ param-name >
< param-value > WEB-INF/mycontainer.xml </ param-value >
</ context-param >
……
< context-param >
< param-name > aspectConfigure </ param-name >
< param-value > WEB-INF/myaspect.xml </ param-value >
</ context-param >
EJB服务
< ejbService name ="testService" >
< jndi name ="TestEJB" />
< ejbLocalObject class ="com.jdon.framework.test.ejb.TestEJBLocal" />
</ ejbService >
< ejbService name ="calculator" >
< jndi name ="CalculatorEJB3" />
< interface class ="com.jdon.framework.test.service.Calculator" />
</ ejbService >
Annonation支持
EJB3、JPA以及整个JavaEE5都已经大量引入的java5的annonation来描述源数据,spring、struts2、hibernate以及国内的operamasks、easyjweb等框架也都大量引入了对annonation的支持,实践证明annonation也是一个非常好的东西。而到上前为止,在这一方面jdon框架中还没有看到有对annonation的支持。最好能把ejbService、pojoService及component等配置可以通过简单的注解来实现,甚至cache、拦截器等也应该引入annonation的支持。
其它Web框架支持
虽然Struts1.x的市场仍然还是很大,但我们应该清醒地看到,今天已经有很多正在逐渐取代Struts1.x的Web框架出现,比如webwork(或struts2)、easyjweb、operamasks(jsf)、grails等。Jdon作为一个应用框架,要能获得更多的应用支持,必须支持与这些框架进行集成,提供更多的支持,让开发者有更多的选择权。
持久层JPA支持
JPA不用质疑肯定是一个趋势,spring、grails、appfuse、struts2、jsf等当前主流的框架都提供了对jpa全面的支持,并且把jpa作为java持久层的首选解决方法。另外, springside、easyjf等国内的开源组织,都已经在他们的框架中大量的使用到了jpa持久层技术。因此,Jdon也应该提供更多的对Jpa的支持。jdon现在所提供的对持久层的封装显得较单薄,很多时候无法满足项目中需求。
过分EJB不一定是好事
EJB是一个好东西,但EJB3以后EJB从技术上来说应该就不算是什么东西了。三年前的Jdon考虑到当时对EJB的支持情况,过分考虑EJB的需求是对的。但都EJB都已经从重量变化轻量了,jdon框架中的很多为支持EJB而提供接口、方法等应该根据实现的情况作相应的调整、改进,不用再“过分EJB”。比如,下图是Jdon中的TargetMetaDef组件定义核心接口的类结构图:
6、写在最后
也许真像就像我在 《中国java开源界最可爱的人们》中说过的一样,Jdon现在给人有点高处不胜寒的感觉。然而我更希望的是Jdon不要停止前进的步伐,同时也希望更多人一起来为Jdon以后的发展、为国人的开源做出力所能及的贡献。拥抱开源肯定是不会错的,能投身到开源的事业中当然就会让我们的职业生涯更加有意义的。
泡一杯清茶,细品Jdon中的代码,他可以带给你很多东西。程序是一种艺术,艺术不一定都实用,他能带给我们艺术享受就已经足够。
也许我们永远都用不到这框架,也许几年以后,你会发现编程的技术又有翻天覆地的变化,如果你发现你为这种变化做出了一些贡献,你会感到非常欣慰。从这一点来说,我认为板桥无论如何肯定都是应该是快乐的。
版权声明:本文版权由Blogjava的小雨开源所有,受法律保护。欢迎转载,转载请保留作者版权声明及连接。
附:
其它相关背景资料:
- 2005年Jdon框架入选SUN公司的Java.net企业应用目录(与AppFuse同列)。
- Jdon框架在全世界最大开源网站Sourceforge的项目网址
- 世界顶级Java网站TheServerSide有关Jdon框架 2004年新闻1 新闻2
- 2004年底Jdon框架刚推出时国内各种评论
- 时值2006年8月道友lhsail对Jdon框架的看法。
- 一位Jdon框架用户的感言
- 对话Jdon 一个开源人的孤独告白.
- 更多关于JdonFramework讨论系列1 以及 讨论系列2