很多人都直接编辑html,保存成xls就当成excel报表了。挺方便的,于是我写了这个简化工作的工具类——将一个html的表格模板解析成一个xls报表
模板如下
<?xml version="1.0" encoding="GB2312" ?>
<div style="width:100%;height:450;overflow-x:auto;overflow-y:auto">
<table width="100%" border="1" cellspacing="2" cellpadding="0">
<tr id="title" bgcolor="#fefcce">
<td nowrap="true" >客户</td>
<td nowrap="true" >产品</td>
<td nowrap="true" >中文名称</td>
<td nowrap="true" >英文名称</td>
<td nowrap="true" >产品分类</td>
<td nowrap="true" >包装</td>
<td nowrap="true" >单位</td>
<td nowrap="true" >数量</td>
<td nowrap="true" >冻结数量</td>
<td nowrap="true" >可用数量</td>
<td nowrap="true" id="CUBIC"></td>
<td nowrap="true" id="WEIGHT"></td>
</tr>
<tr id="record">
<td nowrap="true" id="CUSTOMERID"></td>
<td nowrap="true" id="SKU_ID"></td>
<td nowrap="true" id="SKU_DESCR_C"></td>
<td nowrap="true" id="SKU_DESCR_E"></td>
<td nowrap="true" id="SKU_CLASS"></td>
<td nowrap="true" id="PACKAGE_ID"></td>
<td nowrap="true" id="UOM"></td>
<td nowrap="true" id="QUANTITY"></td>
<td nowrap="true" id="FREEZE_QUANTITY"></td>
<td nowrap="true" id="AVAILABLE_QUANTITY"></td>
<td nowrap="true" id="CUBIC"></td>
<td nowrap="true" id="WEIGHT"></td>
</tr>
</table>
</div>
工具类如下
public class ExcelTemplateUtil {
private static String CHARSET = "";
private static final String ROOT = "ROOT";
private static final String TITLE = "TITLE";
private static final String RECORD = "RECORD";
private static Map temp = new HashMap();
public static String generateListToTemplate(Object titleObj, List recordList, File templateFile)
{
readTemplateFile(templateFile);
ByteArrayOutputStream os = (ByteArrayOutputStream) builderExcelOutput(titleObj, recordList);
return removeXMLHeader(os);
}
public static void readTemplateFile(File file)
{
try {
Document templateDocument = new SAXReader().read(file);
Element root = templateDocument.getRootElement();
List trList = root.selectNodes("//div/table/tr");
Element titleTemp = (Element) trList.get(0);
Element recordTemp = (Element) trList.get(1);
root.element("table").remove(titleTemp);
root.element("table").remove(recordTemp);
temp.put(TITLE, trList.get(0));
temp.put(RECORD, trList.get(1));
temp.put(ROOT, root);
} catch (DocumentException e) {
e.printStackTrace();
throw new RuntimeException("Parse xml file error, Cause:", e);
}
}
public static OutputStream builderExcelOutput(Object titleObj, List list)
{
ByteArrayOutputStream os = new ByteArrayOutputStream();
Element root = (Element) ((Element) temp.get(ROOT)).clone();
Document document = DocumentHelper.createDocument();
document.setRootElement(root);
Element tableEle = root.element("table");
tableEle.add(parseTitleElement(titleObj));
for (int i = 0; i < list.size(); i++) {
tableEle.add(parseRecordElement(list.get(i)));
}
try {
OutputFormat format = new OutputFormat("", true, "GB2312");
XMLWriter writer = new XMLWriter(os, format);
writer.write(document);
writer.flush();
writer.close();
os.close();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
throw new RuntimeException("Parse outstream error, Cause:", e);
} catch (IOException e) {
e.printStackTrace();
throw new RuntimeException("Parse outstream error, Cause:", e);
}
return os;
}
public static Element parseTitleElement(Object titleObj)
{
Element titleEle = (Element) ((Element) temp.get(TITLE)).clone();
if (null == titleObj) return titleEle;
List tdList = titleEle.selectNodes("td");
Element td;
for (int i = 0; i < tdList.size(); i++) {
td = (Element) tdList.get(i);
fullField(td, titleObj);
}
return titleEle;
}
public static Element parseRecordElement(Object recordObj)
{
Element recordEle = (Element) ((Element) temp.get(RECORD)).clone();
List tdList = recordEle.selectNodes("td");
Element td;
for (int i = 0; i < tdList.size(); i++) {
td = (Element) tdList.get(i);
fullField(td, recordObj);
}
return recordEle;
}
public static void fullField(Element tdEle, Object obj)
{
Attribute att = tdEle.attribute("id");
if (null == att || null == att.getText() || 0 == att.getText().trim().length()) {
return;
}
String fieldName = att.getText();
if (null == fieldName || fieldName.trim().length() == 0) return;
Method[] objMethod = obj.getClass().getDeclaredMethods();
Object value;
for (int i = 0; i < objMethod.length; i++) {
if (("get" + (fieldName.trim())).equals(objMethod[i].getName())) {
try {
value = objMethod[i].invoke(obj, new Object[]{});
value = (null == value ? "" : value);
tdEle.setText(value.toString());
} catch (IllegalAccessException e) {
e.printStackTrace();
throw new RuntimeException(e);
} catch (InvocationTargetException e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}
}
}
public static String removeXMLHeader(OutputStream os)
{
String xml = os.toString();
int position = xml.indexOf(">");
// xml = xml.substring(position+1,xml.length());
// position = xml.indexOf(">");
return xml.substring(position + 1, xml.length());
}
}
调用
OutputStream os = response.getOutputStream();
response.setHeader("Content-disposition", "attachment; filename=" + excelFileName);
response.setContentType("application/msexcel");
File excelTemplateFile = new File(report_path + templateFileName);
String out = ExcelTemplateUtil.generateListToTemplate(titleObj, resultList, excelTemplateFile);
response.getOutputStream().write(out.getBytes());
os.close();
异常争论
异常有两个模型:中止模型和继续模型
中止模型认为异常不应该再回来,他做的是善后工作。而继续模型保持异常时环境,希望再一次能运行成功。
Java采用的是前者(一般语言都是前者),而OS一般采用后者。
Java异常有三类:错误,运行时异常,检查型异常。
官方的观点是
第 39 条:最好为异常条件使用异常。也就是说,最好不为控制流使用异常。
第 40 条:为可恢复的条件使用检查型异常,为编程错误使用运行时异常。
第 41 条:避免不必要的使用检查型异常。
第 43 条:抛出与抽象相适应的异常。(使处理异常更直观)
在异常的使用上,专家的观点是很不一样的
C#作者Anders根本就忽略检查型异常。
Bruce Eckel,声称在使用 Java 语言多年后,他已经得出这样的结论,认为检查型异常是一个错误 —— 一个应该被声明为失败的试验。
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
缺点1,代码中包含了过多的catch,使得代码不清晰
缺点2,有时候捕捉的异常没有什么实际意义
缺点3,不够清晰的错误指示。
缺点4,过深的异常层次。
缺点4,性能。
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
Eckel 提倡将所有的异常都作为非检查型的,并且提供将检查型异常转变为非检查型异常的一个方法,同时保留当异常从栈向上扩散时捕获特定类型的异常的能力
Rod Johnson ,他采取一个不太激进的方法。他列举了异常的多个类别,并且为每个类别确定一个策略。一些异常本质上是次要的返回代码(它通常指示违反业务规则),而一些异常则是“发生某种可怕错误”(例如数据库连接失败)的变种。Johnson 提倡对于第一种类别的异常(可选的返回代码)使用检查型异常,而对于后者使用运行时异常。在“发生某种可怕错误”的类别中,其动机是简单地认识到没有调用者能够有效地处理该异常,因此它也可能以各种方式沿着栈向上扩散而对于中间代码的影响保持最小(并且最小化异常淹没的可能性)。
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
解决1:谨慎的抛出检查型异常。或者你认为,你可以处理它。否则,包装为运行时异常。
解决2:如果遵守1,2不是问题
解决3:异常不跨层,否则必须捕捉或者包装。
比如持久层丢出的SalException,你或者丢弃/处理/包装(为运行时异常),或者重新包装为业务层异常。保持JEE层的独立和异常的清晰性。
包装底层异常,保持异常链。
解决4:如果符合1,4也不是问题。再次强调,能捕捉就捕捉。
解决5:减少异常使用,减少层次。
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
在je里面,robin认为异常是流程控制的一部分——当然,考虑到性能问题,这个流程不应该是大概率流程——也就是异常流程
例如用户登录
Try{
用户登录(用户名,密码);
登录成功;
}catch(没有这个用户异常 e){
错误提示界面;
}
Potian则认为,没有用户是正常业务逻辑的一部分
If(!用户业务层.没有这个用户(用户名))错误提示界面;
If(用户业务层.检验密码(用户名,密码))登录成功;
else 登录失败;
Potian认为不应该在一个业务中包含了过多的责任。
Ps:在servlet中,我喜欢仅仅简单的在action中调用最好一个业务层方法就可以完成此action的任务。这意味着我的servlet非常瘦,可以比较容易的被替换。如果采用了potian的办法,则意味着我要把业务层中的代码前移到servlet中来,这模糊了业务层的责任。解决的办法是回到老路子上来。
Ps:我还认为,没有异常的业务方法表达能力太弱,异常给了他们更丰富的表达能力。这使得业务层可以更丰富的表达业务意义。避免将业务责任分散掉。
我认为在业务层中,恰恰要包含足够的责任。不多也不要少(流程分支-2最好)。在别的层次中,要细致一点。
版权声明:可以任意转载,转载时请务必以超链接形式标明文章原始出处和作者信息及本声明
英文原文地址:
http://www.javaworld.com/javaworld/jw-06-2005/jw-0620-tikeswing.html中文地址:
http://www.matrix.org.cn/resource/article/43/43731_Swing_MVC_POJOs.html关键词: Swing MVC POJOs
摘要:TikeSwing 是一个开放源码的Swing框架,它提供了一个高度MVC(模型-视图-控制器)模式的体系结构并且使SWING组件的使用非常简单。它通过将视图组件和JavaBeans直接连接来支持POJO编程模式。在这篇文章中将阐述TikeSwing的特点,并且将示范怎样使用这个框架创建一个清晰的MVC的系结构。(2,400个英文单词;2005年6月20日)
最近,在Java社区里面,丰富的互联网应用程序(RIAs)的兴起成为一个热点话题。另外一些新的技术,像AJAX(异步的JavaScript和XML),MacroMedia Flex, 和Laszlo,以及与Java Web Start一起使用的虽旧而好的Swing,它们都被提议作为RIA技术。
然而,Java社区里面的很多人对Java基础类库(JFC)和Swing提出了批评。Swing在建立高度MVC模式的客户端体系方面不能提供太多的帮助。任何合理的服务器应用程序返回传递的对象,或者称为简单初始Java对象(POJOs),把它传递到客户端的技术证明了J2EE世界的窘境。从POJO范围映射到Swing组件需要太多的手动的代码,反之亦然。
同样的,实现Swing其他的功能,就像线程句柄和验证域,也是很费力的事情。而且有时候Swing组件很难使用:创建一个合适的表格或者树模型通常需要很多的编码,而且需要深入的研究Swing编程文档中的API。
TikeSwing 是一个开放源码的Swing框架,它提供了一个高度MVC(模型-视图-控制器)模式的体系结构并且实现了模型,组件和控制器通信的自动化。它简化了Swing组件的使用,并通过将视图组件和JavaBeans直接连接来支持POJO编程模式。
这篇文章将示范怎样使用TikeSwing创建一个清晰的MVC的体系结构。也将阐述建立TikeSwing组件的原则,并简单描述在这个框架中包含的最佳体验和机制。
MVC体系结构众所周知,MVC范例是推荐的图形用户界面发展的基本体系。它还有很多的可用的变种,就像MVC++, HMVC (Hierarchical MVC), MVC Model 2, MVC Push, and MVC Pull,它们每一个都有些不同之处。TikeSwing基于下面的MVC原则:
●Model 模型:
o来自一些真实世界或者系统的抽象
o包装其数据和函数
o在数据改变时通知观察者 (编者注:observer, 设计模式术语)
●View 视图:
o系统的用户界面
o依附于模型并通过显示界面将它的内容显示出来
o在模型改变时自动刷新受到影响的部分
●Controller 控制器:
o控制应用程序的流程
o接受用户的输入,并根据用户输入指导模型和视图完成任务
下面的图表表示了TikeSwing中MVC的类结构。
图 1. 一个使用TikeSwing的应用的MVC类图
类MyModel, MyView, 和MyController由一个使用框架的应用来实现。MyModel和MyController扩展了TikeSwing的YModel 和YController类。一个视图的类可以是任何实现了YIComponent接口的java.awt.Component。
TikeSwing在装配类结构的时候不使用任何的配置文件。当YController,YModel和视图组件提供了要求的功能特性的时候,扩展适当的类已经足够了。下面讲述如何使用TikeSwing来实现模型、视图和控制器类。
模型TikeSwing的模型是一个为实现视图而包含数据的JavaBeans组件。一个模型类可能包含嵌套的JavaBeans,数组,映射和集合。和标准JavaBeans中要求的一样,所有模型的类变量必须有适当的GET和SET方法。从这种意义上说,TikeSwing就像很多的网络应用程序框架那样工作,所以在不同的技术之间重用模型类是很容易的。
YModel是模型的基类。它提供了报告数据改变的方法。当触发了一个事件的时候,框架会更新与之相连的视图。在分布式环境中,一个模型类有从服务器应用程序中得到POJOs的方法(通常是从隐藏了业务服务的实现细节的业务代理中)。模型自身存储了POJOs,且它有责任通知观察者。在有些MVC的体系结构中,一个控制器类和服务器通信,POJOs存储在控制器中。然而,TikeSwing分离出YModel类的方法有下面的优势:控制器专著于流程,另外的方法(操作模型数据的)可以被加在客户端。YModel遵循了传统的MVC模式,所以MVC中类的责任就清晰地分开了。
下面的代码演示了模型类如何通过给定的参数找到customers。模型的类变量name和id是搜索标准,customers是包含搜索结果的Customer POJOs的集合。findCustomers()方法通过customerServiceDelegate从服务器应用程序中得到customers。当方法notifyObservers()激活时,框架会自动更新相连的视图。
public class FindCustomerModel extends YModel {
private String name;
private String id;
private Collection customers;
private CustomerServiceDelegate delegate = new CustomerServiceDelegate();
public void findCustomers() {
setCustomers(delegate.findCustomers(id, name));
notifyObservers("customers");
}
public void setCustomers(Collection customers) {
this.customers = customers;
}
public Collection getCustomers() {
return customers;
}
public void setId(String id) {
this.id = id;
}
public String getId() {
return id;
}
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
}
视图TikeSwing视图是包含其他Swing组件的Swing组件。通常,一个视图类是一个面板,一个对话框,或者一个帧,它们建立了子组件并将之添加到自身(就像在通常的Swing开发环境中一样)。然而,TikeSwing应用程序中使用的所有组件都必须实现适当的接口以连接框架的MVC体系结构。幸运的是,框架包含一个很大的为了这种目的已经实现的组件的集合。
一个特殊的名字必须赋予一个视图组件,这样框架就能在组件和被命名的模型类变量之间复制数据。命名的惯例和其他的用于网络应用程序框架的和Apache BeanUtils库(它通常用于框架的执行)类似。下面是支持的命名格式:
●简单的: 直接连接到模型域的组件;例如,field1
●嵌套的:连接到模型内部的JavaBeans域的组件;例如,field1.field2
●索引的:连接到模型内的数组域的组件;例如myArray[1]
●映射的:连接到模型内的映射域组件;例如,myHashMap(“foo”)
●组合的:通过结合符号连接到模型的内部域的组件;例如,field.myArray[1].myHashMap["foo"]
除了模型类的GET和SET方法外,视图类必须为每一个视图组件建立一个GET方法。
下面的例子是为FindCustomerModel建立的视图类。它使用了扩展了基础Swing类的TikeSwing组件(从JLabel到YLabel,JTextField到YTextField,等)。例子的代码和标准的Swing视图很像,只有setMVCNames()方法包含了TikeSwing特有的代码。依照上面讲述的原则,它设定了模型组件的连接。resultTable列通过YColumn对象与customers集合中的POJO域相连。findButton不显示任何从模型得到的数据,但是MVC的名字是为TikeSwing的事件句柄设定的(以后再讲)。
public class FindCustomerView extends YPanel {
private YLabel idLabel = new YLabel("Id");
private YLabel nameLabel = new YLabel ("Name");
private YTextField idField = new YTextField();
private YTextField nameField = new YTextField();
private YPanel criteriaPanel = new YPanel();
private YTable resultTable = new YTable();
private YButton findButton = new YButton("Find");
public FindCustomerView () {
addComponents();
setMVCNames();
}
private void setMVCNames() {
idField.getYProperty().put(YIComponent.MVC_NAME,"id");
nameField.getYProperty().put(YIComponent.MVC_NAME,"name");
resultTable.getYProperty().put(YIComponent.MVC_NAME,"customers");
findButton.getYProperty().put(YIComponent.MVC_NAME,"findButton");
YColumn[] columns = {
new YColumn("id"),
new YColumn("name")};
resultTable.setColumns(columns);
}
private void addComponents() {
this.setLayout(new BorderLayout());
this.add(criteriaPanel, BorderLayout.NORTH);
idField.setPreferredSize(new Dimension(100, 19));
nameField.setPreferredSize(new Dimension(100, 19));
criteriaPanel.add(idLabel);
criteriaPanel.add(idField);
criteriaPanel.add(nameLabel);
criteriaPanel.add(nameField);
criteriaPanel.add(findButton);
this.add(resultTable, BorderLayout.CENTER);
}
public YTextField getIdField() {
return idField;
}
public YLabel getIdLabel() {
return idLabel;
}
public YTextField getNameField() {
return nameField;
}
public YLabel getNameLabel() {
return nameLabel;
}
public YTable getResultTable() {
return resultTable;
}
public YButton getFindButton() {
return findButton;
}
}
现在,无论任何时候用户修改idField 或者nameField,改变的地方都会自动更新到模型。而且,当notifyObservers()在 FindCustomerModel中调用的时候,框架会更新变化到resultTable。然而,为了匹配结构,一个控制器必须是特定的。
控制器TikeSwing的控制器通过调用视图和模型的方法来处理应用程序的流程。一个控制器的类必须扩展YController,它提供了控制关系中的必要的方法。通常,控制器也创建视图和模型对象,但是要注意的是,几个视图和控制器可能共享相同的模型对象。
一个控制器类可能有好几种方法来获取用户事件。TikeSwing组件包括基于反射的事件句柄:一个事件可以通过实现带有合适签名的方法而在控制器类中得到处理。例如,当用户点击按钮的时候,一个MVC名字为myButton的按钮在控制器中会调用myButtonPressed()方法(如果实现了的话)。这与标准的Swing事件监听接口和适配器相比是很方便的。
另一方面,事件方法签名中的字符在编译器中是不显示的,但是Swing适配器类的情况是:编译器不说明public void actionperformed是一个新的或者重载的方法。因为监听接口经常需要许多空的方法的执行,基于反射的简单的事件处理一定会加快代码的进程。作为选择,你可以在视图类中使用标准的监听者,而手动调用控制器的方法。
下面的代码是FindCustomerModel和FindCustomerView的控制器的一个例子。控制器通知MVC的结构是通过调用setUpMVC()方法和使用findButton 来处理基于反射的事件。
public class FindCustomerController extends YController {
private FindCustomerView view = new FindCustomerView();
private FindCustomerModel model = new FindCustomerModel();
public FindCustomerController() {
super();
setUpMVC(model, view);
}
public void findButtonPressed() {
model.findCustomers();
}
}
YController是TikeSwing中功能的核心。除了上面讲述的特点之外,它还提供了很多有用的方法能用于:
●捕获特定域的改变
●在控制器中发送和接收信息
●跟踪用户的修改
●取消用户的改变
●捕获模型抛出的异常
●验证域值的有效性
TikeSwing组件TikeSwing基于这样一种思想,组件负责处理在模型中相关联的对象。这种思想以前在Sun的《Swing指南》中的WholeNumberField演示中有体现。组件必须知道怎样在屏幕上面显示模型的值和怎样转换用户给定的值到模型中。
框架现在提供了一个足以使大多数应用程序使用的组件的集合。框架组件的行为就像基础的Swing组件,当然了,你必须阅读Java文档以理解组件和MVC类的交互(组件可以处理什么类型的模型域和它提供了什么事件的方法)。TikeSwing组件也提供了其他的特点和简洁的开发。例如,一个POJOs的集合可以在不创建任何特殊的组件模型的情况下直接使用于YTable和YTree。
TikeSwing组件基本上可以是任何的java.awt.Component。然而,一个组件必须实现适合的TikeSwing接口,那样它就能被集成到框架的MVC的体系结构中。它通常包含扩展了带有四个简单方法的标准Swing组件,因此这将是一个比较琐碎的任务。下面的代码是一个例子。和模型的集成是通过getModelValue() 和setModelValue()方法实现的。组件值的改变的通知是addViewListener()方法实现的。为了能在框架内部使用,必须实现getYProperty()方法。
下面的代码演示了一个支持Integer对象的简单文本域:
public class YIntegerField extends JTextField implements YIModelComponent {
/** Gets value of this field for the model. */
public Object getModelValue() {
try {
return new Integer(getText());
} catch (Exception ex) {
return null;
}
}
/** Sets the model value into this field. */
public void setModelValue(Object obj) {
if (obj == null) {
setText("");
} else {
setText(obj.toString());
}
}
/** Notifies the framework when the component value might have changed. */
public void addViewListener(final YController controller) {
this.addFocusListener(new FocusAdapter() {
public void focusLost(FocusEvent ev) {
controller.updateModelAndController(YIntegerField.this);
}
});
}
// The rest is for the framework internal use,
// the implementation must be copied to each new component:
private YProperty myProperty = new YProperty();
public YProperty getYProperty() {
return myProperty;
}
}
其它的特点除了MVC的体系结构,TikeSwing还有很多协助进行Swing开发的其它的特点。这些特点不是什么革命性的东西,它们可以在很多已经实现的Swing应用程序上面看到。但是,没有必要重新发明轮子,一些最好的Swing开发的体验包含在了这个框架中。
TikeSwing支持控制器多层结构的创建,就像在HMVC和MVC++中描述的那样。框架提供了使控制器之间实现父子关系的方法,这使类结构更协调和清晰。这种关系又助于和客户应用程序通信,而且可以用来和众所周知的设计模式集成。TikeSwing支持任务链模式,这种模式中,一个请求直到控制器对象才处理事件时才被传递。TikeSwing也支持Observer/Observable模式:一个控制器类可能传递一个能被所有已经注册了的控制器处理的事件。
TikeSwing也包含一种为tabbed panes检索慵懒数据(lazy data)的机制。在一个分布式的系统中,一下子从服务器得到所有tabs的数据可能需要很长的时间。为了优化性能,有必要只在每个tab被选择后才为其检索一次数据。框架提供了简化这种功能的机制,所以代码的复杂性,特别是在嵌套的tabbed panes里面,已经减少了许多。
当用户触发一个事件,可能导致刚修改的数据丢失的时候,一些应用程序会检查未被保存的改变。这些事件可能是下面的例子,关闭窗口,改变tabbed pane的tab的焦点,或者选择一个表格的列。TikeSwing 提供了进行检查特殊事件的工具。TikeSwing也会自动弹出“是否保存更新?”的对话框,并委托一个控制器方法来保存。另外,框架记得视图在特定时刻的状态,可以在稍晚的时候返回那种状态。这就意味着框架可以在不取得原始数据的情况下取消改变。
当两个或更多的组件执行相同的函数的时候,Swing的行为被证明是有用的。一个Action对象提供了集中的事件处理,但是如果行为用于单独的类的话,代码会因为增加的耦合而更加复杂。TikeSwing包含了一个集中处理产生事件的场所,因此一个动作可以用于不同的视图类而且不会直接耦合。
Swing组件只能由事件分派的线程进行创造,修改和查询,这使Swing应用程序中的线程处理更加复杂。《Swing指南》中说SwingWorker类对这个问题提供了帮助。TikeSwing封装了SwingWorker,并且使线程处理更加简单。例如,一些应用程序在进行远程调用或I/O操作的时候不会死锁。使用TikeSwing,在进行这样的操作时可以弹出一个可管理的,可重画的对话框,而且实现只需要几行代码。
Summary 总结由于有了高级的MVC和POJO的支持,TikeSwing简化了Swing的开发。使用TikeSwing是合理的,特别是在分布式环境中,由服务器应用程序返回的POJOs可以直接用于模型类,这个类直接连接到视图类。这个框架也包含了一些解决复杂开发问题的最佳实践。因此,TikeSwing减少了为Swing客户所写的代码,加快了开发。
TikeSwing自身提供了丰富的平台无关的用户界面库。Swing开发已经成为这几年一些重要的IDE的一部分,所以可见即所得的设计,单元测试和调试已经被广泛地支持。早先的工作站上性能的问题现在已经不是问题了,Java的网络应用也简化了分布式的Java应用程序。与网络应用程序的框架相比,Swing提供了更加友好的用户界面,没有JavaScript支持的问题,通过工作站上面的客户逻辑简化了网路上的通信量。
对Swing复杂性的批判依旧是正当的。但是,使用像TikeSwing的高级MVC框架,复杂性就减少了,Swing就转换成了一个生产力很高的客户端技术。我希望Java社区为Swing开发和采用一个开源的MVC框架,这将使其成为RIA技术中的一员。可能像Spring似的肥客户端技术更加接近目标。与其等待,不如请出TikeSwing,体验一下它是如何适应你的RIA工程的。
关于作者Tomi Tuomainen是Entra e-Solutions的顾问和架构师,他从1999年开始使用J2EE应用系统和Java框架。他是计算机科学的理学硕士和SUN的认证企业架构师。他的兴趣(Java之外的)在于音乐,吉他和体操训练。你可以说他是芬兰最强的IT顾问之一。
资源 ●最新版本的TikeSwing(包括类路径,源代码,用户指南和Javadoc API的必需的JAR文件)可以在这里下载:
http://sourceforge.net/projects/tikeswing
●关于TikeSwing遵循的MVC范例的基本信息:
http://ootips.org/mvc-pattern.html
●就像JavaBeans规范中说的那样,TikeSwing的模型对象必须包含GET和SET方法:
http://java.sun.com/products/javabeans/docs/spec.html
●Swing指南:
http://java.sun.com/docs/books/tutorial/uiswing/index.html
●HMVC范例分解了客户端为父子MVC层,这也能用于TikeSwing。阅读 “HMVC:用于开发强壮客户端层的层次模式,” Jason Cai, Ranjit Kapila, and Gaurav Pal (JavaWorld, 2000年7月),可获取更多信息:
http://www.javaworld.com/javaworld/jw-07-2000/jw-0721-hmvc.html
●MVC++范例共享了HMVC的关于控制器层次的想法:
http://www.cs.uta.fi/~jyrki/ohto02/mvc.ppt
●Apache BeanUtils库,包含了能用于JavaBeans域(在TikeSwing中使用了)引用的格式的描述:
http://jakarta.apache.org/commons/beanutils/api/index.html
●和TikeSwing有共通之处的Spring肥客户端工程:
http://www.springframework.org/spring-rcp
●关于Swing开发的更多文章,浏览JavaWorld的AWT/Swing部分的论题索引:
http://www.javaworld.com/channel_content/jw-awt-index.shtml
●关于UI设计的更多文章,浏览JavaWorld的User Interface Design部分的论题索引:
http://www.javaworld.com/channel_content/jw-ui-index.shtml
●最后,浏览JavaWorld论题索引的Development Tools部分:
http://www.javaworld.com/channel_content/jw-tools-index.shtml
当初乔布斯在车库里弄出了个人电脑,IBM很不屑;两年以后不屑变成了震惊和无力。于是IBM制定了另外一种政策来抢夺pc市场:“标准”。
IBM定义了一系列标准接口,各自领域的厂家只要生产符合标准的部件,这些部件就可以组装在一起,成为一个可以运行的电脑——毫无疑问,苹果无论如何精致诱人是搞不过一群如狼似虎的家伙的。
于是苹果很快被“湮没”了。
然后事情很是“出乎”IBM的预料,这场战争他击败了对手,可是胜利者并不是IBM自己。而是所谓的WINTEL联盟。
WINTEL就是WINDOWS和INTEL的合称。
其实WINDOWS并不从来是WINDOWS,刚开始的时候他也是黑乎乎的,而且也不是MS自己做的。因为盖茨非常喜欢苹果风格的os,所以MS公司就开始了视窗的研究——MS似乎对自己的视窗并没有什么信心,同时和IBM开发了OS WARP视窗系统。然后MS内部一个GATES不太喜欢的小组成功的开发出了WINDOS。WINDOWS最初的几个版本并不成功,直到版本三。
当然,最初出风头的是95,可是95没有浏览器。稳定性也不是很好,98是真正为人所广泛称道和接受的windows操作系统,他内置了浏览器,内置了虚拟机以及网络协议。
如同IBM刚开始不屑苹果一样,GATES眼睛里只有IBM,根本就不屑新秀netscapes;然而后者同样使他吓了一跳。但是MS不同于恐龙IBM的是,他迅速而坚决的采取了对策——绑定,免费。于是netscape不可抗拒的衰落了(被AOL收购)
如同今天google对ms的挑战一样,多年以前aol也是一个强劲的挑战者;风头甚劲,他资助ns继续开发,产生了现在的netscape6系列和firefox系列。
新锐就是新锐,历史的积淀是无法轻与的,对这些公司来说,没有一个公司能像SUN或者IBM一样,让MS感觉到羡慕和压力。
MS的眼光也不可轻忽,无论是模仿苹果制作windows还是模仿java制作J++/.NET,他都展示了后来居上青出去蓝的技术和商业眼光。
或许傲慢是强者的权利,MS似乎从来不屑按标准出牌,他总是在告诉别人:我就是标准,如果你想在我的标准里打败我,我就改变标准。无论是w3c标准还是css标准,无论是js标准还是java规范......
尤其是WEB开发,造就了现在这样的窘况。
我比较看好ms的XAML技术,可以说这个技术集B/S的快速部署能力、html的简洁UI开发、C/S技术的强大展现能力与一体。作为一个java程序员,我自然最关心的是java里面的对应实现物XUL。
要实现类似于XAML这样的技术,java必须在客户端装JRE。可能SUN觉得说服用户装一个jre很困难,于是提出webstart、jsf等一大堆更复杂的技术——但是我质疑的是jsf比XUL强大简洁么?webstart下载付出的代价难道小于一个jre么?既然flash可以强制没有装播放器的用户去装,为什么jre就不可以?
作为一个JAVA程序员,自然希望JAVA更好。可是不得不承认.net在开发效率,开发体验上,已经远远超越了JAVA。对.net的疑虑之存在于系统平台、第二提供方、健壮性等企业级顾虑中。
自由是痛苦的,无力的。