這篇文章,主要解決以下幾個問題:
- 分析靜態代理存在的問題。
- 分析jdk動態代理執行過程和實現原理。
- 動手實現自己的動態代理。
- 分析CGlib的動態代理實現原理。
- 比較CGlib動態代理和jdk動態代理。
1 靜態代理及存在的問題
1.1 靜態代理
在實際的開發中,我們可能會遇到這種問題,如:現在有一個訂單的服務接口,以及其實現類如下:
/**
* 訂單服務
*/
public interface OrderService{
/**
* 創建訂單
* @throws InterruptedException
*/
void createOrder() throws InterruptedException;
}
/**
* 訂單服務實現
*/
public class OrderServiceImpl implements OrderService {
@Override
public void createOrder() {
System.out.println("創建訂單");
}
}
我們現在有新的需求,需要知道這個服務中createOrder方法的執行時間,我們會怎麼做呢,最簡單最容易的想法就是去修改createOrder方法,或修改調用createOrder方法的代碼,在調用createOrder前後增加一些邏輯。然而這樣需要修改源代碼,不符合開閉原則。
我們可以通過靜態代理的方式去實現這個需求,具體做法如下:
- 創建一個代理類OrderServiceProxy,實現OrderService。
- OrderServiceProxy中持有被代理對象的引用OrderService,創建OrderServiceProxy代理對象的時候,需要告訴OrderServiceProxy被代理的對象是誰。
- 代理類OrderServiceProxy中,重寫OrderService接口方法。接口中調用具體的被代理對象的相應方法,並且可以在被代理對象方法前後,異常捕獲catch,finally等位置增加邏輯。這裏只在方法調用的前後增加邏輯完成打印方法執行時間的功能。
前三步實現如下:
public class OrderServiceProxy implements OrderService {
/**
* 代理對象持有被代理對象的引用
*/
private OrderService orderService;
private Long startTime = 0L;
/**
* 創建代理對象的時候,傳入被代理的對象
* @param orderService
*/
public OrderServiceProxy(OrderService orderService){
this.orderService = orderService;
}
@Override
public void createOrder() {
beforeTime("createOrder");
//調用被代理對象的方法
orderService.createOrder();
afterTime("createOrder");
}
/**
* 方法調用之前執行
* @param methodName
*/
public void beforeTime(String methodName){
startTime = System.currentTimeMillis();
System.out.println(methodName + "開始執行時間:" + startTime);
}
/**
* 方法調用之後執行
* @param methodName
*/
public void afterTime(String methodName){
System.out.println(methodName + "執行總共用時:" + (System.currentTimeMillis() - startTime));
}
}
- 最後,具體調用地方創建OrderService對象的時候,創建的是一個代理類對象OrderServiceProxy,並且告訴OrderServiceProxy具體代理的對象是誰(這裏是OrderService實現類OrderServiceImpl)
public class StaticProxyTest {
public static void main(String[] args) throws Exception{
OrderService orderService = new OrderServiceProxy(new OrderServiceImpl());
orderService.createOrder();
}
}
這樣就完成了,程序執行createOrder的時候,調用過程如下:
- mian方法中調用的是代理類OrderServiceProxy的createOrder方法
- 在OrderServiceProxy的createOrder中,先調用增加的邏輯before方法
- 然後調用具體的被代理類的createOrder方法
- 最後再調用增加的邏輯after方法。
這樣就起到了對被代理類OrderServiceImpl功能的增強。
執行結果如下:
1.2 靜態代理存在問題
(1)如果OrderService中增加新的功能,如增加queryOrder方法,此時,代理類也需要手動實現新增加的方法。如下:
/**
* 訂單服務
*/
public interface OrderService{
/**
* 創建訂單
* @throws InterruptedException
*/
void createOrder();
/**
* 查詢訂單
*/
void queryOrder();
}
public class OrderServiceProxy implements OrderService {
/**
* 代理對象持有被代理對象的引用
*/
private OrderService orderService;
private Long startTime = 0L;
/**
* 創建代理對象的時候,傳入被代理的對象
* @param orderService
*/
public OrderServiceProxy(OrderService orderService){
this.orderService = orderService;
}
@Override
public void createOrder() {
beforeTime("createOrder");
//調用被代理對象的方法
orderService.createOrder();
afterTime("createOrder");
}
@Override
public void queryOrder() {
orderService.createOrder();
}
/**
* 方法調用之前執行
* @param methodName
*/
public void beforeTime(String methodName){
startTime = System.currentTimeMillis();
System.out.println(methodName + "開始執行時間:" + startTime);
}
/**
* 方法調用之後執行
* @param methodName
*/
public void afterTime(String methodName){
System.out.println(methodName + "執行總共用時" + (System.currentTimeMillis() - startTime));
}
}
(2)現在不僅僅有訂單服務,還有支付服務,同樣支付服務業需要知道支付過程中方法的性能,需要打印時間。此時我們需要爲支付服務手動增加一個代理類,在這個代理類中手動實現所有的支付接口,並手動爲每個方法增加before和after方法打印方法的執行時間。如下支付接口:
/**
* 支付服務
*/
public interface PaymentService {
/**
* 支付
*/
void pay();
}
/**
* 支付服務實現
*/
public class PaymentServiceImpl implements PaymentService {
@Override
public void pay() {
System.out.println("支付");
}
}
顯然,OrderServiceProxy是不能夠代理支付的服務,因爲OrderServiceProxy中持有的是訂單服務OrderService的引用。
所以,需要爲 PaymentService創建一個自己的代理類 PaymentServiceProxy,實現 PaymentService接口,並持有 PaymentService接口引用,如下:
public class PaymentServiceProxy implements PaymentService {
private PaymentService paymentService;
private Long startTime = 0L;
public PaymentServiceProxy(PaymentService paymentService){
this.paymentService = paymentService;
}
@Override
public void pay() {
beforeTime("pay");
paymentService.pay();
afterTime("pay");
}
/**
* 方法調用之前執行
* @param methodName
*/
public void beforeTime(String methodName){
startTime = System.currentTimeMillis();
System.out.println(methodName + "開始執行時間:" + startTime);
}
/**
* 方法調用之後執行
* @param methodName
*/
public void afterTime(String methodName){
System.out.println(methodName + "執行總共用時" + (System.currentTimeMillis() - startTime));
}
}
這樣,每次需要對一個服務進行功能增強,都需要手動爲這個服務增加一個屬於自己的代理類,這樣會導致類的數量增多,增加系統的複雜程度,不易於維護。
2 Jdk動態代理
前面我們知道,靜態代理是存在兩個問題的:
- 新增方法,都需要在代理類中手動實現方法。
- 新增服務,需要爲每個服務手動增加一個實現類,即使代理類都是爲了完成同一同能(如上面都是爲了打印方法的執行時間)。
動態代理能夠很好的解決這兩個問題,目前動態代理有兩種方式,JDK動態代理和CGLib動態代理。我們先看看Jdk動態代理是如何使用的:
2.1 jdk動態代理使用
首先,需要實現自己的InvocationHandler,這裏的InvocationHandler是對被代理對象和增強功能的封裝,jdk動態創建的代理類對象會持有InvocationHandler的引用,具體實現如下:
public class JdkInvocationHandler implements InvocationHandler {
/**
* 被代理的對象
*/
private Object object;
public JdkInvocationHandler(Object object){
this.object = object;
}
/**
* 這個方法由代理類調用,此時代理類還沒有被創建,是在程序執行的時候動態創建的
* @param proxy 代理類引用,代理類調用此方法的時候傳遞自身引用this
* @param method 要執行被代理類的哪個方法,這個是代理類獲取被代理類接口中的Method作爲參數傳來的。
* jdk動態創建的代理類和被代理類會實現相同的接口,所以這個Method對象很容易獲得
* @param args 要執行被代理類的方法的參數列表
* @return 方法返回值
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("調用前做一些事情,before");
Object result = method.invoke(object, args);
System.out.println("調用後做一些事情,after");
return result;
}
}
InvocationHandler中有一個方法invoke,invoke供代理類調用,有三個參數:
參數1:代理類引用,代理類調用此方法的時候傳遞自身引用this。
參數2:要執行被代理類的哪個方法,invoke中通過method.invoke(object, args)調用被代理對象object的相應方法。
參數3:要執行方法的參數列表。
動態創建代理對象,以及調用的過程如下:
public class JdkProxyTest {
public static void main(String[] args) {
//創建代理對象
ClassLoader classLoader = JdkProxyTest.class.getClassLoader();
OrderService orderServiceProxy = (OrderService) Proxy.newProxyInstance(
OrderService.class.getClassLoader(),
OrderServiceImpl.class.getInterfaces(),
new JdkInvocationHandler(new OrderServiceImpl()));
orderServiceProxy.createOrder();
}
}
通過調用Proxy.newProxyInstance()動態創建代理類對象,newProxyInstance有三個參數:
參數1:類加載器,運行的時候動態創建的代理類會通過這個類加載器進行加載。
參數2:被代理類實現的接口列表,將來被代理類也會實現這些接口。
參數3:前面實現的InvocationHandler對象,將來被創建的代理類會持有這個對象的引用,用於調用InvocationHandler中的invoke方法。
2.2 jdk動態代理原理分析
到這裏,Jdk動態代理就寫好了,jdk動態代理看似神祕,其實原理很簡單:
- 通過Proxy.newProxyInstance()創建動態創建被代理對象,被創建的代理對象是什麼樣子的:一是實現了被代理對象所實現的全部接口,並重寫了相應的方法(所以需要第二個參數); 二是持有InvocationHandler對象引用(第三個參數)。
- 具體調用的時候,首先調用代理類的某個方法。
- 代理類的所有的方法中,都調用的是InvocationHandler的invoke方法,告知InvocationHandler的invoke我現在調用的方法是誰,並把方法參數傳遞給InvocationHandler的invoke。
- InvocationHandler的invoke中執行具體的被代理對象的相應方法。並在執行具體被代理對象的方法前對其功能進行加強。
具體調用過程如下:
爲了能更清楚的看清楚jdk動態代理的真實面目,下面,我們將jdk動態生成的代理類class字節碼寫到文件中,通過反編譯工具進行反編譯,看看動態生成的代理類類是什麼樣的。
輸出動態代理類class代碼如下:
public class DynamicProxyTest {
public static void main(String[] args) throws Exception{
byte[] bytes = ProxyGenerator.generateProxyClass("Proxy0", new Class[]{OrderService1.class});
FileOutputStream os = new FileOutputStream("d:\\install\\jad\\Proxy.class");
os.write(bytes);
os.close();
}
}
對Proxy.class文件進行反編譯結果如下:
public final class Proxy0 extends Proxy implements OrderService1 {
private static Method m1;
private static Method m2;
private static Method m3;
private static Method m4;
private static Method m0;
public Proxy0(InvocationHandler var1) throws {
super(var1);
}
public final boolean equals(Object var1) throws {
try {
return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
}
public final String toString() throws {
try {
return (String)super.h.invoke(this, m2, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final void createOrder() throws {
try {
super.h.invoke(this, m3, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final void createOrder(int var1) throws {
try {
super.h.invoke(this, m4, new Object[]{var1});
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
}
public final int hashCode() throws {
try {
return (Integer)super.h.invoke(this, m0, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
static {
try {
m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
m2 = Class.forName("java.lang.Object").getMethod("toString");
m3 = Class.forName("blog.designpatterns.proxy.OrderService1").getMethod("createOrder");
m4 = Class.forName("blog.designpatterns.proxy.OrderService1").getMethod("createOrder", Integer.TYPE);
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
}
很清楚就能看到,動態生成的代理類:
- 實現了被代理類所實現的接口,並重寫了其方法。
- 持有InvocationHandler引用。
- 調用動態代理類的方法,方法中都是調用了InvocationHandler的invoke方法,告訴InvocationHandler現在要調用的是哪個方法,並且傳遞自身引用和參數列表。
動態代理能夠很好的解決靜態代理的兩個問題,原因是:
- InvocationHandler中持有引用對象是Object,可以代理所有的服務。
- 代理類是通過Proxy.newProxyInstance()動態創建的,不需要手動去爲每個服務都手動實現一個代理類。
- 代理類動態創建,接口中的方法增多,會自動實現所有的方法,不需要手動實現。
jdk動態代理還有兩個細節:
- 代理類重寫的方法被final修飾,說明限制了代理類不能再次被繼承(後面講到的Cglib動態代理不能代理final修飾的方法,所以也限制了被jdk動態代理類方法不能再次被代理)。
- 除了代理我們定義的方法,還代理了equals, toString,hashCode。
3 實現自己的動態代理
這裏是仿照jdk的動態代理,實現自己的動態代理,旨在更進一步的瞭解jdk動態代理的原理。
要仿照jdk動態代理實現自己的動態代理,主要分爲兩步:
- 定義自己的InvocationHandler接口。
- 動態創建代理類對象。
3.1 定義自己的InvocationHandler接口
和jdk的InvocationHandler一樣定義,具體如下:
public interface MyInvocationHandler {
Object invoke(Object proxy, Method method, Object[] args);
}
3.2 動態創建代理類對象
動態創建代理類對象可以拆分爲以下幾個步驟:
- 代碼構建Java程序,可以是文本文件,可以只是字符串,這個組裝爲一個字符串。
- 編譯java程序,生成Class byte數組。
- 通過構造器加載Class,這裏會定義自己的類加載器。
- 通過加載的Class,創建代理對象。
四個步驟定義如下:
public class MyProxy {
public static Object newProxyInstance(MyClassLoader classLoader, Class[] interfaces, MyInvocationHandler invocationHandler) throws Exception {
//1、代碼構建Java程序,可以是文本文件,可以只是字符串,這個組裝爲一個字符串
String proxyJavaSource = buildProxyJavaSource(interfaces);
//2、編譯java程序,生成Class byte數組
byte[] classBytes = compailerJavaSource(proxyJavaSource);
//3、通過構造器加載Class
Class proxyClass = loadClass(classLoader, classBytes);
//4、創建代理對象
Object proxy = createdProxyInstance(proxyClass, invocationHandler);
return proxy;
}
}
下面就具體實現這四個步驟
3.2.1 構建java程序
構建java代碼,其實就是通過程序去寫程序,過程很簡單,只需要注意一些實現細節即可。這裏寫法很撈,具體實現如下:
/**
* 代碼構建Java程序,可以是文本文件,可以只是字符串,這個組裝爲一個字符串
* @param interfaces 被代理類實現的接口
* @return java源程序字符串
*/
private static String buildProxyJavaSource(Class[] interfaces) {
StringBuilder builder = new StringBuilder();
builder.append("package blog.designpatterns.proxy.dynamicproxy.mydynamicproxy;\n");
//包導入
builder.append("import blog.designpatterns.proxy.dynamicproxy.mydynamicproxy.MyInvocationHandler;\n");
builder.append("import java.lang.reflect.Method;\n");
for (Class ainterface : interfaces) {
builder.append("import " + ainterface.getName() + ";\n");
}
//實現接口
builder.append("public class Proxy0 implements ");
for (Class ainterface : interfaces) {
builder.append(" " + ainterface.getSimpleName() + ",");
}
builder.deleteCharAt(builder.length() - 1);
builder.append("{\n");
//方法變量
int index = 0;
for (int i = 0; i < interfaces.length; i++) {
Class anInterface = interfaces[i];
Method[] declaredMethods = anInterface.getDeclaredMethods();
for (int j = 0; j < declaredMethods.length; j++) {
builder.append("private static Method m" + index + ";\n");
index ++;
}
}
//靜態代碼塊
index = 0;
builder.append("static {\n");
builder.append("try{\n");
for (int i = 0; i < interfaces.length; i++) {
Class anInterface = interfaces[i];
Method[] declaredMethods = anInterface.getDeclaredMethods();
for (int j = 0; j < declaredMethods.length; j++) {
Method method = declaredMethods[j];
builder.append("m" + index + " = Class.forName(\""+anInterface.getName()+"\").getMethod(\""+method.getName()+"\",");
for (Class<?> parameterType : method.getParameterTypes()) {
builder.append(parameterType.getName() + ".class,");
}
builder.deleteCharAt(builder.length() - 1);
builder.append(");\n");
index ++;
}
}
builder.append("} catch (Exception e){ \n");
builder.append("throw new RuntimeException(e);\n");
builder.append("}\n");
builder.append("}\n");
//handler和構造函數
builder.append("private MyInvocationHandler myInvocationHandler;\n");
builder.append("public Proxy0(MyInvocationHandler myInvocationHandler) throws Exception {\n");
builder.append("this.myInvocationHandler = myInvocationHandler;\n");
builder.append(" }\n");
//重寫方法
index = 0;
for (Class ainterface : interfaces) {
Method[] declaredMethods = ainterface.getDeclaredMethods();
for (Method method : declaredMethods){
builder.append("@Override\n");
builder.append("public ");
builder.append(method.getReturnType().getName() + " ");
builder.append(method.getName() + "( ");
Class<?>[] parameterTypes = method.getParameterTypes();
for (int i = 0; i < parameterTypes.length; i++) {
builder.append(parameterTypes[i].getName() + " var" + i + ",");
}
if (parameterTypes.length > 0){
builder.deleteCharAt(builder.length() - 1);
}
builder.append("){\n");
builder.append("try{\n");
//調用handler的invoke方法
if (method.getReturnType().getName().equals("void")){
builder.append("myInvocationHandler.invoke(this, m" + index+ ",");
}else {
builder.append("Object result = myInvocationHandler.invoke(this, m" + index+ ",");
}
if (parameterTypes.length == 0){
builder.append("null");
}else {
builder.append("new Object[]{");
for (int i = 0; i < parameterTypes.length; i++) {
builder.append(" var" + i + ",");
}
builder.deleteCharAt(builder.length() - 1);
builder.append("}");
}
builder.append(");\n");
if (!method.getReturnType().getName().equals("void")){
builder.append("return ("+method.getReturnType().getName()+")result;\n");
}
builder.append("} catch (Exception e){ \n");
builder.append("throw new RuntimeException(e);\n");
builder.append("}\n");
builder.append("}\n");
index ++;
}
}
builder.append("}\n");
return builder.toString();
}
定義測試OrderService接口如下:
/**
* 訂單服務
*/
public interface OrderService {
/**
* 創建訂單
* @throws InterruptedException
*/
void createOrder();
/**
* 查詢訂單
*/
String createOrder(int a);
}
執行上面的方法構建代理類java源程序結果如下:
package blog.designpatterns.proxy.dynamicproxy.mydynamicproxy;
import blog.designpatterns.proxy.dynamicproxy.mydynamicproxy.MyInvocationHandler;
import java.lang.reflect.Method;
import blog.designpatterns.proxy.dynamicproxy.mydynamicproxy.OrderService;
public class Proxy0 implements OrderService{
private static Method m0;
private static Method m1;
static {
try{
m0 = Class.forName("blog.designpatterns.proxy.dynamicproxy.mydynamicproxy.OrderService").getMethod("createOrder");
m1 = Class.forName("blog.designpatterns.proxy.dynamicproxy.mydynamicproxy.OrderService").getMethod("createOrder",int.class);
} catch (Exception e){
throw new RuntimeException(e);
}
}
private MyInvocationHandler myInvocationHandler;
public Proxy0(MyInvocationHandler myInvocationHandler) throws Exception {
this.myInvocationHandler = myInvocationHandler;
}
@Override
public void createOrder( ){
try{
myInvocationHandler.invoke(this, m0,null);
} catch (Exception e){
throw new RuntimeException(e);
}
}
@Override
public java.lang.String createOrder( int var0){
try{
Object result = myInvocationHandler.invoke(this, m1,new Object[]{ var0});
return (java.lang.String)result;
} catch (Exception e){
throw new RuntimeException(e);
}
}
}
3.2.2 編譯java程序
編譯上面寫好的java程序字符串。編譯使用jdk提供的tools進行編譯,細節不講,直接上代碼:
/**
* 編譯java程序,生成Class byte數組
* @param proxyJavaSource java源程序
* @return 編譯後的class byte數組
*/
private static byte[] compailerJavaSource(String proxyJavaSource) throws Exception {
String name = "Proxy0";
URI uri = URI.create("string:///" + name.replace('.', '/') + JavaFileObject.Kind.SOURCE.extension);
MyJavaFile javafile = new MyJavaFile(uri, JavaFileObject.Kind.SOURCE);
javafile.setContent(proxyJavaSource);
List<JavaFileObject> javaFileObjects = Lists.newArrayList(javafile);
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
MyJavaFileManager myJavaFileManager = new MyJavaFileManager(
compiler.getStandardFileManager(null, null, null));
JavaCompiler.CompilationTask task = compiler.getTask(
null, myJavaFileManager, null, null, null, javaFileObjects);
task.call();
MyJavaFile javaFile = myJavaFileManager.getByteArrayJavaFileObjects().get(0);
ByteArrayOutputStream outputStream = (ByteArrayOutputStream) javaFile.openOutputStream();
return outputStream.toByteArray();
}
編譯過程中需要定義自己的JavaFile和JavaFileManager:
JavaFile:
public class MyJavaFile extends SimpleJavaFileObject {
private String content;
private final ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
public MyJavaFile(URI uri, Kind kind) {
super(uri, kind);
}
public void setContent(String content){
this.content = content;
}
@Override
public CharSequence getCharContent(boolean ignoreEncodingErrors) throws IOException {
return content;
}
@Override
public OutputStream openOutputStream() throws IOException {
return outputStream;
}
}
JavaFileManager:
public class MyJavaFileManager extends ForwardingJavaFileManager {
/**
* Creates a new instance of ForwardingJavaFileManager.
*
* @param fileManager delegate to this file manager
*/
public MyJavaFileManager(JavaFileManager fileManager) {
super(fileManager);
}
private Set<MyJavaFile> javaFiles = new HashSet<>();
public Set<MyJavaFile> getByteArrayJavaFileObjects() {
return javaFiles;
}
// 有字節碼的輸出的時候 我們自定義一個JavaFileObject 來接受輸出
@Override
public JavaFileObject getJavaFileForOutput(Location location, String className, JavaFileObject.Kind kind, FileObject sibling) throws IOException {
if (JavaFileObject.Kind.CLASS == kind) {
MyJavaFile javaFile = new MyJavaFile(URI.create("bytes:///" + className.replace(".", "/") + ".class"), kind);
javaFiles.add(javaFile);
return javaFile;
} else {
return super.getJavaFileForOutput(location, className, kind, sibling);
}
}
}
3.2.3 加載class
將java源程序編譯成class字節碼數組後,接下來就要對編譯好的class進行加載:
/**
* 通過構造器加載Class
* @param classLoader 自定義類加載器
* @param classBytes class byte數組
* @return Class對象
*/
private static Class loadClass(MyClassLoader classLoader, byte[] classBytes) throws Exception {
Class<?> clazz = classLoader.myLoadClass(classBytes);
return clazz;
}
這裏需要定義自己的類加載器,簡單實現一下:
public class MyClassLoader extends ClassLoader {
public Class<?> myLoadClass(byte[] bytes) throws ClassNotFoundException {
return defineClass(bytes, 0, bytes.length);
}
}
3.2.4 創建代理對象
最後就是創建代理類對象了:
/**
* 創建代理對象
* @param proxyClass
* @param invocationHandler
* @return
*/
private static Object createdProxyInstance(Class proxyClass, MyInvocationHandler invocationHandler) throws Exception {
Constructor constructor = proxyClass.getDeclaredConstructor(MyInvocationHandler.class);
constructor.setAccessible(true);
Object proxy = constructor.newInstance(invocationHandler);
return proxy;
}
3.2.5 測試
到這裏就成功實現了自己的動態代理,下面測試一下功能如何:
定義兩個測試接口:
/**
* 訂單服務
*/
public interface OrderService {
/**
* 創建訂單
* @throws InterruptedException
*/
void createOrder();
/**
* 查詢訂單
*/
String createOrder(int a);
}
/**
* 支付服務
*/
public interface PaymentService {
/**
* 支付
*/
void pay();
}
定義一個實現類,同時實現這兩個接口:
/**
* 訂單服務
*/
public class OrderServiceImpl implements OrderService, PaymentService {
@Override
public void createOrder() {
System.out.println("創建訂單");
}
@Override
public String createOrder(int a) {
System.out.println("接受到參數 :a = " + a);
return "返回結果數據";
}
@Override
public void pay() {
System.out.println("開始支付了");
}
}
實現自己的InvocationHandler:
public class MyInvocationHandlerImpl implements MyInvocationHandler {
private Object object;
public MyInvocationHandlerImpl(Object object){
this.object = object;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) {
try {
System.out.println("開始調用了");
Object invoke = method.invoke(object, args);
System.out.println("調用結束了");
return invoke;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
具體測試如下,發現創建動態代理類的過程是不是和Jdk的很類似。
public class MyProxyTest {
public static void main(String[] args) throws Exception {
//動態創建自己的代理類
OrderService orderService = (OrderService) MyProxy.newProxyInstance(
new MyClassLoader(),
OrderServiceImpl.class.getInterfaces(),
new MyInvocationHandlerImpl(new OrderServiceImpl()));
//調用createOrder
orderService.createOrder();
//調用帶參數的createOrder
String result = orderService.createOrder(3);
System.out.println(result);
//再次動態創建代理類
PaymentService paymentService = (PaymentService) MyProxy.newProxyInstance(new MyClassLoader(),
OrderServiceImpl.class.getInterfaces(), new MyInvocationHandlerImpl(new OrderServiceImpl()));
//調用pay
paymentService.pay();
}
}
測試結果如下:
結果也符合預期,到這裏就完成了自己動態代理類的實現。
4 CGlib動態代理原理分析
4.1 CGlib使用
先看看CgLib動態代理是如何使用的:
首先要實現MethodInterceptor接口
public class CglibPrintTimeProxyInterceptor implements MethodInterceptor {
/**
*
* @param obj 被代理對象
* @param method 要執行的方法
* @param args 要執行的方法參數列表
* @param proxy
* @return
* @throws Throwable
*/
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("調用前執行");
Object result = proxy.invokeSuper(obj, args);
System.out.println("調用後執行");
return result;
}
}
MethodInterceptor和Jdk動態代理中InvocationHandler相似,但是此處有一個很大的不同的地方,就是此時調用實際的被代理類的方法的時候,使用的是proxy.invokeSuper(obj, args);,很明顯是調用的是父類的方法。
創建代理類並調用:
public class CglibTest {
public static void main(String[] args) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(OrderServiceImpl.class);
enhancer.setCallback(new CglibPrintTimeProxyInterceptor());
//創建代理類對象
OrderService orderService = (OrderServiceImpl)enhancer.create();
orderService.createOrder();
}
}
執行結果如下:
爲了清楚的瞭解Cglib的實現原理,可以添加如下代碼,就可以打印出動態代理生成的代理類Class:
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY,
"F:\\document\\me\\java\\cglib_proxy_class");
執行之後,在文件夾中,找到生成動態代理類的地方,發現生成了四個class文件:
可以通過執行程序打印類型方式找到生成的代理類class,然後對其反編譯:
發現代理類繼承了被代理類,重寫代理類中createOrder方法實現如下:
public final void createOrder() {
MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
if (var10000 == null) {
CGLIB$BIND_CALLBACKS(this);
var10000 = this.CGLIB$CALLBACK_0;
}
if (var10000 != null) {
var10000.intercept(this, CGLIB$createOrder$0$Method, CGLIB$emptyArgs, CGLIB$createOrder$0$Proxy);
} else {
super.createOrder();
}
}
發現,調用代理類的createOrder,實際調用的還是MethodInterceptor的intercept方法,然後在MethodInterceptor的intercept中調用的是父類的方法,也就是被代理類的createOrder方法。
這裏,其實只是簡單的分析了一下Cglib的原理,其實其粗略的原理就是這樣:
- 動態創建的代理類,繼承了被代理類並實現了其方法
- 代理類中的方法實際調用的是MethodInterceptor的intercept方法。
- MethodInterceptor的intercept方法中會調用代理類父類的相應方法,父類就是被代理類。
但是CGlib真正的實現,要比這複雜的多,本人也沒有做過多的研究, 但是下面簡單說一個細節,爲了後面 jdk動態代理和CGlib動態代理比較做鋪墊。
MethodInterceptor中調用intercept方法的時候,調用的是MethodProxy對象的invokeSuper方法。
invokeSuper方法實現如下:
其中fci.f2是FastClass類型:
其實FastClass也是動態生成的,我們前面打印cglib動態生成的class的時候,不是發現有四個class,其中有兩個是FastClass。
重點來了:這裏的兩個動態生成的FastClass,只有當調用動態代理類被代理的方法的時候,纔會動態生成。如果只是創建代理類而不調用被代理的方法,是不會動態創建的。
測試如下:
public class CglibTest {
public static void main(String[] args) {
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY,
"F:\\document\\me\\java\\cglib_proxy_class");
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(OrderServiceImpl.class);
enhancer.setCallback(new CglibPrintTimeProxyInterceptor());
//創建代理類對象
OrderService orderService = (OrderServiceImpl)enhancer.create();
// orderService.createOrder();
}
}
打印生成的class如下,發現,確實沒有生成FastClass類。
好了,到這裏,cglib的實現分析告一段落,更細節的我也沒有再研究,想研究的,可以自行繼續研究實現細節。
5 jdk動態代理和CGlib動態代理比較
Jdk動態代理:被代理對象必須實現接口,不能代理沒有接口的類。
cglib動態代理:對類本身沒有限制,但是cglib有個坑,就是不能代理final方法,因爲繼承不能重寫final方法,這個在使用中要注意。
那實際中我們應該選擇jdk還是cglib?
這個需要兩個因素,一個是代理本身的侷限性,二是代理的性能問題。
代理本身的侷限性已經提到了,jdk代理的對象必須要有接口,所以相比,cglib功能更強大。
對於性能問題,因爲jdk和cglib都是用到了反射, 是比較耗性能的,所以需要寫代碼去測試,主要測試兩個點,一個是代理類的創建性能,一個是執行方法的性能。
這裏使用的jdk版本是1.8, cglib版本是3.2.1,不管是創建對象還是執行方法,我們都會先執行預熱一下,原因是前面提到的,Cglib有的動態Class,不是在創建代理類的時候就生成了,而是在方法具體的執行的時候才創建的,所以會導致第一次執行會比較耗時。
5.1 創建性能比較
這裏比較創建的性能,是模仿爲大量不同的對象創建代理類,而非多次爲同一個對象創建不同的代理類,因爲多次爲同一個對象創建代理類,JDK和CGLib對其會進行優化,不能夠看出明顯的區別。並且在Spring中,所有的代理類都是代理的,也並不需要重複爲同一個類創建多次代理。
這裏主要爲三個類創建代理,OrderService, PaymentService,InvoiceService三個類進行測試:具體測試代碼如下:
public class JdkCglibCreateCompiler {
public static void main(String[] args) {
Long jdkStart1 = System.currentTimeMillis();
OrderService jdkOrderServiceProxy = (OrderService) Proxy.newProxyInstance(
OrderService.class.getClassLoader(),
OrderServiceImpl.class.getInterfaces(),
new JdkInvocationHandler(new OrderServiceImpl()));
Long jdkEnd1 = System.currentTimeMillis();
System.out.println("JDK創建第一個代理類 : " + (jdkEnd1 - jdkStart1));
Long jdkStart2 = System.currentTimeMillis();
PaymentService jdkPaymentServiceProxy = (PaymentService) Proxy.newProxyInstance(
PaymentService.class.getClassLoader(),
PaymentServiceImpl.class.getInterfaces(),
new JdkInvocationHandler(new PaymentServiceImpl()));
Long jdkEnd2 = System.currentTimeMillis();
System.out.println("JDK創建第二個代理類 : " + (jdkEnd2 - jdkStart2));
Long jdkStart3 = System.currentTimeMillis();
InvoiceService jdkPaymentServiceProxy = (InvoiceService) Proxy.newProxyInstance(
InvoiceService.class.getClassLoader(),
InvoiceServiceImpl.class.getInterfaces(),
new JdkInvocationHandler(new InvoiceServiceImpl()));
Long jdkEnd3 = System.currentTimeMillis();
System.out.println("JDK創建第三個代理類 : " + (jdkEnd3 - jdkStart3));
///******************Cglib*********************
Long cglibStart1 = System.currentTimeMillis();
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(OrderServiceImpl.class);
enhancer.setCallback(new CglibPrintTimeProxyInterceptor());
//創建代理類對象
OrderService orderService = (OrderServiceImpl)enhancer.create();
Long cglibEnd1 = System.currentTimeMillis();
System.out.println("CGLIB創建第一個代理類 : " + (cglibEnd1 - cglibStart1));
Long cglibStart2 = System.currentTimeMillis();
enhancer.setSuperclass(PaymentServiceImpl.class);
enhancer.setCallback(new CglibPrintTimeProxyInterceptor());
//創建代理類對象
PaymentService paymentService = (PaymentService)enhancer.create();
Long cglibEnd2 = System.currentTimeMillis();
System.out.println("CGLIB創建第二個代理類 : " + (cglibEnd2 - cglibStart2));
Long cglibStart3 = System.currentTimeMillis();
enhancer.setSuperclass(InvoiceServiceImpl.class);
enhancer.setCallback(new CglibPrintTimeProxyInterceptor());
//創建代理類對象
InvoiceService invoiceService = (InvoiceService)enhancer.create();
Long cglibEnd3 = System.currentTimeMillis();
System.out.println("CGLIB創建第三個代理類 : " + (cglibEnd3 - cglibStart3));
}
}
因爲只有三個測試類,爲了模擬創建大量創建不同的對象,我這裏上面代碼重複執行40次,也就是jdk和cglib各自創建120個對象,得到如下結果:
從圖中能夠看出,不管是jdk還是cglib,第一次創建對象都會比較耗時,這裏把第一次作爲預熱,只考慮後兩次,也就是各自創建80個對象,對其進行加和,結果是Jdk創建80個對象,需要168ms,平均每個2.1ms, Cglib創建80個對象需要227ms,平均每個2.8ms。
結論是:Jdk1.8創建代理類對象的效率略高於cglib3.2.1
5.2 執行性能
這裏採用先預熱10次,然後各自循環執行5萬次,取執行5萬次用時:
public class JdkCglibExecCompiler {
public static void main(String[] args) {
OrderService jdkOrderServiceProxy = (OrderService) Proxy.newProxyInstance(
OrderService.class.getClassLoader(),
OrderServiceImpl.class.getInterfaces(),
new JdkInvocationHandler(new OrderServiceImpl()));
//預熱
for (int i = 0; i < 10; i++){
jdkOrderServiceProxy.createOrder();
}
Long jdkStart1 = System.currentTimeMillis();
for (int i = 0; i < 50000; i++){
jdkOrderServiceProxy.createOrder();
}
Long jdkEnd1 = System.currentTimeMillis();
System.out.println("jdk創建代理類 : " + (jdkEnd1 - jdkStart1));
System.out.println((jdkEnd1 - jdkStart1));
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(OrderServiceImpl.class);
enhancer.setCallback(new CglibPrintTimeProxyInterceptor());
OrderService cglibOrderServiceProxy = (OrderServiceImpl)enhancer.create();
//預熱
for (int i = 0; i < 10; i++){
cglibOrderServiceProxy.createOrder();
}
Long cglibStart1 = System.currentTimeMillis();
for (int i = 0; i < 50000; i++){
cglibOrderServiceProxy.createOrder();
}
Long cglibEnd1 = System.currentTimeMillis();
System.out.println("cglib創建代理類 : " + (cglibEnd1 - cglibStart1));
}
}
爲了防止單次執行5萬次不具有普遍性,這裏執行10次,取每次執行5萬次的時間如下:
結論:Jdk1.8代理類執行效率略高於cglib3.2.1
最後說一點,Spring中默認使用的是Jdk代理,但是可以再配置文件中配置實用其他代理方式。