JoyAop Tutorial

 

 

Shen Li

 

http://joyaop.sourceforge.net

 

Introduction. 2

Interceptor 2

Generic Interceptor 2

AOP Alliance Interceptor 3

Abstract Generic Interceptor 4

Decorator 5

Mixin. 6

Interface. 7

Pointcut 7

Regular expression. 7

Interface. 8

Annotation. 8

Configuration. 8

Interceptor 8

Mixin. 8

Interface. 9

Expression. 9

Pointcut 9

Interceptor-Precedence. 10

Full configuration. 11

Run. 12

Summary. 13

 

Introduction

JoyAop is a dynamic AOP framework based on the CGLIB proxies.

JoyAop supports 3 types of Aspect currently: 

1)    Interceptor. The interceptors could intercept any non-final methods which could be overridden by CGLIB (Of course non-static, public…).

2)    Interface. Actually, it’s a kind of Introduction here, represents the introductions which only have interface.

3)    Mixin. Another kind of Introduction includes both interface and implementation.

JoyAop support 3 types of Pointcut currently:

1)    Regular expression: Picks out the classes or methods by matching their names.

2)    Interface: Picks out the classes by matching their interfaces.

3)    Annotation: Picks out the methods by matching their annotations.

Note This tutorial mainly focuses on the usage of JoyAop, and rarely explain the concepts.

 

Interceptor

JoyAop supports 4 types of Interceptor currently:

Generic Interceptor

Just like the Interceptor of the other dynamic AOP frameworks, must implement a callback interface, and it is the most efficient type.

package net.sf.joyaop.demo.simple;

 

import net.sf.joyaop.Interceptor;

import net.sf.joyaop.Invocation;

 

/**

 * @author Shen Li

 */

public class FooInterceptor implements Interceptor {

    public Object intercept(Invocation invocation) throws Throwable {

        System.out.println();

        System.out.println("invoke FooInterceptor...");

        System.out.println("method name: " + invocation.getMethod().getName());

        System.out.println("argument array length: " + invocation.getArguments().length);

        System.out.println("paramter value: " + invocation.getParameter("paramName"));

        System.out.println("proxy class name: " + invocation.getProxy().getClass().getName());

        return invocation.proceed();

}

}

 

Note the invocation.getParameter(..) method, with it the interceptors could obtain the parameters defined in the current Jointpoint(see below).

AOP Alliance Interceptor

  Using the AOP Alliance API. It’s almost identical to the above type, except for the getParameter() method.

 

package net.sf.joyaop.demo.simple;

 

import org.aopalliance.intercept.MethodInterceptor;

import org.aopalliance.intercept.MethodInvocation;

 

/**

 * @author Shen Li

 */

public class AopAllianceFooInterceptor implements MethodInterceptor {

    public Object invoke(MethodInvocation invocation) throws Throwable {

        System.out.println();

        System.out.println("invoke AopAllianceFooInterceptor...");

        System.out.println("method name: " + invocation.getMethod().getName());

        System.out.println("argument array length: " + invocation.getArguments().length);

        System.out.println("proxy class name: " + invocation.getThis().getClass().getName());

        return invocation.proceed();

    }

}

 Abstract Generic Interceptor

Like the first one, but doesn’t directly use the Invocation object.

 

package net.sf.joyaop.demo.simple;

 

import net.sf.joyaop.AbstractInterceptor;

 

/**

 * @author Shen Li

 */

public abstract class AbstractFooInterceptor implements AbstractInterceptor {

    public Object execute() throws Throwable {

        System.out.println();

        System.out.println("invoke AbstractFooInterceptor...");

        System.out.println("method name: " + getMethod().getName());

        System.out.println("argument array length: " + getArguments().length);

        System.out.println("parameter value: " + getParameter("paramName"));

        System.out.println("proxy class name: " + getProxy().getClass().getName());

        return proceed();

    }

}

 

The AbstractInterceptor interface extends Invocation interface, so it has included all the useful methods, such as proceed (), getMethod(), etc. Just declare our interceptors as abstract class, then directly invoke those methods, the framework will do the rest job. This type is slightly slower than the above ones.

Decorator

It mainly comes from the concept of abstract schema. Like the Decorator pattern of OO, the interceptors (decorators) and their target objects are required to implement the same interfaces.

 

Target interface::

 

package net.sf.joyaop.demo.simple;

 

/**

 * @author Shen Li

 */

public interface Foo {

    void foo();

}

 

Target implementation:

 

package net.sf.joyaop.demo.simple;

 

/**

 * @author Shen Li

 */

public abstract class FooImpl implements Foo {

    public void foo() {

        System.out.println();

        System.out.println("invoking Foo.foo()...");

        System.out.println("parameter value: " + getParamName());

    }

 

    public abstract String getParamName();

}

 

Decorator:

 

public abstract class FooDecorator implements Foo {

