實現SpringAOP

一、動態代理模式簡單理解:

動態代理生成一個對象的代理,這個代理對象擁有和原對象的所有屬性和行爲,而且還多出另外功能。舉個例子:現在有一個對象A1,A1有個方法test(),用動態代理生成對等代理對象A2,A2可以像A1調用test()方法,但是A2會在調用過程插入一些額外的操作。

二、動態代理(JDK)

  • 代理對象接口和實現類,下面代碼中的TestService和TestServiceImpl
  • 代理類,下面代碼中的ProxyUtils,需要實現InvocationHandle,實現invoke()方法
  • 客戶端使用ProxyUtils生成TestService代理對象,調用方法,代碼中的test()方法

三、代碼實現(推薦在本地跑下,加深理解)

代碼實現過程中加入了對SpringAOP的解析,加入了Interceptor攔截器,在代理對象生成類中invoke()方法中實現SpringAOP中的@Before、@After、@AfterReturning、@AfterThrowing註解

  1. 代理對象接口和實現類TestService和TestServiceImpl
public interface TestService {
    public void test();
}


public class TestServiceImpl implements TestService {
    @Override
    public void test() {
        System.out.println("測試開始#############");
    }
}
  1. 代理類ProxyUtils,需要實現InvocationHandle,實現invoke()方法
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * 生成代理對象工具類
 * @param <T>
 */
public class ProxyUtils<T> implements InvocationHandler {

    private TestService testService;

    private Interceptor interceptor;

    /**
     * 生成代理對象
     *
     * @param testService
     * @param interceptor
     * @return
     */
    public Object getProxy (TestService testService, Interceptor interceptor) {

        this.testService = testService;
        this.interceptor = interceptor;

        ClassLoader testServiceClassLoader = testService.getClass().getClassLoader();

        Class[] testServiceInterfaces = testService.getClass().getInterfaces();

        return Proxy.newProxyInstance(testServiceClassLoader, testServiceInterfaces, this);
    }


    /**
     * 執行真實方法步驟:
     * 1、先執行before方法
     * 2、通過method.invoker執行test()方法
     * 3、如果執行test()成功,執行afterReturning方法;否則執行afterThrowing方法
     * 4、最終執行after方法
     *
     * @param proxy
     * @param method
     * @param args
     * @return
     * @throws Throwable
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //標記是否出現異常
        boolean error = false;

        interceptor.before();      //實現SpringAOP中的Before()方法

        try{
            method.invoke(args);
        }
        catch (Exception e) {
            error = true;
            e.printStackTrace();
        }
        finally {
            interceptor.after(); 			//實現SpringAOP中的After()方法

        }

        if (error){
            interceptor.afterThrowing();			//實現SpringAOP中的AfterThrowing()方法

        }
        else {
            interceptor.afterReturning();				//實現SpringAOP中的AfterReturning()方法

        }

        return null;
    }
}
  1. 客戶端使用ProxyUtils生成TestService代理對象,調用方法,代碼中的test()方法
/**
 * 動態代理測試客戶端
 */
public class Client {
    public static void main(String[] args) {

        TestService testService = new TestServiceImpl();

        Interceptor interceptor = new TestServiceInterceptor();

        ProxyUtils proxyUtils = new ProxyUtils();

        TestService proxy = (TestService) proxyUtils.getProxy(testService, interceptor);

        System.out.println("proxy = " + proxy.getClass().getName());
        proxy.test();
    }
}

四、源碼解析

代理對象生成類中兩大關鍵點:

Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h);

生成代理對象的過程如下:

public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h);
 {
        Objects.requireNonNull(h);

        final Class<?> caller = System.getSecurityManager() == null
                                    ? null
                                    : Reflection.getCallerClass();

        /*
         * Look up or generate the designated proxy class and its constructor.
         */
        Constructor<?> cons = getProxyConstructor(caller, loader, interfaces);

        return newProxyInstance(caller, cons, h);
    }

說明:上述代碼其實是使用反射機制ClassLoader獲取對象的Constructor,然後使用Constructor生成類的對象,對等下面的代碼:

public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h);
 {
        Constructor constructor = testServiceClassLoader.getClass().getConstructor(param);
        return constructor.newInstance();
    }

返回的代理對象proxy實現了newProxyInstance方法中的參數interfaces接口,並且proxy封裝了其他行爲,感興趣的同學可以看看$Proxy0類的實現,當我們執行proxy.test()方法時,首先會經過 $Proxy0調用InvocationHandle中的invoke()方法。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章