【Spring】Spring AOP實現原理


Spring AOP實現原理

在之前的一文中介紹過Spring AOP的功能使用,但是沒有深究AOP的實現原理,今天正好看到幾篇好文,於是就自己整理了一下AOP實現的幾種方式,同時把代理模式相關知識也稍微整理一下。

代理模式

代理模式的UML類圖如下:

可以看到還是很簡單的,代理類實現了被代理類的接口,同時與被代理類是組合關系。下面看一下代理模式的實現。

靜態代理

接口類:

interface Person {
    void speak();
}

真實實體類:

class Actor implements Person {
    private String content;
    public Actor(String content) {
        this.content = content;
    }

    @Override
    public void speak() {
        System.out.println(this.content);
    }
}

代理類:

class Agent implements Person {
    private Actor actor;
    private String before;
    private String after;
    public Agent(Actor actor, String before, String after) {
        this.actor = actor;
        this.before = before;
        this.after = after;
    }
    @Override
    public void speak() {
        //before speak
        System.out.println("Before actor speak, Agent say: " + before);
        //real speak
        this.actor.speak();
        //after speak
        System.out.println("After actor speak, Agent say: " + after);
    }
}

測試方法:

public class StaticProxy {
    public static void main(String[] args) {
        Actor actor = new Actor("I am a famous actor!");
        Agent agent = new Agent(actor, "Hello I am an agent.", "That's all!");
        agent.speak();
    }
}

結果:

動態代理

在講JDK的動態代理方法之前,不妨先想想如果讓你來實現一個可以任意類的任意方法的代理類,該怎么實現?有個很naive的做法,通過反射獲得Class和Method,再調用該方法,並且實現一些代理的方法。我嘗試了一下,很快就發現問題所在了。於是乎,還是使用JDK的動態代理接口吧。

JDK自帶方法

首先介紹一下最核心的一個接口和一個方法:

首先是java.lang.reflect包里的InvocationHandler接口:

public interface InvocationHandler {
    public Object invoke(Object proxy, Method method, Object[] args)
        throws Throwable;
}

我們對於被代理的類的操作都會由該接口中的invoke方法實現,其中的參數的含義分別是:

  • proxy:被代理的類的實例
  • method:調用被代理的類的方法
  • args:該方法需要的參數

使用方法首先是需要實現該接口,並且我們可以在invoke方法中調用被代理類的方法並獲得返回值,自然也可以在調用該方法的前后去做一些額外的事情,從而實現動態代理,下面的例子會詳細寫到。

另外一個很重要的靜態方法是java.lang.reflect包中的Proxy類的newProxyInstance方法:

public static Object newProxyInstance(ClassLoader loader,
                                      Class<?>[] interfaces,
                                      InvocationHandler h)
    throws IllegalArgumentException

其中的參數含義如下:

  • loader:被代理的類的類加載器
  • interfaces:被代理類的接口數組
  • invocationHandler:就是剛剛介紹的調用處理器類的對象實例

該方法會返回一個被修改過的類的實例,從而可以自由的調用該實例的方法。下面是一個實際例子。

Fruit接口:

public interface Fruit {
    public void show();
}

Apple實現Fruit接口:

public class Apple implements Fruit{
    @Override
    public void show() {
        System.out.println("<<<<show method is invoked");
    }
}

代理類Agent.java:

public class DynamicAgent {

	//實現InvocationHandler接口,並且可以初始化被代理類的對象
    static class MyHandler implements InvocationHandler {
        private Object proxy;
        public MyHandler(Object proxy) {
            this.proxy = proxy;
        }
			
		//自定義invoke方法
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            System.out.println(">>>>before invoking");
			//真正調用方法的地方
            Object ret = method.invoke(this.proxy, args);
            System.out.println(">>>>after invoking");
            return ret;
        }
    }

	//返回一個被修改過的對象
    public static Object agent(Class interfaceClazz, Object proxy) {
        return Proxy.newProxyInstance(interfaceClazz.getClassLoader(), new Class[]{interfaceClazz},
                new MyHandler(proxy));
    }    
}

測試類:

public class ReflectTest {
    public static void main(String[] args) throws InvocationTargetException, IllegalAccessException {
		//注意一定要返回接口,不能返回實現類否則會報錯
        Fruit fruit = (Fruit) DynamicAgent.agent(Fruit.class, new Apple());
        fruit.show();
    }
}

結果:

可以看到對於不同的實現類來說,可以用同一個動態代理類來進行代理,實現了“一次編寫到處代理”的效果。但是這種方法有個缺點,就是被代理的類一定要是實現了某個接口的,這很大程度限制了本方法的使用場景。下面還有另外一個使用了CGlib增強庫的方法。

CGLIB庫的方法

CGlib是一個字節碼增強庫,為AOP等提供了底層支持。下面看看它是怎么實現動態代理的。

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;

public class CGlibAgent implements MethodInterceptor {

    private Object proxy;

    public Object getInstance(Object proxy) {
        this.proxy = proxy;
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(this.proxy.getClass());
        // 回調方法
        enhancer.setCallback(this);
        // 創建代理對象
        return enhancer.create();
    }
	//回調方法
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println(">>>>before invoking");
		//真正調用
        Object ret = methodProxy.invokeSuper(o, objects);
        System.out.println(">>>>after invoking");
        return ret;
    }

    public static void main(String[] args) {
        CGlibAgent cGlibAgent = new CGlibAgent();
        Apple apple = (Apple) cGlibAgent.getInstance(new Apple());
        apple.show();
    }
}

結果:

可以看到結果和JDK動態代理是一樣的,但是可以直接對實現類進行操作而非接口,這樣會有很大的便利。

參考文獻


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM