Java 中的代理:靜態代理、JDK 動態代理和 Cglib 動態代理

準備工作

代理簡介

代理,簡單來說,就是代替原有操作者,即委託者去處理一件事。在 Java 中代理一般分爲兩種,靜態代理和動態代理,動態代理又分爲 JDK 動態代理和 Cglib 動態代理。

創建項目

創建一個簡單的純後端的 Maven 項目,在其中引入單元測試和 Cglib 相關依賴。

<dependencies>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>cglib</groupId>
        <artifactId>cglib</artifactId>
        <version>3.3.0</version>
    </dependency>
</dependencies>

注:本案例基於 JDK 1.8

靜態代理

實現靜態代理需要由顯式地創建代理類。與動態代理不同的是,靜態代理的 class 文件在編譯之後,程序運行之前就已經存在了。

接口

首先定義一個公共的 Subject 接口,該接口後續需要被委託類 RealSubject 和代理類 ProxySubject 共同實現。

public interface Subject {

    void method();
}

實現類

定義一個實現類 RealSubject,實現 method 方法。

public class RealSubject implements Subject {

    @Override
    public void method() {
        System.out.println("real method implement");
    }
}

代理類

定義一個代理類 ProxySubject,實現 Subject 接口,並使用構造器方法注入 Subject 接口。

public class ProxySubject implements Subject {

    private Subject subject;

    public ProxySubject(Subject subject) {
        this.subject = subject;
    }

    @Override
    public void method() {
        before();
        subject.method();
        after();
    }

    private void before() {
        System.out.println("static proxy: before method");
    }

    private void after() {
        System.out.println("static proxy: after method");
    }
}

構造器注入 Subject 接口,便於注入指定的實現類。在實現 method 方法時,調用委託者的 method 方法:subject.method()。before 和 after 方法分別爲前置和後置的增強方法。

單元測試

爲靜態代理創建相應的單元測試

public class ProxySubjectTest {

    @Test
    public void method() {
        Subject subject = new RealSubject();
        ProxySubject proxySubject = new ProxySubject(subject);
        proxySubject.method();
    }
}

運行該測試方法,在控制檯中觀察輸出。

static proxy: before method
real method implement
static proxy: after method

動態代理

準備工作

接口和實現類

定義一個接口及其實現類,實現類會委託動態代理來對其實現的方法進行增強。

public interface MyInterface {

    void methodA();

    int methodB();

    int methodC(int param1, int param2);
}
public class MyInterfaceImpl implements MyInterface {

    @Override
    public void methodA() {
        System.out.println("method A");
    }

    @Override
    public int methodB() {
        System.out.println("method B");
        return 0;
    }

    @Override
    public int methodC(int param1, int param2) {
        System.out.println("method C");
        return param1 + param2;
    }
}

沒有實現接口的類

定義一個沒有實現接口的類。

public class MyClass {

    public void methodA() {
        System.out.println("method A");
    }

    public int methodB() {
        System.out.println("method B");
        return 0;
    }

    public int methodC(int param1, int param2) {
        System.out.println("method C");
        return param1 + param2;
    }
}

JDK 動態代理

JDK 動態代理是利用反射機制生成一個實現代理接口的匿名類,在調用具體方法前調用 InvocationHandlerinvoke 方法來處理。

實現

public class MyInvocationHandler implements InvocationHandler {

    private Object delegate;

    public MyInvocationHandler(Object delegate) {
        this.delegate = delegate;
    }

