本篇将从源码层面分析,JDK代理的具体实现方式。
摘录源码版本:JDK 1.8
概述
我们知道,在Spring AOP 中,创建代理有两种方式,jdk动态代理与cglib动态代理。本篇先讲一下JDK动态代理的低层原理。
JDK动态代理是使用了Java的反射机制,利用Proxy生成实现了目标接口的代理类。
大纲:
- JDK 动态代理实现步骤
- JDK 动态代理使用方法
- InvocationHander
- Proxy.newProxyInstance
- Proxy 生成代理类的源码解析
JDK Proxy 实现原理
关键接口: java.lang.reflect.InvocationHandler
关键类: java.lang.reflect.Proxy
JDK动态代理实现步骤
JDK实现动态代理简单来说有五个步骤:
1,创建自定义InvocationHander,通过继承 InvocationHandler接口,来实现自定义的 invoke操作。(AOP逻辑就在 invoke() 方法中)
2,通过 Proxy.newProxyInstance
方法,生成并获取代理类的实例对象。
3,在 Proxy.newProxyInstance
方法中,通过反射,获取到代理类的构造方法,方法签名为 getConstructor(InvocationHandler.class)
4,通过构造函数生成Proxy类的对象,并且生成时,将自定义的InvocationHandler实例对象作为参数传入。
5,通过代理对象调用目标方法。
JDK动态代理的使用
通过以下代码,可以看到是如何利用 InvocationHander 和 Proxy 生成代理对象并且使用代理的。
示例代码如下:
// 自定义 InvocationHandler (也可以看做是代理)
public class JDKProxy implements InvocationHandler {
// 要代理的目标实例
private Object proxyObject;
public JDKProxy(Object proxyObject) {
this.proxyObject = proxyObject;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// do something
Object ret = null; //方法返回值
System.out.println("JDK Proxy --- 调用前 --- 调用方法是:"+method.getName() + "---参数是:"+ GsonUtil.toJson(args));
ret = method.invoke(proxyObject, args); //对目标实例调用invoke方法
System.out.println("JDK Proxy --- 调用后");
return ret;
}
}
// 生成代理类的工厂方法
public static Object createJDKProxyInstance(Object proxyObject){
JDKProxy jdkProxy = new JDKProxy(proxyObject);
return Proxy.newProxyInstance(proxyObject.getClass().getClassLoader(), proxyObject.getClass().getInterfaces(), jdkProxy);
}
// 具体使用的代码
DemoManager demoProxy = (DemoManager) ProxyFactory.createJDKProxyInstance(new DemoManagerCustom());
InvocationHandler
先来看看 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 是一个接口,每一个需要 自定义 方法调用处理器的 代理实例都要实现这个接口。
每一个代理实例都关联着一个 invocation handler。当代理实例的方法被调用的时候,这个方法就会编码和转发到 invocation handler的 invoke 方法调用。
这个接口只有一个方法:invoke()
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable;
这个方法接受三个参数:
- proxy:指我们生成的代理对象实例
- method:当前调用的method对象
- args:调用method的方法参数列表。
Proxy
Proxy类,就是用来创建动态代理对象的类。类注释如下:
Proxy provides static methods for creating dynamic proxy classes and instances, and it is also the superclass of all dynamic proxy classes created by those methods
Proxy 提供了创建动态代理类和实例的静态方法,同时这些动态代理类的超类也会在这些方法里被创建。
Proxy类提供了许多方法,动态代理用的方法是 newProxyInstance
,使用方式在示例代码中已经给出。
我们可以看看源码中方法的定义:
/**
* Returns an instance of a proxy class for the specified interfaces
* that dispatches method invocations to the specified invocation
* handler.
* @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
**/
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
该方法接收三个参数
loader:一个ClassLoader对象,定义了由哪个ClassLoader来对生成的代理类进行加载。
interfaces:代理类的继承接口数组,表示的是我需要给代理的类提供的接口,如果我提供了接口给这个代理类,那么这个代理对象就声称实现了这些接口,那么我们就可以调用这些接口的方法。
(JDK动态代理只能对实现了接口的目标对象代理的根本原因)h:方法调用转发的目的 invocation hander 实例。表示这个代理对象会关联到哪个 invocation handler 上。
JDK动态代理原理
通过上面 InvocationHandler 和 Proxy 的介绍。那么动态代理的基本原理就可以捋清了:
1)通过继承 InvocationHander,来自定义一个 invocation hander。这个hander 保存了一个代理目标对象的引用。
2)在实现 invoke() 方法中,我们把接收到的方法调用请求,用proxyObject来执行。即 method.invoke(proxyObject, args)
,得到代理对象的方法调用结果并返回。
3)在 invoke 方法中嵌入 AOP 相关的处理逻辑。这样就实现了目标方法调用时,横切逻辑的执行。
4)代理对象的生成,使用 Proxy.newProxyInstance()
方法生成,传入的三个参数依次为,目标对象的类加载器,目标对象实现的接口列表,invocation handler 实例。
5)这样生成的代理对象,就是继承了目标对象接口列表的代理类的实例对象。
使用向上转型的方式,把对象声明为接口的实例。并且和目标对象有一样的方法。
6)利用多态的机制,对接口对象的方法调用,会动态链接到代理对象的方法调用,然后又会转发到我们自定义 invocation hander 中,对 invoke 函数的调用,实现横切逻辑的调用。
源码解析
通过Debug模式查看代理对像的类型可以发现
Proxy.newProxyInstance() 生成的代理类名称为 com.sun.proxy.$Proxy0
。这个类是JVM在运行时动态生成的,以$开头,Proxy为中间,最后的序号表示类对象的序号。
(生成代理类的逻辑可以参考 Proxy.ProxyClassFactory,下面放出了源码)
其中有一个公共的构造器:
public com.sun.proxy.$Proxy0(java.lang.reflect.InvocationHandler)
,
那么可以知道,我们的代理实例对象,就是用这个构造器生成的。
如图:
Proxy.newProxyInstance
/** parameter types of a proxy class constructor */
private static final Class<?>[] constructorParams =
{ InvocationHandler.class };
@CallerSensitive
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
{
Objects.requireNonNull(h); //h 不能为空,所以 InvocationHandler 是必须的
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.
* 调用这个代理类的构造器,传入指定的 invocation handler 对象
*/
try {
if (sm != null) { //权限校验,不细讲
checkNewProxyPermission(Reflection.getCallerClass(), cl);
}
// 获取参数是 InvocationHander.class 类型的构造器
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);
}
}
getProxyClass0
在上面方法中可以看到,生成动态代理类的方法,是调用了
getProxyClass0
方法。
而其中关键在于 proxyClassCache.get(loader, interfaces);
根据该方法的注释可以明白,这里有一个代理类的缓存,如果这个代理类已经 有指定类加载器并且继承了这些接口,那么将直接返回生成的代理类,否则将由ProxyClassFactory生成代理类。
那么看看这个方法的源码:
/**
* a cache of proxy classes(一个已经生成了代理类的缓存)
*/
private static final WeakCache<ClassLoader, Class<?>[], Class<?>>
proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());
private static Class<?> getProxyClass0(ClassLoader loader,
Class<?>... interfaces) {
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);
}
ProxyClassFactory
创建类的核心还是在 ProxyClassFactory 类中。
可以看到 ProxyClassFactory 实现了函数式接口 BiFunction。(JDK8)
入参是 类加载器(ClassLoader) 和 接口数组(Class<?>[])
/**
* 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
//(所有生成的代理类前缀都是 $Proxy)
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);
// 遍历接口数组,校验:
// 1)接口是否能找到,加载到的实例是否和传入的接口实例一样
// 2)校验加载的实例是否是一个接口
// 3)接口去重
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.
* 生成类的字节码,然后通过本地方法 defineClass0 生成代理类
*/
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());
}
}
}
(如果有什么错误或者建议,欢迎留言指出)
(本文内容是对各个知识点的转载整理,用于个人技术沉淀,以及大家学习交流用)
参考资料:
深度剖析JDK动态代理机制(JDK Proxy源码分析)
java动态代理机制详解
静态代理和动态代理的理解
Spring源码剖析——AOP实现原理 (Spring 使用JDK Proxy )
SpringAOP两种方式——JDKDynamicAopProxy和cglib2AopProxy源码解析 (Spring 使用JDK Proxy )