再谈 AspectJ

回顾一下 AspectJ 提供给我们的工具,简单的看看一个基于 annotation 做的 aspect,讨论一下如何写一个可扩展的 aspect 以及一些高级特性。

其实 AspectJ 单独来看如果要干事情,需要别的东西的话是比较麻烦的,而如果是 Spring 之中使用由于 IoC container 的原因会使得一个 aspect 可以很容易获得其依赖的对象,从而变得容易。那么使用 AspectJ 时常仰仗的 pattern 自然就是 factory pattern:避免直接使用 constructor 将 implementation 与逻辑耦合在一起。

AspectJ 的 cheat sheet

这个部分参看这篇 blog。这里摘选部分:

  • 对方法的 pointcut:call 调用指定方法的方法(在这个方法外面),execution 运行时(在这个方法里面)和 withincode(这个方法里面每个 statement)
  • 类型匹配 within,在指定的类型里
  • 匹配 field 的 get/set

通过 annotation 今后可能能更精确的定位。

例子

我们这里做一件非常弱智的事情,调用某个方法前“暂停”一会。至于如何暂停这个事情我们可以使用不同的实现,我们不妨称之为 Policy,

public interface Policy {
  void take () ;
}

那么为了标识不同位置使用的“暂停”之间的关系,我们可以用一个字符串表示,字符串相同对应一个 Policy,因此实现这个 Aspect 并不难,只需要通过字符串获得 Policy,最后在 before 里面调用一次 take 方法就行了。那么问题是这个 Policy 问谁要呢?我们不妨假定有个

public interface PolicyFactory {
  Policy get (String name) ;
}

那么要是 spring 我们可以在 Aspect 中通过 injection 拿到需要的 PolicyFactory 实现,但是要在 AspectJ 里面,似乎就只能通过 static 变量来设定使用的 PolicyFactory 了,一般说来这种设置只在 load time 做一次,不存在什么 synchronization 的问题。

下面是一个简单的实现使用的 annotation,

import java.lang.annotation.* ;

@Inherited
@Target ({ElementType.METHOD, ElementType.TYPE})
@Retention (RetentionPolicy.RUNTIME)
public @interface Throttle {
   String value () ;
}

对应的 Aspect 如下

import org.aspectj.lang.annotation.Aspect ;
import org.aspectj.lang.annotation.Before ;
import org.aspectj.lang.JoinPoint ;

@Aspect
public class ThrottleAspect {
   private static PolicyFactory factory = ? ; // default factory?

   public static void setPolicyFactory (PolicyFactory f) {
       factory = f ;
   }

   @Before (value = "execution (!@Throttle * *(..)) && @within (throt)")
   public void classAnnos (JoinPoint jp, Throttle throt) {
       wait (jp, throt) ;
   }

   @Before (value="execution (@Throttle * *(..)) && @annotation (throt)")
   public void methodAnnos (JoinPoint jp, Throttle throt) {
       wait (jp, throt) ;
   }

   private static void wait (JoinPoint jp, Throttle throt) {
       Policy p = factory.get (throt.value ()) ;
       p.take () ;
   }
}

inter-type declaration

所谓的 ITD 是一种类似 mixin 的行为,通过 ITD,aspectj 可以为指定类型(或者某个 package 里面的符合某种条件的类)添加额外的成员甚至方法。一个例子参看这里。简而言之包括

  • 定义一个 interface,并提供一个实现
  • 通过 @DeclareParents 匹配对应的类,并将以上实现类作为 defaultImpl。这个 annotation 标注的是这个 interface 实现的成员(相当于嵌入到每个对象里面的 prototype?)
  • 匹配到需要调用这部分的情形(如通过 execution),通过 this 获得对应的 interface,这时就可以调用这部分内容为其他的 advice 所用了

一种想法就是通过这个将 logger 之类的 embed 到对应类里面去,并对某些行为进行记录。

更高级的用法

一种想法就是如何能让用户自定义 annotation 而用在 AspectJ 的 framework 里面,这相当于要求“被我 annotate 过的 annotation 标注”的情形,这种匹配写出来还是很有意思的。

——————
And Reuben went in the days of wheat harvest, and found mandrakes in the field, and brought them unto his mother Leah. Then Rachel said to Leah, Give me, I pray thee, of thy son’s mandrakes.

Advertisements
再谈 AspectJ

发表评论

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / 更改 )

Twitter picture

You are commenting using your Twitter account. Log Out / 更改 )

Facebook photo

You are commenting using your Facebook account. Log Out / 更改 )

Google+ photo

You are commenting using your Google+ account. Log Out / 更改 )

Connecting to %s