    public Object createJDKProxy() {
        return Proxy.newProxyInstance(this.getClass().getClassLoader(), delegate.getClass().getInterfaces(), this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        before();
        Object result = method.invoke(delegate, args);
        after();
        return result;
    }

    private void before() {
        System.out.println("jdk proxy: before method");
    }

    private void after() {
        System.out.println("jdk proxy: after method");
    }

類似於靜態代理,JDK 動態代理也是通過構造器注入委託類對象 delegate

創建動態代理對象

createJDKProxy 方法中,調用 ProxynewProxyInstance 方法來創建一個 JDK 動態代理對象。

首先閱讀 newProxyInstance 方法的源碼註釋:

/**
 * Returns an instance of a proxy class for the specified interfaces
 * that dispatches method invocations to the specified invocation
 * handler.
 *
 * @param   loader the class loader to define the proxy class
 * @param   interfaces the list of interfaces for the proxy class
 *          to implement
 * @param   h the invocation handler to dispatch method invocations to
 * @return  a proxy instance with the specified invocation handler of a
 *          proxy class that is defined by the specified class loader
 *          and that implements the specified interfaces
 */

可知,newProxyInstance 的參數分別應該傳入

  • 代理類的類加載器。在本案例中,代理類即自定義的 InvocationHandler 接口實現類 MyInvocationHandler,所以傳參爲當前類對象的類加載器即可:this.getClass().getClassLoader()
  • 代理類要實現的接口數組類對象。這裏傳入委託類對象實現的接口類對象數組即可:delegate.getClass().getInterfaces()
  • 分發方法調用的 invocation handler。這裏傳入當前代理對象即可:this
  • 返回值爲一個 JDK 代理實例。

重寫 invoke 方法

invoke 方法基於反射原理,當調用代理者中的接口方法時,會首先進入invoke方法中,從 Object result = method.invoke(delegate, args) 可以看出,JDK 動態代理基於反射原理調用委託者實現的接口方法,並獲取其返回值。並按順序執行前置和後置增強方法 beforeafter

單元測試

爲 JDK 動態代理創建單元測試

public class JDKProxyTest {

    private MyInterface myInterfaceJDKProxy;

    @Before
    public void setUp() {
        MyInterface myInterface = new MyInterfaceImpl();
        MyInvocationHandler myInvocationHandler = new MyInvocationHandler(myInterface);
        myInterfaceJDKProxy = (MyInterface) myInvocationHandler.createJDKProxy();
    }

    @Test
    public void methodA() {
        myInterfaceJDKProxy.methodA();
    }

    @Test
    public void methodB() {
        assertEquals(0, myInterfaceJDKProxy.methodB());
    }

    @Test
    public void methodC() {
        int param1 = 2;
        int param2 = 3;
        assertEquals(param1 + param2, myInterfaceJDKProxy.methodC(param1, param2));
    }
}

在控制檯中觀察輸出結果

  • methodA

    jdk proxy: before method
    method A
    jdk proxy: after method
    
  • methodB

    jdk proxy: before method
    method B
    jdk proxy: after method
    
  • methodC

    jdk proxy: before method
    method C
    jdk proxy: after method
    

Cglib 動態代理

Cglib 是一種動態代理方式,底層通過 ASM 產生字節碼來完成動態代理,Cglib 與 JDK 動態代理相比,除了可以代理實現接口的類也可以代理非實現接口的類,通過 FastClass 類來避免了反射的使用。對 JDK 7 以前的版本來說,JDK 8 動態代理執行效率明顯要比 Cglib 動態代理類效率差,JDK 8 即以後版本對 JDK 動態代理進行了相應的優化,這種差距就不那麼明顯了。但是要代理不實現接口的類來說,Cglib 就是一種必要選擇。

實現方式一:實現 MethodInterceptor 接口

public class MyMethodInterceptor implements MethodInterceptor {

    private Object delegate;

    public MyMethodInterceptor(Object delegate) {
        this.delegate = delegate;
    }

    public Object createCglibProxy() {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(delegate.getClass());
        enhancer.setInterfaces(delegate.getClass().getInterfaces());
        enhancer.setCallback(this);
        return enhancer.create();
    }

    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        before();
        Object result = method.invoke(delegate, args);
        after();
        return result;
    }

    private void before() {
        System.out.println("cglib proxy: before method");
    }

    private void after() {
        System.out.println("cglib proxy: after method");
    }
}

首先閱讀一下 setSuperclass 方法的源碼

/**
 * Set the class which the generated class will extend. As a convenience,
 * if the supplied superclass is actually an interface, <code>setInterfaces</code>
 * will be called with the appropriate argument instead.
 * A non-interface argument must not be declared as final, and must have an
 * accessible constructor.
 * @param superclass class to extend or interface to implement
 * @see #setInterfaces(Class[])
 */
public void setSuperclass(Class superclass) {
    if (superclass != null && superclass.isInterface()) {
        setInterfaces(new Class[]{ superclass });
    } else if (superclass != null && superclass.equals(Object.class)) {
        // affects choice of ClassLoader
        this.superclass = null;
    } else {
        this.superclass = superclass;
    }
}

這裏傳入的參數 superClass 將會被生成的代理類繼承,所以這裏的 superClass 傳入委託者(實現類)對應的 Class 對象。

再閱讀一下 setInterfaces 的源碼

/**
 * Set the interfaces to implement. The <code>Factory</code> interface will
 * always be implemented regardless of what is specified here.
 * @param interfaces array of interfaces to implement, or null
 * @see Factory
 */
public void setInterfaces(Class[] interfaces) {
    this.interfaces = interfaces;
}

所以 interfaces 參數傳入委託者實現的接口數組即可。這裏不設置 interfaces 也沒有關係,因爲即使 superClass 參數爲接口類型的話,在 setSuperclass 方法中會進行 setInterfaces 的工作。

callbackEnhancer 中能夠攔截原方法並進行增強的關鍵,MethodInterceptor 接口是 callback 的子接口之一,可以對委託者的非 final 方法進行攔截,所以 callback 設置爲當前 MethodInterceptor 的實現類對象,即 this;如果沒有顯式地創建 MethodInterceptor 的實現類,也可以隱式地創建 MethodInterceptor 的匿名內部類:

enhancer.setCallback(new MethodInterceptor() {
    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        before();
        Object result = method.invoke(delegate, args);
        after();
        return result;
    }
});

