文档章节

动态代理原理

markGao
 markGao
发布于 2014/01/02 11:37
字数 1060
阅读 287
收藏 11
点赞 0
评论 0

生成代理类

//获取代理类
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
博文 151
码字总数 91352
作品 0
宝山
程序员
静态代理与动态代理实现与原理

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

ifree613 ⋅ 2016/03/11 ⋅ 0

代理模式

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

high_m ⋅ 2017/11/15 ⋅ 0

Spring事务管理机制的实现原理-动态代理 .

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

swk998741 ⋅ 2014/03/17 ⋅ 0

JDK动态代理源码学习

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

Javahih ⋅ 2017/07/21 ⋅ 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

Java基础巩固笔记(4)-代理

Contents java基础巩固笔记(4)-代理 概念 动态代理 参考资料 代理是实现AOP(Aspect oriented program,面向切面编程)的核心和关键技术。 概念 代理是一种设计模式,其目的是为其他对象提供一...

卟想苌亣 ⋅ 2017/12/04 ⋅ 0

spring工作原理

一.IoC(Inversion of control): 控制反转 IoC: 概念:控制权由对象本身转向容器;由容器根据配置文件去创建实例并创建各个实例之间的依赖关系 核心:bean工厂;在Spring中,bean工厂创建的各...

路峰 ⋅ 2013/02/23 ⋅ 0

透过现象看原理:详解 Spring 中 Bean 的 this 调用导致 AOP 失效的原因

原文出处:光闪 前言 在我们使用Spring时,可能有前辈教导过我们,在bean中不要使用this来调用被@Async、@Transactional、@Cacheable等注解标注的方法,this下注解是不生效的。 那么大家可曾...

光闪 ⋅ 05/16 ⋅ 0

实战CGLIB系列文章之开篇:CGLIB简介、原理与应用

CGLIB介绍与原理(部分节选自网络) 一、什么是CGLIB? CGLIB是一个功能强大,高性能的代码生成包。它为没有实现接口的类提供代理,为JDK的动态代理提供了很好的补充。通常可以使用Java的动态代...

mn_1127 ⋅ 2016/03/29 ⋅ 0

Java动态代理 深度详解

文章首发于【博客园-陈树义】,点击跳转到原文深入浅出Java动态代理 代理模式是设计模式中非常重要的一种类型,而设计模式又是编程中非常重要的知识点,特别是在业务系统的重构中,更是有举足...

陈树义 ⋅ 2017/11/21 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

RabbitMQ学习以及与Spring的集成(三)

本文介绍RabbitMQ与Spring的简单集成以及消息的发送和接收。 在RabbitMQ的Spring配置文件中,首先需要增加命名空间。 xmlns:rabbit="http://www.springframework.org/schema/rabbit" 其次是模...

onedotdot ⋅ 24分钟前 ⋅ 0

JAVA实现仿微信红包分配规则

最近过年发红包拜年成为一种新的潮流,作为程序猿对算法的好奇远远要大于对红包的好奇,这里介绍一种自己想到的一种随机红包分配策略,还请大家多多指教。 算法介绍 一、红包金额限制 对于微...

小致dad ⋅ 36分钟前 ⋅ 0

Python 数电表格格式化 xlutils xlwt xlrd的使用

需要安装 xlutils xlwt xlrd 格式化前 格式化后 代码 先copy读取的表格,然后按照一定的规则修改,将昵称中的学号提取出来替换昵称即可 from xlrd import open_workbookfrom xlutils.copy ...

阿豪boy ⋅ 今天 ⋅ 0

面试题:使用rand5()生成rand7()

前言 读研究生这3 年,思维与本科相比变化挺大的,这几年除了看论文、设计方案,更重要的是学会注重先思考、再实现,感觉更加成熟吧,不再像个小P孩,人年轻时总会心高气傲。有1 道面试题:给...

初雪之音 ⋅ 今天 ⋅ 0

Docker Toolbox Looks like something went wrong

Docker Toolbox 重新安装后提示错误:Looks like something went wrong in step ´Checking if machine default exists´ 控制面板-->程序与应用-->启用或关闭windows功能:找到Hyper-V,如果处......

随你疯 ⋅ 今天 ⋅ 0

Guacamole 远程桌面

本文将Apache的guacamole服务的部署和应用,http://guacamole.apache.org/doc/gug/ 该链接下有全部相关知识的英文文档,如果水平ok,可以去这里仔细查看。 一、简介 Apache Guacamole 是无客...

千里明月 ⋅ 今天 ⋅ 0

nagios 安装

Nagios简介:监控网络并排除网络故障的工具:nagios,Ntop,OpenVAS,OCS,OSSIM等开源监控工具。 可以实现对网络上的服务器进行全面的监控,包括服务(apache、mysql、ntp、ftp、disk、qmail和h...

寰宇01 ⋅ 今天 ⋅ 0

AngularDart注意事项

默认情况下创建Dart项目应出现以下列表: 有时会因为不知明的原因导致列表项缺失: 此时可以通过以下步骤解决: 1.创建项目涉及到的包:stagehand 2.执行pub global activate stagehand或pub...

scooplol ⋅ 今天 ⋅ 0

Java Web如何操作Cookie的添加修改和删除

创建Cookie对象 Cookie cookie = new Cookie("id", "1"); 修改Cookie值 cookie.setValue("2"); 设置Cookie有效期和删除Cookie cookie.setMaxAge(24*60*60); // Cookie有效时间 co......

二营长意大利炮 ⋅ 今天 ⋅ 0

【每天一个JQuery特效】淡入淡出显示或隐藏窗口

我是JQuery新手爱好者,有时间就练练代码,防止手生,争取每天一个JQuery练习,在这个博客记录下学习的笔记。 本特效主要采用fadeIn()和fadeOut()方法显示淡入淡出的显示效果显示或隐藏元...

Rhymo-Wu ⋅ 今天 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部