0%

Java动态代理源码解析

前言: 动态代理是Java高级编程思想中比较重要的一块,《Java核心技术:卷一》中称其为系统设计开发者必须掌握的一门技术。JAVA RMI API(远程方法调用)中就利用了动态代理的思想实现了客户端代理类与服务端代理类之间的直接交互,而开发者并不需要了解底层的连接细节,实现了代码的解耦,让开发者可以专注业务逻辑的实现而无需关心底层细节。本文将首先给出动态代理的机制,接着结合一个动态代理的实现例子来追踪分析源码。

一. 动态代理机制

代理是一种常用的设计模式,其目的就是为其他对象提供一个代理以控制对某个对象的访问。代理类负责为委托类预处理消息,过滤消息并转发消息,以及进行消息被委托类执行后的后续处理[^1]

[^1]:Java 动态代理机制分析及扩展,第 1 部分

  1. 预定义一个实际类RealSubject,并让其实现某个接口Subject中的某个方法x,该方法即我们的代理类需要调用的
  2. 定义一个ProxyHandler implements InvocationHandler
  3. 利用Proxy的静态方法newProxyInstance生成代理对象
  4. 通过代理对象调用方法x

二. 例子

Dynamic Proxy Test

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package dynamic;

import java.lang.reflect.Proxy;

/**
* 动态代理
*
* @author xcw
*/
public class DynamicProxy {

public static void main(String[] args) {
RealSubject realSubject = new RealSubject(); //1.创建委托对象
ProxyHandler handler = new ProxyHandler(realSubject); //2.创建调用处理器对象
Subject proxySubject = (Subject) Proxy.newProxyInstance(RealSubject.class.getClassLoader(),
RealSubject.class.getInterfaces(), handler); //3.动态生成代理对象
proxySubject.request(); //4.通过代理对象调用方法
}
}

RealSubject

1
2
3
4
5
6
7
8
9
10
11
12
13
package dynamic;

/**
* 实际类
*
* @author xcw
*/
public class RealSubject implements Subject {

public void request() {
System.out.println("====RealSubject Request====");
}
}

ProxyHandler

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
package dynamic;

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

/**
* TODO 类描述
*
* @author xcw
*/
public class ProxyHandler implements InvocationHandler {
private Subject subject;
public ProxyHandler(Subject subject){
this.subject = subject;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
System.out.println("====before====");//定义预处理的工作,当然你也可以根据 method 的不同进行不同的预处理工作
Object result = method.invoke(subject, args);
System.out.println("====after====");
return result;
}

}

Subject接口

1
2
3
4
5
6
7
8
9
10
11
package dynamic;

/**
* Subject接口
*
* @author xcw
*/
public interface Subject {

void request();
}

三. 源码解析

创建调用处理器对象

1.定义ProxyHandler

1
ProxyHandler handler = new ProxyHandler(realSubject);   //2.创建调用处理器对象

2.ProxyHandler implements 了InvocationHanler这个接口,我们先来了解一下InvocationHandler是什么

官方注释

InvocationHandler is the interface implemented by
the invocation handler of a proxy instance.
Each proxy instance has an associated invocation handler.
When a method is invoked on a proxy instance, the method
invocation is encoded and dispatched to the invoke
method of its invocation handler.

这段话翻译一下就是每一个代理对象在调用某个方法时,它的本质都是调用了InvocationHandler其中的invoke方法

3.接下来我们着重看一下InvocationHanler接口下的invoke这个方法

首先是官方文档对其的描述:

Processes a method invocation on a proxy instance and returns the result. This method will be invoked on an invocation handler when a method is invoked on a proxy instance that it is associated with.

解释一下传入的三个参数:

  • proxy: the proxy instance that the method was invoked on (代理)
  • method: the Method instance corresponding to the interface method invoked on the proxy instance(java.lang.Method提供了关于某个方法的一系列信息)
  • args: an array of objects containing the values of the arguments passed in the method invocation on the proxy instance(某个参数需要传入的argument的数组 type 为 Object[])

4. 我们看一下我们对invoke这个函数究竟做了什么

首先我们为它自定义了一系列预处理操作,然后我们调用了method.invoke方法执行了ReaSubject中我们想要访问的方法x,最后我们又做了一系列收尾工作,愉快的退出了我们的invoke方法

1
2
3
4
5
6
7
8
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
System.out.println("====before====");//定义预处理的工作,当然你也可以根据 method 的不同进行不同的预处理工作
Object result = method.invoke(subject, args);
System.out.println("====after====");
return result;
}

接下来是重头戏, method.invoke(subject,args),注意,这里的invoke方法是定义在Method Class 下的一个instance function

1
Object result = method.invoke(subject, args);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@CallerSensitive
public Object invoke(Object obj, Object... args)
throws IllegalAccessException, IllegalArgumentException,
InvocationTargetException
{
if (!override) {
if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) {
Class<?> caller = Reflection.getCallerClass();
checkAccess(caller, clazz, obj, modifiers);
}
}
MethodAccessor ma = methodAccessor; // read volatile
if (ma == null) {
ma = acquireMethodAccessor();
}
return ma.invoke(obj, args);
}

这段代码干的事情简单来说就是通过反射的思想拿到了此method的caller class,并执行方法.当然如果深究下去,就涉及到许多反射的知识了,所以在这里掠过,之后单独写一篇关于反射原理的吧:>

动态生成代理对象

1
2
Subject proxySubject = (Subject) Proxy.newProxyInstance(RealSubject.class.getClassLoader(),
RealSubject.class.getInterfaces(), handler); //3.动态生成代理对象

1.Proxy中的静态方法newProxyInstance为我们返回了一个代理对象

2.我们看一下传入的三个参数

  • loader: the class loader to define the proxy class
  • interfaces: the list of interfaces for the proxy class to implement
  • h: the invocation handler to dispatch method invocations to

3.我们把源码拆解一下,这个函数主要做的操作

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

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

1.我们首先利用getProxyClass0拿到了我们需要的代理class,type Class

1
2
3
//Look up or generate the designated proxy class.

Class<?> cl = getProxyClass0(loader, intfs);

2.接着我们通过代理class拿到了我们代理类的构造函数

1
final Constructor<?> cons = cl.getConstructor(constructorParams);

3.最后我们愉快的通过反射构造了一个代理类

1
return cons.newInstance(new Object[]{h});

注意这里我们传入了h,也就是我们之前自己定义的实现InvocationHanler的ProxyHandler,看到这一步的时候顿时有一种茅塞顿开的感觉,为什么这么说呢,因为其实我们知道,所有生成的代理类,无论是静态还是动态,它都必须继承我们的Proxy类,然后我们来看一下Proxy类的源码

1
protected InvocationHandler h;

这里定义了我们的小h,然后我们再来看一看Proxy类的构造函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/**
* Constructs a new {@code Proxy} instance from a subclass
* (typically, a dynamic proxy class) with the specified value
* for its invocation handler.
*
* @param h the invocation handler for this proxy instance
*
* @throws NullPointerException if the given invocation handler, {@code h},
* is {@code null}.
*/
protected Proxy(InvocationHandler h) {
Objects.requireNonNull(h);
this.h = h;
}

没错,到这一步,我们终于走完了我们所有的流程,也就是我们终于构造了我们的动态代理对象,并且我们也知道了我们在调用x方法时jvm所经历的所有路程,其实这里还遗留了一点magic,就是我们的动态代理类它到底长啥样呢,这里由于是jvm底层做的操作,源码并没有能追踪到,不过我们可以大致想象出来,这边我直接粘贴一下某位大佬的代码了^2

1
2
3
4
5
6
7
8
9
10
11
public final class $Proxy1 extends Proxy implements Subject{
private InvocationHandler h;
private $Proxy1(){}
public $Proxy1(InvocationHandler h){
this.h = h;
}
public int request(int i){
Method method = Subject.class.getMethod("request", new Class[]{int.class}); //创建method对象
return (Integer)h.invoke(this, method, new Object[]{new Integer(i)}); //调用了invoke方法
}
}

四. 总结

其实追踪源码的过程还是挺枯燥又费时的,并且涉及到的java反射知识繁多复杂,但一旦理清整体的脉络就会有一种茅塞顿开的感觉,它能帮助你更好的理解一些框架的底层原理,同时也深刻意识到Java反射大法好,还要继续学习:>