cglib是一款優(yōu)秀的Java 字節(jié)碼生成框架,它可以生成并操縱Java字節(jié)碼(底層基于ASM)。
cglib - Byte Code Generation Library is high level API to generate and transform Java byte code. It is used by AOP, testing, data access frameworks to generate dynamic proxy objects and intercept field access.
動(dòng)態(tài)代理實(shí)現(xiàn)
JDK 動(dòng)態(tài)代理
Java 實(shí)現(xiàn)動(dòng)態(tài)代理主要涉及以下幾個(gè)類:
- java.lang.reflect.Proxy: 這是生成代理類的主類,通過 Proxy 類生成的代理類都繼承了 Proxy 類,即 DynamicProxyClass extends Proxy。
- java.lang.reflect.InvocationHandler: 這里稱他為"調(diào)用處理器",他是一個(gè)接口,我們動(dòng)態(tài)生成的代理類需要完成的具體內(nèi)容需要自己定義一個(gè)類,而這個(gè)類必須實(shí)現(xiàn) InvocationHandler 接口。
package com.bytebeats.codelab.cglib.proxy.impl;
import com.bytebeats.codelab.cglib.proxy.ProxyFactory;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays;
/**
* JDK動(dòng)態(tài)代理實(shí)現(xiàn)
* @author Ricky
*
*/
public class JdkProxyFactory implements ProxyFactory {
@Override
public <T> T getProxy(final Object target) {
return (T) Proxy.newProxyInstance(this.getClass().getClassLoader(), target.getClass().getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (Object.class.equals(method.getDeclaringClass())) {
return method.invoke(this, args);
}
String methodName = method.getName();
//打印日志
System.out.println("[before] The method " + methodName + " begins with " + (args!=null ? Arrays.asList(args) : "[]"));
//調(diào)用目標(biāo)方法
Object result = null;
try {
//前置通知
result = method.invoke(target, args);
//返回通知, 可以訪問到方法的返回值
} catch (NullPointerException e) {
e.printStackTrace();
//異常通知, 可以訪問到方法出現(xiàn)的異常
}
//后置通知. 因?yàn)榉椒梢阅軙?huì)出異常, 所以訪問不到方法的返回值
//打印日志
System.out.println("[after] The method ends with " + result);
return result;
}
});
}
}
JDK 動(dòng)態(tài)生成的代理類具有幾個(gè)特點(diǎn):
- 繼承 Proxy 類,并實(shí)現(xiàn)了在Proxy.newProxyInstance()中提供的接口數(shù)組。
- 命名方式為 $ProxyN,其中N會(huì)慢慢增加,一開始是 $Proxy1,接下來是$Proxy2...
- 有一個(gè)參數(shù)為 InvocationHandler 的構(gòu)造函數(shù)。這個(gè)從 Proxy.newProxyInstance() 函數(shù)內(nèi)部的clazz.getConstructor(new Class[] { InvocationHandler.class }) 可以看出。
Java 實(shí)現(xiàn)動(dòng)態(tài)代理的缺點(diǎn):因?yàn)?Java 的單繼承特性(每個(gè)代理類都繼承了 Proxy 類),只能針對(duì)接口創(chuàng)建代理類,不能針對(duì)類創(chuàng)建代理類。
CGLib 動(dòng)態(tài)代理
net.sf.cglib.proxy.Enhancer 類提供了非常簡(jiǎn)潔的API來創(chuàng)建代理對(duì)象,有兩種回調(diào)的防方式:InvocationHandler和MethodInterceptor。
1、InvocationHandler
@Test
public void testInvocationHandler() throws Exception {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(SampleClass.class);
enhancer.setCallback(new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
if(method.getDeclaringClass() != Object.class && method.getReturnType() == String.class) {
return "Hello cglib!";
} else {
throw new RuntimeException("Do not know what to do.");
}
}
});
SampleClass proxy = (SampleClass) enhancer.create();
assertEquals("Hello cglib!", proxy.test(null));
assertNotEquals("Hello cglib!", proxy.toString());
}
2、
@Test
public void testMethodInterceptor() throws Exception {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(SampleClass.class);
enhancer.setCallback(new MethodInterceptor() {
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy)
throws Throwable {
if(method.getDeclaringClass() != Object.class && method.getReturnType() == String.class) {
return "Hello cglib!";
} else {
return proxy.invokeSuper(obj, args);
}
}
});
SampleClass proxy = (SampleClass) enhancer.create();
assertEquals("Hello cglib!", proxy.test(null));
assertNotEquals("Hello cglib!", proxy.toString());
proxy.hashCode(); // Does not throw an exception or result in an endless loop.
}
對(duì)上面代碼稍作封裝,如下:
package com.bytebeats.codelab.cglib.proxy.impl;
import com.bytebeats.codelab.cglib.proxy.ProxyFactory;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
import java.util.Arrays;
/**
* CgLib動(dòng)態(tài)代理實(shí)現(xiàn)
* @author Ricky
*
*/
public class CgLibProxyFactory implements ProxyFactory {
private final Enhancer en = new Enhancer();
@Override
public <T> T getProxy(Object target) {
//進(jìn)行代理
en.setSuperclass(target.getClass());
en.setCallback(new MethodInterceptor() {
@Override
public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
if (Object.class.equals(method.getDeclaringClass())) {
return method.invoke(this, args);
}
String methodName = method.getName();
//打印日志
System.out.println("[before] The method " + methodName + " begins with " + (args!=null ? Arrays.asList(args) : "[]"));
Object result = null;
try{
//前置通知
result = methodProxy.invokeSuper(o, args);
//返回通知, 可以訪問到方法的返回值
System.out.println(String.format("after method:%s execute", method.getName()));
} catch (Exception e){
e.printStackTrace();
//異常通知, 可以訪問到方法出現(xiàn)的異常
}
//后置通知. 因?yàn)榉椒梢阅軙?huì)出異常, 所以訪問不到方法的返回值
//打印日志
System.out.println("[after] The method ends with " + result);
return result;
}
});
//生成代理實(shí)例
return (T)en.create();
}
}
完整測(cè)試類:
package com.bytebeats.codelab.cglib.proxy;
import com.bytebeats.codelab.cglib.proxy.impl.CgLibProxyFactory;
import com.bytebeats.codelab.cglib.proxy.impl.JdkProxyFactory;
import com.bytebeats.codelab.cglib.service.HelloService;
import com.bytebeats.codelab.cglib.service.HelloServiceImpl;
public class ProxyDemo {
public static void main(String[] args) {
//需要被代理的類
HelloService helloService = new HelloServiceImpl();
//jdk代理
ProxyFactory jdkProxyFactory = new JdkProxyFactory();
HelloService jdkProxy = jdkProxyFactory.getProxy(helloService);
jdkProxy.echo("ricky");
jdkProxy.hashCode();
//CgLib代理
ProxyFactory cgLibProxyFactory = new CgLibProxyFactory();
HelloService cgLibProxy = cgLibProxyFactory.getProxy(helloService);
cgLibProxy.echo("ricky");
jdkProxy.hashCode();
}
}
參考資料
CGLIB Tutorial
Java 動(dòng)態(tài)代理機(jī)制分析及擴(kuò)展,第 1 部分