自定义类加载器
来源:互联网 发布:阿里云 mongodb 编辑:程序博客网 时间:2024/06/15 13:39
1.类的加载方法
- 命令行启动应用时候由JVM初始化加载
- 通过Class.forName()方法动态加载。除了将类的.class文件加载到jvm中之外,还会对类进行解释,执行类中的static块
- 通过ClassLoader.loadClass()方法动态加载。只是将.class文件加载到jvm中,不会执行static中的内容,只有在newInstance才会去执行static块。
注: Class.forName(name, initialize, loader)带参函数也可控制是否加载static块。并且只有调用了newInstance()方法采用调用构造函数,创建类的对象 。
2.双亲委派模型
如果一个类加载器收到了类加载的请求,它首先不会自己去尝试加载这个类,而是把请求委托给父加载器去完成,依次向上,因此,所有的类加载请求最终都应该被传递到顶层的启动类加载器中,只有当父加载器在它的搜索范围中没有找到所需的类时,即无法完成该加载,子加载器才会尝试自己去加载该类。
ClassLoad源码:
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException { synchronized (getClassLoadingLock(name)) { //检测类是否已经被加载 Class<?> c = findLoadedClass(name); if (c == null) { long t0 = System.nanoTime(); try { //若有父加载器,则委托给父加载器加载 if (parent != null) { c = parent.loadClass(name, false); } else { //若没有则委托启动类加载器取加载类 c = findBootstrapClassOrNull(name); } } catch (ClassNotFoundException e) {} //若父加载器和启动类加载器都无法加载类,在自己尝试加载 if (c == null) { long t1 = System.nanoTime(); c = findClass(name); sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0); sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1); sun.misc.PerfCounter.getFindClasses().increment(); } } if (resolve) { resolveClass(c); } return c; } }
3.自定义类加载器
首先介绍几个关键的方法
- loadClass
类加载方法,默认采用双亲委托模式,最好不要重写,因为容易破坏双亲委托模式。- findClass
在loadClass中被调用,该类主要用于获取java类的二进制流,并调用defineClass方法进行解析并返回Class对象。这个方法是自定义类加载器重写的主要方法。- defineClass
在findClass中被调用,解析字节码,将其转化为Class对象
了解了这几个方法,我们知道重写类加载器主要是重写findClass方法,下面就是一个自定义的类加载器的例子。
/** * Created by SJK on 2017/8/16. * 通过.class文件路径动态加载类 */public class MyClassLoader extends ClassLoader { /** *加载.class文件的方式有许多,可以从本地文件中加载,可以网络上下载,也可以从数据库中读取。同时大家也可以在加载前后进行加密解密 */ @Override protected Class<?> findClass(String path) throws ClassNotFoundException { //根据.class文件的路径得到类名 String className = getClassName(path); //读取.class文件数据 byte[] classData = loadClassData(path); if (classData == null) { throw new ClassNotFoundException(); } else { //解析byte[]数据并返回Class对象 return this.defineClass(className, classData, 0, classData.length); } } /** * 读取文件流转化为byte数组 * * @param path 路径 * @return */ private byte[] loadClassData(String path) { try { InputStream is = new FileInputStream(path); ByteArrayOutputStream bos = new ByteArrayOutputStream(); int bufferSize = 1024; byte[] buffer = new byte[bufferSize]; int length = 0; while ((length = is.read(buffer)) != -1) { bos.write(buffer, 0, length); } return bos.toByteArray(); } catch (Exception e) { e.printStackTrace(); return null; } } private String getClassName(String name) { if (name == null && name.trim().length() == 0) { return ""; } //去除.class文件后缀 if (name.indexOf(".") != -1) { name = name.split("\\.")[0]; } //取文件名 例: xxx/xxx/a --> a if (name.lastIndexOf(File.separator) != -1) { name = name.substring(name.lastIndexOf(File.separator) + 1); } return name; }}
注意,我们不能将.class文件放在ClassPath路径下,否则由于双亲加载的原则,会直接导致该类由AppClassLoader 加载,而不会通过我们自定义类加载器来加载。
阅读全文
0 0
- 自定义类加载器
- 自定义类加载器
- 自定义类加载器
- 自定义类加载器
- 自定义类加载器
- 自定义类加载器
- 自定义类加载器
- 自定义类加载器
- 自定义类加载器
- 自定义类加载器
- 自定义类加载器
- 自定义类加载器
- 自定义类加载器
- 自定义类加载器
- 自定义类加载器
- 自定义类加载器
- 自定义类加载器
- 自定义类加载器
- listview适配器
- _crol_和左移的区别
- js杂乱整理
- Android 简单式购物车(全)
- ubuntu下安装CUDA和CUDNN
- 自定义类加载器
- React组件的生命周期
- CodePush热更新常用命令与注意事项
- 判断是否连接上网络和跳转到打开网络的界面
- 自定义view+属性动画实现
- git基本操作指令
- Apache 防盗链配置
- 【nginx】-cache loader process进程分析
- 全局异常捕获器