文档章节

动态代理原理

markGao
 markGao
发布于 2014/01/02 11:37
字数 1060
阅读 288
收藏 11

生成代理类

//获取代理类
Class cl = getProxyClass(loader, interfaces);
//获取带有InvocationHandler参数的构造方法
Constructor cons = cl.getConstructor(constructorParams);
//把handler传入构造方法生成实例
return (Object) cons.newInstance(new Object[] { h });  
其中getProxyClass(loader, interfaces)方法用于获取代理类,它主要做了三件事情:在当前类加载器的缓存里搜索是否有代理类,没有则生成代理类并缓存在本地JVM里。清单三:查找代理类。

 // 缓存的key使用接口名称生成的List
Object key = Arrays.asList(interfaceNames);
synchronized (cache) {
    do {
Object value = cache.get(key);
         // 缓存里保存了代理类的引用
if (value instanceof Reference) {
    proxyClass = (Class) ((Reference) value).get();
}
if (proxyClass != null) {
// 代理类已经存在则返回
    return proxyClass;
} else if (value == pendingGenerationMarker) {
    // 如果代理类正在产生,则等待
    try {
cache.wait();
    } catch (InterruptedException e) {
    }
    continue;
} else {
    //没有代理类,则标记代理准备生成
    cache.put(key, pendingGenerationMarker);
    break;
}
    } while (true);
}

代理类的生成主要是以下这两行代码。 清单四:生成并加载代理类

//生成代理类的字节码文件并保存到硬盘中(默认不保存到硬盘)
proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces);
//使用类加载器将字节码加载到内存中
proxyClass = defineClass0(loader, proxyName,proxyClassFile, 0, proxyClassFile.length);

ProxyGenerator.generateProxyClass()方法属于sun.misc包下,Oracle并没有提供源代码,但是我们可以使用JD-GUI这样的反编译软件打开jre\lib\rt.jar来一探究竟,以下是其核心代码的分析。
清单五:代理类的生成过程


//添加接口中定义的方法,此时方法体为空
for (int i = 0; i < this.interfaces.length; i++) {
  localObject1 = this.interfaces[i].getMethods();
  for (int k = 0; k < localObject1.length; k++) {
     addProxyMethod(localObject1[k], this.interfaces[i]);
  }
}

//添加一个带有InvocationHandler的构造方法
MethodInfo localMethodInfo = new MethodInfo("<init>", "(Ljava/lang/reflect/InvocationHandler;)V", 1);

//循环生成方法体代码(省略)
//方法体里生成调用InvocationHandler的invoke方法代码。(此处有所省略)
this.cp.getInterfaceMethodRef("InvocationHandler", "invoke", "Object; Method; Object;")

//将生成的字节码,写入硬盘,前面有个if判断,默认情况下不保存到硬盘。
localFileOutputStream = new FileOutputStream(ProxyGenerator.access$000(this.val$name) + ".class");
localFileOutputStream.write(this.val$classFile); 


  那么通过以上分析,我们可以推出动态代理为我们生成了一个这样的代理类。把方法doSomeThing的方法体修改为调用LogInvocationHandler的invoke方法。
清单六:生成的代理类源码

package sample.proxy;

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

public class ProxyBusiness{

    private LogInvocationHandler h;

    public void doBusiness2() {
        try {
            Method m = (h.target).getClass().getMethod("doBusiness2", null);
            h.invoke(this, m, null);
        } catch (Throwable e) {
            // 异常处理(略)
        }
    }

    public boolean doBusiness1() {
        try {
            Method m = (h.target).getClass().getMethod("doBusiness1", null);
            return (Boolean) h.invoke(this, m, null);
        } catch (Throwable e) {
            // 异常处理(略)
        }
        return false;
    }

    public ProxyBusiness(LogInvocationHandler h) {
        this.h = h;
    }
    
    /**
     * 打印日志的切面
     */
    public static class LogInvocationHandler implements InvocationHandler {

        private Object target; // 目标对象

        LogInvocationHandler(Object target) {
            this.target = target;
        }

        public Object invoke(Object proxy, Method method, Object[] args)
                throws Throwable {
            // 执行原有逻辑
            Object rev = method.invoke(target, args);
            // 执行织入的日志,你可以控制哪些方法执行切入逻辑
            if (method.getName().equals("doBusiness2")) {
                System.out.println("记录日志");
            }
            return rev;
        }
    }

