Classloader解析&总结
来源:互联网 发布:淘宝同城交易发货快吗 编辑:程序博客网 时间:2024/05/19 00:13
——《java程序员面试宝典》解惑
文/54dabang
昨天晚上在看《java程序员面试宝典》时候,发现里面 Classloader知识点介绍的非常含糊,今天早上又重新从网上搜了相关资料,并且梳理了一下,希望对想了解这一块的同学有所帮助。需要注意一点是,有些地方我也没有进行深入研究,在此标出。
一 What? 什么是classloader
1.定义:在jvm中负责加载java class文件的部分。
2.有多种
(1) Bootstrap ClassLoader本地代码(根据操作系统,非java编写)实现,负责加载核心javaAPI(即以java.*.*开头的所有类) 以及下面的两个classLoader(ExtClassLoader,AppClassLoader)
(2)ExtClassLoader (全称 Extension ClassLoadr扩展类加载器) 加载扩展API (例如 jRE的ext目录下的类)
(3) AppClassLoader 负责加载用户自定义的应用程序类,即我们自己写的程序,编译后存放在CLASSPATH目录下的*.class文件。
3.加载顺序
JVM启动-----àBootstrap classLoader加载---àExtClassLoader加载 ----àAppClassLoader加载
注意:后两个classLoader都是由Bootstrap classLoader来启动的
4.何时使用classLoader
当你使用java去执行一个类,JVM使用ApplicationClassLoader加载这个类;然后如果类A引用了类B,不管是直接引用还是用Class.forName()引用,JVM就会找到加载类A的ClassLoader,并用这个ClassLoader来加载类B。JVM按照运行时的有效执行语句,来决定是否需要装载新类,从而装载尽可能少的类,这一点和编译类是不相同的。
即类A 引用类B(引用方式含有两种:用Class.forName()引用或者直接使用变量来引用)。
Jvm-->通过ApplicationCalssloader加载类A------->通过A的classLoader加载类B
二 why 为什么要自己写classLoader
因为JVM自带的ClassLoader只是懂得从本地文件系统加载标准的java class文件,如果我们的class文件从网络、数据库等其他渠道来获取时候,我们就需要自己写classLoader,方法是要重载父类的findClass()。
自定义classLoader可以实现以下目标:
1)在执行非置信代码之前,自动验证数字签名(安全保证)
2)动态地创建符合用户特定需要的定制化构建类
3)从特定的场所取得java class,例如数据库中、网络中进行加载。
三 How 如何写自己的classLoader
(1)需要继承java.lang.ClassLoader或者它的子类
(2)重写findClass()方法。
四 classLoader加载的方式
(1)使用双亲委托模式进行类加载。
自定义ClassLoader都必须继承ClassLoader这个抽象类,而每个ClassLoader都会有一个parent ClassLoader,我们可以看一下ClassLoader这个抽象类中有一个getParent()方法,这个方法用来返回当前ClassLoader的parent,注意,这个parent不是指的被继承的类,而是在实例化该ClassLoader时指定的一个ClassLoader,如果这个parent为null,那么就默认该ClassLoader的parent是bootstrap classloader,这个parent有什么用呢?
看一下classLoader的源代码:
protected synchronized Class loadClass(String name, boolean resolve)throws ClassNotFoundException {// 首先检查该name指定的class是否有被加载Class c = findLoadedClass(name);if (c == null) { try {if (parent != null) { //如果parent不为null,则调用parent的loadClass进行加载c = parent.loadClass(name, false);} else {//parent为null,则调用BootstrapClassLoader进行加载 c = findBootstrapClass0(name);} } catch (ClassNotFoundException e) { //如果仍然无法加载成功,则调用自身的findClass进行加载 c = findClass(name);//用户自定义的 }}if (resolve) { resolveClass(c);}return c; }
说明:我们使用这个自定义的ClassLoader加载java.lang.String,那么这里String不会被这个ClassLoader加载。原因是:
这里使用了委托模式,根据上述代码。我们进行分情况讨论。如果parent!=null,那么则会调用parent.loadClass()方法,一直递归,所有的classLoader类都继承自BootstrapClassLoader,那么最终会调用BootstrapClassLoader的
findBootstrapClass0(name);方法。如果parent==null那么就会直接调用
BootstrapClassLoader方法。不论哪种方式,都会最终调用BootstrapClassLoader的方法。
(2)这样做的好处(安全、避免程序加载)
第一个原因就是因为这样可以避免重复加载,当父亲已经加载了该类的时候,就没有必要子ClassLoader再加载一次。
第二个原因就是考虑到安全因素,我们试想一下,如果不使用这种委托模式,那我们就可以随时使用自定义的String来动态替代java核心api中定义类型,这样会存在非常大的安全隐患,而双亲委托的方式,就可以避免这种情况,因为String已经在启动时被加载,所以用户自定义类是无法加载一个自定义的ClassLoader。
五 自定义classLoader代码实现
较好的一篇博客
http://rainlife.iteye.com/blog/70072
以下是从本地读取classLoader代码。
package com.cmw.example.one;import java.io.File;import java.io.FileInputStream;import java.io.FileNotFoundException;import java.io.IOException;import java.lang.reflect.InvocationTargetException;import java.lang.reflect.Method; /** * 文件加载类 * 可根据MyFileClassLoader 从文件中动态生成类 * @author chengmingwei * */public class MyFileClassLoader extends ClassLoader { private String classPath; @SuppressWarnings("unchecked") public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException{ MyFileClassLoader fileClsLoader = new MyFileClassLoader(); fileClsLoader.setClassPath("E:\\j2ee_proj\\skythink\\WebContent\\WEB-INF\\classes\\"); Class cls = fileClsLoader.loadClass("com.cmw.entity.sys.AccordionEntity"); Object obj = cls.newInstance(); Method[] mthds = cls.getMethods(); for(Method mthd : mthds){ String methodName = mthd.getName(); System.out.println("mthd.name="+methodName); } System.out.println("obj.class="+obj.getClass().getName()); System.out.println("obj.class="+cls.getClassLoader().toString()); System.out.println("obj.class="+cls.getClassLoader().getParent().toString()); } /** * 根据类名字符串从指定的目录查找类,并返回类对象 */ @Override protected Class<?> findClass(String name) throws ClassNotFoundException { byte[] classData = null; try { classData = loadClassData(name); } catch (IOException e) { e.printStackTrace(); } return super.defineClass(name, classData, 0, classData.length);//调用父类方法 固定格式 } /** * 根据类名字符串加载类 byte 数据流 * @param name 类名字符串 例如: com.cmw.entity.SysEntity * @return 返回类文件 byte 流数据 * @throws IOException */ private byte[] loadClassData(String name) throws IOException{ File file = getFile(name); FileInputStream fis = new FileInputStream(file); byte[] arrData = new byte[(int)file.length()];//一次性读取文件到二进制数组 fis.read(arrData); return arrData; } /** * 根据类名字符串返回一个 File 对象 * @param name 类名字符串 * @return File 对象 * @throws FileNotFoundException */ private File getFile(String name) throws FileNotFoundException {//获取文件 File dir = new File(classPath); if(!dir.exists()) throw new FileNotFoundException(classPath+" 目录不存在!"); String _classPath = classPath.replaceAll("[\\\\]", "/"); int offset = _classPath.lastIndexOf("/"); name = name.replaceAll("[.]", "/"); if(offset != -1 && offset < _classPath.length()-1){ _classPath += "/"; } _classPath += name +".class"; dir = new File(_classPath); if(!dir.exists()) throw new FileNotFoundException(dir+" 不存在!"); return dir; } public String getClassPath() { return classPath; } public void setClassPath(String classPath) { this.classPath = classPath; } }上述代码中最主要的思想是从*.class文件中读取二进制的文件到classData数组当中,然后重写findClass方法:
super.defineClass(name, classData, 0, classData.length);,在调用时直接使用classLoader.loadclass,父类会在内部实现中调用我们重写的findClass方法。
- Classloader解析&总结
- ClassLoader解析
- classloader总结
- ClassLoader总结
- classloader总结
- ClassLoader总结
- ClassLoader总结
- ClassLoader总结
- java classloader 解析
- classloader 的 原理解析
- classloader 的 原理解析
- classLoader深入解析
- ClassLoader 深入解析
- Java Classloader机制解析
- Java Classloader机制解析
- Java Classloader机制解析
- Java Classloader机制解析
- Java Classloader机制解析
- 镶嵌数据集工具小结(九)计算像元大小范围
- ext.net 连接Oracle数据库时,不显示明细内容,只显示对应条数的空白行的解决办法
- 老师讲的,感觉很好记录下来,人生的世界就是从简单开始的。
- Xcode一些实用的插件
- JAVA 十六进制与字符串的转换
- Classloader解析&总结
- C++拷贝构造函数(深拷贝,浅拷贝)
- VisualSVN server安装与使用
- “mysql服务无法启动 发生系统错误1067”的解决办法
- 字符数组排序
- socket网络编程的一些基本函数
- 在使用Scripting.FileSystemObject 的时候报错automation服务器不能创建对象
- the android sdk folder can no longer be inside the application folder
- 初识Spring+SpringMVC+MyBatis框架(一)---web.xml