代理模式,做爲經常使用的設計模式之一,在實際項目中或多或少都會被使用到。當前一些主流的項目框架中,也有很多代理模式的身影。java
代理模式中,代理類與主體類實現一樣的接口,代理類持有實體類的引用,並接受客戶端對代理類中實體引用的外部注入,並代理實體類的功能。
注:描述中的這種外部注入形式有個專有技術名詞:依賴注入設計模式
代理模式通用的類圖爲:安全
按照代理類的產生方式,是在運行期以前就靜態的存在仍是在運行期動態產生,能夠將代理模式分爲靜態代理和動態代理。bash
在真正理解動態代理以前,有必要先簡單回顧下靜態代理的通常過程。多線程
直接看一個具體的實例。
1,定義接口:app
package com.corn.proxy.pstatic;
public interface Subject {
String action();
}
複製代碼
2,定義主體類:框架
package com.corn.proxy.pstatic;
public class RealSubject implements Subject {
@Override
public String action() {
System.out.println("action in RealSubject");
return "action done";
}
}
複製代碼
3,定義靜態代理類:ide
package com.corn.proxy.pstatic;
public class ProxySubject implements Subject{
private Subject realSubject;
public ProxySubject(Subject realSubject) {
this.realSubject = realSubject;
}
@Override
public String action() {
// 主體action前執行
System.out.println("do sth before RealSubject action");
String result = this.realSubject.action();
// 主體action後執行
System.out.println("do sth after RealSubject action");
return result;
}
}
複製代碼
4,客戶端注入實體並訪問:工具
package com.corn.proxy.pstatic;
public class Client {
public static void main(String[] args) {
Subject realSubject = new RealSubject();
ProxySubject proxySubject = new ProxySubject(realSubject);
proxySubject.action();
}
}
複製代碼
run,輸出結果:性能
do sth before RealSubject action
action in RealSubject
do sth after RealSubject action
複製代碼
上述代碼過程亦對應了代理模式類圖結構。
本質上,動態代理也是遵循上述通用的代理模式類圖關係,與靜態代理相比,其動態
主要體如今: 1,具體代理類(ProxySubject)的生成是在運行期動態產生的,而非編譯期就已經靜態存在;
2,具體代理類(ProxySubject)與被代理類的代理關係(ProxySubject持有RealSubject的引用),是想辦法動態注入進入的;
3,具體代理類(ProxySubject)對被代理類的功能的代理是在動態生成的代理類內部,想辦法去動態的調用被代理類的對應方法的。
不管是具體代理類的動態產生,仍是與被代理類的關係創建,以及對被代理類方法的代理調用,這中間,都用到了兩個關鍵的中間媒介,即Proxy和InvocationHandler。
Proxy類,其中提供了動態生成代理類的靜態方法,並持有實現了InvocationHandler接口的引用。同時,全部生成的代理類也都是Proxy類的子類。
InvocationHandler接口,只包含一個抽象出來的方法名:invoke,使得實現InvocationHandler接口的類去具體實現,在實現中經過持有被代理類實體(RealSubject),並經過反射,去調用對應的實體方法。
所以,動態代理整體上的執行流程爲:
當客戶端經過Proxy的靜態方法生成動態代理類後,調用動態代理類對應的接口方法時,內部會調用其內部持有的InvocationHandler接口的實例對象的invoke方法,並得以調用到實際被代理實體的相應方法。
將整體的類之間關係若是用類圖表示,與通用的代理模式類圖稍有區別。
相應代碼實現代碼過程以下:
1,定義接口Subject:
package com.corn.proxy.pdynamic;
public interface Subject {
String action();
}
複製代碼
2,定義主體類:
package com.corn.proxy.pdynamic;
public class RealSubject implements Subject{
@Override
public String action() {
System.out.println("action in RealSubject");
return "action done";
}
}
複製代碼
3,定義實現了InvocationHandler接口的類:
package com.corn.proxy.pdynamic;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class ProxyInvocationHandler implements InvocationHandler {
protected Subject subject;
public ProxyInvocationHandler(Subject subject) {
this.subject = subject;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("do something before in ProxyInvocationHandler");
return method.invoke(subject, args);
}
}
複製代碼
4,客戶端注入實體並訪問:
package com.corn.proxy.pdynamic;
import java.lang.reflect.Proxy;
public class Client {
public static void main(String[] args) {
Subject realSubject = new RealSubject();
ProxyInvocationHandler proxyInvocationHandler = new ProxyInvocationHandler(realSubject);
Subject proxyRealSubject = (Subject) Proxy.newProxyInstance(realSubject.getClass().getClassLoader(),
realSubject.getClass().getInterfaces(), proxyInvocationHandler);
proxyRealSubject.action();
}
}
複製代碼
run,輸出結果:
do something before in ProxyInvocationHandler
action in RealSubject
複製代碼
從上述動態代理執行流程和類圖分析中已經能夠看出,動態代理的關鍵,在於經過InvocationHandler和Proxy媒介,在運行時動態生成動態代理類,生成的動態代理類依然實現了Subject接口,並在調用方法時回調了InvocationHandler實現類的invoke方法,InvocationHandler實現類的invoke方法經過反射,回調了被代理實體的對應方法。
看起來有點繞。
在生成動態代理過程當中,Java工程和Android項目中有點區別,先從源碼角度看下Java工程的具體實現過程。
Android studio中不能直接新建Java工程,但能夠在Android項目中經過創建Java Library的方式,創建Java庫。默認狀況下,Android Studio關聯的JDK爲AS內置的,是看不到源碼的,爲此,須要將其改爲本身下載的JDK路徑,此路中包含了源碼src目錄。 具體修改方式爲:
1,查看本地JDK目錄
/usr/libexec/java_home -V
複製代碼
輸出
Matching Java Virtual Machines (1):
1.8.0_162, x86_64: "Java SE 8" /Library/Java/JavaVirtualMachines/jdk1.8.0_162.jdk/Contents/Home
/Library/Java/JavaVirtualMachines/jdk1.8.0_162.jdk/Contents/Home
複製代碼
2,Android Studio替換JDK設置 File >> Other Setting >> Default Project Structure,在JDK Location中將默認設置
/Applications/Android Studio.app/Contents/jre/jdk/Contents/Home
複製代碼
修改成:
/Library/Java/JavaVirtualMachines/jdk1.8.0_162.jdk/Contents/Home
複製代碼
完成設置後,能夠直接看到JDK源碼。
ProxyInvocationHandler接口只有一個方法:
public interface InvocationHandler {
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
}
複製代碼
下面重點看下Proxy類newProxyInstance過程。
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
{
Objects.requireNonNull(h);
final Class<?>[] intfs = interfaces.clone();
final SecurityManager sm = System.getSecurityManager();
if (sm != null) {
checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
}
/*
* Look up or generate the designated proxy class.
*/
Class<?> cl = getProxyClass0(loader, intfs);
/*
* Invoke its constructor with the designated invocation handler.
*/
try {
if (sm != null) {
checkNewProxyPermission(Reflection.getCallerClass(), cl);
}
final Constructor<?> cons = cl.getConstructor(constructorParams);
final InvocationHandler ih = h;
if (!Modifier.isPublic(cl.getModifiers())) {
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
cons.setAccessible(true);
return null;
}
});
}
return cons.newInstance(new Object[]{h});
} catch (IllegalAccessException|InstantiationException e) {
throw new InternalError(e.toString(), e);
} catch (InvocationTargetException e) {
Throwable t = e.getCause();
if (t instanceof RuntimeException) {
throw (RuntimeException) t;
} else {
throw new InternalError(t.toString(), t);
}
} catch (NoSuchMethodException e) {
throw new InternalError(e.toString(), e);
}
}
複製代碼
其中調用的getProxyClass0對應的具體實現爲:
/**
* Generate a proxy class. Must call the checkProxyAccess method
* to perform permission checks before calling this.
*/
private static Class<?> getProxyClass0(ClassLoader loader,
Class<?>... interfaces) {
if (interfaces.length > 65535) {
throw new IllegalArgumentException("interface limit exceeded");
}
// If the proxy class defined by the given loader implementing
// the given interfaces exists, this will simply return the cached copy;
// otherwise, it will create the proxy class via the ProxyClassFactory
return proxyClassCache.get(loader, interfaces);
}
複製代碼
因而,咱們跟到對應的ProxyClassFactory:
/**
* A factory function that generates, defines and returns the proxy class given
* the ClassLoader and array of interfaces.
*/
private static final class ProxyClassFactory
implements BiFunction<ClassLoader, Class<?>[], Class<?>>
{
// prefix for all proxy class names
private static final String proxyClassNamePrefix = "$Proxy";
// next number to use for generation of unique proxy class names
private static final AtomicLong nextUniqueNumber = new AtomicLong();
@Override
public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {
Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);
for (Class<?> intf : interfaces) {
/*
* Verify that the class loader resolves the name of this
* interface to the same Class object.
*/
Class<?> interfaceClass = null;
try {
interfaceClass = Class.forName(intf.getName(), false, loader);
} catch (ClassNotFoundException e) {
}
if (interfaceClass != intf) {
throw new IllegalArgumentException(
intf + " is not visible from class loader");
}
/*
* Verify that the Class object actually represents an
* interface.
*/
if (!interfaceClass.isInterface()) {
throw new IllegalArgumentException(
interfaceClass.getName() + " is not an interface");
}
/*
* Verify that this interface is not a duplicate.
*/
if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) {
throw new IllegalArgumentException(
"repeated interface: " + interfaceClass.getName());
}
}
String proxyPkg = null; // package to define proxy class in
int accessFlags = Modifier.PUBLIC | Modifier.FINAL;
/*
* Record the package of a non-public proxy interface so that the
* proxy class will be defined in the same package. Verify that
* all non-public proxy interfaces are in the same package.
*/
for (Class<?> intf : interfaces) {
int flags = intf.getModifiers();
if (!Modifier.isPublic(flags)) {
accessFlags = Modifier.FINAL;
String name = intf.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, use com.sun.proxy package
proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
}
/*
* Choose a name for the proxy class to generate.
*/
long num = nextUniqueNumber.getAndIncrement();
String proxyName = proxyPkg + proxyClassNamePrefix + num;
/*
* Generate the specified proxy class.
*/
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
proxyName, interfaces, accessFlags);
try {
return defineClass0(loader, proxyName,
proxyClassFile, 0, proxyClassFile.length);
} catch (ClassFormatError e) {
/*
* A ClassFormatError here means that (barring bugs in the
* proxy class generation code) there was some other
* invalid aspect of the arguments supplied to the proxy
* class creation (such as virtual machine limitations
* exceeded).
*/
throw new IllegalArgumentException(e.toString());
}
}
}
複製代碼
其中,關鍵的過程在於:
/*
* Generate the specified proxy class.
*/
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
proxyName, interfaces, accessFlags);
try {
return defineClass0(loader, proxyName,
proxyClassFile, 0, proxyClassFile.length);
} catch (ClassFormatError e) {
...
}
複製代碼
經過ProxyGenerator類中的generateProxyClass方法生成對應的動態代理類二進制代碼,並經過 ClassLoader加載後,經過反射,生成對應的類實例。
ProxyGenerator類是直接集成在rt.jar中的,包名爲sun.misc,爲擴展類。Android項目中因JDK版本問題,默認是不集成的,那Android中此處是如何實現的呢?
一樣的,在Android項目中直接使用動態代理,跟蹤源碼調用過程。
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
{
Objects.requireNonNull(h);
final Class<?>[] intfs = interfaces.clone();
// Android-removed: SecurityManager calls
/*
final SecurityManager sm = System.getSecurityManager();
if (sm != null) {
checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
}
*/
/*
* Look up or generate the designated proxy class.
*/
Class<?> cl = getProxyClass0(loader, intfs);
/*
* Invoke its constructor with the designated invocation handler.
*/
try {
// Android-removed: SecurityManager / permission checks.
/*
if (sm != null) {
checkNewProxyPermission(Reflection.getCallerClass(), cl);
}
*/
final Constructor<?> cons = cl.getConstructor(constructorParams);
final InvocationHandler ih = h;
if (!Modifier.isPublic(cl.getModifiers())) {
// BEGIN Android-changed: Excluded AccessController.doPrivileged call.
/*
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
cons.setAccessible(true);
return null;
}
});
*/
cons.setAccessible(true);
// END Android-removed: Excluded AccessController.doPrivileged call.
}
return cons.newInstance(new Object[]{h});
} catch (IllegalAccessException|InstantiationException e) {
throw new InternalError(e.toString(), e);
} catch (InvocationTargetException e) {
Throwable t = e.getCause();
if (t instanceof RuntimeException) {
throw (RuntimeException) t;
} else {
throw new InternalError(t.toString(), t);
}
} catch (NoSuchMethodException e) {
throw new InternalError(e.toString(), e);
}
}
複製代碼
很明顯,Android JDK中newProxyInstance方法中部分實現作了修改。但主要執行路徑依然不變。
/**
* Generate a proxy class. Must call the checkProxyAccess method
* to perform permission checks before calling this.
*/
private static Class<?> getProxyClass0(ClassLoader loader,
Class<?>... interfaces) {
if (interfaces.length > 65535) {
throw new IllegalArgumentException("interface limit exceeded");
}
// If the proxy class defined by the given loader implementing
// the given interfaces exists, this will simply return the cached copy;
// otherwise, it will create the proxy class via the ProxyClassFactory
return proxyClassCache.get(loader, interfaces);
}
複製代碼
一樣的,ProxyClassFactory類。
/**
* A factory function that generates, defines and returns the proxy class given
* the ClassLoader and array of interfaces.
*/
private static final class ProxyClassFactory
implements BiFunction<ClassLoader, Class<?>[], Class<?>>
{
// prefix for all proxy class names
private static final String proxyClassNamePrefix = "$Proxy";
// next number to use for generation of unique proxy class names
private static final AtomicLong nextUniqueNumber = new AtomicLong();
@Override
public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {
Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);
for (Class<?> intf : interfaces) {
/*
* Verify that the class loader resolves the name of this
* interface to the same Class object.
*/
Class<?> interfaceClass = null;
try {
interfaceClass = Class.forName(intf.getName(), false, loader);
} catch (ClassNotFoundException e) {
}
if (interfaceClass != intf) {
throw new IllegalArgumentException(
intf + " is not visible from class loader");
}
/*
* Verify that the Class object actually represents an
* interface.
*/
if (!interfaceClass.isInterface()) {
throw new IllegalArgumentException(
interfaceClass.getName() + " is not an interface");
}
/*
* Verify that this interface is not a duplicate.
*/
if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) {
throw new IllegalArgumentException(
"repeated interface: " + interfaceClass.getName());
}
}
String proxyPkg = null; // package to define proxy class in
int accessFlags = Modifier.PUBLIC | Modifier.FINAL;
/*
* Record the package of a non-public proxy interface so that the
* proxy class will be defined in the same package. Verify that
* all non-public proxy interfaces are in the same package.
*/
for (Class<?> intf : interfaces) {
int flags = intf.getModifiers();
if (!Modifier.isPublic(flags)) {
accessFlags = Modifier.FINAL;
String name = intf.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, use the default package.
proxyPkg = "";
}
{
// Android-changed: Generate the proxy directly instead of calling
// through to ProxyGenerator.
List<Method> methods = getMethods(interfaces);
Collections.sort(methods, ORDER_BY_SIGNATURE_AND_SUBTYPE);
validateReturnTypes(methods);
List<Class<?>[]> exceptions = deduplicateAndGetExceptions(methods);
Method[] methodsArray = methods.toArray(new Method[methods.size()]);
Class<?>[][] exceptionsArray = exceptions.toArray(new Class<?>[exceptions.size()][]);
/*
* Choose a name for the proxy class to generate.
*/
long num = nextUniqueNumber.getAndIncrement();
String proxyName = proxyPkg + proxyClassNamePrefix + num;
return generateProxy(proxyName, interfaces, loader, methodsArray,
exceptionsArray);
}
}
}
複製代碼
其中關鍵的,咱們發現
// Android-changed: Generate the proxy directly instead of calling
// through to ProxyGenerator.
...
return generateProxy(proxyName, interfaces, loader, methodsArray,
exceptionsArray);
複製代碼
跟蹤進去看下具體的執行過程
@FastNative
private static native Class<?> generateProxy(String name, Class<?>[] interfaces,
ClassLoader loader, Method[] methods,
Class<?>[][] exceptions);
// END Android-changed: How proxies are generated.
複製代碼
原來,Android JDK中對generateProxy進行了處理,直接使用的是本地的方法。
那麼,生成動態代理時,若是遇到線程安全問題呢?
ProxyClassFactory代碼中,有以下處理過程:
private static final class ProxyClassFactory
implements BiFunction<ClassLoader, Class<?>[], Class<?>>
{
// prefix for all proxy class names
private static final String proxyClassNamePrefix = "$Proxy";
// next number to use for generation of unique proxy class names
private static final AtomicLong nextUniqueNumber = new AtomicLong();
...
/*
* Choose a name for the proxy class to generate.
*/
long num = nextUniqueNumber.getAndIncrement();
String proxyName = proxyPkg + proxyClassNamePrefix + num;
/*
* Generate the specified proxy class.
*/
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
proxyName, interfaces, accessFlags);
try {
return defineClass0(loader, proxyName,
proxyClassFile, 0, proxyClassFile.length);
} catch (ClassFormatError e) {
...
}
}
複製代碼
顯然,經過線程安全的文件命名方式,預防了針對一樣的接口使用多線程狀況下,使用動態代理可能出現的線程安全問題。
那麼,生成的動態代理到底長什麼樣子?咱們能夠直接調用系統的ProxyGenerator.generateProxyClass試一下。
package com.corn.proxy.pdynamic;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.reflect.Proxy;
import sun.misc.ProxyGenerator;
public class Client {
public static void main(String[] args) {
Subject realSubject = new RealSubject();
ProxyInvocationHandler proxyInvocationHandler = new ProxyInvocationHandler(realSubject);
Subject proxyRealSubject = (Subject) Proxy.newProxyInstance(realSubject.getClass().getClassLoader(),
realSubject.getClass().getInterfaces(), proxyInvocationHandler);
proxyRealSubject.action();
String proxyName = "ProxySubject";
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
proxyName, RealSubject.class.getInterfaces());
try (FileOutputStream fos = new FileOutputStream("/Users/corn/T/ProxySubject.class")){
fos.write(proxyClassFile);
fos.flush();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
複製代碼
run,對應目錄下生成ProxySubject.class文件,經過工具能夠看到對應的字節碼內容。
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
import com.corn.proxy.pdynamic.Subject;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
public final class ProxySubject extends Proxy implements Subject {
private static Method m1;
private static Method m2;
private static Method m0;
private static Method m3;
public ProxySubject(InvocationHandler var1) throws {
super(var1);
}
public final boolean equals(Object var1) throws {
try {
return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
}
public final String toString() throws {
try {
return (String)super.h.invoke(this, m2, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final int hashCode() throws {
try {
return (Integer)super.h.invoke(this, m0, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final String action() throws {
try {
return (String)super.h.invoke(this, m3, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
static {
try {
m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
m2 = Class.forName("java.lang.Object").getMethod("toString");
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
m3 = Class.forName("com.corn.proxy.pdynamic.Subject").getMethod("action");
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
}
複製代碼
果真,在生成的動態代理中,實現了Subject接口,並在對應的action方法中,調用了InvocationHandler實現類的實例的invoke方法。
動態代理做爲代理模式中的一種,避免了在運行期以前直接定義靜態代理類,對於須要大量的代理類的情形下,是很是有用的。同時,咱們也應該看到,在總體流程上,其實動態代理與靜態代理整體上仍是同樣的。動態代理不管在代理類的建立過程當中,仍是對代理方法的調用,過程當中都用到了反射,在必定程度上性能上有所損耗,實際使用中須要適量權衡。
動態代理模式,做爲設計模式中相對比較難理解的一種,主要在於其過程通過了層層封裝,最後只是經過Proxy類和InvocationHandler對外直接暴露了使用接口,對使用方直接屏蔽了具體的細節。但對於咱們理解這種模式自己來講,瞭解並適當掌握其中的過程,仍是有所受益的。