工具要求:以下过程在Idea执行
对于类加载器,除了jvm自身的类加载器,还可以通过阅读源码,自定义自己的类加载器,spring框架中和tomcat都有属于自己的类加载器
开始工作
写一个 类继承ClassLoader,如下
/**
* Created by admin on 2019/6/23.
*/
public class ClassLoaderTest01 extends ClassLoader {
}
ctrl+鼠标点击类名ClassLoader进入
找到loadClass()
/**
* Loads the class with the specified <a href="#name">binary name</a>. The
* default implementation of this method searches for classes in the
* following order:
*
* <ol>
*
* <li><p> Invoke {@link #findLoadedClass(String)} to check if the class
* has already been loaded. </p></li>
*
* <li><p> Invoke the {@link #loadClass(String) <tt>loadClass</tt>} method
* on the parent class loader. If the parent is <tt>null</tt> the class
* loader built-in to the virtual machine is used, instead. </p></li>
*
* <li><p> Invoke the {@link #findClass(String)} method to find the
* class. </p></li>
*
* </ol>
*
* <p> If the class was found using the above steps, and the
* <tt>resolve</tt> flag is true, this method will then invoke the {@link
* #resolveClass(Class)} method on the resulting <tt>Class</tt> object.
*
* <p> Subclasses of <tt>ClassLoader</tt> are encouraged to override {@link
* #findClass(String)}, rather than this method. </p>
*
* <p> Unless overridden, this method synchronizes on the result of
* {@link #getClassLoadingLock <tt>getClassLoadingLock</tt>} method
* during the entire class loading process.
*
* @param name
* The <a href="#name">binary name</a> of the class
*
* @param resolve
* If <tt>true</tt> then resolve the class
*
* @return The resulting <tt>Class</tt> object
*
* @throws ClassNotFoundException
* If the class could not be found
*/
protected Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
synchronized (getClassLoadingLock(name)) {
// First, check if the class has already been loaded
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) {
// ClassNotFoundException thrown if class not found
// from the non-null parent class loader
}
if (c == null) {
// If still not found, then invoke findClass in order
// to find the class.
long t1 = System.nanoTime();
c = findClass(name);
// this is the defining class loader; record the stats
sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
sun.misc.PerfCounter.getFindClasses().increment();
}
}
if (resolve) {
resolveClass(c);
}
return c;
}
}
看到
synchronized//避免同步重复问题
//First, check if the class has already been loaded这行注释
意思是在加载一个类之前先检查这个类是否已经被加载,他的方式类似递归向父加载器检查是否被加载 -----即双亲委派(app-ext-boot再boot-ext-app),如下代码
如果最终都没有找到这个类被加载
就执行findClass()
但是,发现findClass直接抛出异常,
从以上看出,我们只需要继承ClassLoader类并重写findClass()即可实现自定义的类加载器
那么如何自定义加载我们的类呢?
进入ClassLoader的实现类
我们可以看到以下两个类,他们分别负责加载不同路径下的class文件
ExtClassLoader
AppClassLoader
如
extClassLoader加载的是java.ext.dirs路径下的
appClassLoader则加载的是java.class.path即我们常说的classpath路劲
通过以上的源码查看和分析,我们实现以上的ClassLoaderTest01
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
/**
* Created by admin on 2019/6/23.
*/
public class ClassLoaderTest01 extends ClassLoader {
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException{
//读取class字符流:注意这里的class最好不要放在classpath下面,要不然会报错
File file=new File("E:/Cybersource_Doc/",name.replace(".","/").concat(".class"));
try {
//转换字节流
FileInputStream fis=new FileInputStream(file);
//定义一个字节数组输出流
ByteArrayOutputStream bos=new ByteArrayOutputStream();
int read=0;
while ((read = fis.read())!=0 ){
bos.write(read);
}
//转换为字节数组
byte[] bytes = bos.toByteArray();
bos.close();
fis.close();
return defineClass(name,bytes,0,bytes.length);
}catch (Exception e){
e.printStackTrace();
}
return null;
}
//测试方法
public static void main(String[] args) {
//实例化我们自定义的classLoader
ClassLoader loader=new ClassLoaderTest01();
try {
//传入class文件名称
Class<?> cl = loader.loadClass("StaticClassLoader");
//通过反射拿到StaticClassLoader
StaticClassLoader scl = (StaticClassLoader)cl.newInstance();
//调用里面的test方法
scl.test();
}catch (Exception e){
e.printStackTrace();
}
}
}
工具中准备一个用来给我们测试的class,如下
/**
* Created by lijinquan on 2019/6/24.
*/
public class StaticClassLoader {
static {
System.out.println("我是StaticClassLoader的静态代码块!");
}
public void test(){
System.out.println("test123");
}
public static void main(String[] args) {
}
}
鼠标右键该java文件,并
得到如下
再右键此class文件,复制到我们一个自己独立的文件夹,方便我们测试
测试代码
public static void main(String[] args) {
//实例化我们自定义的classLoader
ClassLoader loader=new ClassLoaderTest01();
try {
//传入class文件名称
Class<?> cl = loader.loadClass("StaticClassLoader");
//通过反射拿到StaticClassLoader
StaticClassLoader scl = (StaticClassLoader)cl.newInstance();
//调用里面的test方法
scl.test();
}catch (Exception e){
e.printStackTrace();
}
}
可能会出错误:原因是生成的class可能还放在运行的项目里面,需要拿出来放到独立文件夹目录下即可
最终运行结果
当然,还可以测试下我们自定义类加载器的父类
在测试方法中加入
System.out.println(loader.getParent());
System.out.println(ClassLoaderTest01.class.getClassLoader());
打印出
即使自定义的ClassLoader的父类是appClassLoader
这个父类是默认的,自定义中可以通过supper指定父类