java原生动态代理JDK Proxy

Java在JDK1.3后引入的动态代理机制,使我们可以在运行期动态的创建代理类。

本文基于JDK14编写,JDK8后proxy类有较大变化,源代码和配置均不相同.

使用动态代理实现AOP需要四个角色:

  • 被代理的类、
  • 被代理类的接口、
  • 织入器(Proxy.newProxyInstance())、
  • InvocationHandler。

织入器使用接口反射机制生成一个代理类,然后在这个代理类中织入代码(切入逻辑)。InvocationHandler是切面,包含了Advice和Pointcut。

Java proxy源代码分析

JDK proxy的入口是Proxy.newProxyInstance(),其接口声明如下:

 public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h) 

java.lang.reflect.Proxy做了三个事情:

  1. 判断是否有相应接口的缓存,如果有直接活动代理类的构造器缓存.
  2. 如果是第一次创建该Class文件时,会创建ProxyBuilder并调用build()方法中.
  3. 使用InvocationHandler作为构造参数创建代理类的实例.

由此可见,由于缓存,对单一接口Proxy并不会导致jvm堆空间的暴增.但在实现多个接口时,缓存无法命中,动态代理在运行期通过接口动态生成代理类。使用反射大量生成类文件可能引起Full GC造成性能影响,因为字节码文件加载后会 存放在JVM运行时区的方法区中(或持久代)。当方法区满的时候,会引起Full GC。因此当大量使用动态代理时,可以将持久代设置大一些,减少Full GC次数。

Proxy的序列图
在这里插入图片描述

ProxyBuilder负责对需要生成的class类的信息的组装:

  1. 拼装代理类包名和类名,组织需要代理的方法
  2. 调用ProxyGenerator生成.class 文件的字节码。
  3. 调用ClassLoader native方法,传入字节码,生成Class对象。

核心类图
在这里插入图片描述
ProxyGenerator真正实现接口声明到字节码的转换,它继承了asm的ClassWriter抽象类.由于担心与其他工程同样使用ASM的应用版本冲突,JDK直接复制了ASM相关代码到新的包中.

查看生成的Class文件

ProxyGenerator.generateProxyClass()中有这样一个逻辑:

if(saveGeneratedFiles) {
    //...
    //name生成代理类的全路径名
    //.class文件在系统运行的当前路径下的代理文件包名换成路径名.
    Path dir = Path.of(dotToSlash(name.substring(0, i)));
    Files.createDirectories(dir);
    path = dir.resolve(name.substring(i + 1) + ".class");
    //...
    Files.write(path, classFile);
    //...
 }

saveGeneratedFiles这个变量定义:

private final static boolean saveGeneratedFiles =
    java.security.AccessController.doPrivileged( 
    new GetBooleanAction("jdk.proxy.ProxyGenerator.saveGeneratedFiles"))
    .booleanValue();

所以可以在main方法中做以下设置就可以看到生成的Proxy的Class文件了

System.getProperties().setProperty("jdk.proxy.ProxyGenerator.saveGeneratedFiles", "true");

定义一个Action接口

public interface Action {
    void doSomething();
}

创建一个Action的代理

public static void main(String[] args) {
    System.getProperties().setProperty("jdk.proxy.ProxyGenerator.saveGeneratedFiles", "true");
    RealObject realObject = new RealObject();
    Action proxy = (Action) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), new Class[]{Action.class}, new DynamicProxyHandler(realObject));
    proxy.doSomething();
}

反编译.class文件

//com.sun,proxy.$Proxy0.class
package com.sun.proxy;
public final class $Proxy0 extends Proxy implements Action {
    public $Proxy0(InvocationHandler param1) {
        super(var1);
    }
    public final void doSomething() {
        try {
            super.h.invoke(this, m3, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }
}

newInstance生成Proxy实例时,通过$Proxy0的Class对象,选择了这个InvocationHandler为参数的构造方法,传入我们定义的InvocationHandler并生成了一个 Proxy0的实例.InvocationHandler 里有realObject的逻辑以及我们的扩展逻辑,当我们调用Proxy0的doSomething方法时,就会调用到我们InvocationHandler 里 实现的invoke方法。

参考文献

  1. 死磕Spring之AOP篇 - 初识JDK、CGLIB两种动态代理

附录

  • 表达式expression:一个表达式回产生一个值
    Expressions only contain identifiers, literals and operators, where operators include arithmetic and boolean operators, the function call operator () the subscription operator [] and similar, and can be reduced to some kind of “value”.
  • 语句Statement: 一个语句即执行一个动作,完成特定任务
    Statements represent an action or command e.g print statements, assignment statements. Statements are everything that can make up a line (or several lines) of Python code. Note that expressions are statements as well. A statement does something.
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值