Tuesday, December 04, 2007

Spring 中取得 advised-this 的做法

原來 Advised 拿 target 這一招不是經常成功的 orz... 待我有時間再去改改做法 :(



假設我有一個 object (假設叫是 class Foo), 外面墊了幾層 aspect, 如果我在 Foo 裡面 invoke 任何自己的 method, 那麼該 invocation 是沒有被攔截的.

那麼怎樣才能令自己 invoke 自己的 method 也能被 aspect 攔截? 那麼可以嘗試 inject advised 的 instance 給 object 自己. 在 Spring + JDK5 的環境下, 寫了一個簡單的bean post processor + annotation 去達到這目的.

BeanPostProcessor
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import org.springframework.aop.framework.Advised;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;

public class InjectBeanSelfProcessor implements BeanPostProcessor {
___ public Object postProcessAfterInitialization(Object bean, String beanName)
___________ throws BeansException {
_______ // 找出 Proxy 背後真正的 target
_______ Object target = bean;
_______ while (target instanceof Advised) {
___________ try {
_______________ target = ((Advised)bean).getTargetSource().getTarget();
___________ } catch (Exception e) {
_______________ target = null;
_______________ break;
___________ }
_______ }
_______ // 如果順利找到 target
_______ if (target != null) {
___________ Method[] methods = target.getClass().getMethods();
___________ for (Method m : methods) {
_______________ if (m.getAnnotation(BeanSelf.class) != null
_______________________ && m.getParameterTypes().length == 1
_______________________ && m.getParameterTypes()[0].isAssignableFrom(bean.getClass())) {
___________________ try {
_______________________ m.invoke(target, bean);
___________________ } catch (IllegalArgumentException e) {
___________________ } catch (IllegalAccessException e) {
___________________ } catch (InvocationTargetException e) {
___________________ }
_______________ }
___________ }
_______ }
_______ return bean;
___ }

___ public Object postProcessBeforeInitialization(Object bean, String beanName)
___________ throws BeansException {
_______ return bean;
___ }
}


BeanSelf 的 annotation 從略, 反正是一個 Retention at Runtime, annotate at Method 的 annotation.

自己的 class 要 invoke 自己method 時被 aspect 攔截, 便這樣做:

public class FooImpl implements Foo {
___ private Foo self;

___ // 加上 BeanSelf, 讓 Spring inject Advised-self 進來
___ @BeanSelf
___ public void setSelf(Foo self) {
_______ this.self = self;
___ }
___ protected Foo getSelf() {
_______ return this.self;
___ }

___ public void foo() {
_______ // 不要直接 invoke bar() 或 this.bar()
_______ getSelf().bar();
___ }
___ public void bar() {
_______ //....
___ }
}


當然不要忘了在 Spring 的 app context config 加上 InjectBeanSelfProcessor.

Note: 謹記原來可以利用 Advised 來取得真正 target.

Reference:
http://fyting.javaeye.com/blog/109236

No comments: