http://hi.baidu.com/maoshenmusic/blog/item/5e65dc2419baa6044c088d1a.html

一、Java ClassLoader

1,什么是ClassLoader 
与 C 或 C++ 编写的程序不同,Java 程序并不是一个可执行文件,而是由许多独立的类文件组成,每一个文件对应于一个 Java 类。 
此外,这些类文件并非立即全部都装入内存,而是根据程序需要装入内存。ClassLoader 是 JVM 中将类装入内存的那部分。 
而且,Java ClassLoader 就是用 Java 语言编写的。这意味着创建您自己的 ClassLoader 非常容易,不必了解 JVM 的微小细节。

2,一些重要的方法 
A)loadClass 
ClassLoader.loadClass() 是ClassLoader的入口点。该方法的定义为:Class loadClass( String name, boolean resolve ); 
name:JVM 需要的类的名称,如 Foo 或 java.lang.Object。 
resolve:参数告诉方法是否需要解析类。

B)defineClass 
defineClass方法是ClassLoader的主要诀窍。该方法接受由原始字节组成的数组并把它转换成Class对象。

C)findSystemClass 
findSystemClass方法从本地文件系统中寻找类文件,如果存在,就使用defineClass将原始字节转换成Class对象,以将该文件转换成类。

D)resolveClass 
可以不完全地(不带解析)装入类,也可以完全地(带解析)装入类。当编写我们自己的loadClass时可以调用resolveClass,这取决于loadClass的resolve参数的值。

E)findLoadedClass 
findLoadedClass充当一个缓存:当请求loadClass装入类时,它调用该方法来查看ClassLoader是否已装入这个类,这样可以避免重新装入已存在类所造成的麻烦。

3,Java2中ClassLoader的变动 
1)loadClass的缺省实现 
在Java2中loadClass的实现嵌入了大多数查找类的一般方法,并使您通过覆盖findClass方法来定制它,在适当的时候findClass会调用loadClass。 
这种方式的好处是可能不一定要覆盖loadClass,只要覆盖findClass就行了,这减少了工作量。

2)新方法:findClass 
loadClass的缺省实现调用这个新方法。

3)新方法:getSystemClassLoader 
如果覆盖findClass或loadClass,getSystemClassLoader让我们以实际ClassLoader对象来访问系统ClassLoader,而不是固定的从findSystemClass 调用它。

4)新方法:getParent 
为了将类请求委托给父ClassLoader,这个新方法允许ClassLoader获取它的父ClassLoader。

4,定制ClassLoader 
其实我们或多或少都使用过定制的ClassLoader,因为Applet查看器中就包含一个定制的ClassLoader。 
它不在本地文件系统中寻找类,而是访问远程服务器上的 Web 站点,经过 HTTP 装入原始的字节码文件,并把它们转换成JVM 内的类。 
Applet查看器中的ClassLoader还可以做其它事情:它们支持安全性以及使不同的Applet在不同的页面上运行而互不干扰。 
我们将写一个自己的ClassLoader实现示例,它将实现如下步骤,这也是ClassLoader的工作原理: 
# 调用 findLoadedClass 来查看是否存在已装入的类。 
# 如果没有,那么采用那种特殊的神奇方式来获取原始字节。 
# 如果已有原始字节,调用defineClass将它们转换成Class对象。 
# 如果没有原始字节,然后调用findSystemClass查看是否从本地文件系统获取类。 
# 如果resolve参数是true,那么调用resolveClass解析Class对象。 
# 如果还没有类,返回ClassNotFoundException。 
# 否则,将类返回给调用程序。 
话不多说,看看代码先: 
FileClassLoader.java:

代码
  1. import java.io.ByteArrayOutputStream;
  2. import java.io.File;
  3. import java.io.FileInputStream;
  4. import java.io.IOException;
  5. public class FileClassLoader extends ClassLoader {
  6. public Class findClass(String name) {
  7. byte[] data = loadClassData(name);
  8. return defineClass(name, data, 0, data.length);
  9. }
  10. private byte[] loadClassData(String name) {
  11. FileInputStream fis = null;
  12. byte[] data = null;
  13. try {
  14. fis = new FileInputStream(new File("D:\\project\\test\\" + name + ".class"));
  15. ByteArrayOutputStream baos = new ByteArrayOutputStream();
  16. int ch = 0;
  17. while ((ch = fis.read()) != -1) {
  18. baos.write(ch);
  19. }
  20. data = baos.toByteArray();
  21. } catch (IOException e) {
  22. e.printStackTrace();
  23. }
  24. return data;
  25. }
  26. }

MyApp.java:

代码
  1. public class MyApp {
  2. public static void main(String[] args) throws Exception {
  3. FileClassLoader loader = new FileClassLoader();
  4. Class objClass = loader.findClass("MyApp");
  5. Object obj = objClass.newInstance();
  6. System.out.println(objClass.getName());
  7. System.out.println(objClass.getClassLoader());
  8. System.out.println(obj);
  9. }
  10. }

编译并运行MyApp类,结果为:

代码
  1. MyApp
  2. FileClassLoader@757aef
  3. MyApp@9cab16

二、Bytecode

1,什么是Bytecode 
C/C++编译器把源代码编译成汇编代码,Java编译器把Java源代码编译成字节码bytecode。 
Java跨平台其实就是基于相同的bytecode规范做不同平台的虚拟机,我们的Java程序编译成bytecode后就可以在不同平台跑了。 
.net框架有IL(intermediate language),汇编是C/C++程序的中间表达方式,而bytecode可以说是Java平台的中间语言。
了解Java字节码知识对debugging、performance tuning以及做一些高级语言扩展或框架很有帮助。

2,使用javap生成Bytecode 
JDK自带的javap.exe文件可以反汇编Bytecode,让我们看个例子: 
Test.java:

代码
  1. public class Test {
  2. public static void main(String[] args) {
  3. int i = 10000;
  4. System.out.println("Hello Bytecode! Number = " + i);
  5. }
  6. }

编译后的Test.class:

