使用自定义 classloader 的正确姿势
详细的原理就不多说了,网上一大把, 但是, 看了很多很多, 即使看了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, , 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();
while (true) {
int i = fileC.read(buffer);
if (i == || i == -) {
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@513cf0 try 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
java.io.FileNotFoundException: d:\testcl\com\lk\AbcBean.class (系统找不到指定的路径。)
at java.io.FileInputStream.open(Native Method)
at java.io.FileInputStream.<init>(FileInputStream.java:)
at java.io.FileInputStream.<init>(FileInputStream.java:)
at ClassLoaderLK.getClassFileBytes(ClassLoaderLK.java:)
at ClassLoaderLK.findClass(ClassLoaderLK.java:)
at ClassLoaderLK.loadClass(ClassLoaderLK.java:)
at ClassLoaderLK.main(ClassLoaderLK.java:)
Exception in thread "main" java.lang.NullPointerException
at ClassLoaderLK.main(ClassLoaderLK.java:)
ClassLoaderLK.loadClass :com.lk.AbcBean Failed
将com.lk 目录全部复制到 d:\\testcl\\ 下,
currentClassloader is sun.misc.Launcher$AppClassLoader@513cf0 try 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@513cf0 try to use ExtClassLoader to load class : com.lk.AbcBean
ExtClassLoader.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就行了。
使用自定义 classloader 的正确姿势的更多相关文章
- 代码走查25条疑问 C# 跳转新的标签页 C#线程处理 .Net 特性 attribute 学习 ----自定义特性 看懂 ,学会 .NET 事件的正确姿势-简单版
代码走查25条疑问 代码走查(Code Review) 是一个开发人员与架构师集中讨论代码的过程.通过代码走查可以提高代码的 质量,同时减少Bug出现的几率.但是在小公司中并没有代码走查的过程在这 ...
- JAVA 利用JNI加密class文件/自定义ClassLoader 类
利用 JNI 对bytecode 加密.不影响java程序员的正常开发.09年的时候写的,现在拿出来晒晒————————————————————————————混淆才是王道,如果混淆再加密就更酷了.. ...
- 程序员取悦女朋友的正确姿势---Tips(iOS美容篇)
前言 女孩子都喜欢用美图工具进行图片美容,近来无事时,特意为某人写了个自定义图片滤镜生成器,安装到手机即可完成自定义滤镜渲染照片.app独一无二,虽简亦繁. JH定律:魔镜:最漂亮的女人是你老婆魔镜: ...
- jquery选中radio或checkbox的正确姿势
jquery选中radio或checkbox的正确姿势 Intro 前几天突然遇到一个问题,没有任何征兆的..,jquery 选中radio button单选框时,一直没有办法选中,后来查了许多资料, ...
- Taro 多端开发的正确姿势:打造三端统一的网易严选(小程序、H5、React Native)
笔者所在的趣店 FED 早在去年 10 月份就已全面使用 Taro 框架开发小程序(当时版本为 1.1.0-beta.4),至今也上线了 2 个微信小程序.2 个支付宝小程序. 之所以选用 Taro, ...
- Android PermissionUtils:运行时权限工具类及申请权限的正确姿势
Android PermissionUtils:运行时权限工具类及申请权限的正确姿势 ifadai 关注 2017.06.16 16:22* 字数 318 阅读 3637评论 1喜欢 6 Permis ...
- 程序员取悦女票的正确姿势---Tip1(iOS美容篇)
代码地址如下:http://www.demodashi.com/demo/11695.html 前言 女孩子都喜欢用美图工具进行图片美容,近来无事时,特意为某人写了个自定义图片滤镜生成器,安装到手机即 ...
- 读取ClassPath下resource文件的正确姿势
1.前言 为什么要写这篇文章?身为Java程序员你有没有过每次需要读取 ClassPath 下的资源文件的时候,都要去百度一下,然后看到下面的这种答案: Thread.currentThread(). ...
- laravel-nestedset:多级无限分类正确姿势
laravel-nestedset:多级无限分类正确姿势 laravel-nestedset是一个关系型数据库遍历树的larvel4-5的插件包 目录: Nested Sets Model简介 安 ...
随机推荐
- 防御XSS攻击-encode用户输入内容的重要性
一.开场先科普下XSS 跨站脚本攻击(Cross Site Scripting),为不和层叠样式表(Cascading Style Sheets, CSS)的缩写混淆,故将跨站脚本攻击缩写为XSS.恶 ...
- JavaScript权威指南 - 函数
函数本身就是一段JavaScript代码,定义一次但可能被调用任意次.如果函数挂载在一个对象上,作为对象的一个属性,通常这种函数被称作对象的方法.用于初始化一个新创建的对象的函数被称作构造函数. 相对 ...
- 一起学 Java(三) 集合框架、数据结构、泛型
一.Java 集合框架 集合框架是一个用来代表和操纵集合的统一架构.所有的集合框架都包含如下内容: 接口:是代表集合的抽象数据类型.接口允许集合独立操纵其代表的细节.在面向对象的语言,接口通常形成一个 ...
- MIP 官方发布 v1稳定版本
近期,MIP官方发布了MIP系列文件的全新v1版本,我们建议大家尽快完成升级. 一. 我是开发者,如何升级版本? 对于MIP页面开发者来说,只需替换线上引用的MIP文件为v1版本,就可以完成升级.所有 ...
- C语言 · 奇偶判断
问题描述 能被2整除的数称为偶数,不能被2整除的数称为奇数.给一个整数x,判断x是奇数还是偶数. 输入格式 输入包括一个整数x,0<=x<=100000000. 输出格式 如果x是奇数,则 ...
- [开发笔记]GCC 分支预测优化
#define likely(x) __builtin_expect(!!(x),1)#define unlikely(x) __builtin_expect(!!(x),0) 用于优化在做分支判断的 ...
- JavaWeb——Servlet
一.基本概念 Servlet是运行在Web服务器上的小程序,通过http协议和客户端进行交互. 这里的客户端一般为浏览器,发送http请求(request)给服务器(如Tomcat).服务器接收到请求 ...
- ObserverPattern(观察者模式)
import java.util.ArrayList; import java.util.List; /** * 观察者模式 * @author TMAC-J * 牵一发而动全身来形容观察者模式在合适 ...
- 记一次.NET代码重构
好久没写代码了,终于好不容易接到了开发任务,一看时间还挺充足的,我就慢慢整吧,若是遇上赶进度,基本上直接是功能优先,完全不考虑设计.你可以认为我完全没有追求,当身后有鞭子使劲赶的时候,神马设计都是浮云 ...
- 【干货分享】流程DEMO-出差申请单
流程名: 出差申请 业务描述: 员工出差前发起流程申请,流程发起时,会检查预算,如果预算不够,将不允许发起费用申请,如果预算够用,将发起流程,同时占用相应金额的预算,但撤销流程会释放相应金额的预算. ...