JDK中的Proxy動態(tài)代理原理剖析

主要API類是:

```

Proxy.newProxyInstance

public static Object newProxyInstance(ClassLoader loader,

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? Class<?>[] interfaces,

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? InvocationHandler h)

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? throws IllegalArgumentException

返回一個指定接口的代理類實例,該接口可以將方法調(diào)用指派到指定的調(diào)用處理程序。此方法相當于:

? ? Proxy.getProxyClass(loader, interfaces).

? ? ? ? getConstructor(new Class[] { InvocationHandler.class }).

? ? ? ? newInstance(new Object[] { handler });

```

```

Proxy.newProxyInstance 拋出 IllegalArgumentException,原因與 Proxy.getProxyClass 相同。

參數(shù):

loader - 定義代理類的類加載器

interfaces - 代理類要實現(xiàn)的接口列表

h - 指派方法調(diào)用的調(diào)用處理程序

返回:

一個帶有代理類的指定調(diào)用處理程序的代理實例,它由指定的類加載器定義,并實現(xiàn)指定的接口

拋出:

IllegalArgumentException - 如果違反傳遞到 getProxyClass 的參數(shù)上的任何限制

NullPointerException - 如果 interfaces 數(shù)組參數(shù)或其任何元素為 null,或如果調(diào)用處理程序 h 為 null

```

先聲明一個接口

```

package com.czq.proxy;

public interface IPackageManager {

? ? String getPackageInfo() ;

}

```

實現(xiàn)該接口

```

package com.czq.proxy;

public class PackageManagerImpl implements IPackageManager {

? ? @Override

? ? public String getPackageInfo() {

? ? ? ? String s = "com.czq.proxy";

//? ? ? ? System.out.println(s);

? ? ? ? return s;

? ? }

? ? @Override

? ? public String toString() {

? ? ? ? return "PackageManagerImpl";

? ? }

}

```

實現(xiàn)InvocationHandler 接口,關(guān)鍵之處在這

```

package com.czq.proxy;

import java.lang.reflect.InvocationHandler;

import java.lang.reflect.Method;

public class PackageManagerWoker implements InvocationHandler {

? ? private Object mTarget = null;

? ? public PackageManagerWoker(Object target) {

? ? ? ? super();

? ? ? ? this.mTarget = target;

? ? }

? ? @Override

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

? ? ? ? System. out.println("1" );

? ? ? ? System. out.println("method:" +method);

? ? ? ? if (args != null) {

? ? ? ? ? ? for (int i = 0; i < args.length; i++) {

? ? ? ? ? ? ? ? System. out.println("args[" + i + "]:" + args[i]);

? ? ? ? ? ? }

? ? ? ? }

? ? ? ? Object result = method.invoke( mTarget, args);

? ? ? ? System. out.println("2" );

? ? ? ? return result;? ? ?

? ? }

```

測試:

```

package com.czq.proxy;

import java.lang.reflect.Field;

import java.lang.reflect.Method;

import java.lang.reflect.Modifier;

import java.lang.reflect.Proxy;

public class Test {

? ? public static void main(String[] args) {

? ? ? ? // 從源碼中得知,設(shè)置這個值,可以把生成的代理類,輸出出來。

? ? ? ? System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");


? ? ? ? IPackageManager pkgManger = new PackageManagerImpl();

//? ? ? ? System.out.println("pkgManger.toString:"+pkgManger.toString());

? ? ? ? PackageManagerWoker woker = new PackageManagerWoker(pkgManger);

? ? ? ? IPackageManager pm = (IPackageManager) Proxy.newProxyInstance(pkgManger.getClass().getClassLoader(), pkgManger

? ? ? ? ? ? ? ? .getClass().getInterfaces(), woker);

? ? ? //? System.out.println("pm.getName:" +pm.getClass().getName())


? ? ? ? System. out.println("pm.toString:" +pm.toString());

? ? ? ? System.out.println(pm.getPackageInfo());

? ? }

}

```

輸出結(jié)果如下:

```

1

method:public java.lang.String java.lang.Object.toString()

2

pm.toString:PackageManagerImpl

1

method:public abstract java.lang.String com.czq.proxy.IPackageManager.getPackageInfo()

2

com.czq.proxy

```

得出結(jié)論:

pm.getPackageInfo()方法會走到PackageManagerWoker的invoke方法。

思考問題:

PackageManagerWoker不繼承IPackageManager。不能強轉(zhuǎn)成IPackageManager。

也就是pm對象不是PackageManagerWoker對象。

