本文已參與好文召集令活動,點擊查看:後端、大前端雙賽道投稿,2萬元獎池等你挑戰!前端
經過代理層這一中間層,有效的控制對於真實委託類對象的直接訪問,同時又能夠實現自定義的控制策略,好比Spring中的AOP機制,這樣使得在設計上得到更大的靈活性java
// 該方法用於獲取指定代理對象所關聯的調用處理器
public static InvocationHandler getInvocationHandler(Object proxy);
// 該方法用於獲取關聯於指定類裝載器和一組接口的動態代理類的類對象
public static Class<?> getProxyClass(ClassLoader loader, Class<?>... interfaces);
// 該方法用於判斷指定類對象是不是一個動態代理類
public static boolean isProxyClass(Class<?> cl);
// 該方法用於爲指定類裝載器,一組接口以及調用處理器生成動態代理類實例
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h);
複製代碼
/** * 該方法負責集中處理動態代理類上的全部方法調用 * * @param proxy 代理實例 * @param method 被調用的方法對象 * @param args 調用參數 * @return 返回調用處理器根據三個參數進行預處理或者分派到委託類實例上反射執行的對象 */
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
複製代碼
Java動態代理建立對象的過程:後端
/* * InvocationHandlerImpl實現了InvocationHandler接口 * 並能實現方法調用從代理類到委託類的分派轉發向委託類實例的引用,用於真正執行分派轉發過來的方法調用 */
InvocationHandler handler = new InvocationHandlerImpl(...);
複製代碼
// 經過Proxy爲包括Interface接口在內的一組接口動態建立代理類的類對象
Class clazz = Proxy.getProxyClass(classLoader, new Class[] { Interface.class, ... });
複製代碼
// 經過反射從生成的類對象得到構造函數對象
Constructor constructor = clazz.getConstructor(new Class[] { InvocationHandler.class });
複製代碼
// 經過構造函數建立動態代理類實例
Interface proxy = (Interface)constructor.newInstance(new Object[] { handler });
複製代碼
爲了簡化對象建立過程 ,Proxy類中使用newInstanceProxy封裝了步驟2-步驟4, 所以只須要兩個步驟便可完成代理對象的建立設計模式
// InvocationHandlerImpl實現了InvocationHandler接口,並能實現方法調用從代理類到委託類的分派轉發
InvocationHandler handler = new InvocationHandlerImpl(...);
// 經過 Proxy 直接建立動態代理類的實例
Interface proxy = (Interface)Proxy.newProxyInstance(classLoader, new Class[] { Interface.class }, handler);
複製代碼
/* * 記錄非公共代理接口的包,以便在同一個包中定義代理類 * 驗證全部非公共代理接口是否都在同一個包中 */
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 packaes");
}
}
}
if (proxyPkg == null) {
// 沒有使用非公共代理接口代理類的包
proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
}
複製代碼
synchronized (cache) {
/* * 沒必要擔憂獲取到清除掉弱引用的緩存 * 由於若是一個代理類已經被垃圾回收,代理類的類加載器也會被垃圾回收 * 因此獲取到的緩存都是加載到緩存中的映射 */
do {
Object value = cache.get(key);
if (value instanceof Reference) {
proxyClass = (Class) ((Reference) value).get();
if (proxyClass != null) {
/* * 代理類已經生成,返回代理類 */
return proxyClass;
} else if (value == pendingGenerationmarker) {
/* * 代理類正在生成,等待代理類生成 */
try {
cache.wait();
} catch (InterruptedException e) {
/* * 等待生成的代理類有一個極小的限定的時間,所以能夠忽略線程在這裏的影響 */
}
continue;
} else {
/* * 若是沒有這個接口列表已經生成或者正在生成的代理類 * 須要去生成這些接口的代理類,將這些接口標記爲待生成 */
cache.put(key, pendingGenerationMarker);
break;
}
}while (true);
}
複製代碼
Proxy類是父類,這個規則適用於全部由Proxy建立的動態代理類(這也致使Java動態代理的缺陷,因爲Java不支持多繼承,因此沒法實現對class的動態代理,只能對於Interface進行代理),該類實現了全部代理的一組接口,因此Proxy類可以被安全地類型轉換到其所代理的某接口數組
public class serviceProxy implements InvocationHandler {
private Object target;
/** * 綁定委託對象並返回一個代理對象 * @param target 真實對象 * @return 代理對象 */
public Object bind(Object target, Class[] interfaces) {
this.target = target;
// 取得代理對象
return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
}
/** * 經過代理對象調用方法首先進入這個方法 * @param proxy 代理對象 * @param Method 方法,被調用方法 * @param args 方法的參數 */
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
/* * JDK動態代理 */
Object result = null;
// 反射方法前調用
System.err.println("--反射方法前執行的方法--");
// 反射執行方法,至關於調用target.xXX()
result = method.invoke(target, args);
// 反射方法後調用
System.err.println("--反射方法後執行的方法--");
return result;
}
}
複製代碼
public class ProxyTest {
public static void main(String[] args) {
HelloServiceProxy proxy = new HelloServiceProxy();
HelloService service = new HelloServiceImpl();
// 綁定代理對象
service = (HelloService) proxy.bind(service, new Class[] {HelloService.class});
service.sayHello("user");
}
}
複製代碼
// 映射表: 用於維護類裝載器對象到其對應的代理類緩存
private static Map loaderToCache = new WeakHashMap();
// 標記: 用於標記一個動態代理類正在被建立中
private static Object pendingGenerationMarker = new Object();
// 同步表: 記錄已經被建立的動態代理類類型,主要經過方法isProxyClass進行相關判斷
private static Map proxyClasses = Collections.synchronizedMap(new WeakHashMap());
// 關聯的調用處理器引用
protected InvocationHandler h;
複製代碼
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException {
/* * 檢查關聯調用處理器是否爲空,若是是空則拋出異常 */
if (h == null) {
throw new NullPointerException();
}
/* * 得到與指定類型裝載器和一組接口相關的代理類類型對象 */
Class<?> cl = getProxyClass0(loader, interfaces);
/* * 經過反射獲取構造函數對象並生成代理類實例 */
try {
final Constructor<?> cons = cl.getConstructor(constructorParams);
final InvocationHandler ih = h;
SecurityManager sm = System.getSecurityManager();
if (sm != null && ProxyAccessHelper.needsNewInstanceCheck(cl)) {
/* * 使用doPrivilege建立動態代理類實例 * 由於代理類實現可能須要特殊權限的非公共接口 */
return AccessController.doPrivileged(new PrivilegedAction<Object>() {
public Object run() {
return newInstance(cons, ih);
}
});
} else {
return newInstance(cons, ih);
}
} catch (NoSuchMethodException e) {
throw new InternalError(e.toString());
}
}
private static Object newInstance(Constructor<?> cons, InvocationHandler h) {
try {
return cons.newInstance(new Object[] {h});
} catch (IllegalAccessException e) {
throw new InternalError(e.toString());
} catch (InstantationException e) {
throw new InternalException(e.toString());
} catch (InvocationTargetException e) {
Throwable t = e.getCause();
if (t instanceof RuntimeException) {
throw (RuntimeException) t;
} else {
throw new InternalException(e.toString());
}
}
}
複製代碼
getProxyClass0() 方法分爲四個步驟:緩存
1.1 接口類對象是否對類裝載器可見 1.2 接口類對象與類裝載器所識別的接口類對象徹底相同 1.3 確保是interface類型而不是class類型.安全
for (int i = 0; i < interfaces.length; i++ ) {
/* * 驗證類加載器是否將此接口的名稱解析爲相同的類對象 */
String interfaceName = interface[i].getName();
Class interfaceClass = null;
try {
/* * forName(String name, boolean initialize, ClassLoader loader) * Returns the Class object associated with the class or interface with the given string name, * using the given class loader */
interfaceClass = Class.forName(interfaceName, false, loader);
} catch (ClassNotFoundException e) {
}
if (interfaceClass != interface[i]) {
throw new IllegalArgumentException(interface[i] + "is not visible from class loader.");
}
/* * 驗證類加載器獲得的類對象是不是interface類型 */
if (! interfaceClass.isInterface()) {
throw new IllegalArgumentException(interfaceClass.getName() + "is not an interface.");
}
/* * 驗證類加載器獲得的類對象接口不是一個重複的接口 */
if (interfaceSet.contains(interfaceClass)) {
throw new IllegalArgumentException("repeated interface:" + interface.getName());
}
interfaceSet.add(interfaceClass);
interfaceName[i] = interfaceName;
}
複製代碼
2.1 loaderToCache存放鍵值對 : 接口名字列表:動態生成的代理類的類對象的引用 2.2 當代理類正在被建立時,會臨時進行保存 : 接口名字列表:pendingGenerationMarker 2.3 標記pendingGenerationMarker的做用是通知後續的同類請求(接口數組相同且組內接口排列順序也相同)代理類正在被建立,請保持等待直至建立完成服務器
/* * 尋找類加載器的緩存表,若是沒有就爲類加載器建立代理類緩存 */
Map cache;
synchronized (loaderToCache) {
cache = (Map) loaderToCache.get(loader);
if (cache == null) {
cache = new HashMap();
loaderToCache = put(loader, cache);
}
}
do {
/* * 以接口名字做爲關鍵字得到對應的cache值 */
Object value = cache.get(key);
if (value instanceof Reference) {
proxyClass = (Class)((Reference)value).get();
}
if (proxyClass != null) {
// 若是已經建立,直接返回
return proxyClass;
} else if (value == pendingGenerationMarker) {
// 代理類正在建立,保持等待
try {
cache.wait()
} catch (InterruptException e) {
}
// 等待被喚醒,繼續循環並經過二次檢查以確保建立完成,不然從新等待
continue;
} else {
// 標記代理類正在被建立
cache.put(key, pendingGenerationMarker);
// 跳出循環已進入建立過程
break;
}
} while(true)
複製代碼
/* * 選擇一個名字代理類來生成 */
long num;
synchronized (nextUniqueNumberLock) {
num = nextUniqueNumber ++;
}
String proxyName = proxyPkg + proxyClassNamePrefix + num;
/* * 驗證類加載器中沒有使用這個名字定義的類 */
...
// 動態生成代理類的字節碼數組
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces);
try {
// 動態地定義新生成的代理類
proxyClass = defineClass0(loader, proxyName, proxyClassFile, 0, proxyClassFile.length);
} catch (ClassFormatError e) {
/* * 這裏的類格式錯誤指的是生代理類代碼中的錯誤 * 還有一些應用到代理類生成的參數的錯誤,好比一些虛擬機限制的超量 */
throw new IllegalArgumentException(e.toString());
}
// 將生成的代理類對象記錄到proxyClasses中
proxyClasses.put(proxyClass, null);
複製代碼
首先根據接口public與否的規則生成代理類的名稱 - $ProxyN格式,而後動態生成代理類. 全部的代碼生成工做由ProxyGenerator完成,該類在rt.jar中,須要進行反編譯markdown
public static byte[] generateProxyClass(final String name, Class[] interfaces) {
ProxyGenerator gen = new ProxyGenerator(name, interfaces);
// 動態生成代理類的字節碼
final byte[] classFile = gen.generateClassFile();
// 若是saveGeneratedFiles的值爲true,則會把所生成的代理類的字節碼保存到硬盤上
if (saveGeneratedFiles) {
java.security.AccessController.doPrivileged(
new java.security.PrivilegedAction<Void>() {
public Void run() {
try{
FileOutputStream file = new FileOutputStream(doToSlash(name) + ".class");
file.write(classFile);
file.close();
return null;
} catch (IOException e) {
throw new InternalError("I/O exception saving generated file :" + e);
}
}
}
);
}
// 返回代理類的字節碼
return classFile;
}
複製代碼
finally {
synchronized (cache) {
if (proxyClass != null) {
cache.put(key, new WeakReference(proxyClass));
} else {
cache.remove(key);
}
cache.notifyAll();
}
}
return proxyClass;
複製代碼
- JDK動態代理實例:
- 定義兩個接口Vehicle和Rechargeable
- Vehicle接口表示交通工具類,有drive()方法
- Rechargeable接口表示可充電,有recharge()方法
- 定義一個實現兩個接口的類ElectricCar,類圖以下:
- 建立ElectricCar的動態代理類:
/** * 交通工具接口 */ public interface Vehicle { public void drive(); } 複製代碼
/** * 充電接口 */ public interface Rechargable { public void recharge(); } 複製代碼
/** * 電動車類 * 實現Rechargable, Vehicle接口 */ public class ElectricCar implements Rechargable, Vehicle { @Override public void drive() { System.out.println("ElectricCar can drive."); } @Override public void recharge() { System.out.println("ElectricCar can recharge."); } } 複製代碼
/** * 動態代理類的觸發器 */ public class InvocationHandlerImpl implements InvocationHandler { private ElectricCar car; public InvocationHandlerImpl(Electric car) { this.car = car; } @Override public Object invoke(Object paramObject, Method paramMethod, Object[] paramArrayOfObject) throws Throwable { System.out.println("正在調用方法:" + paramMethod.getName() + "..."); paramMethod.invoke(car, null); System.out.println("方法" + paramMethod.getName() + "調用結束."); return null; } } 複製代碼
public class ProxyTest { public static void main(String[] args) { ElectricCar car = new ElectricCar(); // 獲取對應的ClassLoader ClassLoader classLoader = car.getClass().getClassLoader(); // 獲取ElectricCar所實現的全部接口 Class[] interfaces = car.getClass().getInterfaces(); // 設置一個來自代理傳過來的方法調用請求處理器,處理全部的代理對象上的方法調用 InvocationHandler handler = new InvocationHandlerImpl(car); /* * 建立代理對象在這個過程當中: * a. JDK根據傳入的參數信息動態地在內存中建立和 .class 等同的字節碼 * b. 根據相應的字節碼轉換成對應的class * c. 而後調用newInstance()建立實例 */ Object o = Proxy.newProxyInstance(classLoader, interfaces, handler); Vehicle vehicle = (Vehicle) o; vehicle.drive(); Rechargable rechargable = (Rechargable) o; rechargable.recharge(); } } 複製代碼
- JDK提供了sun.misc.ProxyGenerator.generateProxyClass(String proxyName, calss[] interfaces) 底層方法來產生動態代理類的字節碼
- 定義一個工具類,用來將生成的動態代理類保存到硬盤中:
public class proxyUtils { /* * 根據類信息,動態生成二進制字節碼保存到硬盤中 * 默認是clazz目錄下 * * @params clazz 須要生成動態代理類的類 * @proxyName 動態生成代理類的名稱 */ public static void generateClassFile(Class clazz, String proxyName) { // 根據提供的代理類名稱和類信息,生成字節碼 byte[] classFile = ProxyGenerator.generateProxyClass(ProxyName, clazz.getInterfaces()); String paths = clazz.getResource(".").getPath(); System.out.println(paths); FileOutputStream out = null; try { // 保留到硬盤中 out = new FileOutputStream(paths + proxyName + ".class"); out.write(classFile); out.flush(); } catch (Exception e) { e.printStackTrace(); } finally { try { out.close(); } catch (IOException e) { e.printStackTrace(); } } } } 複製代碼
- 修改代理類名稱爲 "ElectricCarProxy", 並保存到硬盤,使用如下語句:
ProxyUtils.generateClassFile(car.getClass(), "ElectricCarProxy"); 複製代碼
這樣將在ElectricCar.class同級目錄下產生ElectricCarProxy.class文件框架
- 使用反編譯工具jd-gui.exe打開,將會看到如下信息:
/** * 生成的動態代理類的組織模式是繼承Proxy類,而後實現須要實現代理的類上的全部接口 * 在實現過程當中,是經過將全部的方法都交給InvocationHandler來處理 */ public final class ElectricCarProxy extends Proxy implements Rechargable,Vehicle { private static Method m1; private static Method m3; private static Method m4; private static Method m0; private static Method m2; public ElectricCarProxy(InvocationHandler paramInvocationHandler) throws { super(paramInvocationHandler); } public final boolean equals(Object paramObject) throws { try { /* * 方法功能的實現交給InvocationHandler處理 */ return ((Boolean) this.h.invoke(this, m1, new Object[] {paramObject})).booleanValue(); } catch (Error | RuntimeException localError) { throw localError; } catch (Throwable localThrowable) { throw new Undeclared ThrowableException(localThrowable); } } public final void recharge() throws { try { /* * 方法功能的實現交給InvocationHandler處理 */ this.h.invoke(this, m3, null); return; } catch (Error | RuntimeException localError) { throw localError; } catch (Throwable localThrowable) { throw new Undeclared ThrowableException(localThrowable); } } public final drive() throws { try { /* * 方法實現交給InvocationHandler處理 */ this.h.invoke(this, m4, null); return; } catch (Error | RuntimeException localError) { throw localError; } catch (Throwable localThrowable) { throw new Undeclared ThrowableException(localThrowable); } } public final int hasCode() throws { try { /* * 方法功能交給InvocationHandler處理 */ return ((Integer) this.h.invoke(this, m0, null)).intValue(); } catch (Error | RuntimeException localError) { throw localError; } catch (Throwable localThrowable) { throw new Undeclared ThrowableException(localThrowable); } } public final String toString() throws { try { /* * 方法功能實現交給InvocationHandler處理 */ return (String)this.h.invoke(this, m2, null); } catch (Error | RuntimeException localError) { throw localError; } catch (Throwable localThrowable) { throw new Undeclared ThrowableException(localThrowable); } } static { try { /* * 爲每個須要方法對象 * 當調用相應的方法時,分別將方法對象做爲參數傳遞給InvocationHandler處理 */ m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") }); m3 = Class.forName("com.oxford.proxy.Rechargable").getMethod("recharge", new Class[0]); m4 = Class.forName("com.oxford.proxy.Vehicle").getMethod("drive", new Class[0]); m0 = Class.forName("java.lang.Object").getMethod("hasCode", 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.getMessge()); } } } 複製代碼
- JDK動態代理類的特色:
- 某個類必須有實現的接口,而生成的代理類只能代理某個類接口定以的方法. 這樣會致使子類實現繼承兩個接口的方法外,另外實現的方法,在產生的動態代理類中不會有這個方法
- 若是某個類沒有實現接口,那麼這個類就不能使用JDK動態代理了
- 定義一個Programmer類,一個Hacker類
/** * 開發人員類 */ public class Programmer { public void code() { System.out.println("開發人員開發程序代碼."); } } 複製代碼
/** * 黑客類 * 實現了方法攔截器接口 */ public class Hacker implements MethodInterceptor { @Override public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { System.out.println("Hacker what to do."); proxy.invokeSuper(obj, args); System.out.println("Hacker done this."); return null; } } 複製代碼
- 測試類:
public class Test { public static void main(String[] args) { Programmer programmer = new Programmer(); Hacker hacker = new Hacker(); // CGLIB中的增強器,用來建立動態代理 Enhancer enhancer = new Enhancer(); // 設置要建立動態代理的類 enhancer.setSuperclass(programmer.getClass()); /* * 設置回調 * 這裏至關於對於代理類上全部方法的調用,都會調用CallBack * 而CallBack則須要實行intercept()方法進行攔截 */ enhancer.setCallBack(hacker); Programmer proxy = (Programmer) enhancer.create(); proxy.code(); } } 複製代碼
public class Programmer EnhancerByCGLIB fa7aa2cd extends Programmer implements Factory {
/* * .... */
// Enhancer傳入的methodInterceptor
private MethodInterceptor CGLIB$CALLBACK_0;
/* * ... */
public final void code() {
MethodInterceptor tmp4_1 = this.CGLIB$CALLBACK_0;
if (tmp4_1 == null) {
tmp4_1;
// 若callback不爲空,則調用methodInterceptor的intercept()方法
CGLIB$BIND_CALLBACKS(this);
}
if (this.CGLIB$CALLBACK_0 != null)
return;
// 若是沒有設置callback回調函數,則默認執行父類的方法
super.code();
}
/* * ... */
}
複製代碼