400-028-6601
建站资讯

网站建设资讯

为你提供网站建设行业资讯、网站优化知识、主机域名邮箱、网站开发常见问题等。

Java的ClassLoader机制解析

JVM在加载类的时候,都是通过ClassLoader的loadClass()方法来加载class的,loadClass(String name)方法:

使用的是双亲委托模式:

jvm启动时,会启动jre/rt.jar里的类加载器:bootstrap classloader,用来加载java核心api;然后启动扩展类加载器ExtClassLoader加载扩展类,并加载用户程序加载器AppClassLoader,并指定ExtClassLoader为他的父类;

当类被加载时,会先检查在内存中是否已经被加载,如果是,则不再加载,如果没有,再由AppClassLoader来加载,先从jar包里找,没有再从classpath里找;

如果自定义loader类,就会存在这命名空间的情况,不同的加载器加载同一个类时,产生的实例其实是不同的;

Java代码

 
 
 
  1. public Class loadClass(String name) throws ClassNotFoundException { 
  2. return loadClass(name, false); 
  3. } 
  4.  
  5. public Class loadClass(String name) throws ClassNotFoundException { 
  6. return loadClass(name, false); 
  7. } 

loadClass(String name)方法再调用loadClass(String name, boolean resolve)方法:

◆ name - 类的二进制名称

◆ resolve - 如果该参数为 true,则分析这个类

Java代码

 
 
 
  1. protected synchronized Class loadClass(String name, boolean resolve) 
  2. throws ClassNotFoundException 
  3. { 
  4. // First, check if the class has already been loaded 
  5. //JVM 规范规定ClassLoader可以在缓存保留它所加载的Class,如果一个Class已经被加载过,则直接从缓存中获取 
  6. Class c = findLoadedClass(name); 
  7. if (c == null) { 
  8. try { 
  9. if (parent != null) { 
  10. c = parent.loadClass(name, false); 
  11. } else { 
  12. c = findBootstrapClass0(name); 
  13. } 
  14. } catch (ClassNotFoundException e) { 
  15. // If still not found, then invoke findClass in order 
  16. // to find the class. 
  17. c = findClass(name); 
  18. } 
  19. } 
  20. if (resolve) { 
  21. resolveClass(c); 
  22. } 
  23. return c; 
  24. } 
  25.  
  26. protected synchronized Class loadClass(String name, boolean resolve) 
  27. throws ClassNotFoundException 
  28. { 
  29. // First, check if the class has already been loaded 
  30. //JVM 规范规定ClassLoader可以在缓存保留它所加载的Class,如果一个Class已经被加载过,则直接从缓存中获取 
  31. Class c = findLoadedClass(name); 
  32. if (c == null) { 
  33. try { 
  34. if (parent != null) { 
  35. c = parent.loadClass(name, false); 
  36. } else { 
  37. c = findBootstrapClass0(name); 
  38. } 
  39. } catch (ClassNotFoundException e) { 
  40. // If still not found, then invoke findClass in order 
  41. // to find the class. 
  42. c = findClass(name); 
  43. } 
  44. } 
  45. if (resolve) { 
  46. resolveClass(c); 
  47. } 
  48. return c; 
  49. } 

如果ClassLoader并没有加载这个class,则调用findBootstrapClass0:

Java代码

 
 
 
  1. private Class findBootstrapClass0(String name) 
  2. throws ClassNotFoundException 
  3. { 
  4. check(); 
  5. if (!checkName(name)) 
  6. throw new ClassNotFoundException(name); 
  7. return findBootstrapClass(name); 
  8. } 
  9.  
  10. private Class findBootstrapClass0(String name) 
  11. throws ClassNotFoundException 
  12. { 
  13. check(); 
  14. if (!checkName(name)) 
  15. throw new ClassNotFoundException(name); 
  16. return findBootstrapClass(name); 
  17. } 

该方法会调用check()方法来判断这个类是否已经初始化,并且通过checkName(name)来判断由name指定的这个类是否存在***调用findBootstrapClass(name):

Java代码

 
 
 
  1. private native Class findBootstrapClass(String name) 
  2. throws ClassNotFoundException; 
  3.  
  4. private native Class findBootstrapClass(String name) 
  5. throws ClassNotFoundException; 

而这个findBootstrapClass方法是一个native方法,这是我们的root loader,这个载入方法并非是由JAVA所写,而是C++写的,它会最终调用JVM中的原生findBootstrapClass方法来完成类的加载。

如果上面两个都找不到,则使用findClass(name)来查找指定类名的Class:

Java代码

 
 
 
  1. protected Class findClass(String name) throws ClassNotFoundException { 
  2. throw new ClassNotFoundException(name); 
  3. } 
  4.  
  5. protected Class findClass(String name) throws ClassNotFoundException { 
  6. throw new ClassNotFoundException(name); 
  7. } 

JDK5.0中的说明:

使用指定的二进制名称查找类。此方法应该被类加载器的实现重写,该实现按照委托模型来加载类。在通过父类加载器检查所请求的类后,此方法将被 loadClass 方法调用。默认实现抛出一个ClassNotFoundException。

所以,我们在自定义类中,只需要重写findClass()即可。

MyClassLoader类:

Java代码

 
 
 
  1. public class MyClassLoader extends ClassLoader { 
  2. private String fileName; 
  3.  
  4. public MyClassLoader(String fileName) { 
  5. this.fileName = fileName; 
  6. } 
  7.  
  8. protected Class findClass(String className) throws ClassNotFoundException { 
  9. Class clazz = this.findLoadedClass(className); 
  10. if (null == clazz) { 
  11. try { 
  12. String classFile = getClassFile(className); 
  13. FileInputStream fis = new FileInputStream(classFile); 
  14. FileChannel fileC = fis.getChannel(); 
  15. ByteArrayOutputStream baos = new 
  16. ByteArrayOutputStream(); 
  17. WritableByteChannel outC = Channels.newChannel(baos); 
  18. ByteBuffer buffer = ByteBuffer.allocateDirect(1024); 
  19. while (true) { 
  20. int i = fileC.read(buffer); 
  21. if (i == 0 || i == -1) { 
  22. break; 
  23. } 
  24. buffer.flip(); 
  25. outC.write(buffer); 
  26. buffer.clear(); 
  27. } 
  28. fis.close(); 
  29. byte[] bytes = baos.toByteArray(); 
  30.  
  31. clazz = defineClass(className, bytes, 0, bytes.length); 
  32. } catch (FileNotFoundException e) { 
  33. e.printStackTrace(); 
  34. } catch (IOException e) { 
  35. e.printStackTrace(); 
  36. } 
  37. } 
  38. return clazz; 
  39. } 
  40. private byte[] loadClassBytes(String className) throws 
  41. ClassNotFoundException { 
  42. try { 
  43. String classFile = getClassFile(className); 
  44. FileInputStream fis = new FileInputStream(classFile); 
  45. FileChannel fileC = fis.getChannel(); 
  46. ByteArrayOutputStream baos = new 
  47. ByteArrayOutputStream(); 
  48. WritableByteChannel outC = Channels.newChannel(baos); 
  49. ByteBuffer buffer = ByteBuffer.allocateDirect(1024); 
  50. while (true) { 
  51. int i = fileC.read(buffer); 
  52. if (i == 0 || i == -1) { 
  53. break; 
  54. } 
  55. buffer.flip(); 
  56. outC.write(buffer); 
  57. buffer.clear(); 
  58. } 
  59. fis.close(); 
  60. return baos.toByteArray(); 
  61. } catch (IOException fnfe) { 
  62. throw new ClassNotFoundException(className); 
  63. } 
  64. } 
  65. private String getClassFile(String name) { 
  66. StringBuffer sb = new StringBuffer(fileName); 
  67. name = name.replace('.', File.separatorChar) + ".class"; 
  68. sb.append(File.separator + name); 
  69. return sb.toString(); 
  70. } 
  71. } 
  72.  
  73. public class MyClassLoader extends ClassLoader { 
  74. private String fileName; 
  75.  
  76. public MyClassLoader(String fileName) { 
  77. this.fileName = fileName; 
  78. } 
  79.  
  80. protected Class findClass(String className) throws ClassNotFoundException { 
  81. Class clazz = this.findLoadedClass(className); 
  82. if (null == clazz) { 
  83. try { 
  84. String classFile = getClassFile(className); 
  85. FileInputStream fis = new FileInputStream(classFile); 
  86. FileChannel fileC = fis.getChannel(); 
  87. ByteArrayOutputStream baos = new 
  88. ByteArrayOutputStream(); 
  89. WritableByteChannel outC = Channels.newChannel(baos); 
  90. ByteBuffer buffer = ByteBuffer.allocateDirect(1024); 
  91. while (true) { 
  92. int i = fileC.read(buffer); 
  93. if (i == 0 || i == -1) { 
  94. break; 
  95. } 
  96. buffer.flip(); 
  97. outC.write(buffer); 
  98. buffer.clear(); 
  99. } 
  100. fis.close(); 
  101. byte[] bytes = baos.toByteArray(); 
  102.  
  103. clazz = defineClass(className, bytes, 0, bytes.length); 
  104. } catch (FileNotFoundException e) { 
  105. e.printStackTrace(); 
  106. } catch (IOException e) { 
  107. e.printStackTrace(); 
  108. } 
  109. } 
  110. return clazz; 
  111. } 
  112. private byte[] loadClassBytes(String className) throws 
  113. ClassNotFoundException { 
  114. try { 
  115. String classFile = getClassFile(className); 
  116. FileInputStream fis = new FileInputStream(classFile); 
  117. FileChannel fileC = fis.getChannel(); 
  118. ByteArrayOutputStream baos = new 
  119. ByteArrayOutputStream(); 
  120. WritableByteChannel outC = Channels.newChannel(baos); 
  121. ByteBuffer buffer = ByteBuffer.allocateDirect(1024); 
  122. while (true) { 
  123. int i = fileC.read(buffer); 
  124. if (i == 0 || i == -1) { 
  125. break; 
  126. } 
  127. buffer.flip(); 
  128. outC.write(buffer); 
  129. buffer.clear(); 
  130. } 
  131. fis.close(); 
  132. return baos.toByteArray(); 
  133. } catch (IOException fnfe) { 
  134. throw new ClassNotFoundException(className); 
  135. } 
  136. } 
  137. private String getClassFile(String name) { 
  138. StringBuffer sb = new StringBuffer(fileName); 
  139. name = name.replace('.', File.separatorChar) + ".class"; 
  140. sb.append(File.separator + name); 
  141. return sb.toString(); 
  142. } 
  143. } 