那pm 是哪個對象,是什么類呢?為什么還能強轉(zhuǎn)成IPackageManager

打印pm的className

```

System.out.println("pm.getName:" +pm.getClass().getName())

```

得出的結(jié)果:

```

pm.getName:com.sun.proxy.$Proxy0

```

也就是pm對象是com.sun.proxy.$Proxy0這個類new出的對象。這個類是剛剛Proxy.newProxyInstance自動生成的class

那這個class里面寫的是什么呢?

查看源碼:Proxy.java

```

/**

? ? * 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

? ? ? ? ? ? /*

? ? ? ? ? ? * 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)) {

? ? ? ? ? ? ? ? ? ? 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);

? ? ? ? ? ? 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());

? ? ? ? ? ? }

? ? ? ? }

? ? }

```

通過ProxyGenerator 生成了這個class。

查看ProxyGenerator源碼:

```

/**

? ? * Generate a proxy class given a name and a list of proxy interfaces.

? ? *

? ? * @param name? ? ? ? the class name of the proxy class

? ? * @param interfaces? proxy interfaces

? ? * @param accessFlags access flags of the proxy class

? ? */

? ? public static byte[] generateProxyClass(final String name,

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? Class<?>[] interfaces,

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? int accessFlags)

? ? {

? ? ? ? ProxyGenerator gen = new ProxyGenerator(name, interfaces, accessFlags);

? ? ? ? final byte[] classFile = gen.generateClassFile();

? ? ? ? if (saveGeneratedFiles) {

? ? ? ? ? ? java.security.AccessController.doPrivileged(

? ? ? ? ? ? new java.security.PrivilegedAction<Void>() {

? ? ? ? ? ? ? ? public Void run() {

? ? ? ? ? ? ? ? ? ? try {

? ? ? ? ? ? ? ? ? ? ? ? int i = name.lastIndexOf('.');

? ? ? ? ? ? ? ? ? ? ? ? Path path;

? ? ? ? ? ? ? ? ? ? ? ? if (i > 0) {

? ? ? ? ? ? ? ? ? ? ? ? ? ? Path dir = Paths.get(name.substring(0, i).replace('.', File.separatorChar));

? ? ? ? ? ? ? ? ? ? ? ? ? ? Files.createDirectories(dir);

? ? ? ? ? ? ? ? ? ? ? ? ? ? path = dir.resolve(name.substring(i+1, name.length()) + ".class");

? ? ? ? ? ? ? ? ? ? ? ? } else {

? ? ? ? ? ? ? ? ? ? ? ? ? ? path = Paths.get(name + ".class");

? ? ? ? ? ? ? ? ? ? ? ? }

? ? ? ? ? ? ? ? ? ? ? ? Files.write(path, classFile);

? ? ? ? ? ? ? ? ? ? ? ? return null;

? ? ? ? ? ? ? ? ? ? } catch (IOException e) {

? ? ? ? ? ? ? ? ? ? ? ? throw new InternalError(

? ? ? ? ? ? ? ? ? ? ? ? ? ? "I/O exception saving generated file: " + e);

? ? ? ? ? ? ? ? ? ? }

? ? ? ? ? ? ? ? }

? ? ? ? ? ? });

? ? ? ? }

? ? ? ? return classFile;

? ? }

```

發(fā)現(xiàn) saveGeneratedFiles 為true報錯生成的class的源碼。

這個saveGeneratedFiles 怎么賦值呢?

```

/** debugging flag for saving generated class files */

? ? private final static boolean saveGeneratedFiles =

? ? ? ? java.security.AccessController.doPrivileged(

? ? ? ? ? ? new GetBooleanAction(

? ? ? ? ? ? ? ? "sun.misc.ProxyGenerator.saveGeneratedFiles")).booleanValue();

```

也就是把sun.misc.ProxyGenerator.saveGeneratedFiles 改成true就可以輸出結(jié)果了。

```

? ? ? ? // 從源碼中得知,設(shè)置這個值,可以把生成的代理類,輸出出來。? ? ?

System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");

```

注意,需要再工程根目錄下,增加 com/sun/proxy目錄,否則會報錯如下:

