参考文献

  • JDK版本

    1
    2
    3
    java 11.0.20 2023-07-18 LTS
    Java(TM) SE Runtime Environment 18.9 (build 11.0.20+9-LTS-256)
    Java HotSpot(TM) 64-Bit Server VM 18.9 (build 11.0.20+9-LTS-256, mixed mode)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
/**
* Returns a proxy instance for the specified interfaces
* that dispatches method invocations to the specified invocation
* handler.
* <p>
* <a id="restrictions">{@code IllegalArgumentException} will be thrown
* if any of the following restrictions is violated:</a>
* <ul>
* <li>All of {@code Class} objects in the given {@code interfaces} array
* must represent interfaces, not classes or primitive types.
*
* <li>No two elements in the {@code interfaces} array may
* refer to identical {@code Class} objects.
*
* <li>All of the interface types must be visible by name through the
* specified class loader. In other words, for class loader
* {@code cl} and every interface {@code i}, the following
* expression must be true:<p>
* {@code Class.forName(i.getName(), false, cl) == i}
*
* <li>All of the types referenced by all
* public method signatures of the specified interfaces
* and those inherited by their superinterfaces
* must be visible by name through the specified class loader.
*
* <li>All non-public interfaces must be in the same package
* and module, defined by the specified class loader and
* the module of the non-public interfaces can access all of
* the interface types; otherwise, it would not be possible for
* the proxy class to implement all of the interfaces,
* regardless of what package it is defined in.
*
* <li>For any set of member methods of the specified interfaces
* that have the same signature:
* <ul>
* <li>If the return type of any of the methods is a primitive
* type or void, then all of the methods must have that same
* return type.
* <li>Otherwise, one of the methods must have a return type that
* is assignable to all of the return types of the rest of the
* methods.
* </ul>
*
* <li>The resulting proxy class must not exceed any limits imposed
* on classes by the virtual machine. For example, the VM may limit
* the number of interfaces that a class may implement to 65535; in
* that case, the size of the {@code interfaces} array must not
* exceed 65535.
* </ul>
*
* <p>Note that the order of the specified proxy interfaces is
* significant: two requests for a proxy class with the same combination
* of interfaces but in a different order will result in two distinct
* proxy classes.
*
* @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 <a href="#restrictions">
* restrictions</a> on the parameters are violated
* @throws SecurityException if a security manager, <em>s</em>, is present
* and any of the following conditions is met:
* <ul>
* <li> 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;</li>
* <li> 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};</li>
* <li> 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.</li>
* </ul>
* @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}
*
* @see <a href="#membership">Package and Module Membership of Proxy Class</a>
* @revised 9
* @spec JPMS
*/
@CallerSensitive
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h) {
Objects.requireNonNull(h);

final Class<?> caller = System.getSecurityManager() == null
? null
: Reflection.getCallerClass();

/*
* 获取生成代理类的构造方法
* Look up or generate the designated proxy class and its constructor.
*/
Constructor<?> cons = getProxyConstructor(caller, loader, interfaces);

return newProxyInstance(caller, cons, h);
}

private static Object newProxyInstance(Class<?> caller, // null if no SecurityManager
Constructor<?> cons,
InvocationHandler h) {
/*
* Invoke its constructor with the designated invocation handler.
*/
try {
if (caller != null) {
checkNewProxyPermission(caller, cons.getDeclaringClass());
}

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);
}
}
}
  • java.lang.reflect.Proxy#getProxyConstructor
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
/**
* Returns the {@code Constructor} object of a proxy class that takes a
* single argument of type {@link InvocationHandler}, given a class loader
* and an array of interfaces. The returned constructor will have the
* {@link Constructor#setAccessible(boolean) accessible} flag already set.
*
* @param caller passed from a public-facing @CallerSensitive method if
* SecurityManager is set or {@code null} if there's no
* SecurityManager
* @param loader the class loader to define the proxy class
* @param interfaces the list of interfaces for the proxy class
* to implement
* @return a Constructor of the proxy class taking single
* {@code InvocationHandler} parameter
*/
private static Constructor<?> getProxyConstructor(Class<?> caller,
ClassLoader loader,
Class<?>... interfaces)
{
// optimization for single interface
if (interfaces.length == 1) {
Class<?> intf = interfaces[0];
if (caller != null) {
checkProxyAccess(caller, loader, intf);
}
return proxyCache.sub(intf).computeIfAbsent(
loader,
(ld, clv) -> new ProxyBuilder(ld, clv.key()).build()
);
} else {
// interfaces cloned
final Class<?>[] intfsArray = interfaces.clone();
if (caller != null) {
checkProxyAccess(caller, loader, intfsArray);
}
final List<Class<?>> intfs = Arrays.asList(intfsArray);
return proxyCache.sub(intfs).computeIfAbsent(
loader,
(ld, clv) -> new ProxyBuilder(ld, clv.key()).build()
);
}
}
  • java.lang.reflect.Proxy.ProxyBuilder#build
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
/**
* Generate a proxy class and return its proxy Constructor with
* accessible flag already set. If the target module does not have access
* to any interface types, IllegalAccessError will be thrown by the VM
* at defineClass time.
*
* Must call the checkProxyAccess method to perform permission checks
* before calling this.
*/
Constructor<?> build() {
Class<?> proxyClass = defineProxyClass(module, interfaces);
final Constructor<?> cons;
try {
cons = proxyClass.getConstructor(constructorParams);
} catch (NoSuchMethodException e) {
throw new InternalError(e.toString(), e);
}
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
cons.setAccessible(true);
return null;
}
});
return cons;
}
  • java.lang.reflect.Proxy.ProxyBuilder#defineProxyClass
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
private static Class<?> defineProxyClass(Module m, List<Class<?>> interfaces) {
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; // non-public, final
String pkg = intf.getPackageName();
if (proxyPkg == null) {
proxyPkg = pkg;
} else if (!pkg.equals(proxyPkg)) {
throw new IllegalArgumentException(
"non-public interfaces from different packages");
}
}
}

if (proxyPkg == null) {
// all proxy interfaces are public
proxyPkg = m.isNamed() ? PROXY_PACKAGE_PREFIX + "." + m.getName()
: PROXY_PACKAGE_PREFIX;
} else if (proxyPkg.isEmpty() && m.isNamed()) {
throw new IllegalArgumentException(
"Unnamed package cannot be added to " + m);
}

if (m.isNamed()) {
if (!m.getDescriptor().packages().contains(proxyPkg)) {
throw new InternalError(proxyPkg + " not exist in " + m.getName());
}
}

/*
* Choose a name for the proxy class to generate.
*/
long num = nextUniqueNumber.getAndIncrement();
// private static final String proxyClassNamePrefix = "$Proxy";
String proxyName = proxyPkg.isEmpty()
? proxyClassNamePrefix + num
: proxyPkg + "." + proxyClassNamePrefix + num;

ClassLoader loader = getLoader(m);
trace(proxyName, m, loader, interfaces);

/*
* Generate the specified proxy class.
*/
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
proxyName, interfaces.toArray(EMPTY_CLASS_ARRAY), accessFlags);
try {
Class<?> pc = UNSAFE.defineClass(proxyName, proxyClassFile,
0, proxyClassFile.length,
loader, null);
reverseProxyCache.sub(pc).putIfAbsent(loader, Boolean.TRUE);
return pc;
} 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());
}
}
  • 如果接口被定义为public公有,那么默认会使用com.sun.proxy作为包名,类名是$Proxy加上一个自增的整数值,初始时是0,因此生成的文件名是$Proxy0,完整类名为com.sun.proxy.$Proxy0
  • 如果接口被定义为non-public或者final,那么代理类的包名为接口的包名,如接口完整包名为com.holelin.sundry.test.TestInterface, 则生产的代理类完整类名为com.holelin.sundry.test.$Proxy0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
/**
* 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
*/
static byte[] generateProxyClass(final String name,
Class<?>[] interfaces,
int accessFlags)
{
ProxyGenerator gen = new ProxyGenerator(name, interfaces, accessFlags);
final byte[] classFile = gen.generateClassFile();

/** debugging flag for saving generated class files */
// private static final boolean saveGeneratedFiles =
// java.security.AccessController.doPrivileged(
// new GetBooleanAction(
// "jdk.proxy.ProxyGenerator.saveGeneratedFiles")).booleanValue();
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 = Path.of(name.substring(0, i).replace('.', File.separatorChar));
Files.createDirectories(dir);
path = dir.resolve(name.substring(i+1, name.length()) + ".class");
} else {
path = Path.of(name + ".class");
}
Files.write(path, classFile);
return null;
} catch (IOException e) {
throw new InternalError(
"I/O exception saving generated file: " + e);
}
}
});
}

return classFile;
}
  • saveGeneratedFilestrue,那么会把字节码存储到硬盘上的class文件中,否则不会保存临时的字节码文件.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// jdk.internal.misc.Unsafe#defineClass
public Class<?> defineClass(String name, byte[] b, int off, int len,
ClassLoader loader,
ProtectionDomain protectionDomain) {
if (b == null) {
throw new NullPointerException();
}
if (len < 0) {
throw new ArrayIndexOutOfBoundsException();
}

return defineClass0(name, b, off, len, loader, protectionDomain);
}
public native Class<?> defineClass0(String name, byte[] b, int off, int len,
ClassLoader loader,
ProtectionDomain protectionDomain);
  • 最后,调用本地方法defineClass0生成Class对象

为什么JDK动态代理只能基于接口实现

  • 要扩展一个类有常见的两种方式,继承父类实现接口.JDK选择继承父类Proxy,并把InvocationHandler存在父类的对象中,通过父类Proxy的构造方法,保存了创建代理对象过程中传进来的InvocationHandler的实例,使用protected修饰保证了它可以在子类中被访问和使用.但是同时,因为Java是单继承的,因此在继承了Proxy后,只能通过实现目标接口的方式来实现方法的扩展,达到我们增强目标方法逻辑的目的.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
package com.holelin.sundry.test.common.reflect;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;

final class $Proxy0 extends Proxy implements InnerTest.InnerInterface {
private static Method m1;
private static Method m3;
private static Method m2;
private static Method m0;

public $Proxy0(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 void run() throws {
try {
super.h.invoke(this, m3, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}

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

static {
try {
m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
m3 = Class.forName("com.holelin.sundry.test.common.reflect.InnerTest$InnerInterface").getMethod("run");
m2 = Class.forName("java.lang.Object").getMethod("toString");
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
}