日常工作記錄,java動態代理

爲什麼要用代理

案例: 我們有個接口Iservice,他有三個方法(分別爲a();接口有兩個實現ServiceAImpl和ServiceBImpl,實現的方法都簡單隻有一個輸出語句,例如:我是ServiceA中的A方法!

突然收到改變需求 調用IService接口中的任何方法的時候,需要記錄方法的耗時 此時你可能覺得只有兩個實現不多,就自己挨個的添加了;那要是改的地方多呢?還有要是這些接口都是別人提供的jar包呢?

比較好的方式:可以爲IService接口創建一個代理類,通過這個代理類來間接訪問IService接口的實現 類,在這個代理類中去做耗時及發送至監控的代碼

// IService的代理類
public class ServiceProxy implements IService {
//目標對象,被代理的對象
private IService target;
public ServiceProxy(IService target) {
this.target = target;
}
@Override
public void a() {
long starTime = System.nanoTime();
this.target.a();
long endTime = System.nanoTime();
System.out.println(this.target.getClass() + ".a()方法耗時(納秒):" +(endTime - starTime));
}

ServiceProxy是IService接口的代理類,target爲被代理的對象,即實際需要訪問的對象,也實現了 IService接口,上面的3個方法中加了統計耗時的代碼,當我們需要訪問IService的其他實現類的時候, 可以通過ServiceProxy來間接的進行訪問

@Test
public void serviceProxy() {
IService serviceA = new ServiceProxy(new ServiceA());//@1
IService serviceB = new ServiceProxy(new ServiceB()); //@2
serviceA.a();
serviceB.a();
}

上面實現中我們沒有去修改ServiceA和ServiceB中的方法,只是給IService接口創建了一個代理類,通過 代理類去訪問目標對象,需要添加的一些共有的功能都放在代理中,當領導有其他需求的時候,我們只 需修改ServiceProxy的代碼,方便系統的擴展和測試。

假如現在我們需要給系統中所有接口都加上統計耗時的功能,若按照上面的方式,我們需要給每個接口 創建一個代理類,此時代碼量和測試的工作量也是巨大的,那麼我們能不能寫一個通用的代理類,來滿 足上面的功能呢?

jdk動態代理詳解

jdk中爲實現代理提供了支持,主要用到2個類:

java.lang.reflect.Proxy
java.lang.reflect.InvocationHandler

jdk自帶的代理使用上面有個限制,只能爲接口創建代理類,如果需要給具體的類創建代理類,需要用到cglib

java.lang.reflect.Proxy

這是jdk動態代理中主要的一個類,裏面有一些靜態方法會經常用到,我們來熟悉一下:

getProxyClass方法

爲指定的接口創建代理類,返回代理類的Class對象
public static Class<?> getProxyClass(ClassLoader loader,Class<?>... interfaces)
參數說明:
loader:定義代理類的類加載器
interfaces:指定需要實現的接口列表,創建的代理默認會按順序實現interfaces指定的接口

newProxyInstance方法

創建代理類的實例對象

public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)
	
這個方法先爲指定的接口創建代理類,然後會生成代理類的一個實例,最後一個參數比較特殊,是
InvocationHandler類型的,這個是個接口如下:

public Object invoke(Object proxy, Method method, Object[] args)throws Throwable;

上面方法會返回一個代理對象,當調用代理對象的任何方法的時候,會就被 InvocationHandler 接口
的 invoke 方法處理,所以主要代碼需要卸載 invoke 方法中,稍後會有案例細說。

isProxy方法

判斷指定的類是否是一個代理類

public static boolean isProxyClass(Class<?> cl) 

getInvocationHandler方法

獲取代理對象的 InvocationHandler 對象

public static InvocationHandler getInvocationHandler(Object proxy)
throws IllegalArgumentException

代理的兩種創建方式

第一種:

  • 1.調用Proxy.getProxyClass方法獲取代理類的Class對象
  • 2.使用InvocationHandler接口創建代理類的處理器
  • 3.通過代理類和InvocationHandler創建代理對象
  • 4.上面已經創建好代理對象了,接着我們就可以使用代理對象了
@Test
public void m1() throws NoSuchMethodException, IllegalAccessException,
InvocationTargetException, InstantiationException {
// 1. 獲取接口對應的代理類
Class<IService> proxyClass = (Class<IService>)Proxy.getProxyClass(IService.class.getClassLoader(), IService.class);
// 2. 創建代理類的處理器
InvocationHandler invocationHandler = new InvocationHandler() {
	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
	System.out.println("我是InvocationHandler,被調用的方法是:" + method.getName());
	return null;
	}
};
// 3. 創建代理實例
IService proxyService =proxyClass.getConstructor(InvocationHandler.class).newInstance(invocationHandler);
// 4. 調用代理的方法
proxyService.m1();

第二種

  • 1.使用InvocationHandler接口創建代理類的處理器
  • 2.使用Proxy類的靜態方法newProxyInstance直接創建代理對象
  • 3.使用代理對象
@Test
public void m2() throws NoSuchMethodException, IllegalAccessException,InvocationTargetException, InstantiationException {
// 1. 創建代理類的處理器
InvocationHandler invocationHandler = new InvocationHandler() {
	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
	System.out.println("我是InvocationHandler,被調用的方法是:" +
	method.getName());
	return null;
	}
};
// 2. 創建代理實例
IService proxyService = (IService)Proxy.newProxyInstance(IService.class.getClassLoader(), new Class[]{IService.class}, invocationHandler);
// 3. 調用代理的方法
proxyService.m1();
}

案例:任意接口中的方法耗時統計

下面我們通過jdk動態代理實現一個通用的代理,解決統計所有接口方法耗時的問題。 主要的代碼在代理處理器 InvocationHandler 實現上面; 上面主要是 createProxy 方法用來創建代理對象,2個參數: target:目標對象,需要實現targetInterface接口 targetInterface:需要創建代理的接口 invoke方法中通過 method.invoke(this.target, args) 調用目標方法,然後統計方法的耗時。

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class CostTimeInvocationHandler implements InvocationHandler {
	private Object target;
public CostTimeInvocationHandler(Object target) {
	this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws
Throwable {
	long starTime = System.nanoTime();
	Object result = method.invoke(this.target, args);//@1
	long endTime = System.nanoTime();
	System.out.println(this.target.getClass() +"》》"+ method.getName() +"方法耗時(納秒):" +(endTime - starTime));
	return result;
}
/**
* 用來創建targetInterface接口的代理對象
*
* @param target 需要被代理的對象
* @param targetInterface 被代理的接口
* @param <T>
* @return
*/
public static <T> T createProxy(Object target, Class<T> targetInterface) {
	if (!targetInterface.isInterface()) {
		throw new IllegalStateException("targetInterface必須是接口類型!");
	} else if (!targetInterface.isAssignableFrom(target.getClass())) {
		throw new IllegalStateException("target必須是targetInterface接口的實現類!");
	}
	return (T) Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(), new CostTimeInvocationHandler(target));
}
}

Proxy使用注意

  1. jdk中的Proxy只能爲接口生成代理類,如果你想給某個類創建代理類,那麼Proxy是無能爲力的,此時需要我們用到下面要說的cglib了。
  2. Proxy類中提供的幾個常用的靜態方法大家需要掌握
  3. 通過Proxy創建代理對象,當調用代理對象任意方法時候,會被InvocationHandler接口中的invoke方法進行處理,這個接口內容是關鍵
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章