```

Exception in thread "main" java.lang.InternalError: I/O exception saving generated file: java.io.FileNotFoundException : com\sun\proxy\$Proxy0.class (系統(tǒng)找不到指定的路徑。)

? ? at sun.misc.ProxyGenerator$1.run(ProxyGenerator.java:336 )

? ? at sun.misc.ProxyGenerator$1.run(ProxyGenerator.java:327 )

? ? at java.security.AccessController.doPrivileged(Native Method)

? ? at sun.misc.ProxyGenerator.generateProxyClass(ProxyGenerator.java:326)

? ? at java.lang.reflect.Proxy$ProxyClassFactory.apply(Proxy.java:672)

? ? at java.lang.reflect.Proxy$ProxyClassFactory.apply(Proxy.java:592)

? ? at java.lang.reflect.WeakCache$Factory.get(WeakCache.java:244)

? ? at java.lang.reflect.WeakCache.get(WeakCache.java:141 )

? ? at java.lang.reflect.Proxy.getProxyClass0(Proxy.java:455 )

? ? at java.lang.reflect.Proxy.newProxyInstance(Proxy.java:738)

? ? at com.czq.proxy.Test.main( Test.java:18)

```

把proxy0輸出的結(jié)果如下:

反編譯看看proxy0是內(nèi)容是啥,有什么秘密

```

package com.sun.proxy;

import com.czq.proxy.IPackageManager;

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 IPackageManager

{

? private static Method m3; // 生成對應(yīng)的方法對象

? private static Method m1;

? private static Method m0;

? private static Method m2;

// proxy0 繼承Proxy,實現(xiàn)IPackageManager 接口,需要傳入 InvocationHandler,初始化對應(yīng)的h對象。

// 我們的h對象就是PackageManagerWoker,所以我們會調(diào)用到PackageManagerWoker的 invoke方法。

// 所以是proxy0,調(diào)用InvocationHandler的 invoke 方法,傳入對應(yīng)的方法。InvocationHandler 放射調(diào)用對應(yīng)的tagret中的方法。

? public $Proxy0(InvocationHandler paramInvocationHandler)

? ? throws

? {

? ? super(paramInvocationHandler);

? }

? public final String getPackageInfo()

? ? throws

? {

? ? try

? ? {

? ? ? return (String)this.h.invoke(this, m3, null);

? ? }

? ? catch (RuntimeException localRuntimeException)

? ? {

? ? ? throw localRuntimeException;

? ? }

? ? catch (Throwable localThrowable)

? ? {

? ? }

? ? throw new UndeclaredThrowableException(localThrowable);

? }

? public final boolean equals(Object paramObject)

? ? throws

? {

? ? try

? ? {

? ? ? return ((Boolean)this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue();

? ? }

? ? catch (RuntimeException localRuntimeException)

? ? {

? ? ? throw localRuntimeException;

? ? }

? ? catch (Throwable localThrowable)

? ? {

? ? }

? ? throw new UndeclaredThrowableException(localThrowable);

? }

? public final int hashCode()

? ? throws

? {

? ? try

? ? {

? ? ? return ((Integer)this.h.invoke(this, m0, null)).intValue();

? ? }

? ? catch (RuntimeException localRuntimeException)

? ? {

? ? ? throw localRuntimeException;

? ? }

? ? catch (Throwable localThrowable)

? ? {

? ? }

? ? throw new UndeclaredThrowableException(localThrowable);

? }

? public final String toString()

? ? throws

? {

? ? try

? ? {

? ? ? return (String)this.h.invoke(this, m2, null);

? ? }

? ? catch (RuntimeException localRuntimeException)

? ? {

? ? ? throw localRuntimeException;

? ? }

? ? catch (Throwable localThrowable)

? ? {

? ? }

? ? throw new UndeclaredThrowableException(localThrowable);

? }

? static

? {

? ? try

? ? {

? ? // 把各個方法,對應(yīng)到成員變量上

? ? ? m3 = Class.forName("com.czq.proxy.IPackageManager").getMethod("getPackageInfo", new Class[0]);

? ? ? m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });

? ? ? m0 = Class.forName("java.lang.Object").getMethod("hashCode", 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());

? }

}

```

結(jié)論如下:

1. proxy0 繼承Proxy,實現(xiàn)IPackageManager 接口,需要傳入 InvocationHandler,初始化對應(yīng)的h對象。

2. 我們的h對象就是PackageManagerWoker,所以我們會調(diào)用到PackageManagerWoker的 invoke方法。

3. 所以是proxy0,調(diào)用InvocationHandler的 invoke 方法,傳入對應(yīng)的方法。InvocationHandler 放射調(diào)用對應(yīng)的tagret中的方法。套了2層

整體類圖如下

![類圖.png](http://upload-images.jianshu.io/upload_images/5251070-4167c3297812f603.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容