準備工作
代理簡介
代理,簡單來說,就是代替原有操作者,即委託者去處理一件事。在 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 動態代理是利用反射機制生成一個實現代理接口的匿名類,在調用具體方法前調用 InvocationHandler
的 invoke
方法來處理。
實現
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
方法中,調用 Proxy
的 newProxyInstance
方法來創建一個 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 動態代理基於反射原理調用委託者實現的接口方法,並獲取其返回值。並按順序執行前置和後置增強方法 before
和 after
。
單元測試
爲 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
的工作。
callback
是 Enhancer
中能夠攔截原方法並進行增強的關鍵,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;
}
});
在設置好 superclass
、interfaces
、callback
後,調用 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
方法中直接調用了 Enhancer
的 create
方法,其源碼如下:
/**
* 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
參數傳入的是代理對象將要繼承的類或實現的接口,在本案例中即指代MyClass
、MyInterfaceImpl
或MyInterface
,這裏傳參爲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"); } }
單元測試
同方式一的單元測試,創建一個 CglibProxyFactory4ClassTest
和 CglibProxyFactory4InterfaceTest
,分別對沒有實現接口的類和實現了接口的類做單元測試,測試結果也是斷言通過,並且能夠完成前置和後置方法中的打印。