在設置好 superclassinterfacescallback後,調用 enhancer.create() 即可獲取 Cglib 動態代理對象。

單元測試

委託者沒有實現接口的類的測試

public class CglibProxy4ClassTest {

    private MyClass myClass;

    @Before
    public void setUp() {
        MyClass myInterfaceImpl = new MyClass();
        MyMethodInterceptor myMethodInterceptor = new MyMethodInterceptor(myInterfaceImpl);
        myClass = (MyClass) myMethodInterceptor.createCglibProxy();
    }

    @Test
    public void methodA() {
        myClass.methodA();
    }

    @Test
    public void methodB() {
        assertEquals(0, myClass.methodB());
    }

    @Test
    public void methodC() {
        int param1 = 2;
        int param2 = 3;
        assertEquals(param1 + param2, myClass.methodC(param1, param2));
    }

}

委託者爲實現了接口的類的測試

public class CglibProxy4InterfaceTest {

    private MyInterface myInterfaceCglibProxy;

    @Before
    public void setUp() {
        MyInterface myInterface = new MyInterfaceImpl();
        MyMethodInterceptor myMethodInterceptor = new MyMethodInterceptor(myInterface);
        myInterfaceCglibProxy = (MyInterface) myMethodInterceptor.createCglibProxy();
    }

    @Test
    public void methodA() {
        myInterfaceCglibProxy.methodA();
    }

    @Test
    public void methodB() {
        assertEquals(0, myInterfaceCglibProxy.methodB());
    }

    @Test
    public void methodC() {
        int param1 = 2;
        int param2 = 3;
        assertEquals(param1 + param2, myInterfaceCglibProxy.methodC(param1, param2));
    }

}

上述兩個測試類的測試結果均爲:

  • methodA

    cglib proxy: before method
    method A
    cglib proxy: after method
    
  • methodB

    cglib proxy: before method
    method B
    cglib proxy: after method
    
  • methodC

    cglib proxy: before method
    method C
    cglib proxy: after method
    

實現方式二:匿名內部類

方式二和方式一的原理是一樣的,只是從形式上更簡潔,因爲使用了匿名內部類。

public class CglibProxyFactory {

    public static Object createProxy(final Object delegate) {
        return Enhancer.create(delegate.getClass(), new MethodInterceptor() {
            @Override
            public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws InvocationTargetException, IllegalAccessException {
                before();
                Object result = method.invoke(delegate, args);
                after();
                return result;
            }

            private void before() {
                System.out.println("inner cglib proxy: before method");
            }

            private void after() {
                System.out.println("inner cglib proxy: after method");
            }
        });
    }
}

createProxy 方法中直接調用了 Enhancercreate 方法,其源碼如下:

/**
 * Helper method to create an intercepted object.
 * For finer control over the generated instance, use a new instance of <code>Enhancer</code>
 * instead of this static method.
 * @param type class to extend or interface to implement
 * @param callback the callback to use for all methods
 */
public static Object create(Class type, Callback callback) {
	Enhancer e = new Enhancer();
	e.setSuperclass(type);
	e.setCallback(callback);
	return e.create();
}
  • type 參數傳入的是代理對象將要繼承的類或實現的接口,在本案例中即指代MyClassMyInterfaceImplMyInterface,這裏傳參爲 delegate.getClass()

  • callback 參數爲所有非 final 方法對應的回調函數,即 MethodInterceptor 的實現類或其匿名內部類。這裏簡化爲匿名內部類:

    new MethodInterceptor() {
    	@Override
    	public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws InvocationTargetException, IllegalAccessException {
    		before();
    		Object result = method.invoke(delegate, args);
    		after();
    		return result;
    	}
    
    	private void before() {
    		System.out.println("inner cglib proxy: before method");
    	}
    
    	private void after() {
    		System.out.println("inner cglib proxy: after method");
    	}
    }
    

單元測試

同方式一的單元測試,創建一個 CglibProxyFactory4ClassTestCglibProxyFactory4InterfaceTest,分別對沒有實現接口的類和實現了接口的類做單元測試,測試結果也是斷言通過,並且能夠完成前置和後置方法中的打印。

發佈了85 篇原創文章 · 獲贊 335 · 訪問量 10萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章