2006年3月4日

http://www.firstui.com/read.php/250.htm

posted @ 2006-11-23 23:47 killvin| 编辑 收藏

很久都已经下定了决心不再维护那里的空间了,即使是成为了特约的作者也难以挽回我对他们Blog的信心,虽然blogjava的系统与csdn的是同样出自一份代码,但显然无论从服务的内容还是代码的理解上,csdn与blogjava相差的太远,当然我并不是完全的赞同目前blogjava的风格,尤其是身为介绍java的博客系统,竟然采用asp的技术来实现,实在是太煞风景了,甚至有些让你匪夷所思,不过瑕不掩瑜,毕竟无论从速, 专业程度还是目前提供的服务上来看,blogjava确实比同类型的网站要优秀一些.

posted @ 2006-10-29 15:54 killvin| 编辑 收藏

Version 1.01 on Bindows enhanced its Mozilla support considerably. At this point, the level of support is such that many complicated applications are available to Mozilla users. For example: now these can use our forum and the registration application.
We are dedicated to make Bindows work at least as well with Mozilla 1.4+ as it does with Internet Explorer 6.0 SP1. The Mozilla support is now integral part of Bindows and we will continue to develop, maintain and support it.
However, some issues are not solved yet and this document details them.


Known Issues
Web Services

We do not yet have a working SOAP web service client working with Mozilla. Mozilla has a built in web service client but it does not yet work with all the major web services platforms. We anticipate this to be resolve in a near future version of Bindows.


Graphs, Charts and Gauges
Graphs, charts and gauges are not supported. For the Bindows charts and gauges we rely on VML to do the drawing. Mozilla does not yet have a way to draw vector graphics at the client side without using a plugin. We consider several alternatives here but there is no time frame for a solution yet.


Miscellaneous
The caret is sometimes not shown in a focused textfield. This is a Mozilla bug and unless this is fixed in Mozilla this issue will remain.
Preferred size is not working consistently. Please report the cases where you are experiencing issues with the preferred size and we will see what can be done in those specific cases.
Icons in menu items works inconsistently between Mozilla versions. This is due to differences in the Mozilla toolkits used by different browsers.
Menu labels do not accept HTML. If you use HTML, the markup will be stripped.
The rendering of the group box is incorrect.
Labels with text align center and icon position set to top or bottom are a bit jumpy, ie. the icon move around depending on the state.
Dragging elements sometimes inconsistent. If an image is dragged an OS level drag and drop is initiated. Also, for some components a dragging will not work when the mouse leaves the browser window or enters a frame.
The upper border for the tab pages are not correctly drawn in Mozilla Firefox using Windows Classic. This is a Mozilla bug.
BiKeyboardEvent keyCode is sometimes incorrect in a keypress event (for special keys). The reason for this bug is that Internet Explorer and Mozilla are handling keyboard events differently.
Resize events are not fired on all BiComponents.
Focus and blur events are inconsistent when using BiRichEdit.

posted @ 2006-10-14 16:21 killvin| 编辑 收藏

Jsparse is a parse to parse the schema file with javascript.
If you are interested in it, you can vist the url
http://code.google.com/p/jsparse/  

get source with svn
svn checkout http://jsparse.googlecode.com/svn/trunk/ jsparse

posted @ 2006-09-13 23:23 killvin| 编辑 收藏

ACE中的Double Checked Locking 模式


(作者:Douglas C. Schmidt ,by huihoo.org CORBA课题 Thzhang 译 , Allen整理,制作)

意图

无论什么时候当临界区中的代码仅仅需要加锁一次,同时当其获取锁的时候必须是线程安全的,可以用Double Checked Locking 模式来减少竞争和加锁载荷。

动机

1、标准的单例。开发正确的有效的并发应用是困难的。程序员必须学习新的技术(并发控制和防止死锁的算法)和机制(如多线程和同步API)。此外,许多熟悉的设计模式(如单例和迭代子)在包含不使用任何并发上下文假设的顺序程序中可以工作的很好。为了说明这点,考虑一个标准的单例模式在多线程环境下的实现。单例模式保证一个类仅有一个实例同时提供了全局唯一的访问这个实例的入口点。在c++程序中动态分配单例对象是通用的方式,这是因为c++程序没有很好的定义静态全局对象的初始化次序,因此是不可移植的。而且,动态分配避免了单例对象在永远没有被使用情况下的初始化开销。


class Singleton
{
public:
static Singleton *instance (void)
{
if (instance_ == 0)
// Critical section.
instance_ = new Singleton;
return instance_;
}
void method (void);
// Other methods and members omitted.
private:
static Singleton *instance_;
};



应用代码在使用单例对象提供的操作前,通过调用静态的instance方法来获取单例对象的引用,如下所示:
Singleton::instance ()->method ();
2、问题:竞争条件。不幸的是,上面展示的标准单例模式的实现在抢先多任务和真正并行环境下无法正常工作。例如,如果在并行主机上运行的多个线程在单例对象初始化之前同时调用Singleton::instance方法,Singleton的构造函数将被调用多次,这是因为多个线程将在上面展示的临界区中执行new singleton操作。临界区是一个必须遵守下列定式的指令序列:当一个线程/进程在临界区中运行时,没有其他任何线程/进程会同时在临界区中运行。在这个例子中,单例的初始化过程是一个临界区,违反临界区的原则,在最好的情况下将导致内存泄漏,最坏的情况下,如果初始化过程不是幂等的(idempotent.),将导致严重的后果。

3、 通常的陷阱和弊端。实现临界区的通常方法是在类中增加一个静态的Mutex对象。这个Mutex保证单例的分配和初始化是原子操作,如下:


class Singleton
{
public:
static Singleton *instance (void)
{
// Constructor of guard acquires lock_ automatically.
Guard guard (lock_);
// Only one thread in the critical section at a time.
if (instance_ == 0)
instance_ = new Singleton;
return instance_;
// Destructor of guard releases lock_ automatically.
}
private:
static Mutex lock_;
static Singleton *instance_;
};


guard类使用了一个c++的习惯用法,当这个类的对象实例被创建时,它使用构造函数来自动获取一个资源,当类对象离开一个区域时,使用析构器来自动释放这个资源。通过使用guard,每一个对Singleton::instance方法的访问将自动的获取和释放lock_。
即使这个临界区只是被使用了一次,但是每个对instance方法的调用都必须获取和释放lock_。虽然现在这个实现是线程安全的,但过多的加锁负载是不能被接受的。一个明显(虽然不正确)的优化方法是将guard放在针对instance进行条件检测的内部:


