一、如何获取动态生成的class 字节码

结合生成的class文件是一个学习cglib的比较好的方法。在cglib中,生成的class文件默认只存储在内存中,我们可以在代码中加入下面语句来获取class file。

System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "F:\\code ");

这里涉及到了一个比较关键的类DebuggingClassWriter,接下来我们简单的分析一下这个类。DebuggingClassWriter继承于ClassVisitor,熟悉ASM的TX就可以得知他是访问链上的一员。此类只有一个有参构造函数:

    public DebuggingClassWriter(int flags) {
super(Opcodes.ASM4, new ClassWriter(flags));
}

我们可以看到访问链上的最后一名访问者就是ClassWriter,ClassWriter也是ASM中的一个类,他的主要作用就是产生class 字节码,DebuggingClassWriter最终是委托ClassWriter来生成字节码的。DebuggingClassWriter自己做的主要工作就是 将字节码持久化到硬盘上。

static {
debugLocation = System.getProperty(DEBUG_LOCATION_PROPERTY);
if (debugLocation != null) {
System.err.println("CGLIB debugging enabled, writing to '" + debugLocation + "'");
try {
Class clazz = Class.forName("org.objectweb.asm.util.TraceClassVisitor");
traceCtor = clazz.getConstructor(new Class[]{ClassVisitor.class, PrintWriter.class});
} catch (Throwable ignore) {
}
}
}

上面代码是DebuggingClassWriter中的静态代码块,这部分代码在调用类的构造函数之前完成的。在类初始化的时候就会获取DEBUG_LOCATION_PROPERTY 的属性值,同时会通过java的反射机制来生成一个TraceClassVisitor对象,TraceClassVisitor 是 ASM 中的一个工具类,它的作用是将字节码转换成易读的文本,也就是反编译,和javap差不多。

DebuggingClassWriter中完成关键工作的方法是toByteArray:

                byte[] b = ((ClassWriter) DebuggingClassWriter.super.cv).toByteArray();
if (debugLocation != null) {
System.out.println(className);
String dirs = className.replace('.', File.separatorChar);
try {
new File(debugLocation + File.separatorChar + dirs).getParentFile().mkdirs(); File file = new File(new File(debugLocation), dirs + ".class");
OutputStream out = new BufferedOutputStream(new FileOutputStream(file));
try {
out.write(b);
} finally {
out.close();
} if (traceCtor != null) {
file = new File(new File(debugLocation), dirs + ".asm");
out = new BufferedOutputStream(new FileOutputStream(file));
try {
ClassReader cr = new ClassReader(b);
PrintWriter pw = new PrintWriter(new OutputStreamWriter(out));
ClassVisitor tcv = (ClassVisitor)traceCtor.newInstance(new Object[]{null, pw});
cr.accept(tcv, 0);
pw.flush();
} finally {
out.close();
}
}
} catch (Exception e) {
throw new CodeGenerationException(e);
}
}

首先委托ClassWriter来生成字节码,然后根据用户指定的路径来生成文件夹,最后生成两个文件,一个是.class 文件,另外一个是后缀为.asm的文件。用户可以直接打开.asm来直观的分析生成的class文件。

二、命名策略

通过第一步我们可以发现指定的文件夹下面有一些命名奇怪的class文件。本节我们就来分析一下这些类名都是怎么生成的。在上一章我们分析key和缓存的时候提到过,class generator必须继承AbstractClassGenerator ,然后实现抽象方法 generateClass,generateClass的作用就是用来生成class ,那么class name的生成也应该是在这个方法中。在每个generateClass方法中,当调用ClassEmitter 的begin_class方法时都要求提供一个入参 class internal name。这时会调用AbstractClassGenerator  的 getClassName 来生成一个可用的class internal name。

final protected String getClassName() {
if (className == null)
className = getClassName(getClassLoader());
return className;
} private String getClassName(final ClassLoader loader) {
final Set nameCache = getClassNameCache(loader);
return namingPolicy.getClassName(namePrefix, source.name, key, new Predicate() {
public boolean evaluate(Object arg) {
return nameCache.contains(arg);
}
});
} private Set getClassNameCache(ClassLoader loader) {
return (Set)((Map)source.cache.get(loader)).get(NAME_KEY);
}

上面代码可以看到,getClassName首先根据classloader和NAME_KEY来获取已经生成的类名的列表。命名策略是封装在NamingPolicy中的getClassName方法中的。用户可以根据自己的实际需求通过实现接口NamingPolicy来定制命名策略。Cglib提供了一个默认的命名策略DefaultNamingPolicy。DefaultNamingPolicy 同样实现了NamingPolicy接口。这部分代码比较简单,我们可以分析得出,默认的命名策略为

被代理class name + "$$" + class generator name + "ByCGLIB" + "$$" + key的hashcode

如果命名冲突的话,会在上面产生的类名后面+”_”+index(index >=2).

Note:如果需要自定义命名规则,比如return prefix+"_"+source+"_"+key.hashCode(); 要把source的包名去掉,否则将产生比较深的文件夹,key的使用要注意,不能直接+key,应使用key的hashcode。

