1. 程式人生 > >動態代理雙劍客--JDK Proxy與CGLIB

動態代理雙劍客--JDK Proxy與CGLIB

背景:

研究過設計模式的同胞們都知道代理模式可以有兩種實現方案:

1.介面實現(或繼承抽象類)

核心程式碼片段

ProxySubject-->>doOperation()

//dosomething before
realSubject.doOperation()
//dosomething after

2.繼承父類



核心程式碼片段

ProxySubject-->>doOperation()

//dosomething before
super.doOperation()
//dosomething after

總結:

相同點

都可以通過Proxy控制對Target

的訪問

不同點


可行性

如果Target實現了介面,那麼這兩種方式都可以;

如果沒有實現任何介面,那隻能採取“繼承父類”的方式了


正文

Java中動態代理對應著也有兩種實現方式

1.“介面實現"---JDK Proxy

用到JDK提供的InvocationHandler介面和Proxy

類之間的關係如下


InvocationHandler介面

用於處理方法請求

Proxy

用於生成代理物件

程式碼演示

ISubject介面

public interface ISubject {
	
	public void showName(String name);
}

RealSubject

public class RealSubject implements ISubject {

	@Override
	public void showName(String name) {
		System.out.println(name+"閃亮登場");
	}

}

LogHandler

為了更明確的說明動態代理的工作原理,將代理的建立過程放到了LogHandler的外部,即main方法中

public class LogHandler implements InvocationHandler {

	Object target=null;
	
	public Object getTarget() {
		return target;
	}

	public void setTarget(Object target) {
		this.target = target;
	}

	@Override
	public Object invoke(Object proxy, Method method, Object[] args)
			throws Throwable {
		Object result=null;
		//呼叫目標物件方法前的邏輯
		System.out.println("下面有一個大人物要出現");
		//呼叫目標物件的方法,這句程式碼將代理與目標類聯絡了起來
		method.invoke(target, args);
		//呼叫目標物件方法後的邏輯
		System.out.println("大家鼓掌歡迎");
		return result;
				
	}

}
客戶端類Client
public class Client {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		
		LogHandler logHandler=new LogHandler();
		logHandler.setTarget(new RealSubject());
		//建立代理物件
		ISubject proxySubject=(ISubject)Proxy.newProxyInstance(RealSubject.class.getClassLoader(), RealSubject.class.getInterfaces(), logHandler);
		System.out.println("-------JDK Proxy-------------");
		proxySubject.showName("委座");

	}

}

執行結果


呼叫過程時序圖



2.“繼承父類”---CGLIB

用到了CBLIB提供的Enhancer類和MethodInterceptor介面 

類之間的關係如下


需要引入第三方jar包

  • cglib-2.2.jar
  • asm-3.1.jar

Enhancer

用於建立代理物件

MethodInterceptor介面

用於處理方法請求

程式碼演示

RealSubject類同上,但是個pojo類

public class RealSubject {

	public void showName(String name) {
		System.out.println(name+"閃亮登場");
	}

}


LogIntercept類

public class LogIntercept implements MethodInterceptor {
	Object target=null;
	
	public Object getTarget() {
		return target;
	}

	public void setTarget(Object target) {
		this.target = target;
	}
	
	@Override
	public Object intercept(Object arg0, Method arg1, Object[] arg2,
			MethodProxy arg3) throws Throwable {
		
		Object result=null;
		//呼叫目標物件方法前的邏輯
		System.out.println("下面有一個大人物要出現");
		//呼叫目標物件的方法,這句程式碼將代理與目標類聯絡了起來
		arg3.invoke(target, arg2);
		//呼叫目標物件方法後的邏輯
		System.out.println("大家鼓掌歡迎");
		return result;
	}

}

客戶端類Client

public class Client {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		
		LogIntercept logIntercept=new LogIntercept();
		logIntercept.setTarget(new RealSubject());
		Enhancer enhancer=new Enhancer();
		enhancer.setSuperclass(RealSubject.class);
		enhancer.setCallback(logIntercept);
		
		<pre name="code" class="java">                RealSubject proxySubject=(RealSubject )enhancer.create();
		System.out.println("-------CBLIB-------------");
		proxySubject.showName("委座");

	}

}


呼叫過程時序圖


總結

大家可以看到JDK ProxyCGLIB這兩種動態代理的實現過程是非常相似的,但也有區別

相同點:

  • 都用到了一個介面一個類;
  • 介面用於處理方法呼叫,類用於建立代理物件

JDK Proxy

InvocationHandler介面

Proxy

CGLIB

MethodIntercept介面

Enhancer

不同點:

JDK Proxy

使用目標類的介面建立動態代理

CBLIB

使用目標類的子類建立動態代理

最後

          JDK ProxyCGLIB兩種動態代理各有千秋,具體用哪個方案要看具體情況。如果目標類實現了對應介面,兩種方案都可以;如果沒有實現任何介面則要使用CBLIB比如Hibernate中的實體類是POJO類,沒有實現任何介面,那麼要通過代理實現延遲載入就只能採用CGLIB方案了。