static Singleton *instance (void)
{
if (instance_ == 0) {
Guard guard (lock_);
// Only come here if instance_ hasn't been initialized yet.
instance_ = new Singleton;
}
return instance_;
}

这将减少加锁负载,但是不能提供线程安全的初始化。在多线程的应用中,仍然存在竞争条件,将导致多次初始化instance_。例如,考虑两个线程同时检测 instance_ == 0,都将会成功,一个将通过guard获取lock_另一个将被阻塞。当第一线程初始化Singleton后释放lock_,被阻塞的线程将获取lock_,错误的再次初始化Singleton。
4、解决之道,Double Checked Locking优化。解决这个问题更好的方法是使用Double Checked Locking。它是一种用于清除不必要加锁过程的优化模式。具有讽刺意味的是,它的实现几乎和前面的方法一样。通过在另一个条件检测中包装对new的调用来避免不必要的加锁:


class Singleton
{
public:
static Singleton *instance (void)
{
// First check
if (instance_ == 0)
{
// Ensure serialization (guard constructor acquires lock_).
Guard guard (lock_);
// Double check.
if (instance_ == 0)
instance_ = new Singleton;
}
return instance_;
// guard destructor releases lock_.
}
private:
static Mutex lock_;
static Singleton *instance_;
};


第一个获取lock_的线程将构建Singleton,并将指针分配给instance_,后续调用instance方法的线程将发现instance_ != 0,于是将跳过初始化过程。如果多个线程试图并发初始化Singleton,第二个检测件阻止竞争条件的发生。在上面的代码中,这些线程将在lock_上排队,当排队的线程最终获取lock_时,他们将发现instance_ != 0于是将跳过初始化过程。

上面Singleton::instance的实现仅仅在Singleton首次被初始化时,如果有多个线程同时进入instance方法将导致加锁负载。在后续对Singleton::instance的调用因为instance_ != 0而不会有加锁和解锁的负载。 通过增加一个mutex和一个二次条件检测,标准的单例实现可以是线程安全的,同时不会产生过多的初始化加锁负载。

适应性

> 当一个应用具有下列特征时,可以使用Double Checked Locking优化模式:
1、应用包含一个或多个需要顺序执行的临界区代码。
2、多个线程可能潜在的试图并发执行临界区。
3、临界区仅仅需要被执行一次。
4、在每一个对临界区的访问进行加锁操作将导致过多加锁负载。
5、在一个锁的范围内增加一个轻量的,可靠的条件检测是可行的。

结构和参与者

通过使用伪代码能够最好地展示Double Checked Locking模式的结构和参与者,图1展示了在Double Checked Locking模式有下列参与者:



1、仅有一次临界区(Just Once Critical Section,)。临界区所包含的代码仅仅被执行一次。例如,单例对象仅仅被初始化一次。这样,执行对new Singleton的调用(只有一次)相对于Singleton::instance方法的访问将非常稀少。
2、mutex。锁被用来序列化对临界区中代码的访问。
3、标记。标记被用来指示临界区的代码是否已经被执行过。在上面的例子中单例指针instance_被用来作为标记。
4、 应用线程。试图执行临界区代码的线程。

协作

图2展示了Double Checked Locking模式的参与者之间的互动。作为一种普通的优化用例,应用线程首先检测flag是否已经被设置。如果没有被设置,mutex将被获取。在持有这个锁之后,应用线程将再次检测flag是否被设置,实现Just Once Critical Section,设定flag为真。最后应用线程释放锁。



结论

使用Double Checked Locking模式带来的几点好处:
1、最小化加锁。通过实现两个flag检测,Double Checked Locking模式实现通常用例的优化。一旦flag被设置,第一个检测将保证后续的访问不要加锁操作。
2、防止竞争条件。对flag的第二个检测将保证临界区中的事件仅实现一次。
使用Double Checked Locking模式也将带来一个缺点:产生微妙的移植bug的潜能。这个微妙的移植问题能够导致致命的bug,如果使用Double Checked Locking模式的软件被移植到没有原子性的指针和正数赋值语义的硬件平台上。例如,如果一个instance_指针被用来作为Singleton实现的flag,instance_指针中的所有位(bit)必须在一次操作中完成读和写。如果将new的结果写入内存不是一个原子操作,其他的线程可能会试图读取一个不健全的指针,这将导致非法的内存访问。
在一些允许内存地址跨越对齐边界的系统上这种现象是可能的,因此每次访问需要从内存中取两次。在这种情况下,系统可能使用分离的字对齐合成flag,来表示instance_指针。
如果一个过于激进(aggressive)编译器通过某种缓冲手段来优化flag,或是移除了第二个flag==0检测,将带来另外的相关问题。后面会介绍如何使用volatile关键字来解决这个问题。

实现和例子代码

ACE在多个库组件中使用Double Checked Locking模式。例如,为了减少代码的重复,ACE使用了一个可重用的适配器ACE Singleton来将普通的类转换成具有单例行为的类。下面的代码展示了如何用Double Checked Locking模式来实现ACE Singleton。


// A Singleton Adapter: uses the Adapter
// pattern to turn ordinary classes into
// Singletons optimized with the
// Double-Checked Locking pattern.
template 
class ACE_Singleton
{
public:
static TYPE *instance (void);
protected:
static TYPE *instance_;
static LOCK lock_;
};
template  TYPE *
ACE_Singleton::instance ()
{
// Perform the Double-Checked Locking to
// ensure proper initialization.
if (instance_ == 0) {
ACE_Guard lock (lock_);
if (instance_ == 0)
instance_ = new TYPE;
}
return instance_;
}


ACE Singleton类被TYPE和LOCK来参数化。因此一个给定TYEP的类将被转换成使用LOCK类型的互斥量的具有单例行为的类。
ACE中的Token Manager.是使用ACE Singleton的一个例子。Token Manager实现在多线程应用中对本地和远端的token(一种递归锁)死锁检测。为了减少资源的使用,Token Manager被按需创建。为了创建一个单例的Token Manager对象,只是需要实现下面的typedef:

typedef ACE_Singleton Token_Mgr;
Token Manager单例将被用于本地和远端的token死锁检测。在一个线程阻塞等待互斥量之前,它首先查询Token Manager单例,来测试阻塞是否会导致死锁状态。对于系统中的每一个token,Token Manager单例维护一个持有token线程和所有阻塞等待在该token的线程记录链表。这些数据将提供充足的检测死锁状态的依据。使用Token Manager单例的过程如下:


// Acquire the mutex.
int Mutex_Token::acquire (void)
{
// If the token is already held, we must block.
if (mutex_in_use ()) {
// Use the Token_Mgr Singleton to check
// for a deadlock situation *before* blocking.
if (Token_Mgr::instance ()->testdeadlock ()) {
errno = EDEADLK;
return -1;
				}
else
// Sleep waiting for the lock...
		// Acquire lock...
		}

变化

一种变化的Double Checked Locking模式实现可能是需要的,如果一个编译器通过某种缓冲方式优化了flag。在这种情况下,缓冲的粘着性(coherency)将变成问题,如果拷贝flag到多个线程的寄存器中,会产生不一致现象。如果一个线程更改flag的值将不能反映在其他线程的对应拷贝中。

另一个相关的问题是编译器移除了第二个flag==0检测,因为它对于持有高度优化特性的编译器来说是多余的。例如,下面的代码在激进的编译器下将被跳过对flag的读取,而是假定instance_还是为0,因为它没有被声明为volatile的。

Singleton *Singleton::instance (void)
{
if (Singleton::instance_ == 0)
{
// Only lock if instance_ isn't 0.
Guard guard (lock_);
// Dead code elimination may remove the next line.
// Perform the Double-Check.
if (Singleton::instance_ == 0)
// ...



解决这两个问题的一个方法是生命flag为Singleton的volatile成员变量,如下:
private:
static volatile long Flag_; // Flag is volatile.
使用volatile将保证编译器不会将flag缓冲到编译器,同时也不会优化掉第二次读操作。使用volatile关键字的言下之意是所有对flag的访问是通过内存,而不是通过寄存器。

相关模式

Double Checked Locking模式是First-Time-In习惯用法的一个变化。First-Time-In习惯用法经常使用在类似c这种缺少构造器的程序语言中,下面的代码展示了这个模式:


	static const int STACK_SIZE = 1000;
static T *stack_;
static int top_;
void push (T *item)
{
// First-time-in flag
if (stack_ == 0) {
stack_ = malloc (STACK_SIZE * sizeof *stack);
assert (stack_ != 0);
top_ = 0;
}
stack_[top_++] = item;
// ...
}



第一次push被调用时,stack_是0,这将导致触发malloc来初始化它自己。

posted @ 2006-09-13 23:16 killvin| 编辑 收藏

在上一篇的文章中,比较仔细的分析了IE对于Cookie处理过程中出现的问题,既然存在问题就要想办法解决,而问题的关键在于IE没有能够"老实"的报告用户对于Cookie的设置问题

IE
GET /mail HTTP/1.1
Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, application/x-shockwave-flash, */*
Accept-Language: zh-cn
Accept-Encoding: gzip, deflate
User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; Maxthon)
Host: mail.google.com
Connection: Keep-Alive
Cookie:
gmailchat=killvin.liu@gmail.com/519974 ; PREF=ID=d68c481e542af276:NW=1:TM=1151742225:LM=1151742225:S=2qbdhg0_z3i-OAbW; SID=DQAAAG8AAACEdcjD2IZMNqZVatDbD62X8_U18oJuTVQc9XZUJi7MgCkM8sggJ8M5npZ35GXjdalT2o8QWPUve04tepy61MPv4v_EpILafg3JdIf8AFjD91aMT0tI5gb763FouV3e_2-C364HDO5Qzb4P4gjjgpHC

Firefox
GET /mail HTTP/1.1
Host: mail.google.com
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.0.3) Gecko/20060426 Firefox/1.5.0.3
Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Connection: keep-alive

我们可以注意到:如果用户关闭了浏览器的Cookie选项,在Firefox中对于新的请求是包含Cookie的信息的,然而IE却恰恰相反!这也就直接用IE浏览一些网站所导致的Cookie校验失败问题(比如即使用户在使用过程中突然关闭Cookie,依然可以自由的访问网站的各项服务),进一步,如果某个网站是读取依靠Cookie中的信息来验证用户(尤其是那种保存客户信息的提示),即使这个用户离开或者关闭窗口,任何一个后来的人都可以轻松的浏览这个用户的私有信息!这就是Cookie的最大的问题所在,当然这是另外的话题,不属于本次讨论的范畴,这一次要说的是
如何避免IE浏览器下,用户关闭Cookie的问题。

有一个思路,经过测试我们发现IE浏览器只是在首次页面请求的时候才会去读取Cookie的值,所以,为了避免IE浏览器关闭Cookie的问题,可以在用户请求某个链接的时候,先将用户的Cookie存放在某处,并清空此时所有的Cookie值,再次的请求页面,而这样做的目的就是强迫IE读取硬盘上的Cookie,如果此时用户关闭了Cookie,就不会在请求的头信息中看到Cookie信息;如果没有关闭Cookie,就可以放心的将原先的Cookie写回Reponse对象。


只是不知道Google的网站是否也是按照这个思路去解决此问题的?

posted @ 2006-07-02 21:53 killvin| 编辑 收藏

IE在处理COOKIE方面的问题

1。即使你提高了浏览器的隐私登记,在第一次打开窗口的时候,你获取不到任何的Cookies对象(很显然的结果),然而当你再次刷新本页面的,Cookie此时会奇迹般的出现!而在Firefox浏览器中按照以上的步骤,是不会出现这样的情况的。

2。不仅如此,你还可以透过Action的处理,多次的往Cookie中增加Cookie的数量(当然在Action中你依然可以自由的获取到Cookie这个数组,并且不为空),然而让人匪夷所思的是在Cookie的存放目录下你是找寻不到任何的蛛丝马迹的。而在Firefox没有出现以上的情况。

-解决
1。在首次进入页面时查询客户端的Cookie,如果不存在则警告用户,并要求再次的登陆。
2。在用户登陆后,如果更改了浏览器的隐私级别,对于Firefox标准的浏览器,此时肯定不会再找到Cookie数组对象了,你需要做的仅仅只是将页面调转到登陆窗口;而在IE下就非常的麻烦了甚至无法解决,因为你依然可以访问到原来的Cookie数组值,(比如,用IE在CSDN登陆后提高隐私级别,你依然可以登陆到其他的服务区域)此时没有什么好的办法,不过Google解决了这样的问题,只是不知道如何解决的。



IE在处理Frame标记的问题
1。如果你在某个页面中嵌入了Frame标签,并且希望与这个嵌入的页面共享某些存放在Session中的数据,此时你需要将外部的sessionId传入到frame标记的页面中。然而在IE中你可能并不能完全的享受这样的逻辑,原因在于IE对于嵌入的页面不会自动的传递sessionId,也许你碰巧可以,也许不行,也就是说完全是在IE的"掌控"之下。而在Firefox没有出现这样的情况。

-解决
为嵌入的页面中所有的链接增加sessionId参数。


最好的办法就是:说服客户使用标准的浏览器Firefox!

posted @ 2006-07-01 15:53 killvin| 编辑 收藏

在Struts中我们经常这样循环的打印Message
<logic:messagesPresent message="true">
  <html:messages id="msg" message="true">
    <div class="success">
      <bean:write name="msg"/>
    </div><br/>
  </html:messages>
</logic:messagesPresent>

查阅struts tag的文档我们看到了关于messagesPresent的message属性注释如下

message

By default the tag will retrieve the request scope bean it will iterate over from the Globals.ERROR_KEY constant string, but if this attribute is set to 'true' the request scope bean will be retrieved from the Globals.MESSAGE_KEY constant string. Also if this is set to 'true', any value assigned to the name attribute will be ignored


也就是说在将message设置为true时,会去request中寻找Globals.MESSAGE_KEY所代表的bean,然而我们在具体的Action使用ActionMessages类的时候往往是这样的
ActionMessages messages = getErrors(request);
messages.add(ActionMessages.GLOBAL_MESSAGE , new ActionMessage(key , value0));
saveMessages(request,messages);

而往往困扰人的就在于为什么要给messages中放入名称为"ActionMessages.GLOBAL_MESSAGE"的ActionMessage对象,而且还需要再次的调用saveErrors(request,messages)方法?

首先要说明的是你为ActionMessage起任何的名称都没有关系,因为ActionMessages本身维持着一个HashMap,而参数property就是这个HashMap中的key值,如果不存在则会建立相应的key,并将需要保存的ActionMessage对象存入到这个key所对应的List中。
    public void add(String property, ActionMessage message) {

        ActionMessageItem item = (ActionMessageItem) messages.get(property);
        List list = null;

        if (item == null) {
            list = new ArrayList();
            item = new ActionMessageItem(list, iCount++, property);

            messages.put(property, item);
        } else {
            list = item.getList();
        }

        list.add(message);

    }

至于为什么一定要调用saveMessages(request,messages)?看看它具体的实现逻辑就清楚了
    protected void saveMessages(
        HttpServletRequest request,
        ActionMessages messages) {

        // Remove any messages attribute if none are required
        if ((messages == null) || messages.isEmpty()) {
            request.removeAttribute(Globals.MESSAGE_KEY);
            return;
        }

        // Save the messages we need
        request.setAttribute(Globals.MESSAGE_KEY, messages);
    }

再对比前面介绍的messagesPresent标签的使用,是不是就清楚了呢?原来它是将ActionMessages对象保存在request中,并且名称是Globals.ERROR_KEY!从而为tag的顺利解析铺平了道路。当然按理你可以选择将这样的对象放置在任何的scope中,但Action只是提供了request , session两种Scope(不过page , application不经常使用,可以理解,但不提供相应的结构就不太好了)

至于messagesPresent标签是如何在scope中寻找ActionMessages对象

org.apache.struts.taglib.logic.MessagesPresentTag
    protected boolean condition(boolean desired) throws JspException {
        ActionMessages am = null;

        String key = name;
        if (message != null && "true".equalsIgnoreCase(message)){
           key = Globals.MESSAGE_KEY;
        }

        try {
            am = TagUtils.getInstance().getActionMessages(pageContext, key);
           
        } catch (JspException e) {
            TagUtils.getInstance().saveException(pageContext, e);
            throw e;
        }

        Iterator iterator = (property == null) ? am.get() : am.get(property);

        return (iterator.hasNext() == desired);

    }

org.apache.struts.taglib.TagUtils
   public ActionErrors getActionErrors(PageContext pageContext, String paramName)
            throws JspException {

        ActionErrors errors = new ActionErrors();

        Object value = pageContext.findAttribute(paramName);
        if (value != null) {
            try {
                if (value instanceof String) {
                    errors.add(
                            ActionMessages.GLOBAL_MESSAGE,
                            new ActionMessage((String) value));

                } else if (value instanceof String[]) {
                    String keys[] = (String[]) value;
                    for (int i = 0; i < keys.length; i++) {
                        errors.add(
                                ActionMessages.GLOBAL_MESSAGE,
                                new ActionMessage(keys[i]));
                    }

                } else if (value instanceof ActionErrors) {
                    errors = (ActionErrors) value;

                } else {
                    throw new JspException(
                            messages.getMessage(
                                    "actionErrors.errors",
                                    value.getClass().getName()));
                }

            } catch (JspException e) {
                throw e;

            } catch (Exception e) {
                log.debug(e, e);
            }
        }
        return errors;
    }

PageContext中的findAttribute会帮你在scope中寻找名称为Globals.MESSAGE_KEY的ActionMessage对象。


注意
虽然Struts已经声明:不推荐使用ActionErrors & ActionError对象,但在一些遗留的系统中,依然还是可以看到其影子,所以如果你的系统不幸属于这样的两种混合系统,有以下的几种方法可以参考
1。两次调用messagesPresent,如下
<!-- Print ActionErrors Object -->
<logic:messagesPresent>
  <html:messages id="msg" message="true">
    <div class="success">
      <bean:write name="msg"/>
    </div><br/>
  </html:messages>
</logic:messagesPresent>

<!-- Print ActionMessages Object -->
<logic:messagesPresent message="true">
  <html:messages id="msg" message="true">
    <div class="success">
      <bean:write name="msg"/>
    </div><br/>
  </html:messages>
</logic:messagesPresent>

2.分别使用<html:messages> <html:errors>标签,当然在老系统中需要调用Action的saveErrors方法,而在新的应用中要调用saveMessages方法。

3.更换所有的ActionErrors为ActionMessages,并将所有调用saveErrors的地方更换成saveMessages,并将<html:errors>标签相应的更换成<html:messages message="true"> - 推荐!

 

posted @ 2006-06-27 23:02 killvin| 编辑 收藏

第2章 并发问题及控制手段

什么是并发问题?假设有这么一家书吧,顾客可以到那里喝茶读书。顾客拿着选好要读的图书到柜台登记,然后找个地方去阅读,临走时将图书归还店家。有一天,一个顾客相中了一本书后正要拿去登记,另一个顾客的手也抓住了这仅有的一本书,并发问题出现了。两个顾客要读同一本书,互不相让,这让店主伤透了脑筋。这个案例仅仅是众多并发问题中的一个微小部分,但从中我们可以看出并发问题主要出现在多个用户对有限资源进行访问的时候,如果解决不好会直接影响系统的有效、正常运行。数据库是一个共享的资源,并发问题的出现是必不可免的,如何识别并发类型并加以控制是这一章重点要讲述的内容。

本章将分成两大部分,一部分主要讲Visual FoxPro中并发控制机制。VFP中并发控制相对简单,数据加锁的形式比较单一,非常适合作为初步了解并发问题的切入点。第二部分以SQL Server 2000、ADO.NET以及C#为主要工具,深入了解并发一致性问题、封锁协议、事务隔离等内容,难度相对较深。象一些更为深入的并发控制手段,例如多粒度封锁和意象锁等内容在本章中将不做深入讨论,感兴趣可以参考相关书籍。

 

[实在不好意思COPY别人的成果,不过这篇文章出奇的精彩,将并发操作的来龙去脉说的清清楚楚,也是我正要找的,比JAVAEYE上面所谓的专家叫嚷着什么"悲观锁"、"乐观锁"而不解是原因要强的多!值得收藏]

posted @ 2006-04-06 10:57 killvin| 编辑 收藏

痛快!。。。人们总是认为骂人是没有素质的象征,然而我到觉得要看对谁而言,如果你的面前是个谦虚、严谨而又懂得尊重别人的人,你根本就不可能让自己愤怒,也不可能让自己表现的很没有素质;相反如果你的面前是个先入为主、自傲、不懂得尊重别人的人,你的愤怒就会在最短的时间内激发出来。-对于小人,就要骂!

而这有与北大青鸟有什么关系呢?

在我的职业生涯里,有一段让自己愤怒的培训经历,到今天我都为自己在这样的培训部的学习经历而感到耻辱!甚至不愿意在简历里提及ACCP的字样,而这样的培训经历就是在北大青鸟(西安兆隆)里度过的。

我参加的是北大青鸟(西安兆隆)的第二期培训,培训的课程涉及到数据库、编程语言、职业素质、项目实践等,看上去非常的正规,可是真正的学习效果却是让人非常的失望的!

首先,就是它的课程体系,完全的引入印度的教程,而翻译的质量非常的差,其实这到没有什么,只是教程的讲解范围和深度都非常的浅(这还是第二期的培训),如果你只是按照它的教程我可以说你根本学不到任何有价值的东西,只是皮毛而已。-拿着淡薄的教学资料的同时你也就上了贼船了。


再次,就是它的老师的水平实在不敢恭维,(当然并不是说根本就没有好的老师,曾经就遇到过一个讲解JAVA与UML的老师,水平非常的高深,可惜ACCP没有留住),比如编程语言中学习了VB,仅仅只是这一门课程就换了3个老师,最后是一位号称是西工大的博士上的课,然而这位博士显然是心思不再这里,除了讲解课本上已经有的代码,根本不敢深入的讲解原理,这门课程也就这样混过去了。-如果这样学习编程语言道不如自学来得轻松。


还有,就是所谓的项目实践根本就不是老师带的项目,而是一些书本上或者捏造出来的一些项目,比如图书管理系统等,如果学员无法真正的理解需求,怎么去理解设计,又如何来进行实际的编码呢?如果遇到图书管理的专业问题来问谁呢?-别指望在他们宣传的所谓的项目实践中得到锻炼。

在看看他们引以为傲的工作分配问题吧?就我个人的亲身感受来言,这里会提供一些工作的机会,但都是一些小公司,而且往往是一些前三个月不给工资的公司;当时我在我们班级里是比较认真的一个(我是班里唯一的一个交了对项目分析文档的学员,并且是项目实践中的小组组长),可是在一次很好的面试机会面前,她们竟然由于工作的失误忘记了给我打电话?!-如果这样的工作分配策略也算作是给予你的答案的话,你一定会变得和我一样的愤怒!


我没有在ACCP学到任何的有用的东西,然而在真实的世界里却学到了人们对于ACCP的看法,很多的技术经理 、 技术总监从骨子里非常的厌恶从北大青鸟出来的学生,对于他们开设的一些课程也是相当的反感,当时在西部世纪工作的时候,同组的就有和我一起上课的同班同学,在他交了8000多元的学习费用(总共的费用接近1万多元)后,得到的仅仅只是西部世纪的一张试用通知单,而条件是必须无条件的为公司工作三个月!


这就是我在北大青鸟的学习经历,然而愤怒的不是这个,却是今天参加的面试经历,对照着自己的个人的工作经历我希望自己能够成为一名培训部的老师,教给那些原来如我一样的学生一些在实践当中有用的东西,而不是空洞的理论!所以我给ACCP投了自己的简历,然而让我却又一次的遇到了那个欺骗过我的咨询师,不过她的记忆力也是非常的好,一眼就认出了我(当然了我是曾经给她带来奖金的人),在紧接着的面试过程中已经变了味道,谈论的话题也不是具体项目技术的问题,(不是我笑话她与她谈论技术问题根本就如同对牛弹琴)她显然是一个没有素质的WOMAN,也可以肯定的是她根本没有在外企工作过的经历,所以说话的词汇以及与其如同大街上的泼妇一般,可以想见她一定是一个被ACCP严重洗过脑的人,而且是属于那种技术失败的产品,她无法接受别的培训部的名字,一旦你提起别的培训部的某个老师的水平如何如何的好,她就如同一个快被点燃的鞭炮一般,两眼充满了愤怒!

然而让我最终和她一般的没有素质的大骂的是她对于我辞职的评点,我很纳闷的是她在不知道一件事情的原因的情况下,可以讲述自己的观点!并且是带着教育的口吻,实在是人才呀!这就是北大青鸟的"人才力量",对于这样的白痴级别的任务的观点,我只好打断,因为它甚至连我在的那个单位的名字都不知道!!

实在是一场闹剧,而她更像是个小丑,除了唧唧喳喳的重复一些"洗脑"的词汇已经不知道窗外的天气了,可以预料北大青鸟的牌子必将会毁在这些个白痴的手中,世界需要白痴,那如同鲜花需要牛粪一样,我们改变不了白痴的世界观的,你能够做的就是别被这臭烘烘的牛粪给熏臭了。

posted @ 2006-04-01 12:53 killvin| 编辑 收藏


[深入的思考] 为什么要采用java这个平台?


从开发项目的类别角度看java平台

基于B/S结构的系统,在这个方向上的竞争是激烈的,有专注于此的LAMP(Linux + Apache + Mysql + Php);也有刚刚兴起的Rails(Ruby Frameworks)甚至是号称快速开发的ASP.NET;当然了java在这个领域里的MVC框架数都数不完,比如Struts . Webwork等,然而即便是如此,选择java作为开发的理由也是不充分的,因为在这个梯队里java顶多排名最后。

基于C/S结构的系统,在这个方面java显然没有考虑周到,面对VB 、DELPHI、vc这些个如狼似虎的快速开发IDE,JAVA实在是显得异常的淡薄,即使你找到了一个可以匹敌这些个ide的工具,面对第三方的组件又会成为一大障碍,所以java在这个方面又一次的输了。


从java所强调的特性角度看java平台

java的重点是业务逻辑!(我以前也是如此坚信不移)可是谁有能够说别的语言不注重业务逻辑呢,业务逻辑只是一个抽象的概念,java只是依靠ejb提出了业务组件而已,其他的语言在实现业务逻辑的时候也可以包装成POJO的形式,看来这个观点也是失败的。

java强调的是跨平台的优势!这可以理解为初级的、商业的、忽悠人的词汇,面对众多动态语言如Python,在若干平台上的表现,java又如何来强调自己这方面的优势呢?失败

java支持分布式应用的项目!可笑的言论,分布式根本不是值得炫耀的资本,在java之前的c/s项目中何尝不是分布式的应用呢?失败


既然没有了这些个优势,我们看看java到底还剩下些什么?对了其实就是应用服务器!然而看过J2EE WITHOUT EJB的读者肯定知道Spring所希望达到的目的,也就是脱离应用服务器概念上的J2EE体系实现,既然在作者的眼里APPLICATION SERVER只不过是一个忽悠人的词汇,那么任何项目都选择java作为开发的依据显然就是自找苦吃,

那么什么情况下改选择java作为开发的平台呢?
<1> 如果你真的遇到了大型的系统开发任务,恭喜你,你终于可以看到分布式对象、集群的优势了。
<2> 客户是一个java的忠实fans或者是sun、ibm的金牌合作伙伴之类的,选择java是不得已的,但记住并不能证明java是最好的实现方式
<3> 如果你只想关心业务逻辑的实现,对于事务、缓存、查找等服务的实现没有兴趣的话,倒是不妨考虑采用ejb的形式,当然前提是你不愿意在寻找合适的替代品的情况下。
<4> 如果项目迫切的寻找某种框架的支持,选择java就是对的,你有众多优秀的、免费的、可扩展的、天才的框架可以选择,更多的时候你是出于尴尬的境地,因为任何一个都让你心动、而这样的选择往往是最痛苦、和快乐的。


正确的选择
<1>
条件: 如果项目仅仅只是一个小型的网站系统
选择: LAMP、Rails

<2>
条件: 项目规模中等
并且项目的时间比较紧,
项目可以架构在windows的系统之上,
选择: .Net  / Delphi

<3>
条件: 大型的系统,有支持分布式对象、集群的要求;或者SUN / IBM的金牌合作伙伴 ; 想要寻找某种优秀的框架来解决问题
选择: java是不二的选择,可是我想问一下,在现实中你能遇到这样的项目吗?

所以,从实际的角度出发,我们面对的99%可能都是一些符合条件1,2的系统,而选择java实在是得不偿失的。最后以一段Code Complete中的话来作为结束语

每个程序员都有很多的工具,但并不存在任何一个能够适用于所有工作的工具,因地制宜的选择正确工具是成为能有效编程的程序员的关键。

posted @ 2006-03-29 13:49 killvin| 编辑 收藏

 

作者简介
XincChen是Xtremework公司创始人.a自从.NET推出以来,1他已使用.NET帮助很多行业的用户开发了体现其商业理念的软件产品.aXincChen是.NET和EAI方面的专家,1他与Microsoft和Accenture等多家技术领先的公司合作,1为它们的客户提供了优秀的解决方案.a在工作之余,1他喜欢读书.c写书.c以及静静地休息.aApress出版社的另一本书——《BizTalkc2002cDesigncandcImplementation》——也是出自他的笔下.aXincChen拥有哥伦比亚大学的统计学文科硕士学位,1现居国内,1正在筹建公司在北京的研发中心.a可通过xchen@Xtremework.com和作者取得联系.

个人评论

这本书本来购买时的期望值非常的高,想必是一本细数.NET框架类的图书,结果却大失所望,而这本书也就因为其技术上的薄弱而消弱了其价值的表现力,不过作为一本.NET入门级的图书还是比较的合适,但在CSDN上竟然有四颗半的星的待遇实在不敢恭维。

而且作者的代码显然非常的粗造,竟然连起码的业务级数据校验都没有,在一些功能的分解上也是抹浆糊,可以想象作者并没有深入的思考而仅仅是去实现功能而已,猜想这样的框架很可能是作者对于以前工作的简单抽象而已。

整体上本书除了第一章应用框架介绍 、 第二章应用框架解析比较的出彩,尤其是创造性的提出了业务假设(Business assumption)的概念还比较的有深度,其他的章节只是对于其SAF框架的介绍,但在技术实现上只是对于.NET的若干相关服务的简单包装而已,对于已经在JAVA世界里的人来说,SAF框架其实就是Spring框架在.NET世界的简单实现版本,但相对于Spring的技术实力,作者还需要努力才行,不过作者显然是.NET方面的高手,对于.NET世界提供的杂乱无章的服务特性以及服务的原理非常的清楚,这也难怪在这样的平台上再去"多此一举"的开发另外的框架,无论从技术上还是推广上都非常的难!不过领略一下..NET的秀发也是多少有所收获的 :)

再次强调这本书仅仅只能够算作.NET方面的初级读物,个人评星3.5 ,请朋友们谨慎购买。

posted @ 2006-03-24 14:22 killvin| 编辑 收藏

wfc是构建在B/S结构上的流程定义工具

具备以下的功能
1> 实现了B/S结构上的工作流定义工具(没有看到同类型的产品)。
2> 流程定义格式与具体的工作流格式相分离,并可以在此基础上实现其他的流程定义工具产品。
3> 采用了Buffalo(XML-RPC的javascript实现)实现与后端Servlet的绑定。
4> 可以对具体的节点进行属性的配置(配置后的数据会被绑定为java的List自动传递到后端)。

目前还需要改进的
1> 修改目前工作流的图标(需要分支、状态、合并这样专业的图标)
2> 解决浏览器的刷新问题。
3> 增加线段的表现形式选项
4> 增加曲线的表现形式,尤其是在多线段的情况下要自动调整为曲线的形式。


目前的WFC已经被Michael Chen(buffalo的作者)收录在其网站上!地址如下
http://demo.amowa.net/wfc

对于buffalo的关注早在作者刚发布的时候就已经开始了,起初不太了解buffalo的作用,然而在具体的工作中寻找基于XML-RPC协议的产品时突然明白了它的价值,不过说简单一点buffalo是实现了基于javascript的对象的序列化以及反序列化工作。

posted @ 2006-03-21 17:58 killvin| 编辑 收藏

通用类图书

卓越奖: Prefactoring by Ken Pugh (O'Reilly)

生产力奖:
Innovation Happens Elsewhere: Open Source as Business Strategy by Ron Goldman, Richard P. Gabriel (Morgan Kaufmann)
Producing Open Source Software: How to Run a Successful Free Software Project by Karl Fogel (O'Reilly)
The Art of Project Management by Scott Berkun (O'Reilly)

技术类图书

卓越奖:Agile Web Development with Rails by Dave Thomas, David Hansson, Leon Breedt and Mike Clark (Pragmatic Bookshelf)

生产力奖:
Framework Design Guidelines: Conventions, Idioms, and Patterns for Reusable .NET Libraries by Krzysztof Cwalina and Brad Abrams (Addison-Wesley)
Practical Common Lisp by Peter Seibel (Apress)
Why Programs Fail: A Guide to Systematic Debugging by Andreas Zeller (Morgan Kaufmann)

企业项目管理

卓越奖: WelcomRisk 2.6 (Welcom)

生产力奖:
Corticon Business Rules Management 4.0 (Corticon)
JBoss 2 Portal (JBoss)
Visual Studio Team System 2005 (Microsoft)


数据库引擎和数据工具

卓越奖: Microsoft SQL Server 2005 (Microsoft)

生产力奖:
Berkeley DB 4.4 (Sleepycat Software)
Google Maps API 2005 (Google)
MySQL 5.0 (MySQL)

缺陷跟踪,变更和配置管理

卓越奖: Perforce SCM 2005 (Perforce)

生产力奖:
FogBugz 4.0 (Fog Creek)
Guiffy SureMerge 7.0 (Guiffy Software)
JIRA 3.4 (Atlassian Software)

设计和建模工具

卓越奖:Lattix LDM 2.0 (Lattix)

生产力奖:
Borland Together 2006 for Eclipse (Borland)
Enterprise Architect 6.0 (Sparx Systems)
MindManager Pro 6.0 (Mindjet)

开发环境

卓越奖: Visual Studio Team System 2005 (Microsoft)

生产力奖:
Eclipse SDK 3.1 (Eclipse.org)
IntelliJ IDEA 5.0 (JetBrains)
Komodo 3.5 (ActiveState)

类库、框架和组建

卓越奖: .NET Framework 2.0 (Microsoft)

生产力奖:
Dundas Chart for .NET 5.0 (Dundas Software)
Qt 4.0 (Trolltech)
Spring Framework 1.2.6 (SpringFramework.org)

移动开发工具

卓越奖: Crossfire 5.6 (AppForge)

生产力奖:
Carbide.c++ Express (Nokia)
Flash Lite 2.0 (Adobe)
NetBeans IDE 4.1 (Sun Microsystems)

项目质量管理

卓越奖: Rally 5.6 (Rally Software Development)

生产力奖:
CollabNet Enterprise Edition with Project Dashboard/Task Management 2005 (CollabNet)
QACenter Enterprise Edition 5.1 (Compuware)
TargetProcess Suite 1.4 (TargetProcess)

安全工具

卓越奖: Elemental Compliance System 1.4 (Elemental)

生产力奖:
CodeAssure 2.0 (Secure Software)
DevPartner SecurityChecker 1.0 (Compuware)
Fortify Security Tester 1.0 (Fortify)

测试工具

卓越奖:VMTN Subscription 2005 (VMware)

生产力奖:
Agitator 3.0 (Agitar Software)
AQtime 4.7 (AutomatedQA)
Clover 1.3 (Cenqua)

实用程序

卓越奖:Camtasia Studio 3.0 (TechSmith)

生产力奖:
DevPartner Studio 8 (Compuware)
Fog Creek Copilot 1.2 (Fog Creek Software)
ReSharper 1.5 (JetBrains)

Web开发工具

卓越奖: Rails 1.0 (rubyonrails.org)

生产力奖:
JBoss Application Server 4x (JBoss)
Macromedia Studio 8 2005 (Adobe)
Zend Studio - Enterprise Edition 5.0 (Zend)

名人堂产品获奖者:

?Visual Studio Professional Edition (Microsoft)

感慨
    MS在这次的JOLT中屡获殊荣,深思。。。

posted @ 2006-03-19 09:32 killvin| 编辑 收藏

很久都不骂人了,可是一不留心又看到了他写了恶心文章,实在是让人愤慨!这位板桥里人的"大名"是从透明的blog上知道的,首先就是感觉这个人的脑子有点问题!不去评论他发布的那个所谓的框架,就看看他写的一些文章就足以知道这个人脑袋非常的混乱,在这样混乱的情况下写出来的文章也就可想而知了,最近这位所谓的"大侠"又开始害人了

你还在用if else吗?- http://www.jdon.com/artichect/ifelse.htm


面向过程设计和面向对象设计的主要区别是:是否在业务逻辑层使用冗长的if else判断。如果你还在大量使用if else,当然,界面表现层除外,即使你使用Java/C#这样完全面向对象的语言,也只能说明你的思维停留在传统的面向过程语言上。

-我很纳闷了作者怎么可以从是否使用if else来判断一个设计是否符合OO特性呢?我们看到的if else属于语言这个层次的东西,而if else仅仅是完成逻辑判断的语句;如果说作者的这个观点成立的话,java、c#的语言发明者应该早就明白或者预测到if else 的"不OO"的特性,也会考虑到在语言层面删除这样的逻辑判断语句,但事实呢?发明者非但没有删除相反其他语言的发明者也一起发明了if else语句?!难道是大师们错了?

还是以大家熟悉的论坛帖子为例子,如ForumMessage是一个模型,但是实际中帖子分两种性质:主题贴(第一个根贴)和回帖(回以前帖子的帖子),这里有一个朴素的解决方案:
建立一个ForumMessage,然后在ForumMessage加入isTopic这样判断语句,注意,你这里一个简单属性的判断引入,可能导致你的程序其他地方到处存在if else 的判断。

  如果我们改用另外一种分析实现思路,以对象化概念看待,实际中有主题贴和回帖,就是两种对象,但是这两种对象大部分是一致的,因此,我将ForumMessage设为表达主题贴;然后创建一个继承ForumMessage的子类ForumMessageReply作为回帖,这样,我在程序地方,如Service中,我已经确定这个Model是回帖了,我就直接下溯为ForumMessageReply即可,这个有点类似向Collection放入对象和取出时的强制类型转换。通过这个手段我消灭了以后程序中if else的判断语句出现可能。

-作者在这里似乎列举了一个例子,可是对于帖子和回帖这样简单的问题,只要遵守OO的设计师都会抽象出一个帖子的父类,然而这又能说明什么呢?在具体的业务逻辑中难道你不判断传递过来的对象的类别?(现在主题贴与回帖的处理方法是不同的),同样你无法避免在具体的编码中使用if else的可能?!


最后总结:将if else用在小地方还可以,如简单的数值判断;但是如果按照你的传统习惯思维,在实现业务功能时也使用if else,那么说明你的思维可能需要重塑,你的编程经验越丰富,传统过程思维模式就容易根深蒂固,想靠自己改变很困难;建议接受专业头脑风暴培训。

 用一句话总结:如果你做了不少系统,很久没有使用if else了,那么说明你可能真正进入OO设计的境地了。(这是本人自己发明的实战性的衡量考核标准)。

-显然作者并不是去讨论if else的语言问题,而是为自己的"洗脑培训"打广告!并讲这样的问题上升到设计模式、禅的境界,可谓是煞费苦心呀,没有人说设计模式不好也没有人怀疑禅的境界的高深,但不是作者这样的人靠读一两篇文章或者发布个所谓的"毫无实际意义"的框架就可以领悟到的,还是那句话:长得丑不要紧,不要出来吓人!不过我还要补充一句就是,不懂不要紧,不要乱说免得害人(因为我们都知道泼妇骂街的道理,虽然没文化但确实能够带来眼球效应)。

posted @ 2006-03-10 11:49 killvin| 编辑 收藏

该死,鼠标瘫痪了!
本来以为是Maxthon的问题(在鼠标单击的时候会关闭一些窗口,并且是随机的?!)还就这样的问题给作者写了一封信?汗。。。经过仔细的寻找原因,原来是自己的鼠标的问题(这个鼠标跟了我快7年了,Logitech最好的滚轴鼠标,要300多块大洋)有点舍不得,又没有办法,该下岗了,一同下岗的也许还有我的键盘(TCL的手感级差!:( )

不过为了找原因我倒是废了一番周折,下载和试用了N多的浏览器,也看了很多网上的评论对比文章,现在主流的浏览器主要就是Opera , Firefox, Maxthon , GreenBrowser , MyIE , TheWorld 等,然而在资源的利用率上这些个产品都非常的让人失望,难道就没有一款适合于程序员的胃口的浏览器?!

可喜的是偶发现了GOSURF这款浏览器,在众多的浏览器中它的内存占用率是最低的,而且它的作者似乎在这方面也是煞费苦心,"一味"的强调速度,甚至对于鼠标手这个及其流行的功能的加入也是非常的谨慎,可以看出作者是个很有个性的程序员,值得期待!

如果你和我一样对资源的利用率近乎苛刻,可以考虑这样的一款产品 http://gosurfbrowser.com/?ln=cn

posted @ 2006-03-07 17:42 killvin| 编辑 收藏

不错的文章
http://www-128.ibm.com/developerworks/cn/java/j-junit4.html

JUnit 是 Java™ 语言事实上的 标准单元测试库。JUnit 4 是该库三年以来最具里程碑意义的一次发布。它的新特性主要是通过采用 Java 5 中的标记(annotation)而不是利用子类、反射或命名机制来识别测试,从而简化测试。在本文中,执着的代码测试人员 Elliotte Harold 以 JUnit 4 为例,详细介绍了如何在自己的工作中使用这个新框架。注意,本文假设读者具有 JUnit 的使用经验。

JUnit 由 Kent Beck 和 Erich Gamma 开发,几乎毫无疑问是迄今所开发的最重要的第三方 Java 库。正如 Martin Fowler 所说,“在软件开发领域,从来就没有如此少的代码起到了如此重要的作用”。JUnit 引导并促进了测试的盛行。由于 JUnit,Java 代码变得更健壮,更可靠,bug 也比以前更少。JUnit(它本身的灵感来自 Smalltalk 的 SUnit)衍生了许多 xUnit 工具,将单元测试的优势应用于各种语言。nUnit (.NET)、pyUnit (Python)、CppUnit (C++)、dUnit (Delphi) 以及其他工具,影响了各种平台和语言上的程序员的测试工作。

然而,JUnit 仅仅是一个工具而已。真正的优势来自于 JUnit 所采用的思想和技术,而不是框架本身。单元测试、测试先行的编程和测试驱动的开发并非都要在 JUnit 中实现,任何比较 GUI 的编程都必须用 Swing 来完成。JUnit 本身的最后一次更新差不多是三年以前了。尽管它被证明比大多数框架更健壮、更持久,但是也发现了 bug;而更重要的是,Java 不断在发展。Java 语言现在支持泛型、枚举、可变长度参数列表和注释,这些特性为可重用的框架设计带来了新的可能。

JUnit 的停滞不前并没有被那些想要废弃它的程序员所打败。挑战者包括 Bill Venners 的 Artima SuiteRunner 以及 Cedric Beust 的 TestNG 等。这些库有一些可圈可点的特性,但是都没有达到 JUnit 的知名度和市场占有份额。它们都没有在诸如 Ant、Maven 或 Eclipse 之类的产品中具有广泛的开箱即用支持。所以 Beck 和 Gamma 着手开发了一个新版本的 JUnit,它利用 Java 5 的新特性(尤其是注释)的优势,使得单元测试比起用最初的 JUnit 来说更加简单。用 Beck 的话来说,“JUnit 4 的主题是通过进一步简化 JUnit,鼓励更多的开发人员编写更多的测试。”JUnit 4 尽管保持了与现有 JUnit 3.8 测试套件的向后兼容,但是它仍然承诺是自 JUnit 1.0 以来 Java 单元测试方面最重大的改进。

注意:该框架的改进是相当前沿的。尽管 JUnit 4 的大轮廓很清晰,但是其细节仍然可以改变。这意味着本文是对 JUnit 4 抢先看,而不是它的最终效果。

posted @ 2006-03-05 13:10 killvin| 编辑 收藏

http://blog.raylife.com/?p=336

在这个暖暖的午后享受着阳光的温暖的同时,听着CUSCO 的专辑实在是一种享受,我喜欢那种宁静的音乐,因为它带给你的除了纯净还是纯净。。。。

不想被任何的人打扰,城市的喧闹让自己的思想变得复杂,而唯独音乐才能够真正的唤醒你心灵深处的东西,

posted @ 2006-03-04 13:23 killvin| 编辑 收藏