    public void foo() {

        System.out.println();

        System.out.println("invoke FooDecorator...");

        System.out.println("method name: foo");

        System.out.println("argument array length: 0");

        System.out.println("paramter value: " + getParamName());

        System.out.println("proxy class name: " + getFoo().getClass().getName());

        getFoo().foo();

    }

 

    protected abstract Foo getFoo();

 

    protected abstract String getParamName();

}

 

The target object has an interface and the decorator also implements that interface. In the above example we want the decorator to intercept the foo() method of the target object, we just implement that method, and add the extra code. Then if it needs to proceed the invocation, we should declare an abstract getter method getFoo() (must start with “get” and the return type should be the target interface) , which will be implemented by the framework at runtime and returns the target object. When using getFoo().foo() in the foo() method of the decorator, the framework will  proceed the current method invocation. Besides, we can also access the other methods of the target object by using getFoo().someOtherMethod(). (Not for proceeding invocation now) But here we have another more simple way, just directly use someOtherMethod() in the foo() method of the decorator, and the framework will automatically route the invocation to the target object for us. Note if the someOtherMethod() method has been declared in the decorator itself (Maybe we want to intercept it too), which means that method has been overridden, we can’t use the latter simple way now. (Maybe the rule is like the Java inheritance rule: if the subclass has overridden the super class’s methods, we should use super.someMethod() instead of someMethod().)

Then how does the decorator obtain the Joinpoint’s parameters? Because the decorator doesn’t implements any framework specific interface as the preceding generic ones, we should declare another abstract getter method, which follows the JavaBean standard: “get” + “name of the parameter”. Then just like the getter method of target object, we simply use getParamName() to obtain the parameter named paramName.

At last, Interceptor of this type has the worst performance.

Mixin

Here we focus on using the concept of abstract schema to implement Mixin. The target object could also implement the interfaces of the mixins which will be weaved at runtime.

 

Target object

 

package net.sf.joyaop.demo.simple;

 

/**

 * @author Shen Li

 */

public abstract class Target implements Foo {

    public void target() {

        foo();

        System.out.println();

        System.out.println("Target.target() invoked.");

    }

}

 

The mixin’s code is in the above Decorator section.

 

The target() method of the Target class could directly invoke the foo() method of the mixin, just like it’s own method. And at the client side, we could directly use targetInstance.foo() too.

Interface

Not used yet.

Pointcut

JoyAop supports pointcut composition, by using the operators “AND”, “OR”, “!” and parentheses.

Regular expression

JoyAop use the regex library of JDK. To pick out classes use target(regular expression of the full class name), to pick methods use execution(regular expression of method name). It’s the same as the AspectJ. (But the current definition model is somewhat limited.)

 

target(net.sf.joyaop.demo.simple.Target) AND execution(foo)

 

Interface

To pick a class use interface(full class name of the interface)

 

interface(net.sf.joyaop.demo.simple.Foo)

 

Annotation

To pick out a method use annotation(full class name of the annotation)

 

annotation(net.sf.joyaop.demo.rbac.annotation.Permission)

 

Configuration

All the aspects and pointcuts should be declared in a configuration file, currently an XML file.

Interceptor

Element: interceptor

Attributes:

Name

Description

required

Class

The class name

true

Scope

The deployment scope (jvm, class, instance, thread), default is JVM

false

 

<interceptor class="net.sf.joyaop.demo.simple.FooInterceptor"/>

 

Mixin

Element: mixin

Attributes:

Name

Description

required

Class

The implementation class name

true

Scope

The deployment scope (jvm, class, instance, thread), default is instance

False

Interface

The interface class name

true

 

<mixin interface="net.sf.joyaop.demo.simple.Foo" 

class="net.sf.joyaop.demo.simple.FooImpl"/>

 

Interface

Element: interface

Attributes:

Name

Description

required

class

The interface class name

true

 

<interface class="java.io.Serializable"/>

 

Expression

The expression of Pointcut. If the same expression is used by many pointcuts, you can declare it in this element, and then refer to it by the name.

Element: expr

Attributes:

name

Description

required

Name

The name of the expression, as the reference of the element

true

 

<expr name="fooIntf">

    interface(net.sf.joyaop.demo.simple.Foo)

</expr>

Pointcut

Bind Interceptor, Mixin, and Interface to the Pointcut.

Element: pointcut

Attributes:

Name

Description

required

expr

Expression of the pointcut, you can refer to the other named expressions here.

True

Sub elements:

l        Interceptor: It’s the same as the preceding interceptor element. If there is already an interceptor element has the same class attribute be defined outside the pintcut element, the aspect runtime will bind that interceptor definition to the Pointcut, and this element is just as a reference, only the class attribute required. Otherwise, you must provide the full config info here, it will be automatically bound to the Pointcut, and only visible in current Pointcut..

l        Mixin: The same

l        Interface: The same

l        Parameter: Parameter of Joinpoint. It may be useful for declaring transaction attribute, permission, etc.

Attributes:

Name

Description

required

Name

The name of parameter.

true

Value

The value of parameter

true

 