代码
  1. 漱壕   1 +
  2. <init> ()V Code LineNumberTable main ([Ljava/lang/String;)V
  3. SourceFile     Test.java
  4. ! " java/lang/StringBuilder Hello Bytecode! Number = # $ # % & ' ( ) * Test java/lang/Object java/lang/System out Ljava/io/PrintStream; append -(Ljava/lang/String;)Ljava/lang/StringBuilder; (I)Ljava/lang/StringBuilder; toString ()Ljava/lang/String; java/io/PrintStream println (Ljava/lang/String;)V !
  5. *                      >     ' < Y

使用javap -c Test > Test.bytecode生成的Test.bytecode:

代码
  1. Compiled from "Test.java"
  2. public class Test extends java.lang.Object{
  3. public Test();
  4. Code:
  5. 0:   aload_0
  6. 1:   invokespecial   #1; //Method java/lang/Object."<init>":()V
  7. 4:  return
  8. public static void main(java.lang.String[]);
  9. Code:
  10. 0:   sipush  10000
  11. 3:   istore_1
  12. 4:   getstatic   #2; //Field java/lang/System.out:Ljava/io/PrintStream;
  13. 7:  new   #3; //class java/lang/StringBuilder
  14. 10:   dup
  15. 11:   invokespecial   #4; //Method java/lang/StringBuilder."<init>":()V
  16. 14:   ldc   #5; //String Hello Bytecode! Number =
  17. 16:   invokevirtual   #6; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
  18. 19:   iload_1
  19. 20:   invokevirtual   #7; //Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
  20. 23:   invokevirtual   #8; //Method java/lang/StringBuilder.toString:()Ljava/lang/String;
  21. 26:   invokevirtual   #9; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
  22. 29:  return
  23. }

JVM就是一个基于stack的机器,每个thread拥有一个存储着一些frames的JVM stack,每次调用一个方法时生成一个frame。 
一个frame包括一个local variables数组(本地变量表),一个Operand LIFO stack和运行时常量池的一个引用。

简单分析一下生成的字节码指令: 
aload和iload指令的“a”前缀和“i”分别表示对象引用和int类型,其他还有“b”表示byte,“c”表示char,“d”表示double等等 
我们这里的aload_0表示将把local variable table中index 0的值push到Operand stack,iload_1类似 
invokespecial表示初始化对象,return表示返回 
sipush表示把10000这个int值push到Operand stack 
getstatic表示取静态域 
invokevirtual表示调用一些实例方法 
这些指令又称为opcode,Java一直以来只有约202個Opcode,具体请参考Java Bytecode规范。

我们看到Test.class文件不全是二进制的指令,有些是我们可以识别的字符,这是因为有些包名、类名和常量字符串没有编译成二进制Bytecode指令。

3,体验字节码增强的魔力 
我们J2EE常用的Hibernate、Spring都用到了动态字节码修改来改变类的行为。 
让我们通过看看ASM的org.objectweb.asm.MethodWriter类的部分方法来理解ASM是如何修改字节码的:

代码
  1. class MethodWriter implements MethodVisitor {
  2. private ByteVector code = new ByteVector();
  3. public void visitIntInsn(final int opcode, final int operand) {
  4. // Label currentBlock = this.currentBlock;
  5. if (currentBlock != null) {
  6. if (compute == FRAMES) {
  7. currentBlock.frame.execute(opcode, operand, null, null);
  8. } else if (opcode != Opcodes.NEWARRAY) {
  9. // updates current and max stack sizes only for NEWARRAY
  10. // (stack size variation = 0 for BIPUSH or SIPUSH)
  11. int size = stackSize + 1;
  12. if (size > maxStackSize) {
  13. maxStackSize = size;
  14. }
  15. stackSize = size;
  16. }
  17. }
  18. // adds the instruction to the bytecode of the method
  19. if (opcode == Opcodes.SIPUSH) {
  20. code.put12(opcode, operand);
  21. } else { // BIPUSH or NEWARRAY
  22. code.put11(opcode, operand);
  23. }
  24. }
  25. public void visitMethodInsn(
  26. final int opcode,
  27. final String owner,
  28. final String name,
  29. final String desc)
  30. {
  31. boolean itf = opcode == Opcodes.INVOKEINTERFACE;
  32. Item i = cw.newMethodItem(owner, name, desc, itf);
  33. int argSize = i.intVal;
  34. // Label currentBlock = this.currentBlock;
  35. if (currentBlock != null) {
  36. if (compute == FRAMES) {
  37. currentBlock.frame.execute(opcode, 0, cw, i);
  38. } else {
  39. /*
  40. * computes the stack size variation. In order not to recompute
  41. * several times this variation for the same Item, we use the
  42. * intVal field of this item to store this variation, once it
  43. * has been computed. More precisely this intVal field stores
  44. * the sizes of the arguments and of the return value
  45. * corresponding to desc.
  46. */
  47. if (argSize == 0) {
  48. // the above sizes have not been computed yet,
  49. // so we compute them...
  50. argSize = getArgumentsAndReturnSizes(desc);
  51. // ... and we save them in order
  52. // not to recompute them in the future
  53. i.intVal = argSize;
  54. }
  55. int size;
  56. if (opcode == Opcodes.INVOKESTATIC) {
  57. size = stackSize - (argSize >> 2) + (argSize & 0x03) + 1;
  58. } else {
  59. size = stackSize - (argSize >> 2) + (argSize & 0x03);
  60. }
  61. // updates current and max stack sizes
  62. if (size > maxStackSize) {
  63. maxStackSize = size;
  64. }
  65. stackSize = size;
  66. }
  67. }
  68. // adds the instruction to the bytecode of the method
  69. if (itf) {
  70. if (argSize == 0) {
  71. argSize = getArgumentsAndReturnSizes(desc);
  72. i.intVal = argSize;
  73. }
  74. code.put12(Opcodes.INVOKEINTERFACE, i.index).put11(argSize >> 2, 0);
  75. } else {
  76. code.put12(opcode, i.index);
  77. }
  78. }
  79. }

通过注释我们可以大概理解visitIntInsn和visitMethodInsn方法的意思。 
比如visitIntInsn先计算stack的size,然后根据opcode来判断是SIPUSH指令还是BIPUSH or NEWARRAY指令,并相应的调用字节码修改相关的方法。

深入了解Java ClassLoader、Bytecode 、ASM、cglib (I)相关推荐

  1. [转载] 深入了解Java ClassLoader、Bytecode 、ASM、cglib

    转载自http://www.iteye.com/topic/98178 一.Java ClassLoader 1,什么是ClassLoader  与 C 或 C++ 编写的程序不同,Java 程序并不 ...

  2. Java Agent与ASM字节码介绍

    Java Agent Java Agent是jdk1.5以后引入的,也叫做Java代理. javaAgent是运行方法之前的拦截器.我们利用javaAgent和ASM字节码技术,在JVM加载class ...

  3. Spring中AOP的两种代理方式(Java动态代理和CGLIB代理)

    第一种代理即Java的动态代理方式上一篇已经分析,在这里不再介绍,现在我们先来了解下GCLIB代理是什么?它又是怎样实现的?和Java动态代理有什么区别? cglib(Code Generation ...

  4. Java JDK代理、CGLIB、AspectJ代理分析比较

    前言 什么是代理,在Design patterns In java这个本书中是这样描述的,简单的说就是为某个对象提供一个代理,以控制对这个对象的访问.在不修改源代码的基础上做方法增强,代理是一种设计模 ...

  5. Java ClassLoader详解

    翻译原文链接 背景 Java平台旨在提供健壮,安全和可扩展的功能,以支持代码和数据的移动性.Java虚拟机(JVM)中的Java ClassLoader是实现这些目标的关键组件. JVM负责在Java ...

  6. Java ClassLoader

    Java ClassLoader (1) – What is a ClassLoader? Java ClassLoader (2) – Write your own ClassLoader Java ...

  7. 深入理解Java ClassLoader及在 JavaAgent 中的应用

    转载自   深入理解Java ClassLoader及在 JavaAgent 中的应用 背景 众所周知, Java 或者其他运行在 JVM(java 虚拟机)上面的程序都需要最终便以为字节码,然后被 ...

  8. Java动态代理之cglib

    转载自 Java动态代理之cglib cglib是对jdk动态代理的补充,弥补了因没有接口的类生成代理类的缺失.  下面通过简单的例子学习一下cglib的使用,当然,要是用需要第三方的jar包,jar ...

  9. 了解和扩展Java ClassLoader

    Java ClassLoader是项目开发中Java的关键但很少使用的组件之一. 就我个人而言,我从未在任何项目中扩展ClassLoader,但是拥有自己的可以自定义Java类加载的ClassLoad ...

最新文章

  1. c语言自定义char*函数返回值是乱码_[每日C语言」printf()函数的修饰符和返回值...
  2. 安卓手机测评_安卓手机8G运存,比不上iPhone 2G?终于知道原因了!
  3. linux查看网卡物理编号_关于如何查看多网卡物理机中网卡序号与物理网卡的对应该关系...
  4. Altera之VIP TPG学习笔记
  5. Nacos注册中心介绍
  6. 高性能web系统的架构和系统优化
  7. 【笔记+总结】Andrew Ng-神经网络和深度学习
  8. 金融现金贷用户数据分析和用户画像
  9. mysql慢日志分析工具_MySQL慢日志分析工具mysqlsla
  10. gui框架 java_现在选择哪个Java GUI框架
  11. win7显示文件扩展名
  12. Mac制作windows10安装U盘
  13. 报修下单上门维修小程序开发制作
  14. 商用密码应用安全性评估(密评)六大基础问题解答
  15. 剑指Offe 50:数组中重复的数字
  16. 哪款蓝牙耳机吃鸡没延迟?游戏蓝牙耳机推荐
  17. qbo_webi: cherry server 有效url
  18. 深入浅出Python元编程,不仅仅是Metaclass
  19. 工业机器人安装调试与维护课程试卷_工业机器人安装、调试与维护教学课件作者阙正湘项目五工业机器人机械结构件的维修...
  20. 小程序-一夜狼人杀-使用说明(新增酒鬼)

热门文章

  1. 世界之窗如何保存html,世界之窗(文字版)
  2. ios html转json,iOS 中 Model 和 JSON 互相转换
  3. 计算机机房维护保养计划表,机房日常维护保养计划
  4. c语言报刊杂志订阅系统,中国报刊杂志大全_报刊大全_报刊杂志订阅
  5. css窗口最大化,你如何使用css变换与jquery和地址窗口最大化不一致?
  6. mysql客户端新建一个表_MySQL 建库、建用户及建表事项
  7. c语言程序设计教程课后选择题答案,C语言程序设计教程课后习题包括答案.docx...
  8. java map 为空_java中如何判断map是否为空
  9. 首都师范大学计算机科学与技术考研分数线,2017考研:计算机科学与技术专业考研院校推荐之首都师范...
  10. 表情识别(四)--多网络级联表情识别