Two ways to implement AOP in Java

Posted by adige72 on Fri, 26 Nov 2021 21:27:20 +0100

Everyone who knows Spring knows the aspect oriented programming (AOP) of Spring. We won't talk about the aspect of Spring here. Later, we will have an opportunity to dissect the aspect programming of Spring. We want to explain how to implement AOP in ordinary Java code. There are two ways to implement AOP. One is the implementation of the native SDK, and the other is based on the third-party package cglib.

1. Native jdk implementation

Let's first introduce the JDK native, which is based on interface programming:
Define an interface first:

public interface ISayHelloWorld {
    public String say();
}

Implement this interface:

public class ManSayHelloWorld implements ISayHelloWorld{
    @Override
    public String say() {
        System.out.println("Hello world!");
        return "MAN";
    }
}

To implement the facet proxy of ManSayHelloWorld, the native AOP needs to implement the InvocationHandler interface to implement AOP.

public class AOPHandle implements InvocationHandler{
    private Object obj;
    AOPHandle(Object obj){
        this.obj = obj;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //Method return value
        System.out.println("Pre agent");
        //Reflection call method
        Object ret=method.invoke(obj, args);
        //End of statement
        System.out.println("Post agent");
        //Returns the return value of the reflection calling method
        return ret;
    }
}

Test this Code:

public class Main {
    public static void main(String[] args) {
        ManSayHelloWorld sayHelloWorld = new ManSayHelloWorld();
        AOPHandle handle = new AOPHandle(sayHelloWorld);
        ISayHelloWorld i = (ISayHelloWorld) Proxy.newProxyInstance(ManSayHelloWorld.class.getClassLoader(), new Class[] { ISayHelloWorld.class }, handle);
        i.say();
    }
}

results of enforcement

Pre agent
Hello world! 
Post agent

2.cglib section

Next, we implement the aspect of cglib. The implementation functions are the same as above. Cglib does not need to define an interface. Ordinary classes can:

public class SayHello {
    public void say(){
        System.out.println("hello world!");
    }
}

Implementation section:

public class CglibProxy implements MethodInterceptor {
    private Enhancer enhancer = new Enhancer();
    public Object getProxy(Class clazz){
        enhancer.setSuperclass(clazz);
        enhancer.setCallback(this);
        return enhancer.create();
    }
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("Pre agent");
        //Call the method in the parent class through the proxy class
        Object result = methodProxy.invokeSuper(o, objects);

        System.out.println("Post agent");
        return result;
    }
}

Test code:

public class Main {
    public static void main(String[] args) {
        CglibProxy proxy = new CglibProxy();
        //Create a proxy class by generating subclasses
        SayHello proxyImp = (SayHello)proxy.getProxy(SayHello.class);
        proxyImp.say();
    }
}

results of enforcement

Pre agent
hello world!
Post agent

So what are the similarities and differences between the two methods?
The same thing is that they all use the proxy mode.
The difference is that the former uses interface proxy and the latter uses inheritance proxy. What does it mean?
Interface agent:
The proxy will generate a class that inherits the interface and the class that the proxy wants to cut into. For example, AOPHandle will generate a class named AspectSayHelloWorld implements ISayHelloWorld. When implementing the say method, it actually calls the say method of ManSayHelloWorld, but does something before and after the call.
Take a look at the source code of the newProxyInstance method

Objects.requireNonNull(h);

        final Class<?>[] intfs = interfaces.clone();
        final SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
        }

        /*
         * Look up or generate the designated proxy class.
         */
        Class<?> cl = getProxyClass0(loader, intfs);

        /*
         * Invoke its constructor with the designated invocation handler.
         */
        try {
            if (sm != null) {
                checkNewProxyPermission(Reflection.getCallerClass(), cl);
            }

            final Constructor<?> cons = cl.getConstructor(constructorParams);
            final InvocationHandler ih = h;
            if (!Modifier.isPublic(cl.getModifiers())) {
                AccessController.doPrivileged(new PrivilegedAction<Void>() {
                    public Void run() {
                        cons.setAccessible(true);
                        return null;
                    }
                });
            }
            return cons.newInstance(new Object[]{h});
        } catch (IllegalAccessException|InstantiationException e) {
            throw new InternalError(e.toString(), e);
        } catch (InvocationTargetException e) {
            Throwable t = e.getCause();
            if (t instanceof RuntimeException) {
                throw (RuntimeException) t;
            } else {
                throw new InternalError(t.toString(), t);
            }
        } catch (NoSuchMethodException e) {
            throw new InternalError(e.toString(), e);
        }

Succession agent:
Inheriting proxy is to generate a proxy class, which inherits from the class we need to cut into, such as SayHello proxyImp = (SayHello)proxy.getProxy(SayHello.class) above; This code returns a subclass of SayHello, proxies the say() method of SayHello, and does something before and after this method.

Topics: Java Linux Nginx server