博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
使用自定义 classloader 的正确姿势
阅读量:4961 次
发布时间:2019-06-12

本文共 10127 字,大约阅读时间需要 33 分钟。

详细的原理就不多说了,网上一大把, 但是, 看了很多很多, 即使看了jdk 源码, 说了罗里吧嗦, 还是不很明白:

 

到底如何正确自定义ClassLoader, 需要注意什么

ExtClassLoader 是什么鬼

自定义ClassLoader具体是如何加载 类的。。

 

直接上代码:

import java.io.ByteArrayOutputStream;import java.io.FileInputStream;import java.io.FileNotFoundException;import java.io.IOException;import java.io.InputStream;import java.lang.reflect.InvocationTargetException;import java.lang.reflect.Method;import java.nio.ByteBuffer;import java.nio.channels.Channels;import java.nio.channels.FileChannel;import java.nio.channels.WritableByteChannel;import java.util.Date;import com.lk.AbcBean;public class ClassLoaderLK extends ClassLoader {    /**     * @param args     */    public static void main(String[] args) {//        this.class.getSystemClassLoader();                String ext = "java.ext.dirs";        System.out.println("java.ext.dirs :\n" + System.getProperty(ext));        String cp = "java.class.path";        System.out.println("java.class.path :\n" + System.getProperty(cp));                ClassLoader currentClassloader = ClassLoaderLK.class.getClassLoader();                String pp = "d:\\testcl\\";        ClassLoaderLK cl = new ClassLoaderLK(currentClassloader, pp);                System.out.println();        System.out.println("currentClassloader is " + currentClassloader);        System.out.println();        String name = "com.lk.AbcBean.class";        name = "com.lk.AbcBean";        try {            Class
loadClass = cl.loadClass(name); Object object = loadClass.newInstance(); // AbcBean ss = (AbcBean) object; // 无法转换的 (1)// ss.greeting(); (1) System.out.println(); System.out.println(" invoke some method !"); System.out.println(); Method method = loadClass.getMethod("greeting"); method.invoke(object); } catch (ClassNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (InstantiationException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IllegalAccessException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (NoSuchMethodException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (SecurityException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IllegalArgumentException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (InvocationTargetException e) { // TODO Auto-generated catch block e.printStackTrace(); } } private ClassLoader parent = null; // parent classloader private String path; public ClassLoaderLK(ClassLoader parent, String path) { super(parent); this.parent = parent; // 这样做其实是无用的 this.path = path; } public ClassLoaderLK(String path) { this.path = path; } @Override public Class
loadClass(String name) throws ClassNotFoundException {// return super.loadClass(name); Class
cls = findLoadedClass(name); if (cls == null) {// cls = getSystemClassLoader().loadClass(name); (2)// SystemClassLoader 会从classpath下加载// if (cls == null) {(2) // 默认情况下, 当前cl的parent是 SystemClassLoader, // 而当前cl的parent的parent 才是ExtClassLoader ClassLoader parent2 = getParent().getParent();// System.out.println("Classloader is : " + parent2); try { System.out.println("try to use ExtClassLoader to load class : " + name); cls = parent2.loadClass(name); } catch (ClassNotFoundException e) { System.out.println("ExtClassLoader.loadClass :" + name + " Failed"); }// }(2) if (cls == null) { System.out.println("try to ClassLoaderLK load class : " + name); cls = findClass(name); if (cls == null) { System.out.println("ClassLoaderLK.loadClass :" + name + " Failed"); } else { System.out.println("ClassLoaderLK.loadClass :" + name + " Successful"); } } else { System.out.println("ExtClassLoader.loadClass :" + name + " Successful"); } } return cls; } @Override @SuppressWarnings("rawtypes") protected Class
findClass(String name) throws ClassNotFoundException {// return super.findClass(name); System.out.println( "try findClass " + name); InputStream is = null; Class class1 = null; try { String classPath = name.replace(".", "\\") + ".class";// String[] fqnArr = name.split("\\."); // split("."); 是不行的, 必须split("\\.")// if (fqnArr == null || fqnArr.length == 0) {// System.out.println("ClassLoaderLK.findClass()");// fqnArr = name.split("\\.");// } else {// System.out.println( name + fqnArr.length);// } String classFile = path + classPath; byte[] data = getClassFileBytes(classFile ); class1 = defineClass(name, data, 0, data.length); if (class1 == null) { System.out.println("ClassLoaderLK.findClass() ERR "); throw new ClassFormatError(); } } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } catch (Exception e) { e.printStackTrace(); } finally { if (is != null) { try { is.close(); } catch (IOException e) { e.printStackTrace(); } } } return class1; } private byte[] getClassFileBytes(String classFile) throws Exception { FileInputStream fis = new FileInputStream(classFile ); FileChannel fileC = fis.getChannel(); ByteArrayOutputStream baos = new ByteArrayOutputStream(); WritableByteChannel outC = Channels.newChannel(baos); ByteBuffer buffer = ByteBuffer.allocateDirect(1024); while (true) { int i = fileC.read(buffer); if (i == 0 || i == -1) { break; } buffer.flip(); outC.write(buffer); buffer.clear(); } fis.close(); return baos.toByteArray(); } }

 

随便的一个java 类, 简单起见,就写一个bean吧

package com.lk;import java.util.Date;public class AbcBean {        @Override    public String toString() {        return "AbcBean [name=" + name + ", age=" + age + "]";    }        String name;    int age;    Date birthDay;        public Date getBirthDay() {        return birthDay;    }    public void setBirthDay(Date birthDay) {        this.birthDay = birthDay;    }    public String getName() {        return name;    }    public void setName(String name) {        this.name = name;    }    public int getAge() {        return age;    }    public void setAge(int age) {        this.age = age;    }        public void greeting() {        System.out.println("AbcBean.greeting()");    }}

 

直接执行,结果:

currentClassloader is sun.misc.Launcher$AppClassLoader@513cf0try to use ExtClassLoader to load class : com.lk.AbcBeanExtClassLoader.loadClass :com.lk.AbcBean Failedtry to ClassLoaderLK load class : com.lk.AbcBeantry findClass com.lk.AbcBeanjava.io.FileNotFoundException: d:\testcl\com\lk\AbcBean.class (系统找不到指定的路径。)    at java.io.FileInputStream.open(Native Method)    at java.io.FileInputStream.
(FileInputStream.java:138) at java.io.FileInputStream.
(FileInputStream.java:97) at ClassLoaderLK.getClassFileBytes(ClassLoaderLK.java:172) at ClassLoaderLK.findClass(ClassLoaderLK.java:145) at ClassLoaderLK.loadClass(ClassLoaderLK.java:112) at ClassLoaderLK.main(ClassLoaderLK.java:38)Exception in thread "main" java.lang.NullPointerException at ClassLoaderLK.main(ClassLoaderLK.java:40)ClassLoaderLK.loadClass :com.lk.AbcBean Failed

 

将com.lk 目录全部复制到 d:\\testcl\\ 下,

currentClassloader is sun.misc.Launcher$AppClassLoader@513cf0try to use ExtClassLoader to load class : com.lk.AbcBean ExtClassLoader.loadClass :com.lk.AbcBean Failed try to ClassLoaderLK load class : com.lk.AbcBean try findClass com.lk.AbcBean try to use ExtClassLoader to load class : java.lang.Object ExtClassLoader.loadClass :java.lang.Object Successful ClassLoaderLK.loadClass :com.lk.AbcBean Successful invoke some method ! try to use ExtClassLoader to load class : java.lang.String ExtClassLoader.loadClass :java.lang.String Successful try to use ExtClassLoader to load class : java.lang.System ExtClassLoader.loadClass :java.lang.System Successful try to use ExtClassLoader to load class : java.io.PrintStream ExtClassLoader.loadClass :java.io.PrintStream Successful AbcBean.greeting()

 

 

将AbcBean打包成 jar 放置到 jdk 下的jre 的ext目录 ( 打包成 zip 也是可行的! 但是rar是不行的!!!  why ? 估计zip和jar都是使用的java 的zip流, 而rar是后面产生的新格式,故没有被支持。另外, 仅仅拷贝class 过去也是不行的! )

执行结果:

currentClassloader is sun.misc.Launcher$AppClassLoader@513cf0try to use ExtClassLoader to load class : com.lk.AbcBeanExtClassLoader.loadClass :com.lk.AbcBean Successful invoke some method !AbcBean.greeting()

 

可见ExtClassLoader 是如何作用了的吧!!

 

总结,

1 从 invoke some method 前后的日志,可见 类加载 的大致过程。

2 代码中 (1), 的部分是注释了的, 因为 不同类加载器加载的类是 不能直接cast的。。   但把(1),(2) 同时解开注释, 又可以了, 这是因为他们都是使用的系统类加载器, 自定义的类加载器相当于没有生效。。( 这个当然不是我们需要的结果。)

3 loadClass, findClass 两个方法的复写是必须的。 上面代码中的loadClass 的写法其实有点问题, 参照classloader 源码, 应该还需要一步: parent加载不上了, 使用bootstrap 加载, 不过感觉一般应该是用不上的—— 谁需要去替换 jdk 的rt.jar 的类 ??

4 ExtClassLoader 是去加载 jdk 下 jre  ext 目录的类似jar 的文件——  后缀是不是jar 不要紧, 内容是jar就行了。

 

posted on
2016-07-04 15:21 阅读(
...) 评论(
...)

转载于:https://www.cnblogs.com/FlyAway2013/p/5640586.html

你可能感兴趣的文章
java小技巧
查看>>
POJ 3204 Ikki's Story I - Road Reconstruction
查看>>
【BZOJ】2959: 长跑(lct+缩点)(暂时弃坑)
查看>>
iOS 加载图片选择imageNamed 方法还是 imageWithContentsOfFile?
查看>>
toad for oracle中文显示乱码
查看>>
SQL中Group By的使用
查看>>
错误org/aopalliance/intercept/MethodInterceptor解决方法
查看>>
Pylint在项目中的使用
查看>>
使用nginx做反向代理和负载均衡效果图
查看>>
access remote libvirtd
查看>>
(4) Orchard 开发之 Page 的信息存在哪?
查看>>
ASP.NET中 GridView(网格视图)的使用前台绑定
查看>>
深入了解Oracle ASM(二):ASM File number 1 文件目录
查看>>
Boosting(提升方法)之AdaBoost
查看>>
Binding object to winForm controller through VS2010 Designer(通过VS2010设计器将对象绑定到winForm控件上)...
查看>>
Spring Boot实战笔记(二)-- Spring常用配置(Scope、Spring EL和资源调用)
查看>>
活现被翻转生命
查看>>
POJ 1228
查看>>
SwaggerUI+SpringMVC——构建RestFul API的可视化界面
查看>>
springmvc怎么在启动时自己执行一个线程
查看>>