2008年(3500)
分类:
2008-05-04 23:22:48
作者:Norbert Ehreke;deafwolf(作者的blog:http://blog.matrix.org.cn/page/deafwolf)
原文: Annotations.html
关键字:Java;Annotations
Annotations
Annotations是J2SE 5.0引入的新语言特性。通常, Annotations允许开发者用一种跟运行代码无关的次要信息来标注类,方法以及成员。这样就可以使用类似评价的 Annotations,比如“好方法”、“坏方法”,或者更详细一些,“不推荐的方法”、“覆写的方法”。这些用法的可能性是无穷的。不过请注意,方法或类跟标注实际可能不相关,比如“不推荐的”。如果想知道关于 Annotations的更多详细讨论,请阅读Java 5.0 Tiger: A Developer's Notebook。
因为 Annotations可以用来描述用例或者实体比如方法和类的意思,所以这是一种语法棒棒糖。反过来,这些附加信息也可以被其他东西(比如框架)用于各种各样的动作,比如生成文档(Javadoc),或者像这里讨论的,作为一种特殊内容来控制行为,比如对象的生命周期。
生命周期管理通常发生在中间件环境中,比如应用服务器
示例问题
1.创建:mask在这一状态中最好只安排一次。
2.初始化:在这一状态,数据从文件和数据库等地方找回,并填充到mask的字段中。
3.激活:这里,用户放弃对mask的控制。
在现实中,涉及到很多方面:访问、验证、控制依赖等等。
Phases
在这篇讨论中,我提到了每一步操作的phase,基本思想非常简单:我们把类方法标注成操作链中的phases,然后把这些方法的调用交给服务(框架)来做。实际上,这种方法并不仅限于生命周期管理。它可以用做商务流程中所有调用的控制机制。
我们使用的 Annotations简单的命名为Phase,我们使用它来把一个方法标注成操作链的一部分。在下面的代码里,你可以看到 Annotations的声明与接口很接近。
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface Phase {
int index();
String displayName() default "";}
我们简单看一下代码。在头两行,你看到 Annotations跟另外两个 Annotations一起使用。刚看上去时,这有点混乱,但是这两行很简单的就指定了 AnnotationsPhase只允许并且应该保留到编译后。之所以增加这两个 Annotations,是因为有些 Annotations可能只会在编译期间被使用,并且可能指向类或者成员。
@interface是一个 Annotations的标准描述。在接下来的代码中,index和displayName??不只声明了一个成员,还声明了一个方法??也是Java的新语法。如果没提供初始值的话, displayName将被赋予了一个空字符串作为初始值,同时这个displayName能够被用来作为监测用途,叫做progress bar. index()是必须的,它告诉框架这些phase可以被缺省的执行。
像我早先说的那样,我们应该把这个逻辑从对象中分离出来,所以我们定义了一个必须实现的接口以用于调用管理。这个接口可以被一个客户端对象实现。为了达到管理的目的,我们定义了一个通用的标记接口,所有的“phaseable”接口必须从这里继承,这样框架就可以通过一个唯一的访问点来管理类。
public interface Phased {}
public interface PhasedMask extends Phased {
@Phase(index=0)
public void construct();
@Phase(index=1)
public void initialize();
@Phase(index=2,displayName="Activating...")
public void activate();}
Phaser
主要处理类也许应该被称为Phaser。(喂,我们不都挺喜欢星际旅行吗?)它执行全部的phase,并且为用户提供简单的监视机制。这个类的实现并不包含在这篇文章里,当然,你可以从资源找到框架代码的下载。
假设我们有一个像这样的MyMask类:
public class MyMask implements PhasedMask {
@Phase(index = 0)
public void construct() {
// Do the layout }
@Phase(index = 1)
public void initialize() {
// Fill the mask with data }
@Phase(index = 2)
public void activate() {
// Activate the listeners and allow the user to interact with the mask }
// Business code}
现在,我们可以像下面那样调用这些PhasedMask方法:
Phaser phaser = new Phaser( phasedMask );phaser.invokeAll();
这样,方法construct()、initialize()和activate()就都被调用了。
那么我们如何控制phase呢?我们跳过构造阶段,因为当我们第二次调用 phasedMask()时,并不需要再布置一次。本意是我们不需要construct()被调用多次。因为我们把这个方法用0索引标注,所以我们可以简单的跳过这个索引,并且告诉Phaser应该执行哪些phase。
Phaser phaser = new Phaser( phasedMask );phaser.invoke( new int[] {1,2} );
这样就好了,不过不够清晰。谁会记得phase的索引实际代表什么?幸运的是,我们可以像下面这样写得详细一点:
Phaser phaser = new Phaser( phasedMask );
phaser.invoke( new String[] {"initialize","activate"} );
这里,我们使用从接口继承来的方法名。当然,如果需要的话,我们可以重新安排phase。所以,为了交换顺序,我们可以这样写:
Phaser phaser = new Phaser( phasedMask );
phaser.invoke( new String[] {"activate","initialize"} );
这个好象没什么意义,但是,当某个设置中一些phase是紧耦合的时,这种做法是有用的。
PhaseListener listener = new PhaseListener() {
public void before( PhaseEvent e ) {
// This is called before the Phaser invokes a phase }
public void after( PhaseEvent e ) {
// This is called after the Phaser has successfully invoked a phase }
}; Phaser phaser = new Phaser( phasedMask );phaser.addPhaseListener( listener );
phaser.invoke( new String[] {"initialize","activate"} );
讨论和总结
这种方法也显示了一种比Javadoc的 Annotations使用更进一步的用法。它们不只用于生命周期管理,也可以用于常规的对象初始化。
如果phase必须重新排列或者忽略,这些行为会发生在实现类中。
最后,调用链对IDE来说是不明晰的。
下载本文示例代码