任何试过过Flickr、GMail、Google Suggest或者是Google Maps的人都会意识到一种新型的动态Web应用正在逐渐浮出水面。这些应用外观和表现都和传统的桌面应用程序很像,而他们不需要依赖于插件或者是特定于 浏览器的功能。过去Web应用只是一系列HTML页面,他们任意一部份内容的更改都必须重新载入页面。像JavaScript编程语言和层叠样式表 (CSS)之类的技术已经成熟,可以有效地应用他们来创建高动态的Web应用,而且可以运行在所有的主流浏览器中。本文将会详细介绍你马上就可以使用的一 些技术,让他们使你的Web应用像桌面应用更加丰富和更有交互性。
介绍异步JavaScript技术和XML(AJAX)
使用JavaScript技术,一个HTML页面可以异步地对服务器(一般是载入页面的服务器)发送请求并获取XML文档。然后JavaScript可以使用XML文档来更新或改动HTML页面的文档对象模型(DOM)。最近形成了一个术语AJAX(Asynchronous JavaScript Technology and XML)来描述这种交互模型。
AJAX其实不是很新的东西。这些技术对于Windows平台上专注于Internet Explorer的开发人员来说,已经存在好几年了。直到最近,这个技术才被作为Web远程技术或者远程脚本技术被大家了解。Web开发人员也有一段时间 曾经使用过插件、Java applet和隐藏框架来模拟这种交互模型。最近发生的变化是,对XMLHttpRequest对象的支持已经成为所有平台上的主流浏览器都包括的特性了。JavaScript技术的XMLHttpRequest对 象是。尽管在正式的JavaScript技术标准中并没有提到这种对象,然而今天主流的浏览器都对他提供了支持。而当代的浏览器如Firefox、 Internet Explorer以及Safari在JavaScript技术和CSS的支持上有些细微的差别,但是这种差别是可以处理的。如果你要考虑支持较老的浏览 器,AJAX也许就不能成为你的解决方法。
基于AJAX的客户端之所以独特的原因是客户端包含了用JavaScript嵌入的特定于页面的控制逻辑。应用JavaScript技术的页面基于 事件进行交互,如文档载入、鼠标点击、焦点改变甚至是定时器。AJAX交互使得表现层逻辑更加清晰地与数据分离。一个HTML页面也可以根据需要每次读入 适当的数据,而不是每次需要显示一个更改时都重新载入整个页面。AJAX要求一种不同的服务器架构来支持它这种交互模型。以前,服务器端Web应用关注于 对每个导致服务器调用的客户端事件都生成HTML文档。然后客户端对每个回应都要重新读入并重新渲染完整的HTML页面。富Web应用(Rich Web Application)关注于,让一个客户端获取一个HTML文档让它表现为一个模板或者是一个容器,可以基于事件并使用从服务器端组件中获取的XML 数据来对文档注入内容。
一些AJAX交互的应用如:
- 实时表单数据检验:像用户ID、序列号、邮政编码或者是特殊的票据代码这类需要服务器端验证的数据也可以在用户提交表单之前进行验证。
- 自动补全:像电子邮件地址、姓名或城市名之类的表单数据都可以根据用户情况自动补全。
- 处理细节操作:根据一个客户端事件,一个HTML页面可以根据现存的一些数据再去获取更多详细的信息,如现在有一个产品列表,客户端可以控制查看单独的产品信息而无需刷新页面。
- 复杂的用户界面控件:像树型控件、菜单和进度条之类不要求页面刷新的控件也能实现。
- 页面内刷新数据:HTML页面可以从服务器上查询最新的数据如分数、股指、天气还有其它的特定于应用的数据。
- 服务器端通知:一个HTML页面可以通过对服务器进行定时查询来模拟一个服务器的事件通知推送,实现像通知客户端一个消息、刷新页面数据或将客户端重定向到另一个页面。
这个列表并未把所有的应用都列出来,但它已经显示了AJAX交互可以让Web应用比从前能做更多的事情。但尽管这些好处是值得关注的,这种方式也有一些缺点:
- 复杂度:服务器端开发人员必需理解,HTML客户端页面中的表现层逻辑以及生成HTML 客户端页面所需的XML内容的服务器端逻辑。HTML页面开发人员必须了解JavaScript技术。如果开发新的框架和发展已有的框架来支持这种交互模 型,那么AJAX应用的创建就会越来越简单。
XMLHttpRequest对象的标准化:XMLHttpRequest对象还不是JavaScript技术标准的一部分,这就意味着根据客户端的不同,应用的行为也有所会不同。- JavaScript技术的实现:AJAX交互极大地依赖于JavaScript技术,而由于客户端的原因JavaScript还有一些细微的差别。见QuirksMode.org来了解更多关于浏览器之间区别的内容。
- 调试:AJAX应用也难于调试,因为流程逻辑是同时嵌在客户端中和服务器上的。
- 代码可见:客户端的JavaScript可以很容易通过“查看源代码”被人看见。一个没有良好设计的AJAX应用很可能被黑客攻击或被他人剽窃。
当开发人员在使用AJAX交互模型上获得更多的经验后,AJAX技术的框架和模式就会慢慢浮现出来。现在就关注于完全通用的AJAX交互框架,还为 时过早。本文和相关的解决方案将关注于在现有的Java 2平台企业版(J2EE)上如何对AJAX进行支持,像servlet,JavaServer Page(JSP)软件、JavaServer Face应用和Java标准标签库(JSTL)。
AJAX交互剖析
现在我们已经讨论了AJAX是什么以及一些高层次的问题。那现在让我们把所有的零件放在一起来展示一个具有AJAX的J2EE应用。
首先考虑一个例子。一个Web应用包括了一个静态HTML页面,或者是一个由JSP生成的HTML页面,这个JSP中还包括了一个HTML表单,它需要服务器端逻辑来对表单中的数据进行检验,而不用刷新页面。一个名为ValidateServlet服务器端组件(servlet)用来提供这种验证逻辑。图一描述了这种具有验证逻辑的AJAX交互的细节。
|
|
图1: 一个提供验证逻辑的AJAX交互
|
以下条目代表了图1中出来AJAX交互的过程:
- 发生一个客户端事件。
- 创建和配置一个
XMLHttpRequest对象。 XMLHttpRequest对象进行一个调用。ValidateServlet对请求进行处理。ValidateServlet返回一个包含了结果的XML文档。XMLHttpRequest对象调用callback()函数并处理结果。- 更新 HTML DOM。
现在让我们逐个研究这个AJAX模型的每一步。
1.发生一个客户端事件。
在一个事件发生时可以调用相应的JavaScript函数。在这里,validate()函数可以被映射到一个链接或者是表单组件的onkeyup事件上去。
<input type="text" |
每次用户在表单域中按下一个键时,表单元素将都调用validate()函数。
2. 建立和配置一个XMLHttpRequest对象
创建和配置一个XMLHttpRequest对象
var req; |
validate()函数建立了一个XMLHttpRequest对象并对象中的open函数。open函数需要两个参数:HTTP方法,可以是GET或POST; 和对象进行交互的服务器端组件的URL;一个布尔变量,表示是否要进行异步调用。API是XMLHttpRequest.open(String method, String URL, boolean asynchronous)。如果一个交互被设置为异步, (true) 那就必须指明一个回调函数。可以使用req.onreadystatechange = callback;来设置这个交互的回调函数。详细内容见第六节。
3.XMLHttpRequest对象进行调用
当收到了语句req.send(null);,就会进行一次调用。HTTPGET的情况下,内容可以是null或者留空。当调用XMLHttpRequest的这个函数时,也会对已经配置了的URL进行调用。在下面这个例子中,要发送的数据(id)将作为一个URL参数。
使用HTTPGET,两个重复的请求将返回同样的结果。当使用HTTPGET方法时,要注意URL的长度,包括已经转义的URL参数,可能会受到某些浏览器和服务器端的Web容器的限制。当发送的数据会影响到服务器端的应用程序的状态时,就应该使用HTTPPOST方法。使用HTTPPOST必须要对XMLHttpRequest对象设置一个Content-Type头,使用以下语句:
req.setRequestHeader("Content-Type", "application/x-www-form-urlencoded"); |
当从JavaScript中发送表单值得时候,你应该考虑对字段值进行编码。JavaScript中有一个函数escape(),应该用他来确保区域化的内容被正确编码,同时特殊字符也被正确转义。
4. ValidateServlet对请求进行处理.
一个映射到URI "validate" 的servlet将检验user ID是不是已经在数据库中存在了。
一个servlet处理一个XMLHttpRequest ,就像对待其它的HTTP请求一样。下面的例子显示了服务器从请求中抽取出id参数并检验是否被占用了。
public class ValidateServlet extends HttpServlet { |
在这个例子中,一个简单的HashMap用来存放存在的用户名。在这个例子中,我们假设用户的ID是duke。
5.ValidateServlet返回一个包含结果的XML文档
用户ID "duke" 在users HashMap的用户ID列表中出现了。将在应答中写一个包含值为invalid的message元素的XML文档。更复杂的用例将要求DOM、XSLT或其他API来生成这个应答。
response.setContentType("text/xml"); |
开发人员必须注意两个事情。第一,Content-Type必须设为text/xml。第二,Cache-Control必须设为no-cache。XMLHttpRequest对象只会处理Content-Type为text/xml的应答,同时把将Cache-Control设为no-cache将确保浏览器不会从缓存相同的URL(包括参数)返回的应答。
6.XMLHttpRequest对象调用callback()函数并处理结果。
XMLHttpRequest对象已经配置为当有readyState改变的时候就调用callback()函数。让我们假设已经ValidateServlet调用了而且ValidateServlet是4,表示XMLHttpRequest的调用已经完成。HTTP状态代码200表示一个成功的HTTP交互。
function callback() { |
浏览器维护了一个所显示的文档的对象形式(也就是所谓的Docuemt Object Model或DOM)。HTML页面中的JavaScript可以访问DOM,同时在页面载入完之后,可以使用API来修改DOM。
根据成功的请求,JavaScript代码可以修改HTML页面的DOM。从ValidateServlet获得的对象形式的XML文档可以通过req.responseXML在JavaScript中获得,req是一个XMLHttpRequest对象。DOM API给JavaScript提供了获取这个文档中的内容以及修改HTML页面的DOM的方法。所返回的字符串形式的XML文档可以通过req.responseText获得。现在我们看看如何在JavaScript中使用DOM API,先看以下从ValidateServlet返回的XML文档。
<message> |
这个例子是一个简单的只包含了一个message元素的XML片断,里面只有一个简单的字符串valid或invalid。一个更高级的例子可以包含多于一个的消息和可以给用户看的有效的名字:
function parseMessage() { |
parseMessages()函数将处理一个从ValidateServlet获取的XML文档。这个函数会调用setMessage()with the,并给出message作为参数来更新HTML DOM。
7.更新HTML DOM
JavaScript技术可以使用很多API从HTML DOM中获得任何元素对象的引用。推荐的获得元素引用的方法是调用document.getElementById("userIdMessage"), "userIdMessage"是HTML文档中出现的一个元素的ID属性。有了这个元素的引用,就可以使用JavaScript来修改元素的属性、修改元素的样式、添加、删除或修改子元素。
一个常见的改变元素主体内容的方法是设置元素的innerHTML属性,如下所示:
<script type="text/javascript"> |
受到影响的那部分HTML页面会立刻根据innerHTML的设置重新渲染。如果innerHTML属性包含类似<image>或者是<iframe>之类的元素,那么由那些元素所指定的内容同样会被获取并渲染。
这种途径的主要缺点是HTML元素是作为字符串硬编码在JavaScript中的。JavaScript中硬编码的HTML标记不是一种好的实践, 因为它使代码难于阅读、维护和修改。我们应该考虑在JavaScript中使用DOM API来创建和修改HTML元素。把显示和JavaScript代码的字符串混在一起只会让页面更难于阅读和编辑。
另一种修改HTML DOM的方法是动态地产生新的元素并把他们作为子元素追加到目标元素,如下面的例子所示:
<script type="text/javascript"> |
这个范例展示了JavaScript技术的DOM API可以用来更有目的地建立或改变一个元素。当然JavaScript的DOM AP在不同的浏览器上也可能有差别,所以你必须在开发应用程序时小心。
作者: Greg Murray
http://developer.51cto.com/art/200704/45811.htm
J2EE系统优化的几点体会(一、对象)
说到系统优化,是一个比较复杂的问题,涉及到软件的各个方面:需求、模块划分、数据库设计、程序编码以及一些特殊的优化方法如缓存技术等。而不同的应用又有其特殊的优化策略和技术。同时优化是贯穿系统从需求到实现再到维护的各个阶段的一项活动,而在各个阶段又有其不同的着眼点和具体方法。
前言
在java 的应用领域,有许多成熟的开源软件,利用它们一样可以打造优越、灵巧的应用框架,本文首先将先介绍 所要构建的系统结构和借助的开源产品。然后逐一讲解各开源软件的具体运用。希望本文能给那些正在学习这些 开源软件的同行提供参考。续该文之后笔者将结合这些开源软件,借助实际项目,做更深入的应用讲解。
本文中涉及到的例子、源代码均经过本人调试,且正常运行的,但是不否定这其中有不合理的使用。运行环境: win2000 adivance server + tomcat5.0.25 + mysql-4.0.14-nt
几种面向对象的数据库访问策略:
1 JDBC
是最原始的方法,写sql语句,维护性差
下面面向对象的方法:
例如update: 要先取出对象,更新对象,然后再保存
OrderInfo order = orderService.getOrder(orderId);
order.setStatus(new Integer(2));
orderService.updateOrder(order);
2 Hibernate
使用Hql
3 iBatis
将查询和更新放在maps文件中
<dynamic-mapped-statement name="searchProductList" result-map="result">
select productid, name, descn, category from product
<dynamic prepend="where">
<iterate property="keywordList" open="(" close=")" conjunction="OR">
lower(name) like #keywordList[]# OR lower(category) like #keywordList[]# OR lower(descn) like #keywordList[]#
</iterate>
</dynamic>
4 EasyDBO:
有三种实现方法,我们只看其中采用annotation的
@Table(tableName="Customer")
public class Customer implements Serializable {
来确定表名
采用反射的方法,不需要配置文件:
public List getRootCustomers() {
return this.dao.query(Customer.class,"(parent_id is null or parent_id='')");
}
List list=dao.query(CustomerPrice.class, "customer_id="+cu.getId()+" and product_id="+p.getId()+" order by vdate desc");
该方法已经和ADODB很像了,但和adodb不同的是,仍然没有实现完全自动的Plain SQL转换到PO.
5 PHP's ADODB的j2ee移植
以jdbc查询sql为基础,通过反射,范型等方法自动装载。
adodb的方法和上面的easydbo很像,不过在obj到sql的生成上更加成熟一些,驱动也更加多
对于查询
/////////////////////////////////////////////////////////////////////////////
//Function: 完成ResultSet对象向ArrayList对象为集合的对象的转化
//Para:sql,指定的查询Sql
//Para:className,Sql相对应得JavaBean/FormBean类的名字
//Return:以类className为一条记录的结果集,完成ResultSet对象向ArrayList对象为集//合的className对象的转化
//////////////////////////////////////////////////////////////////////////////
public ArrayList Select(String sql,String className){
ArrayList paraList=new ArrayList();
try{
if (conn == null){
Connection();
}
PreparedStatement stmt = conn.prepareStatement(sql);
ResultSet rs = stmt.executeQuery();
String recordValue="";
Object c1=null;
paraList=new ArrayList();
ResultSetMetaData rsmd = rs.getMetaData();
int columnCount = rsmd.getColumnCount();
while (rs.next()){
c1=Class.forName(className).newInstance();
for (int i=1; i<=columnCount; i++) {
if(rs.getString(rsmd.getColumnName(i))!=null){
recordValue=rs.getString(rsmd.getColumnName(i));
}else{
recordValue="";
}
Method m=c1.getClass().getMethod(getSetMethodName(rsmd.getColumnName(i)),new Class[]{recordValue.getClass()});
m.invoke (c1, new Object[]{recordValue});
}
paraList.add(c1);
}
}catch(SQLException ex){
}catch(ClassNotFoundException e){
}catch(NoSuchMethodException e) {
}catch(InvocationTargetException e){
}catch (IllegalAccessException e){
}catch(InstantiationException e){
} finaly{
closeConnection();
return paraList;
}
}
//Function:取得用户列表
//Para:
//Return:返回用户列表
/////////////////////////////////////////////////////////////////////////////
public ArrayList getUsers(){
ArrayList ret=null;
DatabaseManage db=new DatabaseManage();
String sql=" select usr_id,usr_name "
+" from users " ; //该方法的好处是SQL可以随便写,需要的字段也可以随便写,甚至免去了持久层的LazyLoad
ret=db.Select(sql,"com.domain.User");
return ret;
}
对于单张表,用PO/Formbean来存放
如果有关联多张表,需要一个VO/Map来存放
对于保存和更新
检查对象里面的每一个属性,如果不是null,就组成SQL语句,
更新的时候,先查出这个对象,如果属性不是null,并且属性值变了,才将该属性组成SQL语句
这种方法和Hibernate/Ibatis相比可能牺牲一些性能,但是免去了大量的配置文件,如果对字段有特殊的要求,可以
通过annotation来定义。
Annotation可是个好东西,根据jdk手册:Annotations can be read from source files, class files, or reflectively at run time.
所以可以充分利用反射读取annotation来减少代码。
下面是一些例子
@com.acme.util.Name(first=Alfred, middle=E., last=Neuman)
@Table(tableName="Customer")
@ManyToOne(column = "parent_id", fieldType=java.util.HashSet.class,type = Customer.class,lazy=false)
读取的方法是在反射里面使用下面的方法
<T extends Annotation> T xxx = getAnnotation(Class<T> annotationClass)
当然,实现必须声明annotationClass,下面是手册的一个简单例子
/**
* Describes the Request-For-Enhancement(RFE) that led
* to the presence of the annotated API element.
定义一个标签记号
*/
public @interface RequestForEnhancement {
int id();
String synopsis();
String engineer() default "[unassigned]";
String date(); default "[unimplemented]";
}
这个标签,和它的属性都在上面声明了,下面是某个函数中需要用到这个标签的示例
@RequestForEnhancement(
id = 2868724,
synopsis = "Enable time-travel",
engineer = "Mr. Peabody",
date = "4/1/3007"
)
public static void travelThroughTime(Date destination) { ... }
因此我们如果想要调用这个标签
for (Method m : Class.forName('假设是travelThroughTime所在的类名').getMethods()) {
if (m.isAnnotationPresent(RequestForEnhancement.class)) {
RequestForEnhancement rfe = m.getAnnotation(RequestForEnhancement.class);
下面就可以取得rfe的属性了
}
}
Commons Attributes也是一个替代jdk标准annotation的方案
PHP中的ADODB之所以强大,高效是在于PHP数组的强大和弱变量定义的方便。
当然这里的每一个对象就相当于对应数据库的一张表,这样也很好。
因为在业务相当复杂的时候,关联要尽量少用,把业务精心设计在数据库表上面而非java对象的集合的关联上。
在大型应用中,一般1:1的关联都多使用View来处理关联,1:n和n:n的关联,建议还是在DAO里面手动保存,装载和更新
===================
此外还有一些方法,可以方便我们快速的将RS变成可操作的对象,简单举几个例子如下
1 commons dbutils
Custom RowProcessor
java.lang.Object[] toArray(java.sql.ResultSet rs)
Convert a ResultSet row into an Object[].
java.lang.Object toBean(java.sql.ResultSet rs, java.lang.Class type)
Convert a ResultSet row into a JavaBean.
java.util.List toBeanList(java.sql.ResultSet rs, java.lang.Class type)
Convert a ResultSet into a List of JavaBeans.
java.util.Map toMap(java.sql.ResultSet rs)
Convert a ResultSet row into a Map.
Custom BeanProcessor
java.lang.Object toBean(java.sql.ResultSet rs, java.lang.Class type)
Convert a ResultSet row into a JavaBean.
java.util.List toBeanList(java.sql.ResultSet rs, java.lang.Class type)
Convert a ResultSet into a List of JavaBeans.
2 commons beanutils
ResultSetDynaClass (Wraps ResultSet in DynaBeans)
Connection conn = ...;
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery
("select account_id, name from customers");
Iterator rows = (new ResultSetDynaClass(rs)).iterator();
while (rows.hasNext()) {
DynaBean row = (DynaBean) rows.next();
System.out.println("Account number is " +
row.get("account_id") +
" and name is " + row.get("name"));
}
rs.close();
stmt.close();
============
总得来说,使用反射机制可以极大得方便对数据的各种操作,使操作变得更加透明,无需配置文件在 最近的Web应用开发中,Hibernate,Spring,Struts框架做为开源的轻量级框架,正被越来越多的开发者使用,而如何将这些框架集成起 来,应用到WebSphere Portlet开发中去,将是本文讨论的内容。本文还描述了将这些框架应用到Portlet上的时候,遇到的一些细节问题。
引言
Hibernate 是最近比较流行的一个用来处理O/R Mapping的持久层框架。它的工作原理是通过文件把值对象和数据库表之间建立起一个映射关系,这样,我们只需要通过操作这些值对象和 Hibernate提供的一些基本类,就可以达到使用数据库的目的。使用Hibernate可以很好的将持久层和逻辑层进行隔离。请参阅参考资料一节获得 更多Hibernate框架的信息。
Spring框架是一个包含了MVC层,中间层和持久层管理的框架,其核心模块是bean管理,现在很多的应用都采用Spring的bean管理机制来管理其逻辑层。请参阅参考资料一节获得更多Spring框架的信息。
Struts 框架是Apache Jakarta项目的一部分,它为构建Web应用程序提供了很流行的MVC框架。WebSphere Portal V5提供了Struts Portlet框架,这个框架针对Portlet应用,将Struts的类包和taglib在URL生成,URL解析等处,做了自己的改写,使 Portlet框架也可以支持Struts应用,将其作为Portlet来部署。
本文将通过构建一个使用Hibernate, Spring,Struts框架的Portlet应用,来描述如何在Portlet应用中使用这些框架。对于那些并不熟悉使用这些框架进行开发的Web应 用程序的读者来说,本文提供了足够的信息使您可以掌握一些基础知识。但本文并不是一篇介绍如何使用这些框架的教程。
在本文中讨论的应用程序的开发或部署中用到了以下产品:
WebSphere Portal 5.0.2.2
WebSphere Studio Application Developer 5.1.2
请 您注意!如果您的部署服务器Portal Server的版本低于5.0.2.2,您在部署web.xml的时候,在过滤器设置上将会遇到问题导致不能部署,从而无法通过设置过滤器来解决应用程 序的中文问题。如果您的开发环境低于WebSphere Studio Application Developer 5.1.2,您可能不能得到本文中所述的Struts Portlet的全部支持。
示例应用程序
我们的示例应用程序将实现对一组持久数据的标准的创建、读取、更新、删除(Create/Read/Update/Delete,CRUD)操作。这个示例应用程序为一个新闻编辑程序,用户可以在列表中查看新闻,并新建,修改,删除新闻。
虽然这个示例应用程序是一个比较简单的应用,但为了更好的阐述Hibernate,Spring和Struts的作用范围,我们还是将这个应用程序进行分层的阐述:
应用程序的分层
和通常大多数的Web应用程序一样,本应用程序分为四层,这四层是:presentation(描述),business(业务),persistence(持久)和domain model(域模型)。
表示层(The Presentation Layer)
一般来讲,一个典型的Web应用的的末端应该是表示层。用来管理用户的请求,做出相应的响应,给出显示。在这里,我们使用了Struts Portlet框架来实现本应用程序的表示层。
域模型层(The Domain Model Layer )
域 模块层由实际需求中的业务对象组成,即我们常说的BO(Business Object) 比如, Order , Pet等等。 开发者在这层 不用管那些DTOs,仅关注domain object即可。 例如,Hibernate允许你将数据库中的信息存放入对象(domain objects),这样你可以在连接断开的情况下把这些数据显示到UI层。 而那些对象也可以返回给持久层,从而在数据库里更新。
业务层(The Business Layer)
一 个典型Web应用的中间部分是业务层或者服务层。这一层最容易受到忽视,从而导致大量的代码紧密的耦合在一起,从而使整个程序变的难以维护。在这里,我们 使用Spring框架来解决这个问题,Spring把程序中所涉及到包含业务逻辑和Dao?的Objects--例如transaction management handler(事物管理控制)、Object Factories(对象工厂)、service objects(服务组件)--都通过XML来配置联系起来,从而使业务层变得非常灵活和易于维护。
持久层(The Persistence Layer)
持 久层是我们典型的Web应用的另一个末端。现在已经有很多很好的ORM开源框架来解决持久层的各种问题,尤其是Hibernate。 Hibernate为Java提供了OR持久化机制和查询服务, 它还给已经熟悉SQL和JDBC API 的Java开发者一个学习桥梁,他们学习起来很方便。 Hibernate的持久对象是基于POJO和Java collections。
示例介绍
本文的示例实现对员工信息的增删查改等基本功能。用 Tapestry 实现表示层,用 Hibernate 开发持久层,用 Spring 提供事务控制等跨模块服务,并用 Acegi 进行安全管理。本示例只用到一个域模型:Employee,下面是它的 UML 图。
图 1. Employee UML 图
搭建开发环境
本文的代码开发平台采用的是 Windows 操作系统,因此,以下环境设置也是针对 Windows 操作系统的。
- 从 AppFuse 下载页面 下载 appfuse-tapestry-1.9.3-src.zip,并解压缩在任意目录下。这个 zip 已经定制了使用 Tapestry 作为表现层的实现框架,因而使用起来较为直接。
- 从 http://java.sun.com 下载最新的 JDK,并安装或解压缩到任意目录下。本文采用 JDK 1.5.0。设置环境变量 JAVA_HOME 指向 JDK 所在的目录,并在 PATH 中添加 %JAVA_HOME%/bin。
- 从 http://jakarta.apache.org/tomcat 下载最新版的 Tomcat,并安装或解压缩到任意目录下。本文采用 Tomcat 5.5.17。设置环境变量 CATALINA_HOME 指向 Tomcat 的安装目录。
- 从 http://ant.apache.org下载最新版的 Ant,并解压缩到任意目录下。AppFuse 要求的最低版本是 1.6.2,本文采用的是 1.6.5。设置 ANT_HOME 指向 Ant 所在的目录,并在 PATH 中添加 %ANT_HOME%/bin。另外,要拷贝一个 junit.jar 到 %ANT_HOME%/lib 下,如果 lib 下没有 junit.jar,AppFuse 的脚本在运行时会给出警告信息。junit.jar 可以从 http://www.junit.org 获得,也可以从 %AppFuse%/lib/junit3.8.1 目录下获得。
- 从 http://www.mysql.com 下载最新版的 MySQL,并安装或解压缩到任意目录下。本文采用的是 5.0。
- 从 http://www.eclipse.org 下载 Eclipse 3.1 或 3.2,安装到任意目录下。
AppFuse 的 Ant 脚本可以在命令行中运行,也可以在 Eclipse 里运行。有关如何在 Eclipse 里执行 Ant 脚本,请参考《用Eclipse开发AppFuse应用》。到此,我们已经为 AppFuse 开发应用准备好了环境,下面让我们开始使用 AppFuse 创建项目。
新建项目
AppFuse 的便捷与强大之处在于它已经为我们提供了多种开源框架的集成,并且通过使用 Ant 将所有的构建过程自动化。另外,AppFuse 利用 XDoclet 能够为我们生成绝大多数重要的代码,例如 dao 类、service 类以及测试用例,等等,并且能够将大量的配置文件也一并生成好,从而极大地节省了开发人员的时间。
用 AppFuse 进行开发通常有三种模式:“自上而下”,“自下而上”以及“混合模式”。采用“自上而下”(由 Java 对象向数据库对象创建的过程)的方式固然比较符合“面向对象”的设计思维,但是为此要编写大量的 XDoclet 的 tag 也确是一件痛苦的事情。相比较而言,采用“自下而上”(由数据库对象生成 Java 对象的过程)就显得简单许多 -- 只需要提供数据库表结构。然而,对于较为复杂的系统,尤其是类之间具有大量的关联的情形,仍然需要采用“自上而下”的创建模式。因此,在实际的项目开发 中,将两种模式进行混合使用比较常见,这也就是“混合”模式。本文采用“自下而上”的模式。