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; } |
那么我们可以用Annotation来干什么呢?一些人想用Annotation来产生代码并替代XDoclet,其他人,象J2EE和EJB3.0专家组,将它视为部署描述符的替代。本文谈论在AOP中如何使用Annotation
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字段的访问。它里面具有一个专门的ThreadLocal变量跟踪thradlocal变为一个特殊的字段。它还有一个单独的access()方法,该方法根据一个字段的get或set方法是否被调用决定它是否被调用。这些方法委托给ThreadLocal来获得字段的当前值。
最后,我们必须定义一个指向表达式,当@ThreadbasedAnnotation在某个字段被指定时触发ThreadbasedAspect的应用。
清单13: jboss-aop.xml
<aop> <aspect class="org.jboss.aspects.ThreadbasedAspect" scope="PER_JOINPOINT"/> <bind pointcut="field(* *->@org.jboss.aspects.Threadbased)"> <advice name="access" aspect="org.jboss.aspects.ThreadbasedAspect"/> </bind> </aop> |
只有当我们具有多个@Threadbased变量定义在同一个类时,我们需要为每个静态字段分配一个ThreadbasedAspect实例。对于成员变量,我们需要为每个字段,每个对象实例分配一个ThreadbasedAspect实例。为了促进这一行为,方面定义通过设定实例为PER_JOINPOINT限制方面类的实例何时和何地被分配出去的范围。如果我们不做限制,Jboss
AOP会只分配一个ThreadbasedAspect实例并且不同的字段会共享相同的ThreadLocal接口——这不是我们所希望的。
好就这样。一个清晰容易的扩展Java来指定一个新的特殊类型的方法。注意:该特殊的方法来自Jboss AOP束。
依赖注入
字段Annotation和AOP可以使用的一个有趣的地方是依赖注入。依赖注入是关于对象声明它们需要什么信息,配置或服务引用以及运行时自动注入这些依赖而不是用代码明确地在一个注册中心查找。在J2EE领域,获得javax.transaction.TransactionManager服务的访问并未标准化并且实际上不同的厂商有不同的实现。许多框架开发者需要使用TransactionManager来实现定制事务服务。使用字段AnnotationAOP提供依赖注入并抽取出一个需要TransactionManager的组件如何引用它的细节是一个了不起的方法。让我们定义一个方面,它将注入一个TransactionManager引用到一个字段值中。
首先,我们再次定义我们的Annotation。
清单14: Inject.java
package org.jboss.aspects; import java.lang.annotation.ElementType; import java.lang.annotation.Target; @Target({ElementType.FIELD}) public @interface Inject {} |
下面我们将定义方面类,它封装了TransactionManager的解析。该方面是特定于JBoss应用服务器,但您可以定义为每个厂商定义不同的实现。
清单15: InjectTMAspect.java
package org.jboss.aspects; import org.jboss.aop.joinpoint.*; import java.lang.reflect.Field; import javax.transaction.TransactionManager; import org.jboss.tm.TxManager; public InjectTMAspect { private TransactionManager tm = TxManager.getInstance(); public Object access(FieldReadInvocation invocation) throws Throwable { return tm; } public Object access(FieldWriteInvocation invocation) throws Throwable { throw new RuntimeException( "Setting an @Injected variable is illegal"); } } |
最后,我们必须定义XML绑定来触发当@Inject标签应用到一个字段时InjectTMAspect的应用。指向表达式基本上说明了对任意一个标记为@Inject的TransactionManager字段应用InjectTMAspect。
清单16:
<aop> <aspect class="org.jboss.aspects.InjectTMAspect"/> <bind pointcut="field(javax.transaction.TransactionManager *->@org.jboss.aspects.Inject)"> <advice name="access" aspect="org.jboss.aspects.InjectTMAspect"/> </bind> </aop> |
现在Annotation、方面类和XML绑定已经定义,我们可以在我们的代码中使用了。
清单17:
import javax.transaction.TransactionManager; import org.jboss.aspects.Inject; public class MyTransactionalCache { @Inject private TransactionManager tm; ... } |
更多预打包例子
Jboss AOP不仅仅是关于AOP框架。它还有一个丰富的方面库,您可以直接在您的应用中使用。在这个库中是一个比我们现在在本文展示的例子更为复杂的Annotation方面集。这些方面包括异步调用,事务划分,事务锁定和基于角色的安全。让我们简要地浏览一下以提供给您一个更好的关于Annotation和AOP共同工作的考虑。
异步方面
Jboss AOP异步方面允许您定义任何方法为异步的,这样它可以在后台被执行。这对于我们的@Oneway例子来说有些困难,因为它使用Oswego并行包中的执行器工具,并为那些具有一个返回类型的方法提供了一个方法来异步地接收回响应。要使用这个方面,您只需标记一个方法为@Asybchronous.
清单18:
public Foo { @Asynchronous public int someMethod(int someArg) {...} } |
@Asynchronous标签的应用做了一些事情。与在本文中的@Oneway例子一样,它应用一个在后台运行该方法的方面。而且,采用@Asynchronous标签,您并不仅限于void方法并可于实际上返回一个值的方法进行交互。当@Asynchronous标签被应用,它强制Foo类实现AsynchronousFacade接口。在AOP领域,这称为接口引入(interface introduction)。AsynchronousFacade接口允许您预测一个响应或以超时限定等待一个响应。最好用一个例子来解释。
清单19:
Foo foo = new Foo(); someMethod(555); // executes in background AsynchronousFacade facade = (AsynchronousFacade)foo; AsynchronousResponse response = facde.waitForResponse(); System.out.println(response.getReturnValue()); |
您可以启动多个不同对象的多个不同方法的多个调用,并异步积累它们的响应。
事务锁定
有时在J2EE事务期间而不是一个方法执行,构造函数调用或同步块执行期间同步一个对象或类会很有用。对这类事务同步或锁定,Jboss AOP发明了@TxSynchronized关键字。您可以使用@TxSynchronized在任意成员或静态方法已经构造函数上。
清单20:
import org.jboss.aspects.txlock.TxSynchronized; public FooBar { @TxSynchronized public FooBar() {} @TxSynchronized static void staticMethod() {} @TxSynchronized public memberMethod() {} } |
如果一个被标记为@TxSynchronized的构造函数或静态方法被调用,类的锁监视器会在事务执行期间被保持着。如果一个标记为@TxSynchronized的成员方法被调用,该对象实例的锁监视器将被保持直到目前的事务提交或回退。控制该行为的方面也将做死锁检测并在发生死锁时抛出RuntimeException。
J2EE 散餐(原文法文:a la carte)之:事务划分
EJB3.0已经定义了一些Annotation进行事务划分。Jboss AOP在此基础上构建。这样您可以通过指定Annotation应用事务划分到任意方法(静态或成员)以及任何Java类构造函数。
清单21:
import org.jboss.aspects.tx.*; public class Foo { @Tx(TxType.REQUIRED) public Foo {} @Tx(TxType.REQUIRESNEW) public static createFoo() { return new Foo(); } } |
J2EE 散餐之:基于角色的安全
EJB 3.0也定义了一些Annotation实现基于角色的安全。Jbos AOP是基于此构建的,所以您可以应用基于角色的安全到任何的字段或方法(静态或成员)已经构造函数。
清单22:
import org.jboss.aspects.security.*; @SecurityDomain("LDAP Repository") public class Foo { @Permissions({"admin"}) public Foo() {} @Permissions({"developer"}) public static int status; @Permissions({"anybody"}) public static void doSomething() {...} } |
EJB演变
随着AOP与EJB规范一起渐渐成熟,我真正希望发生的是EJB规范定义的Annotation将能在任何上下文作为新的Java语言的形容词被使用,而不是让它们有限的使用在会话bean中。想象一下,一个真正的无状态bean仅仅成为一个明文Java类的一个静态方法集。
清单23:
public MySessionBean { @Tx(TxType.REQUIRED) public static doSomething() {...} } |
无论如何,这些关于AOP和EJB的讨论很可能就是为了EJB4.0。
结论
并非是限制J2SE5.0Annotation用于代码生成,Annotation和AOP可以被结合起来提供新的能力给框架开发者。这一结合允许开发者定义新的具有行为附加到其上的Java语法。基本上,以安全的方式扩展Java语言的能力已尽在掌握。
资源
·Matrix-Java开发者社区: http://www.matrix.org.cn
·onjava.com: onjava.com
分享到:
相关推荐
- AOP(面向切面编程):Spring的`@Aspect`和`@Pointcut`注解定义切面和切点。 - 数据持久化:JPA中的`@Entity`、`@Table`、`@Column`等注解用于将Java对象映射到数据库表。 7. **最佳实践** - 适当使用注解:...
10. **类定义**:在面向对象编程中,可能定义了一个`Model`类来封装整个CNN模型的创建和训练过程。 在实际分析代码前,无法提供更多细节。不过,以上列出的是根据文件名和标签可能涉及的一些核心概念。理解并应用...
Spring框架通过引入Annotation,极大地简化了Java开发中的依赖注入(Dependency Injection, DI)和面向切面编程(AOP)的过程。Annotation是一种元数据,允许开发者在代码中嵌入额外的信息,Spring则能够读取这些信息来...
在Java世界中,Hibernate是一个流行的持久层框架,它极大地简化了数据库操作,尤其是在对象关系映射(ORM)方面。在这个项目中,我们将深入探讨Hibernate如何通过注解来简化数据库操作。 首先,让我们了解什么是...
本手册将深入探讨Java注解的各个方面,包括其基本概念、使用场景、自定义注解以及与工具的集成。 一、注解基础 1.1 注解类型 Java预定义了多种注解,如@Override、@Deprecated、@ SuppressWarnings等,它们分别用于...
这里主要讨论的是如何将这三者结合,并利用注解(Annotation)进行配置,以简化开发过程。 Struts2是MVC(模型-视图-控制器)框架,主要负责处理HTTP请求和控制应用的流程。它通过Action类处理业务逻辑,使用拦截器...
AspectJ是一种Java语言的 aspect-oriented programming(面向方面编程)框架,用于实现面向方面编程。AspectJ提供了许多 annotation 来描述方面,例如@Before、@After、@Around 等。 二、SpringBoot中AspectJ的使用...
它们在 Java 中的应用广泛,尤其是在简化开发和自动化配置方面,例如在 Spring、Hibernate 和 JUnit4 等框架中。 为何使用 Annotation? 1. 简化配置:Annotation 可以减少甚至消除传统的 XML 配置文件,使代码更...
### Spring AOP面向方面编程原理:AOP概念详解 #### 一、引言 随着软件系统的日益复杂,传统的面向对象编程(OOP)逐渐暴露出难以应对某些横切关注点(cross-cutting concerns)的问题。为了解决这一挑战,面向方面编程...
Spring Framework是一个全面的后端开发解决方案,涵盖了依赖注入、AOP(面向切面编程)、Web开发、事务管理等多个方面。"spring-framework-reference"文档中详细介绍了Spring的核心特性,包括Bean的声明和管理、...
Spring通过注解简化了依赖注入(DI)和面向切面编程(AOP)。例如,`@Autowired`用于自动装配bean,`@Service`、`@Repository`和`@Controller`定义服务层、数据访问层和视图层组件。此外,`@Transactional`实现事务...
面向语言注释的综合LOD词汇表 Web上语言注释的表示对于(至少)两种应用很重要: 发布和集成分布式的,可通过网络访问的数据集,例如,作为语言链接开放数据云的一部分或在语言数据的数字版本中 需要或产生语言注释...
这两个概念在Java开发中具有深远的影响,尤其是在进行元编程、动态类型处理以及自动生成代码等方面。 反射是Java提供的一种强大的机制,允许程序在运行时检查类、接口、字段和方法的信息,甚至可以动态地创建对象并...
总结起来,"Struts2+Hibernate3.5+Spring3.0(Annotation)"的组合为Java Web开发提供了一个强大的基础架构,尤其在权限管理方面。通过合理的配置和使用注解,可以实现高效、灵活且易于维护的权限控制系统。这不仅...
标题明确地表明了本文的主题,即使用AspectJ开发AOP(面向方面编程),并基于Annotation(注解)来实现。 描述: 主要介绍了Spring用AspectJ开发AOP(基于Annotation),文中通过示例代码介绍的非常详细,对大家的学习...
文档涵盖了Hibernate的基本配置、实体映射、会话管理、查询语言(HQL)和 Criteria API等多个方面。对于初学者,它能帮助快速理解和掌握ORM的核心思想,而对于有经验的开发者,中文版文档则有助于在遇到问题时进行...
在项目结构方面,按照Maven的标准目录结构,源代码、资源文件、测试代码等都有各自的存放位置,如`src/main/java`、`src/main/resources`和`src/test/java`。这样的布局有助于团队协作和代码组织。 总的来说,这个...
与编程式事务(即手动编写事务管理代码)不同,声明式事务是通过AOP(面向切面编程)实现的,它将事务管理逻辑与业务逻辑分离,使得事务管理更加灵活且易于维护。 在Spring中,我们可以使用XML配置文件来声明事务,...