- 相關(guān)概念
1.1 代理
??在某些情況下,我們不希望或是不能直接訪問對象 A,而是通過訪問一個中介對象 B,由 B 去訪問 A 達成目的,這種方式我們就稱為代理。
??這里對象 A 所屬類我們稱為委托類,也稱為被代理類,對象 B 所屬類稱為代理類。代理優(yōu)點有:
- 隱藏委托類的實現(xiàn)
- 解耦,不改變委托類代碼情況下做一些額外處理,比如添加初始判斷及其他公共操作
根據(jù)程序運行前代理類是否已經(jīng)存在,可以將代理分為靜態(tài)代理和動態(tài)代理。
1.2 靜態(tài)代理
??代理類在程序運行前已經(jīng)存在的代理方式稱為靜態(tài)代理。通過上面解釋可以知道,由開發(fā)人員編寫或是編譯器生成代理類的方式都屬于靜態(tài)代理,如下是簡單的靜態(tài)代理實例:
class ClassA {
public void operateMethod1() {};
public void operateMethod2() {};
public void operateMethod3() {};
}
public class ClassB {
private ClassA a;
public ClassB(ClassA a) {
this.a = a;
}
public void operateMethod1() {
a.operateMethod1();
};
public void operateMethod2() {
a.operateMethod2();
};
// not export operateMethod3()
}
上面ClassA是委托類,ClassB是代理類,ClassB中的函數(shù)都是直接調(diào)用ClassA相應(yīng)函數(shù),并且隱藏了Class的operateMethod3()函數(shù)。
??靜態(tài)代理中代理類和委托類也常常繼承同一父類或?qū)崿F(xiàn)同一接口。
1.3 動態(tài)代理
??代理類在程序運行前不存在、運行時由程序動態(tài)生成的代理方式稱為動態(tài)代理。
??Java 提供了動態(tài)代理的實現(xiàn)方式,可以在運行時刻動態(tài)生成代理類。這種代理方式的一大好處是可以方便對代理類的函數(shù)做統(tǒng)一或特殊處理,如記錄所有函數(shù)執(zhí)行時間、所有函數(shù)執(zhí)行前添加驗證判斷、對某個特殊函數(shù)進行特殊操作,而不用像靜態(tài)代理方式那樣需要修改每個函數(shù)。
- 動態(tài)代理實例
實現(xiàn)動態(tài)代理包括三步:
(1). 新建委托類;
(2). 實現(xiàn)InvocationHandler接口,這是負責連接代理類和委托類的中間類必須實現(xiàn)的接口;
(3). 通過Proxy類新建代理類對象。
下面通過實例具體介紹,假如現(xiàn)在我們想統(tǒng)計某個類所有函數(shù)的執(zhí)行時間,傳統(tǒng)的方式是在類的每個函數(shù)前打點統(tǒng)計,動態(tài)代理方式如下:
2.1 新建委托類
public interface Operate {
public void operateMethod1();
public void operateMethod2();
public void operateMethod3();
}
public class OperateImpl implements Operate {
@Override
public void operateMethod1() {
System.out.println("Invoke operateMethod1");
sleep(110);
}
@Override
public void operateMethod2() {
System.out.println("Invoke operateMethod2");
sleep(120);
}
@Override
public void operateMethod3() {
System.out.println("Invoke operateMethod3");
sleep(130);
}
private static void sleep(long millSeconds) {
try {
Thread.sleep(millSeconds);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
- Operate是一個接口,定了了一些函數(shù),我們要統(tǒng)計這些函數(shù)的執(zhí)行時間。
- OperateImpl是委托類,實現(xiàn)Operate接口。每個函數(shù)簡單輸出字符串,并等待一段時間。動態(tài)代理要求委托類必須實現(xiàn)了某個接口,比如這里委托類OperateImpl實現(xiàn)了Operate,原因會后續(xù)公布。
2.2. 實現(xiàn) InvocationHandler 接口
public class TimingInvocationHandler implements InvocationHandler {
private Object target;
public TimingInvocationHandler() {}
public TimingInvocationHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
long start = System.currentTimeMillis();
Object obj = method.invoke(target, args);
System.out.println(method.getName() + " cost time is:" + (System.currentTimeMillis() - start));
return obj;
}
}
- target屬性表示委托類對象。
- InvocationHandler是負責連接代理類和委托類的中間類必須實現(xiàn)的接口。其中只有一個
public Object invoke(Object proxy, Method method, Object[] args)
函數(shù)需要去實現(xiàn),參數(shù):
- proxy表示下面2.3 通過Proxy.newProxyInstance() 生成的代理類對象
- method表示代理對象被調(diào)用的函數(shù)。
- args表示代理對象被調(diào)用的函數(shù)的參數(shù)。
調(diào)用代理對象的每個函數(shù)實際最終都是調(diào)用了InvocationHandler的invoke函數(shù)。這里我們在invoke實現(xiàn)中添加了開始結(jié)束計時,其中還調(diào)用了委托類對象target的相應(yīng)函數(shù),這樣便完成了統(tǒng)計執(zhí)行時間的需求。invoke函數(shù)中我們也可以通過對method做一些判斷,從而對某些函數(shù)特殊處理。
2.3. 通過 Proxy 類靜態(tài)函數(shù)生成代理對象
public class Main {
public static void main(String[] args) {
// create proxy instance
TimingInvocationHandler timingInvocationHandler = new TimingInvocationHandler(new OperateImpl());
Operate operate = (Operate)(Proxy.newProxyInstance(Operate.class.getClassLoader(), new Class[] {Operate.class},
timingInvocationHandler));
// call method of proxy instance
operate.operateMethod1();
System.out.println();
operate.operateMethod2();
System.out.println();
operate.operateMethod3();
}
}
這里我們先將委托類對象new OperateImpl()作為TimingInvocationHandler構(gòu)造函數(shù)入?yún)?chuàng)建timingInvocationHandler對象;然后通過Proxy.newProxyInstance(…)函數(shù)新建了一個代理對象,實際代理類就是在這時候動態(tài)生成的。我們調(diào)用該代理對象的函數(shù)就會調(diào)用到timingInvocationHandler的invoke函數(shù)(是不是有點類似靜態(tài)代理),而invoke函數(shù)實現(xiàn)中調(diào)用委托類對象new OperateImpl()相應(yīng)的 method(是不是有點類似靜態(tài)代理)。
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
- loader表示類加載器interfaces表示委托類的接口,生成代理類時需要實現(xiàn)這些接口h是InvocationHandler實現(xiàn)類對象,負責連接代理類和委托類的中間類我們可以這樣理解,如上的動態(tài)代理實現(xiàn)實際是雙層的靜態(tài)代理,開發(fā)者提供了委托類 B,程序動態(tài)生成了代理類 A。
- 開發(fā)者還需要提供一個實現(xiàn)了InvocationHandler的子類 C,子類 C 連接代理類 A 和委托類 B,它是代理類 A 的委托類,委托類 B 的代理類。用戶直接調(diào)用代理類 A 的對象,A 將調(diào)用轉(zhuǎn)發(fā)給委托類 C,委托類 C 再將調(diào)用轉(zhuǎn)發(fā)給它的委托類 B。
- 動態(tài)代理原理
實際上面最后一段已經(jīng)說清了動態(tài)代理的真正原理。我們來仔細分析下
3.1 生成的動態(tài)代理類代碼
下面是上面示例程序運行時自動生成的動態(tài)代理類代碼,如何得到這些生成的代碼請見ProxyUtils,查看 class 文件可使用 jd-gui
import com.codekk.java.test.dynamicproxy.Operate;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
public final class $Proxy0 extends Proxy
implements Operate
{
private static Method m4;
private static Method m1;
private static Method m5;
private static Method m0;
private static Method m3;
private static Method m2;
public $Proxy0(InvocationHandler paramInvocationHandler)
throws
{
super(paramInvocationHandler);
}
public final void operateMethod1()
throws
{
try
{
h.invoke(this, m4, null);
return;
}
catch (Error|RuntimeException localError)
{
throw localError;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
public final boolean equals(Object paramObject)
throws
{
try
{
return ((Boolean)h.invoke(this, m1, new Object[] { paramObject })).booleanValue();
}
catch (Error|RuntimeException localError)
{
throw localError;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
public final void operateMethod2()
throws
{
try
{
h.invoke(this, m5, null);
return;
}
catch (Error|RuntimeException localError)
{
throw localError;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
public final int hashCode()
throws
{
try
{
return ((Integer)h.invoke(this, m0, null)).intValue();
}
catch (Error|RuntimeException localError)
{
throw localError;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
public final void operateMethod3()
throws
{
try
{
h.invoke(this, m3, null);
return;
}
catch (Error|RuntimeException localError)
{
throw localError;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
public final String toString()
throws
{
try
{
return (String)h.invoke(this, m2, null);
}
catch (Error|RuntimeException localError)
{
throw localError;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
static
{
try
{
m4 = Class.forName("com.codekk.java.test.dynamicproxy.Operate").getMethod("operateMethod1", new Class[0]);
m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
m5 = Class.forName("com.codekk.java.test.dynamicproxy.Operate").getMethod("operateMethod2", new Class[0]);
m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
m3 = Class.forName("com.codekk.java.test.dynamicproxy.Operate").getMethod("operateMethod3", new Class[0]);
m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
return;
}
catch (NoSuchMethodException localNoSuchMethodException)
{
throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
}
catch (ClassNotFoundException localClassNotFoundException)
{
throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
}
}
}
從中我們可以看出動態(tài)生成的代理類是以$Proxy為類名前綴,繼承自Proxy,并且實現(xiàn)了Proxy.newProxyInstance(…)第二個參數(shù)傳入的所有接口的類。如果代理類實現(xiàn)的接口中存在非 public 接口,則其包名為該接口的包名,否則為com.sun.proxy。其中的operateMethod1()、operateMethod2()、operateMethod3()函數(shù)都是直接交給h去處理,h在父類Proxy中定義為
protected InvocationHandler h;即為Proxy.newProxyInstance(…)第三個參數(shù)。所以InvocationHandler的子類 C 連接代理類 A 和委托類 B,它是代理類 A 的委托類,委托類 B 的代理類。
3.2. 生成動態(tài)代理類原理
以下針對 Java 1.6 源碼進行分析,動態(tài)代理類是在調(diào)用Proxy.newProxyInstance(…)函數(shù)時生成的。
(1). newProxyInstance(…)
函數(shù)代碼如下:
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
{
if (h == null) {
throw new NullPointerException();
}
/*
* Look up or generate the designated proxy class.
*/
Class cl = getProxyClass(loader, interfaces);
/*
* Invoke its constructor with the designated invocation handler.
*/
try {
Constructor cons = cl.getConstructor(constructorParams);
return (Object) cons.newInstance(new Object[] { h });
} catch (NoSuchMethodException e) {
throw new InternalError(e.toString());
} catch (IllegalAccessException e) {
throw new InternalError(e.toString());
} catch (InstantiationException e) {
throw new InternalError(e.toString());
} catch (InvocationTargetException e) {
throw new InternalError(e.toString());
}
}
從中可以看出它先調(diào)用getProxyClass(loader, interfaces)得到動態(tài)代理類,然后將InvocationHandler作為代理類構(gòu)造函數(shù)入?yún)⑿陆ù眍悓ο蟆?br>
(2). getProxyClass(…)
函數(shù)代碼及解釋如下(省略了原英文注釋):
/**
* 得到代理類,不存在則動態(tài)生成
* @param loader 代理類所屬 ClassLoader
* @param interfaces 代理類需要實現(xiàn)的接口
* @return
*/
public static Class<?> getProxyClass(ClassLoader loader,
Class<?>... interfaces)
throws IllegalArgumentException
{
if (interfaces.length > 65535) {
throw new IllegalArgumentException("interface limit exceeded");
}
// 代理類類對象
Class proxyClass = null;
/* collect interface names to use as key for proxy class cache */
String[] interfaceNames = new String[interfaces.length];
Set interfaceSet = new HashSet(); // for detecting duplicates
/**
* 入?yún)?interfaces 檢驗,包含三部分
* (1)是否在入?yún)⒅付ǖ?ClassLoader 內(nèi)
* (2)是否是 Interface
* (3)interfaces 中是否有重復(fù)
*/
for (int i = 0; i < interfaces.length; i++) {
String interfaceName = interfaces[i].getName();
Class interfaceClass = null;
try {
interfaceClass = Class.forName(interfaceName, false, loader);
} catch (ClassNotFoundException e) {
}
if (interfaceClass != interfaces[i]) {
throw new IllegalArgumentException(
interfaces[i] + " is not visible from class loader");
}
if (!interfaceClass.isInterface()) {
throw new IllegalArgumentException(
interfaceClass.getName() + " is not an interface");
}
if (interfaceSet.contains(interfaceClass)) {
throw new IllegalArgumentException(
"repeated interface: " + interfaceClass.getName());
}
interfaceSet.add(interfaceClass);
interfaceNames[i] = interfaceName;
}
// 以接口名對應(yīng)的 List 作為緩存的 key
Object key = Arrays.asList(interfaceNames);
/*
* loaderToCache 是個雙層的 Map
* 第一層 key 為 ClassLoader,第二層 key 為 上面的 List,value 為代理類的弱引用
*/
Map cache;
synchronized (loaderToCache) {
cache = (Map) loaderToCache.get(loader);
if (cache == null) {
cache = new HashMap();
loaderToCache.put(loader, cache);
}
}
/*
* 以上面的接口名對應(yīng)的 List 為 key 查找代理類,如果結(jié)果為:
* (1) 弱引用,表示代理類已經(jīng)在緩存中
* (2) pendingGenerationMarker 對象,表示代理類正在生成中,等待生成完成通知。
* (3) null 表示不在緩存中且沒有開始生成,添加標記到緩存中,繼續(xù)生成代理類
*/
synchronized (cache) {
do {
Object value = cache.get(key);
if (value instanceof Reference) {
proxyClass = (Class) ((Reference) value).get();
}
if (proxyClass != null) {
// proxy class already generated: return it
return proxyClass;
} else if (value == pendingGenerationMarker) {
// proxy class being generated: wait for it
try {
cache.wait();
} catch (InterruptedException e) {
}
continue;
} else {
cache.put(key, pendingGenerationMarker);
break;
}
} while (true);
}
try {
String proxyPkg = null; // package to define proxy class in
/*
* 如果 interfaces 中存在非 public 的接口,則所有非 public 接口必須在同一包下面,后續(xù)生成的代理類也會在該包下面
*/
for (int i = 0; i < interfaces.length; i++) {
int flags = interfaces[i].getModifiers();
if (!Modifier.isPublic(flags)) {
String name = interfaces[i].getName();
int n = name.lastIndexOf('.');
String pkg = ((n == -1) ? "" : name.substring(0, n + 1));
if (proxyPkg == null) {
proxyPkg = pkg;
} else if (!pkg.equals(proxyPkg)) {
throw new IllegalArgumentException(
"non-public interfaces from different packages");
}
}
}
if (proxyPkg == null) { // if no non-public proxy interfaces,
proxyPkg = ""; // use the unnamed package
}
{
// 得到代理類的類名,jdk 1.6 版本中缺少對這個生成類已經(jīng)存在的處理。
long num;
synchronized (nextUniqueNumberLock) {
num = nextUniqueNumber++;
}
String proxyName = proxyPkg + proxyClassNamePrefix + num;
// 動態(tài)生成代理類的字節(jié)碼
// 最終調(diào)用 sun.misc.ProxyGenerator.generateClassFile() 得到代理類相關(guān)信息寫入 DataOutputStream 實現(xiàn)
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
proxyName, interfaces);
try {
// native 層實現(xiàn),虛擬機加載代理類并返回其類對象
proxyClass = defineClass0(loader, proxyName,
proxyClassFile, 0, proxyClassFile.length);
} catch (ClassFormatError e) {
throw new IllegalArgumentException(e.toString());
}
}
// add to set of all generated proxy classes, for isProxyClass
proxyClasses.put(proxyClass, null);
} finally {
// 代理類生成成功則保存到緩存,否則從緩存中刪除,然后通知等待的調(diào)用
synchronized (cache) {
if (proxyClass != null) {
cache.put(key, new WeakReference(proxyClass));
} else {
cache.remove(key);
}
cache.notifyAll();
}
}
return proxyClass;
}
函數(shù)主要包括三部分:
入?yún)?interfaces 檢驗,包含是否在入?yún)⒅付ǖ?ClassLoader 內(nèi)、是否是 Interface、interfaces 中是否有重復(fù)以接口名對應(yīng)的 List 為 key 查找代理類,如果結(jié)果為:弱引用,表示代理類已經(jīng)在緩存中;pendingGenerationMarker 對象,表示代理類正在生成中,等待生成完成返回;null 表示不在緩存中且沒有開始生成,添加標記到緩存中,繼續(xù)生成代理類。
如果代理類不存在調(diào)用ProxyGenerator.generateProxyClass(…)
生成代理類并存入緩存,通知在等待的緩存。
函數(shù)中幾個注意的地方:
代理類的緩存 key 為接口名對應(yīng)的 List,接口順序不同表示不同的 key 即不同的代理類。
如果 interfaces 中存在非 public 的接口,則所有非 public 接口必須在同一包下面,后續(xù)生成的代理類也會在該包下面。
代理類如果在 ClassLoader 中已經(jīng)存在的情況沒有做處理??梢蚤_啟 System Properties 的sun.misc.ProxyGenerator.saveGeneratedFiles
開關(guān),保存動態(tài)類到目的地址。
Java 1.7 的實現(xiàn)略有不同,通過etProxyClass0(…)
函數(shù)實現(xiàn),實現(xiàn)中調(diào)用代理類的緩存,判斷代理類在緩存中是否已經(jīng)存在,存在直接返回,不存在則調(diào)用proxyClassCache的valueFactory屬性進行動態(tài)生成,valueFactory的apply函數(shù)與上面的getProxyClass(…)函數(shù)邏輯類似。
- 使用場景
4.1 J2EE Web 開發(fā)中 Spring 的 AOP(面向切面編程) 特性
作用:目標函數(shù)之間解耦。比如在 Dao 中,每次數(shù)據(jù)庫操作都需要開啟事務(wù),而且在操作的時候需要關(guān)注權(quán)限。一般寫法是在 Dao 的每個函數(shù)中添加相應(yīng)邏輯,造成代碼冗余,耦合度高。使用動態(tài)代理前偽代碼如下:
Dao {
insert() {
判斷是否有保存的權(quán)限;
開啟事務(wù);
插入;
提交事務(wù);
}
delete() {
判斷是否有刪除的權(quán)限;
開啟事務(wù);
刪除;
提交事務(wù);
}
}
使用動態(tài)代理的偽代碼如下:
// 使用動態(tài)代理,組合每個切面的函數(shù),而每個切面只需要關(guān)注自己的邏輯就行,達到減少代碼,松耦合的效果
invoke(Object proxy, Method method, Object[] args)
throws Throwable {
判斷是否有權(quán)限;
開啟事務(wù);
Object ob = method.invoke(dao, args);
提交事務(wù);
return ob;
}
4.2 基于 REST 的 Android 端網(wǎng)絡(luò)請求框架 Retrofit
作用:簡化網(wǎng)絡(luò)請求操作。一般情況下每個網(wǎng)絡(luò)請求我們都需要調(diào)用一次HttpURLConnection或者HttpClient進行請求,或者像 Volley 一樣丟進等待隊列中,Retrofit 極大程度簡化了這些操作,示例代碼如下:
public interface GitHubService {
@GET("/users/{user}/repos")
List<Repo> listRepos(@Path("user") String user);
}
RestAdapter restAdapter = new RestAdapter.Builder()
.setEndpoint("https://api.github.com")
.build();
GitHubService service = restAdapter.create(GitHubService.class);
以后我們只需要直接調(diào)用
List<Repo> repos = service.listRepos("octocat");
即可開始網(wǎng)絡(luò)請求,Retrofit的原理就是基于動態(tài)代理,它同時用到了 注解 的原理,本文不做深入介紹,具體請等待 Retrofit 源碼解析 完成。