<pointcut expr="fooIntf">

    <interceptor class="net.sf.joyaop.demo.simple.FooDecorator"/>

   <param name="paramName">decoratorValue</param>

</pointcut>

 

<pointcut expr="fooIntf">

    <mixin interface="net.sf.joyaop.demo.simple.Foo"

          class="net.sf.joyaop.demo.simple.FooImpl"/>

    <param name="paramName">mixinValue</param>

</pointcut>

 

Interceptor-Precedence

The precedence of the interceptors, the top is higher.

Element: interceptor-precedence

 

 <interceptor-precedence>

      <interceptor class="net.sf.joyaop.demo.simple.FooInterceptor"/>

      <interceptor class="net.sf.joyaop.demo.simple.AopAllianceFooInterceptor"/>

      <interceptor class="net.sf.joyaop.demo.simple.AbstractFooInterceptor"/>

      <interceptor class="net.sf.joyaop.demo.simple.FooDecorator"/>

 </interceptor-precedence>

Full configuration

<runtime>

    <expr name="fooIntf">

        interface(net.sf.joyaop.demo.simple.Foo)

    </expr>

 

    <interceptor-precedence>

        <interceptor class="net.sf.joyaop.demo.simple.FooInterceptor"/>

        <interceptor class="net.sf.joyaop.demo.simple.AopAllianceFooInterceptor"/>

        <interceptor class="net.sf.joyaop.demo.simple.AbstractFooInterceptor"/>

        <interceptor class="net.sf.joyaop.demo.simple.FooDecorator"/>

    </interceptor-precedence>

 

    <pointcut expr="target(net.sf.joyaop.demo.simple.Target) AND execution(foo)">

        <interceptor class="net.sf.joyaop.demo.simple.FooInterceptor"/>

        <interceptor class="net.sf.joyaop.demo.simple.AopAllianceFooInterceptor"/>

        <interceptor class="net.sf.joyaop.demo.simple.AbstractFooInterceptor"/>

        <param name="paramName">interceptorValue</param>

    </pointcut>

 

    <pointcut expr="fooIntf">

        <interceptor class="net.sf.joyaop.demo.simple.FooDecorator"/>

        <param name="paramName">decoratorValue</param>

    </pointcut>

 

    <pointcut expr="fooIntf">

        <mixin interface="net.sf.joyaop.demo.simple.Foo"

            class="net.sf.joyaop.demo.simple.FooImpl"/>

        <param name="paramName">mixinValue</param>

    </pointcut>

</runtime>

Put the contents into a file named aop.xml.

 

Run

When we have written the aspects and configuration file, how to use them with our applications? Since JoyAop is based on the proxy solution, the target objects that should be weaved must be instantiated by the ObjectFactory. To create the ObjectFactory follow the code below:

 

package net.sf.joyaop.demo.simple;

 

import net.sf.joyaop.ObjectFactory;

import net.sf.joyaop.config.XmlConfiguration;

import net.sf.joyaop.impl.AspectRuntimeImpl;

 

/**

 * @author Shen Li

 */

public class Main {

    public static void main(String[] args) {

        ObjectFactory objectFactory = new AspectRuntimeImpl(new XmlConfiguration("net/sf/joyaop/demo/simple/aop.xml"));

        Target target =  (Target) objectFactory.newInstance(Target.class);

        System.out.println("invoking Target.target()...");

        target.target();

    }

}

 

The above code is located in the demos directory of the distribution. To test it, type “ant run:demo:simple” in the console, the output should be like this:

run:demo:simple:

     [java] invoking Target.target()...

 

     [java] invoke FooInterceptor...

     [java] method name: foo

     [java] argument array length: 0

     [java] paramter value: interceptorValue

     [java] proxy class name: net.sf.joyaop.demo.simple.Target$$EnhancerByCGLIB$$e85b3db7

 

     [java] invoke AopAllianceFooInterceptor...

     [java] method name: foo

     [java] argument array length: 0

     [java] proxy class name: net.sf.joyaop.demo.simple.Target$$EnhancerByCGLIB$$e85b3db7

 

     [java] invoke AbstractFooInterceptor...

     [java] method name: foo

     [java] argument array length: 0

     [java] parameter value: interceptorValue

     [java] proxy class name: net.sf.joyaop.demo.simple.Target$$EnhancerByCGLIB$$e85b3db7

 

     [java] invoke FooDecorator...

     [java] method name: foo

     [java] argument array length: 0

     [java] paramter value: decoratorValue

     [java] proxy class name: net.sf.joyaop.demo.simple.Target$$EnhancerByCGLIB$$e85b3db7

 

     [java] invoking Foo.foo()...

     [java] parameter value: mixinValue

 

     [java] Target.target() invoked.

 

Summary

This tutorial has simply introduced the basic usage of JoyAop, for more information about (dynamic) AOP, refer to the links below:

http://jroller.com/page/rickard

http://www.springframwork.org

https://dynaop.dev.java.net/release/1.0-beta/manual/

http://aspectwerkz.codehaus.org/

http://www.jboss.org/products/aop

http://aspectj.org