该类中通过调用defineClass(String name, byte[] b, int off, int len)方法来定义一个类:

Java代码

 
 
 
  1. protected final Class defineClass(String name, byte[] b, int off, int len) 
  2. throws ClassFormatError 
  3. { 
  4. return defineClass(name, b, off, len, null); 
  5. } 
  6.  
  7. protected final Class defineClass(String name, byte[] b, int off, int len) 
  8. throws ClassFormatError 
  9. { 
  10. return defineClass(name, b, off, len, null); 
  11. } 

注:MyClassLoader加载类时有一个局限,必需指定.class文件,而不能指定.jar文件。该类中的大部分代码是从网上搜索到的,是出自一牛人之笔,只是不知道原帖在哪,希望不会被隐藏。

MainClassLoader类:

Java代码

 
 
 
  1. public class MainClassLoader { 
  2. public static void main(String[] args) { 
  3. try { 
  4. MyClassLoader tc = new MyClassLoader("F:\\OpenLib\\"); 
  5. Class c = tc.findClass("Test"); 
  6. c.newInstance(); 
  7. } catch (ClassNotFoundException e) { 
  8. e.printStackTrace(); 
  9. } catch (IllegalAccessException e) { 
  10. e.printStackTrace(); 
  11. } catch (InstantiationException e) { 
  12. e.printStackTrace(); 
  13. } 
  14. } 
  15. } 
  16.  
  17. public class MainClassLoader { 
  18. public static void main(String[] args) { 
  19. try { 
  20. MyClassLoader tc = new MyClassLoader("F:\\OpenLib\\"); 
  21. Class c = tc.findClass("Test"); 
  22. c.newInstance(); 
  23. } catch (ClassNotFoundException e) { 
  24. e.printStackTrace(); 
  25. } catch (IllegalAccessException e) { 
  26. e.printStackTrace(); 
  27. } catch (InstantiationException e) { 
  28. e.printStackTrace(); 
  29. } 
  30. } 
  31. } 

***是一个简单的Test测试类:

Java代码

 
 
 
  1. public class Test 
  2. { 
  3. public Test() { 
  4. System.out.println("Test"); 
  5. } 
  6. public static void main(String[] args) { 
  7. System.out.println("Hello World"); 
  8. } 
  9. }  

原文链接:http://huangcanqin.iteye.com/blog/1273170 【编辑推荐】

  1. Java内存泄露的理解与解决
  2. 精解Java中代理模式的实现
  3. Java自带的Future多线程模式
  4. 解析Java finally的神秘面纱
  5. 调用Java NIO提高文件读写速度

名称栏目:Java的ClassLoader机制解析
URL链接:http://www.hnjierui.cn/article/dhphdoe.html

其他资讯