cglib源码分析(二):Class name 生成策略的更多相关文章

  1. 框架-springmvc源码分析(二)

    框架-springmvc源码分析(二) 参考: http://www.cnblogs.com/leftthen/p/5207787.html http://www.cnblogs.com/leftth ...

  2. Tomcat源码分析二:先看看Tomcat的整体架构

    Tomcat源码分析二:先看看Tomcat的整体架构 Tomcat架构图 我们先来看一张比较经典的Tomcat架构图: 从这张图中,我们可以看出Tomcat中含有Server.Service.Conn ...

  3. Vue源码分析(二) : Vue实例挂载

    Vue源码分析(二) : Vue实例挂载 author: @TiffanysBear 实例挂载主要是 $mount 方法的实现,在 src/platforms/web/entry-runtime-wi ...

  4. Fresco 源码分析(二) Fresco客户端与服务端交互(1) 解决遗留的Q1问题

    4.2 Fresco客户端与服务端的交互(一) 解决Q1问题 从这篇博客开始,我们开始讨论客户端与服务端是如何交互的,这个交互的入口,我们从Q1问题入手(博客按照这样的问题入手,是因为当时我也是从这里 ...

  5. 十、Spring之BeanFactory源码分析(二)

    Spring之BeanFactory源码分析(二) 前言 在前面我们简单的分析了BeanFactory的结构,ListableBeanFactory,HierarchicalBeanFactory,A ...

  6. Vue.js 源码分析(二十) 指令篇 v-once指令详解

    数据绑定最常见的形式就是使用“Mustache”语法 (双大括号) 的文本插值,例如:<p>Message: {{ msg }}</p>以后每当msg属性发生了改变,插值处的内 ...

  7. 多线程之美8一 AbstractQueuedSynchronizer源码分析<二>

    目录 AQS的源码分析 该篇主要分析AQS的ConditionObject,是AQS的内部类,实现等待通知机制. 1.条件队列 条件队列与AQS中的同步队列有所不同,结构图如下: 两者区别: 1.链表 ...

  8. spring源码分析(二)Aop

    创建日期:2016.08.19 修改日期:2016.08.20-2016.08.21 交流QQ:992591601 参考资料:<spring源码深度解析>.<spring技术内幕&g ...

  9. cglib源码分析(一): 缓存和KEY

    cglib是一个java 字节码的生成工具,它是对asm的进一步封装,提供了一系列class generator.研究cglib主要是因为它也提供了动态代理功能,这点和jdk的动态代理类似. 一. C ...

  10. 5.2 Spring5源码--Spring AOP源码分析二

    目标: 1. 什么是AOP, 什么是AspectJ 2. 什么是Spring AOP 3. Spring AOP注解版实现原理 4. Spring AOP切面原理解析 一. 认识AOP及其使用 详见博 ...

随机推荐

  1. tbody添加垂直滚动条

    法一: 用2个table: <table width="300" border="0" cellpadding="0" cellspa ...

  2. Oracle系列之视图

    涉及到表的处理请参看原表结构与数据  Oracle建表插数据等等 创建视图,把tb_Employee表sal<1000的雇员,映射到该视图( view) create or replace vi ...

  3. c#调用c++动态库的一些理解

    调用c++动态库一般我们这样写   [DllImport("UCamer.dll", CallingConvention = CallingConvention.Winapi)] ...

  4. QNX环境

    QNX开发环境和QNX虚拟机都搭建好了,开始写第一个QNX程序. 关于QNX程序开发的最好参考是QNX官网上的pdf书<10 Steps to Developing a QNX Program: ...

  5. C# 多线程(lock,Monitor,Mutex,同步事件和等待句柄)

    本篇从 Monitor,Mutex,ManualResetEvent,AutoResetEvent,WaitHandler 的类关系图开始,希望通过本篇的介绍能对常见的线程同步方法有一个整体的认识,而 ...

  6. (转载)SQL Server 2005 日志文件过大处理

    由于安装的时候没有计划好空间,默认装在系统盘,而且又没有做自动备份.截断事务日志等,很快LDF文件就达到十几G,或者几十G ,此时就不得不处理了. 备份和计划就不说了,现在就说下怎么把它先删除吧: 1 ...

  7. POJ2492 A Bug's Life 带权并查集

    分析:所谓带权并查集,就是比朴素的并查集多了一个数组,记录一些东西,例如到根的距离,或者和根的关系等 这个题,权数组为relation 代表的关系  1 和父节点不同性别,0,和父节点同性别 并查集一 ...

  8. java基础之运算符

    运算符是用于表示数据的赋值,运算和比较的一种特殊符号.1.赋值运算符:=,+=,-=,*=,/=,%= x=1;x+=2;(相当于x=x+2,等于3),其他运算符同理 2.算术运算符:+,-,*,/, ...

  9. [codevs1557]热浪

    本题地址:http://www.luogu.org/problem/show?pid=1339 http://codevs.cn/problem/1557/ http://www.tyvj.cn/p/ ...

  10. [codevs1287]矩阵乘法

    题目描述 Description 小明最近在为线性代数而头疼,线性代数确实很抽象(也很无聊),可惜他的老师正在讲这矩阵乘法这一段内容.当然,小明上课打瞌睡也没问题,但线性代数的习题可是很可怕的.小明希 ...