1. 程式人生 > >jdk動態代理 和 CGLIB動態代理 詳解

jdk動態代理 和 CGLIB動態代理 詳解

1. 靜態代理模式

//目標介面
public interface IDoSomething {
	public void doSomething();
}
//目標物件
public class DoSomething implements IDoSomething{
	public void doSomething() {
		System.out.println(" do something ");
	}
}
如果使用繼承的方式,可以直接繼承後,覆蓋父類方法,然後在此方法內部與呼叫父類的方法,實現代理此種形式無需父類實現介面
public class SubDoSomething extends DoSomething{
	@Override
	public void doSomething() {
		System.out.println(" before ");
		super.doSomething();
		System.out.println(" end ");
	}
	public static void main(String[] args) {
		DoSomething t = new SubDoSomething();
		t.doSomething();
	}
}

如果使用介面的形式,那麼代理類必須跟被代理類實現同一個介面,代理類擁有被代理的物件在代理類實現介面方法時,直接呼叫被代理的物件來實現
public class StaticProxyDoSomething implements IDoSomething{
	IDoSomething t;
	public StaticProxyDoSomething(IDoSomething t) {
		this.t = t;
	}
	@Override
	public void doSomething() {
		System.out.println(" before ");
		t.doSomething();
		System.out.println(" end ");
	}
	public static void main(String[] args) {
		IDoSomething t1 = new DoSomething();
		IDoSomething t2 = new StaticProxyDoSomething(t1);
		t2.doSomething();
	}
}

靜態代理模式,必須針對單個指定的類進行代理,如果有多個類需要代理的話,需要寫太多重複的代理類

而且如果被代理類增加一個方法,對應的代理類都必須進行修改

2. JDK動態代理,可以直接使用反射的方式,動態呼叫被代理類的方法,具體步驟:

1.寫一個通用代理類,實現java.lang.reflect.InvocationHandler介面,實現invoke()方法

2.代理類需要通過反射,來呼叫被代理的物件,那麼首先需要獲取被代理的物件,一般可以通過建構函式傳入

   然後需要構造一個被代理的物件(Proxy.newProxyInstance()),最後在invoke方法裡面呼叫相應方法

   實際上,JDK直接生成了一個代理類的class檔案類似:$DoSomethingProxy.class,這個class實現了IDoSomething介面並繼承了Proxy
   (因為要繼承Proxy,所以不能直接繼承DoSomething,那麼就必須要實現一個介面了,這就是JDK動態代理必須有介面的原因)

package com.ourselvesoft.catering.test.other.proxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class MyInvocationHandler implements InvocationHandler{
//獲取目標物件
	public Object target;
	public MyInvocationHandler(Object target) {
		this.target = target;
	}
	//使用反射呼叫對應的方法
	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		System.out.println(" before ");
		Object result = method.invoke(target, args);
		System.out.println(" end ");
		return result;
	}
	
	//獲取代理物件
	public Object getProxy() {
		return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
	}
	
	public static void main(String[] args) {
		IDoSomething t = new DoSomething();
		MyInvocationHandler handler = new MyInvocationHandler(t);
		//IDoSomething proxy = (IDoSomething)Proxy.newProxyInstance(t.getClass().getClassLoader(), t.getClass().getInterfaces(), handler);
		IDoSomething proxy = (IDoSomething)handler.getProxy();
		proxy.doSomething();
	}
}

3. cglib動態代理

    cglib動態代理則無需被代理類實現任何介面(但是cglib採用了動態建立子類的方法,因而無法代理final方法)

    首先實現net.sf.cglib.proxy.MethodInterceptor 介面,實現其intercept方法(getProxy傳入被代理class)

public class CGLibProxy implements MethodInterceptor {
	private Enhancer enhancer = new Enhancer();

	public Object getProxy(Class clazz) {
		// 設定需要建立子類的類
		enhancer.setSuperclass(clazz);
		enhancer.setCallback(this);
		// 通過位元組碼技術動態建立子類例項
		return enhancer.create();
	}

	@Override
	public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
		System.out.println(" before ");
		// 通過代理類呼叫父類中的方法
		Object result = proxy.invokeSuper(obj, args);
		System.out.println(" after ");
		return result;
	}
	
	public static  void main(String[] args) {
		CGLibProxy proxy = new CGLibProxy();
		DoSomething t = (DoSomething)proxy.getProxy(DoSomething.class);
		t.doSomething();
	}
}

spring aop 預設使用JDK 動態代理,在沒有介面的情況下,會使用cglib動態代理