深入理解 Java 動態代理機制

Java 有兩種代理方式,一種Java 有兩種代理方式,一種是靜態代理,另一種是動態代理。對於靜態代理,其實就是通過依賴注入,對對象進行封裝,不讓外部知道實現的細節。很多 API 就是通過這種形式來封裝的。

代理模式結構圖(圖片來自《大話設計模式》)

深入理解 Java 動態代理機制

下面看下兩者在概念上的解釋:

靜態代理

靜態代理類:由程序員創建或者由第三方工具生成,再進行編譯;在程序運行之前,代理類的.class文件已經存在了。

靜態代理類通常只代理一個類。

靜態代理事先知道要代理的是什麼。

動態代理

動態代理類:在程序運行時,通過反射機制動態生成。

動態代理類通常代理接口下的所有類。

動態代理事先不知道要代理的是什麼,只有在運行的時候才能確定。

動態代理的調用處理程序必須事先InvocationHandler接口,及使用Proxy類中的newProxyInstance方法動態的創建代理類。

Java動態代理只能代理接口,要代理類需要使用第三方的CLIGB等類庫。

動態代理的好處

Java動態代理的優勢是實現無侵入式的代碼擴展,也就是方法的增強;讓你可以在不用修改源碼的情況下,增強一些方法;在方法的前後你可以做你任何想做的事情(甚至不去執行這個方法就可以)。此外,也可以減少代碼量,如果採用靜態代理,類的方法比較多的時候,得手寫大量代碼。

動態代理實例

靜態代理的實例這裡就不說了,比較簡單。在 java 的 java.lang.reflect 包下提供了一個 Proxy 類和一個 InvocationHandler 接口,通過這個類和這個接口可以生成 JDK 動態代理類和動態代理對象。下面講講動態代理的實現。

先定義一個接口:

public interface Person {
void setName(String name);
}

再定義一個學生 Student 類來實現 Person 接口,每一個學生都有一個自己的名字:

public class Student implements Person {

private String mName;

public Student(String name) {
mName = name;
}
public void setName(String name) {
mName = name;
}
}

Student 類中,定義了一個私有變量 mName,用來表示 Student 的名字。接下去定義一個代理 handler,就是用來幫我們處理代理的 :

public class PersonHandler implements InvocationHandler {
// 代理的目標對象
private T mTarget;
public PersonHandler(T target) {
mTarget = target;
}
@Override
public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
// 調用開始前的操作
ProxyUtil.start();
// 調用方法,通過反射的形式來調用 mTarget 的 method
Object result = method.invoke(mTarget, objects);
// 打印改名前的名字
ProxyUtil.log(objects[0].toString());
// 調用結束後的操作
ProxyUtil.finish();
return result;
}
}

可以發現,我們在調用代碼前後都做了一些操作,甚至可以直接攔截該方法,不讓其運行。其中定義了一個 ProxyUtil 類,方便我們做一些操作:

public class ProxyUtil {
private static final String TAG = "ProxyUtil";
public static void start() {
Log.d(TAG, "start: " + System.currentTimeMillis());
}
public static void finish() {
Log.d(TAG, "finish: " + System.currentTimeMillis());
}
public static void log(String name) {
Log.d(TAG, "log: " + name);
}
}

接下去開始編寫代理的實現:

public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//創建一個實例對象,這個對象是被代理的對象
Person zhangsan = new Student("張三");
//創建一個與代理對象相關聯的 InvocationHandler
PersonHandler stuHandler = new PersonHandler<>(zhangsan);
//創建一個代理對象 stuProxy 來代理 zhangsan,代理對象的每個執行方法都會替換執行 Invocation 中的 invoke 方法
Person stuProxy = (Person) Proxy.newProxyInstance(Person.class.getClassLoader(), new Class>[]{Person.class}, stuHandler);
//代理執行 setName 的方法
stuProxy.setName("王五");
}

看下打印輸出:

深入理解 Java 動態代理機制

可以發現代理成功了。並且我們在調用方式的之前之後,都做了一些操作。Spring 的 AOP 其就是通過動態代理的機制實現的。

其中,我們將 stuProxy 的類名打印出來:

Log.d(TAG, "onCreate: " + stuProxy.getClass().getCanonicalName());
深入理解 Java 動態代理機制

發現其名字竟然是 $Proxy0。具體原因下面會解釋。

源碼分析

上面我們利用 Proxy 類的 newProxyInstance 方法創建了一個動態代理對象,查看該方法的源碼:

/**
* Returns an instance of a proxy class for the specified interfaces
* that dispatches method invocations to the specified invocation
* handler.
*
*

{@code Proxy.newProxyInstance} throws
* {@code IllegalArgumentException} for the same reasons that
* {@code Proxy.getProxyClass} does.
*
* @param loader the class loader to define the proxy class
* @param interfaces the list of interfaces for the proxy class
* to implement
* @param h the invocation handler to dispatch method invocations to
* @return a proxy instance with the specified invocation handler of a
* proxy class that is defined by the specified class loader
* and that implements the specified interfaces
* @throws IllegalArgumentException if any of the restrictions on the
* parameters that may be passed to {@code getProxyClass}
* are violated
* @throws SecurityException if a security manager, s, is present
* and any of the following conditions is met:
*


    *
  • the given {@code loader} is {@code null} and
    * the caller's class loader is not {@code null} and the
    * invocation of {@link SecurityManager#checkPermission
    * s.checkPermission} with
    * {@code RuntimePermission("getClassLoader")} permission
    * denies access;

  • *
  • for each proxy interface, {@code intf},
    * the caller's class loader is not the same as or an
    * ancestor of the class loader for {@code intf} and
    * invocation of {@link SecurityManager#checkPackageAccess
    * s.checkPackageAccess()} denies access to {@code intf};

  • *
  • any of the given proxy interfaces is non-public and the
    * caller class is not in the same {@linkplain Package runtime package}
    * as the non-public interface and the invocation of
    * {@link SecurityManager#checkPermission s.checkPermission} with
    * {@code ReflectPermission("newProxyInPackage.{package name}")}
    * permission denies access.

  • *

* @throws NullPointerException if the {@code interfaces} array
* argument or any of its elements are {@code null}, or
* if the invocation handler, {@code h}, is
* {@code null}
*/
@CallerSensitive
public static Object newProxyInstance(ClassLoader loader,
Class>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
{
     // 判空,判斷 h 對象是否為空,為空就拋出 NullPointerException
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() {
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) {
     // 數量超過 65535 就拋出異常,665535 這個就不用說了吧
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);
}

最後發現會對生成的代理類進行緩存,有了,就不直接返回,沒有的,還得生成代理類,我們繼續往下走:

proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());

關鍵點在於 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[], 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, 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());
}
}
}

這裡會通過反射獲取接口的各種修飾符,包名等,然後根據規則命名代理類,最後調用 ProxyGenerator.generateProxyClass 生成了代理類。

ProxyGenerator.generateProxyClass 具體實現在 eclipse 上打開後,說是找不到源碼:

深入理解 Java 動態代理機制

不過,從其他地方找到了部分代碼:

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() {
public Void run() {
try {
FileOutputStream file =
new FileOutputStream(dotToSlash(name) + ".class");
file.write(classFile);
file.close();
return null;
} catch (IOException e) {
throw new InternalError(
"I/O exception saving generated file: " + e);
}
}
});
}

// 返回代理類的字節碼
return classFile;
}

我們可以自己試試 ProxyGenerator.generateProxyClass 的功能。

public class ProxyGeneratorUtils {
/**
* 把代理類的字節碼寫到硬盤上

* @param path 保存路徑
*/
public static void writeProxyClassToHardDisk(String path) {
// 獲取代理類的字節碼
byte[] classFile = ProxyGenerator.generateProxyClass("$Proxy11", Student.class.getInterfaces());

FileOutputStream out = null;

try {
out = new FileOutputStream(path);
out.write(classFile);
out.flush();
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}

main 方法裡面進行調用 :

public class Main {
public static void main(String[] args) {
ProxyGeneratorUtils.writeProxyClassToHardDisk("$Proxy0.class");
}
}

可以發現,在根目錄下生成了一個 $Proxy0.class 文件,文件內容反編譯後如下:

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
import proxy.Person;
public final class $Proxy0 extends Proxy implements Person
{
private static Method m1;
private static Method m2;

private static Method m3;
private static Method m0;

/**
*注意這裡是生成代理類的構造方法,方法參數為InvocationHandler類型,看到這,是不是就有點明白
*為何代理對象調用方法都是執行InvocationHandler中的invoke方法,而InvocationHandler又持有一個
*被代理對象的實例,不禁會想難道是....? 沒錯,就是你想的那樣。
*
*super(paramInvocationHandler),是調用父類Proxy的構造方法。
*父類持有:protected InvocationHandler h;
*Proxy構造方法:
* protected Proxy(InvocationHandler h) {
* Objects.requireNonNull(h);
* this.h = h;
* }
*
*/
public $Proxy0(InvocationHandler paramInvocationHandler)
throws
{
super(paramInvocationHandler);
}

//這個靜態塊本來是在最後的,我把它拿到前面來,方便描述
static
{
try
{
//看看這兒靜態塊兒裡面有什麼,是不是找到了giveMoney方法。請記住giveMoney通過反射得到的名字m3,其他的先不管
m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
m3 = Class.forName("proxy.Person").getMethod("giveMoney", new Class[0]);
m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);

return;
}
catch (NoSuchMethodException localNoSuchMethodException)
{
throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
}
catch (ClassNotFoundException localClassNotFoundException)
{
throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
}
}

/**
*
*這裡調用代理對象的giveMoney方法,直接就調用了InvocationHandler中的invoke方法,並把m3傳了進去。
*this.h.invoke(this, m3, null);這裡簡單,明瞭。
*來,再想想,代理對象持有一個InvocationHandler對象,InvocationHandler對象持有一個被代理的對象,
*再聯繫到InvacationHandler中的invoke方法。嗯,就是這樣。
*/
public final void giveMoney()
throws
{
try
{
this.h.invoke(this, m3, null);
return;
}
catch (Error|RuntimeException localError)
{
throw localError;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
//注意,這裡為了節省篇幅,省去了toString,hashCode、equals方法的內容。原理和giveMoney方法一毛一樣。
}

jdk 為我們的生成了一個叫 $Proxy0(這個名字後面的0是編號,有多個代理類會一次遞增)的代理類,這個類文件時放在內存中的,我們在創建代理對象時,就是通過反射獲得這個類的構造方法,然後創建的代理實例。通過對這個生成的代理類源碼的查看,我們很容易能看出,動態代理實現的具體過程。

我們可以對 InvocationHandler 看做一箇中介類,中介類持有一個被代理對象,在 invoke 方法中調用了被代理對象的相應方法,而生成的代理類中持有中介類,因此,當我們在調用代理類的時候,就是再調用中介類的 invoke 方法,通過反射轉為對被代理對象的調用。

代理類調用自己方法時,通過自身持有的中介類對象來調用中介類對象的 invoke 方法,從而達到代理執行被代理對象的方法。也就是說,動態代理通過中介類實現了具體的代理功能。

生成的代理類:$Proxy0 extends Proxy implements Person,我們看到代理類繼承了 Proxy 類,所以也就決定了 java 動態代理只能對接口進行代理,Java 的繼承機制註定了這些動態代理類們無法實現對 class 的動態代理。是靜態代理,另一種是動態代理。對於靜態代理,其實就是通過依賴注入,對對象進行封裝,不讓外部知道實現的細節。很多 API 就是通過這種形式來封裝的。

代理模式結構圖(圖片來自《大話設計模式》)

深入理解 Java 動態代理機制

下面看下兩者在概念上的解釋:

靜態代理

靜態代理類:由程序員創建或者由第三方工具生成,再進行編譯;在程序運行之前,代理類的.class文件已經存在了。

靜態代理類通常只代理一個類。

靜態代理事先知道要代理的是什麼。

動態代理

動態代理類:在程序運行時,通過反射機制動態生成。

動態代理類通常代理接口下的所有類。

動態代理事先不知道要代理的是什麼,只有在運行的時候才能確定。

動態代理的調用處理程序必須事先InvocationHandler接口,及使用Proxy類中的newProxyInstance方法動態的創建代理類。

Java動態代理只能代理接口,要代理類需要使用第三方的CLIGB等類庫。

動態代理的好處

Java動態代理的優勢是實現無侵入式的代碼擴展,也就是方法的增強;讓你可以在不用修改源碼的情況下,增強一些方法;在方法的前後你可以做你任何想做的事情(甚至不去執行這個方法就可以)。此外,也可以減少代碼量,如果採用靜態代理,類的方法比較多的時候,得手寫大量代碼。

動態代理實例

靜態代理的實例這裡就不說了,比較簡單。在 java 的 java.lang.reflect 包下提供了一個 Proxy 類和一個 InvocationHandler 接口,通過這個類和這個接口可以生成 JDK 動態代理類和動態代理對象。下面講講動態代理的實現。

先定義一個接口:

public interface Person {
void setName(String name);
}

再定義一個學生 Student 類來實現 Person 接口,每一個學生都有一個自己的名字:

public class Student implements Person {

private String mName;
public Student(String name) {
mName = name;
}
public void setName(String name) {
mName = name;
}
}

Student 類中,定義了一個私有變量 mName,用來表示 Student 的名字。接下去定義一個代理 handler,就是用來幫我們處理代理的 :

public class PersonHandler implements InvocationHandler {
// 代理的目標對象
private T mTarget;

public PersonHandler(T target) {
mTarget = target;
}
@Override
public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
// 調用開始前的操作
ProxyUtil.start();
// 調用方法,通過反射的形式來調用 mTarget 的 method
Object result = method.invoke(mTarget, objects);
// 打印改名前的名字
ProxyUtil.log(objects[0].toString());
// 調用結束後的操作
ProxyUtil.finish();
return result;
}
}

可以發現,我們在調用代碼前後都做了一些操作,甚至可以直接攔截該方法,不讓其運行。其中定義了一個 ProxyUtil 類,方便我們做一些操作:

public class ProxyUtil {
private static final String TAG = "ProxyUtil";
public static void start() {
Log.d(TAG, "start: " + System.currentTimeMillis());
}
public static void finish() {
Log.d(TAG, "finish: " + System.currentTimeMillis());
}
public static void log(String name) {
Log.d(TAG, "log: " + name);
}
}

接下去開始編寫代理的實現:

public class MainActivity extends AppCompatActivity {
@Override

protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//創建一個實例對象,這個對象是被代理的對象
Person zhangsan = new Student("張三");
//創建一個與代理對象相關聯的 InvocationHandler
PersonHandler stuHandler = new PersonHandler<>(zhangsan);
//創建一個代理對象 stuProxy 來代理 zhangsan,代理對象的每個執行方法都會替換執行 Invocation 中的 invoke 方法
Person stuProxy = (Person) Proxy.newProxyInstance(Person.class.getClassLoader(), new Class>[]{Person.class}, stuHandler);
//代理執行 setName 的方法
stuProxy.setName("王五");
}

看下打印輸出:

深入理解 Java 動態代理機制

可以發現代理成功了。並且我們在調用方式的之前之後,都做了一些操作。Spring 的 AOP 其就是通過動態代理的機制實現的。

其中,我們將 stuProxy 的類名打印出來:

Log.d(TAG, "onCreate: " + stuProxy.getClass().getCanonicalName());
深入理解 Java 動態代理機制

發現其名字竟然是 $Proxy0。具體原因下面會解釋。

源碼分析

上面我們利用 Proxy 類的 newProxyInstance 方法創建了一個動態代理對象,查看該方法的源碼:

/**
* Returns an instance of a proxy class for the specified interfaces
* that dispatches method invocations to the specified invocation
* handler.
*
*

{@code Proxy.newProxyInstance} throws
* {@code IllegalArgumentException} for the same reasons that
* {@code Proxy.getProxyClass} does.
*
* @param loader the class loader to define the proxy class
* @param interfaces the list of interfaces for the proxy class
* to implement
* @param h the invocation handler to dispatch method invocations to
* @return a proxy instance with the specified invocation handler of a
* proxy class that is defined by the specified class loader
* and that implements the specified interfaces
* @throws IllegalArgumentException if any of the restrictions on the
* parameters that may be passed to {@code getProxyClass}
* are violated
* @throws SecurityException if a security manager, s, is present
* and any of the following conditions is met:
*


    *
  • the given {@code loader} is {@code null} and
    * the caller's class loader is not {@code null} and the
    * invocation of {@link SecurityManager#checkPermission
    * s.checkPermission} with
    * {@code RuntimePermission("getClassLoader")} permission
    * denies access;

  • *
  • for each proxy interface, {@code intf},
    * the caller's class loader is not the same as or an
    * ancestor of the class loader for {@code intf} and
    * invocation of {@link SecurityManager#checkPackageAccess
    * s.checkPackageAccess()} denies access to {@code intf};

  • *
  • any of the given proxy interfaces is non-public and the
    * caller class is not in the same {@linkplain Package runtime package}
    * as the non-public interface and the invocation of
    * {@link SecurityManager#checkPermission s.checkPermission} with
    * {@code ReflectPermission("newProxyInPackage.{package name}")}
    * permission denies access.

  • *

* @throws NullPointerException if the {@code interfaces} array
* argument or any of its elements are {@code null}, or
* if the invocation handler, {@code h}, is
* {@code null}
*/
@CallerSensitive
public static Object newProxyInstance(ClassLoader loader,
Class>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
{
     // 判空,判斷 h 對象是否為空,為空就拋出 NullPointerException
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() {
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) {
     // 數量超過 65535 就拋出異常,665535 這個就不用說了吧
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);
}

最後發現會對生成的代理類進行緩存,有了,就不直接返回,沒有的,還得生成代理類,我們繼續往下走:

proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());

關鍵點在於 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[], 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, 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());
}
}
}