    // 测试用
    public static void main(String[] args) {
        // 构建AOP的Advice
        LogInvocationHandler handler = new LogInvocationHandler(new Business());
        new ProxyBusiness(handler).doBusiness1();
        new ProxyBusiness(handler).doBusiness2();
    }
}

小结 
    从前两节的分析我们可以看出,动态代理在运行期通过接口动态生成代理类,这为其带来了一定的灵活性,但这个灵活性却带来了两个问题,第一代理类必须实现一 个接口,如果没实现接口会抛出一个异常。第二性能影响,因为动态代理使用反射的机制实现的,首先反射肯定比直接调用要慢,经过测试大概每个代理类比静态代 理多出10几毫秒的消耗。其次使用反射大量生成类文件可能引起Full GC造成性能影响,因为字节码文件加载后会存放在JVM运行时区的方法区(或者叫持久代)中,当方法区满的时候,会引起Full GC,所以当你大量使用动态代理时,可以将持久代设置大一些,减少Full GC次数。

© 著作权归作者所有

共有 人打赏支持
markGao
粉丝 15
博文 187
码字总数 91352
作品 0
宝山
程序员
私信 提问
静态代理与动态代理实现与原理

基础代码准备 接口类: public interface IUser { /** * 判断用户的权限 * @param uid 用户的UID * @return / public boolean isAuthUser(int uid);} 实现类: /* * 类的实现 * @author Jaso......

ifree613
2016/03/11
892
0
代理模式

代理模式一般分为两种,即静态代理和动态代理,静态代理限制比较严格,代理类和委托类必须实现相同的接口;而动态代理则更加灵活,除了jdk的动态代理,其他的代理方式,如cglib和javassist则...

high_m
2017/11/15
0
0
JDK动态代理源码学习

继上一篇博客设计模式之代理模式学习之后http://blog.csdn.net/u014427391/article/details/75115928,本博客介绍JDK动态代理的实现原理,学习一下JDK动态代理的源码。 Proxy类。该类即为动态...

Javahih
2017/07/21
0
0
Spring事务管理机制的实现原理-动态代理 .

分析一下Spring事务管理机制的实现原理。由于Spring内置AOP默认使用动态代理模式实现,我们就先来分析一下动态代理模式的实现方 法。动态代理模式的核心就在于代码中不出现与具体应用层相关联...

swk998741
2014/03/17
0
0
java jdk与cglib动态代理模式的认识和实现

1.使用java jdk Proxy实现动态代理,该原理是反射机制。 建立一个普通的接口 package com.tester.cls.design.mode; public interface IUser { public String getName(); public void setNam......

IamOkay
2014/10/30
0
0

没有更多内容

加载失败,请刷新页面

加载更多

09.ajax局部渲染---《Beetl视频课程》

本期视频实现分类实时获取; 内容简介:使用了局部渲染技术,实现分类的实时获取 一起学beetl目录:https://my.oschina.net/u/1590490?tab=newest&catalogId=6214598 作者:GK Beetl满足了更...

Gavin-King
7分钟前
0
0
同步访问共享的可变数据(66)

关键字synchronized 保证同一时刻,只有一个线程执行某一个方法或代码块 当一个对象被一个线程修改时,可以阻止其他线程看到其内部的不一致状态 正确的使用同步可以避免任何对象看到其不一致...

Java搬砖工程师
9分钟前
0
0
银行卡二要素真实性查询

验证用户的银行卡号、持卡人姓名是否真实。 示例代码: private static String host = "https://bank.market.alicloudapi.com";private static String path = "/bank2";private sta...

貔貅叔
13分钟前
0
0
iOS补位动画、沙漏效果、移动UITableViewCell、模拟贪吃蛇、拖拽进度等源码

iOS精选源码 JHAlertView - 一款黑白配色的HUD之沙漏效果 继承UIButton的自定义按钮SPButton 用递归算法实现iOS补位动画 iOS 长按移动UITableViewCell JHLikeButton - 有趣的点赞动画 兼容X...

Android爱开源
23分钟前
0
0
上币至iamToken

https://github.com/consenlabs/token-profile 点击Fork按钮,插入到自己的github项目中 cd /Users/shijun/Desktop/blockChain/iamToken git clone https://github.com/yellmi1983/token-pro......

八戒八戒八戒
27分钟前
0
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部