JDK動態代理VS CgLib

業務場景

描述:每一個旅客乘坐火車時,在上車、下車以前須要檢票。站在乘客的角度,這個過程能夠看作一件事:檢票坐車檢票;也能夠看作兩件事:檢票,坐車;站在檢票員的角度,那就是一件事,檢票。java

若是咱們用代碼實現,多是這樣的過程:spring

checkTicket();
takingTrain();
checkTicket();

若是有一天,車站變成刷身份證進站了,咱們就得回去改代碼了... 下面咱們將這個過程分開,用代理來解決坐車意外的問題: 業務邏輯:坐火車 額外須要:檢票/刷卡ide

具體實現

  • JDK動態代理,主要類、接口:Proxy, InvocationHandle,JDK動態代理特色只能對接口進行代理

因此須要一個業務邏輯接口:TakingTrain;函數

package com.spring.mybean;
public interface TakingTrain {
	public void takeTrain(String name);	
}

實現類:TakingTrainImpl------>要被代理的目標類,實現TakingTrain接口this

package com.spring.mybean.imp;
import com.spring.mybean.TakingTrain;
public class TakingTrainImpl implements TakingTrain {
	public void takeTrain(String name) {
		System.out.println("Hi "+name+ "Welcome to take the train");
	}
}

加強的橫切邏輯:CheckTicket------>加強類,實現InvocationHandle接口代理

package com.spring.myadvice;

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

public class CheckTicket implements InvocationHandler {
	private Object target;
	public CheckTicket(Object target) {
		this.target = target;
	}
	public Object invoke(Object proxy, Method method, Object[] args)
			throws Throwable {
		System.out.println("please show your tickes");
		Object obj=method.invoke(target, args);
		return obj;
	}
}

調用:code

public class TakingTainingTest {
	
	@Test
	public void takingTrain(){
		TakingTrain target=new TakingTrainImpl(); 
		CheckTicket ct=new CheckTicket(target);
		TakingTrain proxy=(TakingTrain) Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), ct);
		proxy.takeTrain("zhangsan");
	}

}

打印結果對象

please show your tickes
Hi zhangsan Welcome to take the train

JDK中具體的動態代理類是怎麼產生

  1. 產生代理類$Proxy0類 執行了Proxy.newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h) 將產生$Proxy0類,它繼承Proxy對象,並根據第二個參數,實現了被代理類的全部接口,天然就能夠生成接口要實現的全部方法了(這時候會重寫hashcode,toString和equals三個方法),可是尚未具體的實現體;
  2. 將代理類$Proxy0類加載到JVM中 這時候是根據Proxy.newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h)它的第一個參數----就是被代理類的類加載器,把當前的代理類加載到JVM中
  3. 建立代理類$Proxy0類的對象 調用的$Proxy0類的$Proxy0(InvocationHandler)構造函數,生成$Proxy0類的對象 參數就是Proxy.newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h)它的第三個參數 這個參數就是咱們本身實現的InvocationHandler對象,咱們知道InvocationHandler對象中組合加入了代理類代理的接口類的實現類;因此,$Proxy0對象調用全部要實現的接口的方法,都會調用InvocationHandler對象的invoke()方法實現;
  4. 生成代理類的class byte動態代理生成的都是二進制class字節

CgLib實現

好了,咱們知道JDK的動態代理只能對接口進行代理,若是沒有接口怎麼辦呢? CgLib能夠爲一個類建立一個子類,在子類中採用方法攔截的技術攔截父類全部方法的調用,你能夠在這裏加入你的橫切邏輯。 仍是上面的例子,下面咱們用Cglib來實現:繼承

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 arg0, Method arg1, Object[] arg2,
			MethodProxy proxy) throws Throwable {
		 System.out.println("please show your tickes");
		return proxy.invokeSuper(arg0, arg2);
	}

	
}

而後咱們用這個CglibProxy代理類來獲取TakingTrainImpl這個業務的實現類:接口

CglibProxy cglibProxy = new CglibProxy();
	TakingTrainImpl takingTrain = (TakingTrainImpl) cglibProxy.getProxy(TakingTrainImpl.class);
	takingTrain.takeTrain("zhangsan");

上面的方法,咱們直接使用了TakingTrainImpl,沒有在使用它的接口,這就是JDK動態代理和CgLib的區別。

相關文章
相關標籤/搜索