Implementing a wormhole pattern using AspectJ

空扰寡人 提交于 2019-11-28 12:32:30

Indeed there is an example in AspectJ in Action. If you look at the table of contents you notice that chapter 12.2 is what you are looking for. It would be a good idea to buy the book. I can recommend it warmly. Because I am not sure if it is okay to just copy & paste parts of the book, I will just quote the template here:

public aspect WormholeAspect {
    pointcut callerSpace(<caller context>) :
        <caller pointcut>;

    pointcut calleeSpace(<callee context>) :
        <callee pointcut>;

    pointcut wormhole(<caller context>, <callee context>) :
        cflow(callerSpace(<caller context>)) && 
        calleeSpace(<callee context>);

    // advice to wormhole
    before(<caller context>, <callee context>) :
        wormhole(<caller context>, <callee context>)
    {
            ... advice body
    }
}

There is an old article by Laddad on TheServerSide.com with a more concrete example. It is not the same one from the book, but similar.

As you can see, it is easy to do in AspectJ because there you have the cflow() pointcut. I have never used Guice, but its AOP introduction page mentions that their implementation is part of the AOP Alliance specification. Looking at the AOP Alliance API, there is nothing which looks like a cflow() pointcut, it is all around constructor & method invocation plus field access.

So what can you do in Spring (without AspectJ) or Guice if you want to avoid passing through the parameter through all layers? The obvious solution is a ThreadLocal variable declared and managed (i.e. assigned, but also cleared) by the caller and accessed by the callee. This is not nice, only a workaround so as not to bloat the API. But it requires that both caller and callee have a common understanding of what they want to share and how. In a way that kind of implementation is more of an anti-pattern than a pattern. If you can, use AspectJ so as to solve this in a clean and modular way, encapsulating the concern to be addressed in one module (aspect).

A simple example. Imagine you have context and target objects that provide functionality, which somehow depends on the state of the context:

class T {   
    public void foo() {
        System.out.println("T.foo()");
    }
}

class Context {
    public boolean isValid = true;
    public void doStuff() {
        T t = new T();
        t.foo();
    }
}

public class Main { 
    public static void main(String[] args) {
        Context c = new Context();
        c.doStuff();
    }
}

an aspect that would ensure that instance of Context can invoke foo() on instance of T only if the member isValid is set to true could look the following way:

public aspect ContextStateValidation {

    pointcut MyContext(Context c) :
        execution(* Context.*()) && this(c);

    pointcut FooCalls(T t) :
        call(* T.foo()) && target(t);

    pointcut FooCallsInMyContext(Context c, T t) :
        cflow(MyContext(c)) && FooCalls(t);

    void around(Context c, T t) : FooCallsInMyContext(c, t) {
        if (c.isValid)
            proceed(c, t);
    }
}

Don't use the wormhole pattern... In fact, use AOP only if you are really sure you need it, otherwise leave it.

THe disadvantage of wormhole pattern is that you skip a lot of layers ... is that what you really want ? :)

Grtz,

Kristof

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!