动态代理理论及Mybatis实战
动态代理在我们平时编程并不常用,但因为Spring AOP以及大量框架均使用动态代理机制。
所以必须要掌握。

动态代理其本质是:PrxoyGenerator.generateProxyClass生成字节码文件,然后通defineClass0加载这个类
说白了是重生成了一个字节码文件。

那先看下源码,在来简单实践一下

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

//通过getProxyClass0方法动态生成Class类
Class<?> cl = getProxyClass0(loader, intfs);

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

该方法首先创建了一个接口的克隆,之后检查权限。
最重要的一步是Class<?> cl = getProxyClass0(loader, intfs)
Java通过该方法中proxyClassCache.get()方法拿到生成的字节码

1
2
3
4
5
6
7
8
9
10
11
private static Class<?> getProxyClass0(ClassLoader loader,
Class<?>... interfaces) {
//首先,限制一个类接口不能多余65535
if (interfaces.length > 65535) {
throw new IllegalArgumentException("interface limit exceeded");
}

// private static final WeakCache<ClassLoader, Class<?>[], Class<?>>
// proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());
return proxyClassCache.get(loader, interfaces);
}

proxyClassCache.get()方法的部分实现

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
//最终通过 supplier.get()返回对象
while (true) {
if (supplier != null) {
// supplier might be a Factory or a CacheValue<V> instance
V value = supplier.get();
if (value != null) {
return value;
}
}

if (factory == null) {
factory = new Factory(key, parameter, subKey, valuesMap);
}

if (supplier == null) {
supplier = valuesMap.putIfAbsent(subKey, factory);
if (supplier == null) {
// successfully installed Factory
supplier = factory;
}
// else retry with winning supplier
} else {
if (valuesMap.replace(subKey, supplier, factory)) {
// successfully replaced
// cleared CacheEntry / unsuccessful Factory
// with our Factory
supplier = factory;
} else {
// retry with current supplier
supplier = valuesMap.get(subKey);
}
}
}
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
//supplier.get()的实现
public synchronized V get() { // serialize access
Supplier<V> supplier = valuesMap.get(subKey);
if (supplier != this) {
return null;
}
// else still us (supplier == this)

// create new value
V value = null;
try {
value = Objects.requireNonNull(valueFactory.apply(key, parameter));
} finally {
if (value == null) { // remove us on failure
valuesMap.remove(subKey, this);
}
}
// the only path to reach here is with non-null value
assert value != null;

// wrap value with CacheValue (WeakReference)
CacheValue<V> cacheValue = new CacheValue<>(value);

// put into reverseMap
reverseMap.put(cacheValue, Boolean.TRUE);

// try replacing us with CacheValue (this should always succeed)
if (!valuesMap.replace(subKey, this, cacheValue)) {
throw new AssertionError("Should not reach here");
}

// successfully replaced us with new CacheValue -> return the value
// wrapped by it
return value;
}

之后回过头看apply方法(value = Objects.requireNonNull(valueFactory.apply(key, parameter));
找到类:ProxyClassFactory

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
//·········省略部分验证代码
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) {
throw new IllegalArgumentException(e.toString());
}

其关键部分位于PrxoyGenerator.generateProxyClass生成代理类字节码文件。

之后返回newProxyInstance方法,看这个类如何被实例化,

1
2
3
4
private static final Class<?>[] constructorParams = { InvocationHandler.class };
····
final Constructor<?> cons = cl.getConstructor(constructorParams);
return cons.newInstance(new Object[]{h});

首先,获取了带参数InvocationHandler类的构造器,然后通过构造器,往里看最后通过调用NativeConstructorAccessorImpl的本地方法实例化了这个类,这样就完成了整个代理过程

接下来看下一个实践,知道原理还是要用一下:

1
2
3
4
public interface Subject {
//先做一个接口,里面只有一个方法
void sayHello(String str);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

//实现InvocationHandler接口
public class Invoke implements InvocationHandler {
//这个就是我们要代理的真实对象
private Object subject;

//构造方法,给我们要代理的真实对象赋初值
public Invoke(Object subject) {
this.subject = subject;
}

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//doSomeThing
System.out.println("即将被代理的方法");
method.invoke(subject, args);
return null;
}
}

以上就完成了代理类的构建,接下倆我们去调用一下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import java.lang.reflect.Proxy;

public class doMain {

public static void main(String[] args){
Subject subjectImpl = str -> System.out.println("Hello:" + str);

Invoke invoke = new Invoke(subjectImpl);

Subject subject = (Subject) Proxy.newProxyInstance(subjectImpl.getClass().getClassLoader(), subjectImpl.getClass().getInterfaces(), invoke);

//并未直接调用subjectImpl,而是调用生成的代理类
subject.sayHello("test");
}
}
/* 控制台输出:
* 即将被代理的方法
* Hello:test
*/