您的位置:首页技术文章
文章详情页

一个基于Annotation的持久层框架-去除getter和setter

【字号: 日期:2024-07-18 18:30:34浏览:6作者:猪猪
内容: 使用J2SE 5.0的注解来去除getter和setter摘要getter/setter这种习惯用法一直是有问题的,它允许你的类更容易被访问,却使这些类失去了可维护性。J2SE 5.0的注解(或者说元数据)提供了另一种可能性。比起用自省寻找get/set方法,你可以用注解“标注类,然后在编译或者运行时访问那个注解。这篇文章不仅描述了注解机制,还介绍了一个基于XML的持久化机制的输出端,这个机制使用注解来标注类和字段。我曾经在JavaWorld对getter/setter这种习惯用法的缺点做了很详细的讨论(请看资源)。这种习惯用法一开始是在JavaBean规范中被介绍的,以作为一种“标注对象属性的方法,这样,一个扩展的用户界面层工具(叫做BeanBox)可以为那个对象创建一系列的属性列表。你可以像下面那样提供方法来“标注属性。String getFoo();void setFoo( String newValue );BeanBox使用Class类中的自省API获取方法列表,然后使用模式匹配来寻找getter/setter对。根据这些推断出属性是否存在,并确定属性的类型(在这个例子中,有一个String类型的Foo属性)。你是不会调用这些方法的,它们只会被BeanBox调用。有趣的是,JavaBean规范的作者完全清楚getter/setter标注机制的问题所在(主要缺点已经在以前的文章中讨论过了,getter/setter方法暴露了过多的对象实现信息,所以底层类很难维护)。因此,设计者提供了大量的面向对象解决方法,比如BeanInfo和Customizer接口。用户实现了这些接口以后,就可以在没有setter/getter的条件下建立图形用户界面。不幸的是,这些过度复杂的面向对象方法在规范里很少提到。Getter/setter方法是简单的,可是如果你不能理解面向对象关系的维护问题,getter/setter方法好象是很合理的。因此,BeanInfo/Customizer方法就没落了,而getter/setter策略则像兔子一样快速繁殖。然而,你所经常看到的习惯用法并不是最好的做法。JavaBean刚被提出时,许多人(包括我自己)赞成在Java中使用新的关键字来消除getter和setter。利用新引入的关键字的能力,我在早些时候像下面那样描述Foo属性:private @property String foo;因为foo是私有的,所以用新的关键字把它暴露给BeanBox并没有违反封装的原则。可是这时,引入新关键字有些离经叛道,尽管这个关键字不可能跟已经存在的标识符混淆,毕竟它们包含一个非法字符@。当J2SE 5.0出现时,Sun已经领会到了它的微妙,并且对语言的主要语法做了一点调整。现在,你可以引入一个新的关键字(叫做注解)来表明一个属性会在编译时或者运行时被检查。你可以引入你选择的任何关键字。只需要做到这点,注解(关键字)必须有一个前导@符号,并且你必须像使用形容词一样使用注解(注解可以放在任何你可以声明static,final,或者public的地方)。最后,你可以抛开getter和setter了,一种更干净的语法能够做到相同的事情。Java内置了两个很棒的关于注解的例子。想想这样一种情况,你的类继承自AWT/Swing的Adapter,可是却不小心拼错了基类方法的名字。你认为你覆写了基类的方法,实际上却没有。这种意料之外的继承是非常难以发现,但是如下的代码中的错误却很容易被编译器检测出来。public class myListener implements MouseListener{ @Overrides void MousePressed(MouseEvent e) { System.out.println('Mouse button clicked!'); }}编译器在这里会抱怨,因为基类的方法叫做mousePressed()(m是小写的),而不是MousePressed(),就像类定义的那样。另外,程序里的注解@Deprecated在语法上也比Javadoc中的要简洁(因为注释内容不会影响类的兼容性)。有两种途径可以处理注解。首先,Class类的自省API可以获取关联到类的注解,以及关联到类的字段和方法的注解。BeanBox可以使用这种机制来寻找被标注的属性,并建立起属性列表。如果你没有自己的BeanBox,那么还有另一种选择。JDK提供了apt (Annotation Processing Tool)处理器,它是javac的前端,能够理解注解,并允许你构建Java源代码。你需要给apt提供不同的注解处理器插件。在这个例子中,插件会建立一个包装类,像老式的BeanBox做的那样,使用getter/setter这种习惯用法来把被注解的属性暴露给外界。不过,(就算是按Sun的标准)apt的文档也是非常糟糕的。我会在以后的文章中在介绍如何使用。在这篇文章中,我会展示一个小的持久化框架的“输出端来告诉你如何使用运行时注解。这个框架并没有解决全部持久关系问题的打算,但是它很容易的把一个对象的状态表示为一个XML字符串。你可以用这种原理来取代其他应用中的getter和setter,比如GUI或者帮助系统(通过注解一个类来说明详细的信息)。版权声明:任何获得Matrix授权的网站,转载时请务必保留以下作者信息和链接作者:Allen Holub ;deafwolf(作者的blog:http://blog.matrix.org.cn/page/deafwolf)原文:http://www.javaworld.com/javaworld/jw-03-2005/jw-0321-toolbox.htmlMatrix:http://www.matrix.org.cn/resource/article/44/44458_annotation+persistence.html关键字:annotation;persistence使用XMLExporter类清单1示范了我的持久化框架是如何使用注解的,清单2则展示了相应的输出。清单1. Test.java:使用XMLExporter 1 package com.holub.persist.test; 2 3 import java.io.*; 4 import java.util.*; 5 import com.holub.persist.*; 6 import com.holub.persist.Exportable; 7 //---------------------------------------------------------------------- 8 @Exportable 9 class Address 10 { private @Persistent String street; 11 private @Persistent String city; 12 private @Persistent String state; 13 private @Persistent('zipcode') int zip; 14 15 public Address( String street, String city, String state, int zip ) 16 { this.street = street; 17 this.city = city; 18 this.state = state; 19 this.zip = zip; 20 } 21 } 22 //---------------------------------------------------------------------- 23 public class Test 24 { 25 @Exportable( name='customer', description='A Customer' ) 26 public static class Customer 27 { 28 @com.holub.persist.Persistent 29 private String name = 'Allen Holub'; 30 31 @Persistent 32 private Address streetAddress = 33 new Address( '1234 MyStreet', 34'Berkeley', 'CA', 99999 ); 35 @Persistent 36 private StringBuffer notes = new StringBuffer( 'Notes go here '); 37 38 private int garbage; // Is not persistant 39 40 @Persistent Collection invoices = new LinkedList(); 41 { invoices.add( new Invoice(0) ); 42 invoices.add( new Invoice(1) ); 43 } 44 } 45 46 @Exportable 47 public static class Invoice 48 { private @Persistent int number; 49 public Invoice( int number ){ this.number = number; } 50 } 51 52 public static void main(String[] args ) throws IOException 53 { Customer x = new Customer(); 54 XmlExporter out = 55 new XmlExporter( 56 new PrintWriter(System.out, true) ); 57 out.flush( x ); 58 } 59 }Listing 2. Test output 1
相关文章: