Annotation概述
首先让我们给出这两项技术的一个概述。Annotation是JDK5.0的新功能,它在JSR-175规范中有详细定义。它们允许您以安全的方法定义元数据并应用到类,方法,构造程序,字段或参数中。对于你们中熟悉XDoclet的人来说,Annotation将非常直观,您可以用来声明标签以产生代码。两者的主要不同是Annotation是Java语言的一部分而XDoclet标签可能会打错并且难以创建。我喜欢用例子来说明,所以让我们展示一个简单的例子。
要定义一个Annotation,您所要做的就是声明一个特殊类型的Java接口。
清单1:Orange.java
package org.jboss.collors;
public @interface Orange{}
定义了这个接口,您就可以用来提供更多的描述给您的Java元素。
清单2:Foo.java
package org.jboss.examples;
public class Foo
{
@Orange void someMethod();
@Orange private int someField;
}
AOP概述
有许多的文章和书籍解释AOP到底是什么,例如Graham O'Regan的ONJava文章“Introduction to Aspect-Oriented Programming."我将在本文给出一个快速的概览,但我鼓励您在线做更多的研究。
假设您要添加代码到一个应用程序去测试调用一个特定的java方法所需的总的时间。该代码可能看起来如下:
清单3:
public class BankAccount
{
public void withdraw(double amount)
{
long startTime = System.currentTimeMillis();
try
{
// Actual method body...
}
finally
{
long endTime = System.currentTimeMillis() - startTime;
System.out.println("withdraw took: " + endTime);
}
}
}
虽然这些代码能够正常工作,但这个方法有一些问题:1.它难以打开和关闭测试,您必须在try/finally块中对每个方法或购置函数手工增加代码以进行基准测试。
2.这一轮廓代码并不真正属于贯穿整个应用的代码。它使得您的代码臃肿并难以理解,因为您必须将计时放在try/finally块中。
3、如果您想扩展它的功能以包含一个方法或是失败计数,或甚至是注册这些统计数据到一个更为复杂的报告机制中,您必须修改大量不同文件(又一次)。
Metrics类提供了一个什么是横切(cross-cutting)关系的完美,简洁的小例子。Jboss AOP以一种含蓄的方式提供了一个简单的方法来封装和应用这样的关系,这样某些象度量操作代码不会弄乱您的编码。让我们稍为深入到Jboss AOP一些来看看如何实现。
为了使用Jboss AOP封装度量功能,您首先需要定义一个方面来指出该度量行为。
清单4:
public class Metrics
{
public Object profile(MethodInvocation invocation) throws Throwable
{
long startTime = System.currentTimeMillis();
try
{
return invocation.invokeNext();
}
finally
{
long endTime = System.currentTimeMillis() - startTime;
java.lang.reflect.Method m = invocation.getMethod();
System.out.println("method " + m.toString() +
" time: " + endTime + "ms");
} }
}
一个方面只是一个具有定义了您想要附加到您的对象模型的行为的普通Java类。这些方法的签名必须返回一个java.lang.Object并且必须具有一个(并且只有一个)Jboss AOP 调用对象参数,它被用来封装方法,构造函数或字段调用。方法名可以是任何你想要的并且当您绑定该方面到您的代码片断时被引用。
下面要做的事情就是实际应用方面到您想要它勾勒一个方法的执行的某个程序点。大多数AOP框架提供了一个指向表达式语言,在此处您可以定义您想要某个方面行为被附加到的位置。下面是在Jboss AOP中的做法。
清单5:jboss-aop.xml
<aop>
<aspect class="Metrics"/>
<bind pointcut="execution(public void BankAccount->withdraw(double amount))">
<advice name="profile" aspect="Metrics"/>
</bind>
</aop>
采用在Metrics.java中对方面的定义和jboss-aop.xml中的指向定义,该度量代码现在以含蓄而又透明地应用到BankAccount.withdraw()方法中并能在勾勒代码不再需要时轻易地移除。
对于Jboss AOP更多的信息,请查询分发包中的指南。其中具有大约20个例子来带领您漫游如何使用Jboss AOP框架。
嘘!现在我们已经进行了一个概览,让我们深入到本文的中心内容。我将再次给您提供一些例子,因为这是我所知道的讲授一个新的概念的最好的方法。
正如我前面说的,Annotation加上AOP几乎是给予您扩展Java语言的能力。Annotation提供了声明新的,可兼容的,类型安全的语法机制。AOP提供了封装和应用新的行为到一个语法表达式的机制。
方法Annotation和AOP
让我们看看如何使用方法Annotation和AOP.使用Annotation和AOP并应用到一个方法类似于使用Java的synchronized关键字。当您设定一个方法为synchronized,您在告诉JVM:您想该方法在被调用时以一种特殊的方式进行。Annotation允许您定义一个新的关键字来触发您自己的特殊的定制行为。AOP给予您封装这一行为的能力并将其“编织”进该方法的执行中。再次的,这一概念的最佳描述是通过一个例子。
让我们假设我们想要添加新的语法,使用该语法使得我们可以在方法被标签为@Oneway时,在后台以另一个线程调用这个void方法。可以象这样使用新的语法:
清单6:
Import org.jboss.aspects.Oneway
public class Foo
{
@Oneway public static void someMethord () {…}
public static void main(String[] args){
somMethod();//executes in
backgroud
}
}
当someMethod()在main中被调用,它将异步运行,这样main中的代码可以并行执行其他任务。
要实现这一功能,首先要在一个Annotation中为我们的@Oneway标签定义新的Java语法。
清单7:Oneway.java
package org.jboss.aspects;
import java.lang.annotation.ElementType;
import java.lang.annotation.Target;
@Target({ElementType.METHOD})
public @interface Oneway {}
够简单的。@Target标签允许您缩小Annotation可以应用的地方。在本例中,我们的@OnewayAnnotation只能应用到一个方法。记住,这些都是J2SE5.0百分之百可用的纯Java。
下面要做的事是定义一个封装我们的@Oneway行为的方面类。
清单8:OnewayAspect.java
package org.jboss.aspects;
public OnewayAspect
{
private static class Task implements Runnable
{
private MethodInvocation invocation;
public Task(MethodInvocation invocation)
{
this.invocation = invocation;
}
public void run()
{
try { invocation.invokeNext(); }
catch (Throwable ignore) { }
}
}
public Object oneway(MethodInvocation invocation) throws Throwable
{
MethodInvocation copy = invocation.copy();
Thread t = new Thread(new Task(copy));
t.setDaemon(false);
t.start();
return null;
}
}
这个方面够简单。oneway()方法拷贝invocation,创建一个线程,在后台启动整个调用并返回。我们可以想象一个更为复杂的例子:使用J2SE 5.0 java.util.concurrent包中的某些新的Executors,但这些代码很有希望阐明了如何基于这个例子构建更为复杂的实现。
最后必须要做的事情是指定当@OnewayAnnotation在一个方法中声明时触发OnewayAspect应用的指向表达式。
清单9:jboss-aop.xml
<aop>
<aspect class="org.jboss.aspects.OnewayAspect"/>
<bind pointcut="execution(void *->@org.jboss.Oneway(..))">
<advice name="oneway"
aspect="org.jboss.aspects.OnewayAspect"/>
</bind>
</aop>
该指向表达式规定任何具有@Oneway标签的void方法都应该有OnewayAspect.oneway()方法在它本身执行前被执行。随着Annotation,方面和现在定义的指向表达式,@Oneway语法现在可以用于您的应用程序中。一个简单,清晰,易于实现的方法来扩展Java 语言!
字段Annotation和AOP
让我们看看如何使用字段Annotation和AOP.使用Annotation和AOP,您可以改变一个对象的字段或是作为一个类的静态成员的实际存储方式。在这个例子里我们要完成的是当您将一个字段(静态或是成员)标记上@ThreadBased,尽管是将它存储在java.lang.ThreadLocal,但它的值依然正常。当然,您可以直接使用ThreadLocal变量,但问题是ThreadLocal并非一个类型并且您必须使用“麻烦的”(好,它们并没有那么罗嗦)get()和set()方法。那么我们现在做的就是创建一个ThreadLocal类型的字段。我们主要的将创建一个称为@Thradbased变量的新的Java字段类型。
象这样使用新的类型:
清单10:
import org.jboss.aspects.Threadbased;
public class Foo
{
@Threadbased private int counter;
}
为了实现这个功能,我们必须先定义Annotation
清单11:Threadbased.java
package org.jboss.aspects;
import java.lang.annotation.ElementType;
import java.lang.annotation.Target;
@Target({ElementType.FIELD})
public @interface Threadbased {}
够简单。@Target标签允许您缩小Annotation可以应用的地方。在本例中,我们的@ThreadbasedAnnotation只能应用到字段。
下面的事情是定义封装我们的ThreadLocal行为的方面。
清单12:ThreadbasedAspect.java
package org.jboss.aspects;
import org.jboss.aop.joinpoint.*;
import java.lang.reflect.Field;
public class ThreadbasedAspect
{
private ThreadLocal threadbased = new ThreadLocal();
public Object access(FieldReadInvocation invocation)
throws Throwable
{
// just in case we have a primitive,
// we can't return null
if (threadbased.get() == null)
return invocation.invokeNext();
return threadbased.get();
}
public Object access(FieldWriteInvocation invocation)
throws Throwable
{
threadbased.set(invocation.getValue());
return null;
}
}
ThreadbasedAspect 封装到一个Java