這裡會通過反射獲取接口的各種修飾符,包名等,然後根據規則命名代理類,最後調用 ProxyGenerator.generateProxyClass 生成了代理類。

ProxyGenerator.generateProxyClass 具體實現在 eclipse 上打開後,說是找不到源碼:

深入理解 Java 動態代理機制

不過,從其他地方找到了部分代碼:

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() {
public Void run() {
try {
FileOutputStream file =
new FileOutputStream(dotToSlash(name) + ".class");
file.write(classFile);
file.close();
return null;
} catch (IOException e) {
throw new InternalError(
"I/O exception saving generated file: " + e);
}
}
});
}

// 返回代理類的字節碼
return classFile;
}

我們可以自己試試 ProxyGenerator.generateProxyClass 的功能。

public class ProxyGeneratorUtils {
/**
* 把代理類的字節碼寫到硬盤上

* @param path 保存路徑
*/
public static void writeProxyClassToHardDisk(String path) {
// 獲取代理類的字節碼
byte[] classFile = ProxyGenerator.generateProxyClass("$Proxy11", Student.class.getInterfaces());

FileOutputStream out = null;

try {
out = new FileOutputStream(path);
out.write(classFile);
out.flush();
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}

main 方法裡面進行調用 :

public class Main {
public static void main(String[] args) {
ProxyGeneratorUtils.writeProxyClassToHardDisk("$Proxy0.class");
}
}

可以發現,在根目錄下生成了一個 $Proxy0.class 文件,文件內容反編譯後如下:

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
import proxy.Person;
public final class $Proxy0 extends Proxy implements Person
{
private static Method m1;
private static Method m2;

private static Method m3;
private static Method m0;

/**
*注意這裡是生成代理類的構造方法,方法參數為InvocationHandler類型,看到這,是不是就有點明白
*為何代理對象調用方法都是執行InvocationHandler中的invoke方法,而InvocationHandler又持有一個
*被代理對象的實例,不禁會想難道是....? 沒錯,就是你想的那樣。
*
*super(paramInvocationHandler),是調用父類Proxy的構造方法。
*父類持有:protected InvocationHandler h;
*Proxy構造方法:
* protected Proxy(InvocationHandler h) {
* Objects.requireNonNull(h);
* this.h = h;
* }
*
*/
public $Proxy0(InvocationHandler paramInvocationHandler)
throws
{
super(paramInvocationHandler);
}

//這個靜態塊本來是在最後的,我把它拿到前面來,方便描述
static
{
try
{
//看看這兒靜態塊兒裡面有什麼,是不是找到了giveMoney方法。請記住giveMoney通過反射得到的名字m3,其他的先不管
m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
m3 = Class.forName("proxy.Person").getMethod("giveMoney", new Class[0]);
m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);

return;
}
catch (NoSuchMethodException localNoSuchMethodException)
{
throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
}
catch (ClassNotFoundException localClassNotFoundException)
{
throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
}
}

/**
*
*這裡調用代理對象的giveMoney方法,直接就調用了InvocationHandler中的invoke方法,並把m3傳了進去。
*this.h.invoke(this, m3, null);這裡簡單,明瞭。
*來,再想想,代理對象持有一個InvocationHandler對象,InvocationHandler對象持有一個被代理的對象,
*再聯繫到InvacationHandler中的invoke方法。嗯,就是這樣。
*/
public final void giveMoney()
throws
{
try
{
this.h.invoke(this, m3, null);
return;
}
catch (Error|RuntimeException localError)
{
throw localError;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
//注意,這裡為了節省篇幅,省去了toString,hashCode、equals方法的內容。原理和giveMoney方法一毛一樣。
}

jdk 為我們的生成了一個叫 $Proxy0(這個名字後面的0是編號,有多個代理類會一次遞增)的代理類,這個類文件時放在內存中的,我們在創建代理對象時,就是通過反射獲得這個類的構造方法,然後創建的代理實例。通過對這個生成的代理類源碼的查看,我們很容易能看出,動態代理實現的具體過程。

我們可以對 InvocationHandler 看做一箇中介類,中介類持有一個被代理對象,在 invoke 方法中調用了被代理對象的相應方法,而生成的代理類中持有中介類,因此,當我們在調用代理類的時候,就是再調用中介類的 invoke 方法,通過反射轉為對被代理對象的調用。

代理類調用自己方法時,通過自身持有的中介類對象來調用中介類對象的 invoke 方法,從而達到代理執行被代理對象的方法。也就是說,動態代理通過中介類實現了具體的代理功能。

生成的代理類:$Proxy0 extends Proxy implements Person,我們看到代理類繼承了 Proxy 類,所以也就決定了 java 動態代理只能對接口進行代理,Java 的繼承機制註定了這些動態代理類們無法實現對 class 的動態代理。


分享到:


相關文章: