点入ClassLoader的源码查看样例:
* <blockquote><pre>* class NetworkClassLoader extends ClassLoader {* String host;* int port;** public Class findClass(String name) {* byte[] b = loadClassData(name);* return defineClass(name, b, 0, b.length);* }** private byte[] loadClassData(String name) {* // load the class data from the connection* . . .* }* }* </pre></blockquote>
只需继承CalssLoader,重写 findClass方法即可:
package reflect;import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;public class MyClassLoader extends ClassLoader{// 用于保存加载的文件的大小private int classSize; // 新增的类加载路径private static List<String> paths = new ArrayList<>();static {paths.add("D:\\Users\\jeff.chan\\Desktop\\");}// 默认会将app类加载器做为该类加载器的父加载器public MyClassLoader() {}public MyClassLoader(ClassLoader parent) {super(parent);}@Overrideprotected Class<?> findClass(String name) throws ClassNotFoundException {byte[] b = loadBinaryData(name);// 打印执行标记System.out.println("use custom classloader...");return defineClass(name, b,0, classSize);}public byte[] loadBinaryData(String name) throws ClassNotFoundException{String pathName = name.replaceAll("\\.", "/");File file = null;for (String path : paths) {file = new File(path+pathName+".class");if(file.exists() && file.isFile()){break;}}if(file==null || !file.isFile()){throw new ClassNotFoundException();}// 读取文件的二进制信息try (InputStream in = new FileInputStream(file)){// 一般这个大小够了,这里是为了方便读取// 文件的内容,所以一次性读入,并且将大小返回// 用于defineClassbyte[] b = new byte[1024 * 1024 * 10];classSize = in.read(b);return b;}catch (IOException ioException){;}return null;}
}
这里加入了一个MyClassLoader自定义类加载器,扫描的路径为:
D:\\Users\\jeff.chan\\Desktop\\
那么新加入的类加载器就和app类加载器,扩展类加载器,启动类加载器一起起作用。
测试用例:
package reflect;public class Person {private String name;private String age;public Person() {}public Person(String name, String age) {this.name = name;this.age = age;}public String getName() {return name;}public void setName(String name) {this.name = name;}public String getAge() {return age;}public void setAge(String age) {this.age = age;}@Overridepublic String toString() {return "Person{" +"name='" + name + '\'' +", age='" + age + '\'' +'}';}
}
客户端:
package reflect;public class Client {public static void main(String[] args) throws Exception {MyClassLoader classLoader = new MyClassLoader();Class<?> aClass = classLoader.loadClass("reflect.Person");System.out.println(aClass);System.out.println(aClass.getClassLoader());}
}
此时直接运行代码:那么会输出
class reflect.Person
sun.misc.Launcher$AppClassLoader@18b4aac2
发现自己的类加载器根本不起作用。其实是因为双亲委托机制:
1、MyClassLoader 查看是否已经加载 -》 2、App类加载查看是否已经加载 -》3、 扩展类加载器查看是否已经加载
-》 4、启动类加载器查看是否已经加载,没有加载就尝试加载 -》 5、扩展类尝试加载 -》 6、app类加载尝试加载 -》 7、MyClassLoader尝试加载 -》 8、抛出找不到类的异常
上述的流程中,第6步会找到对应的类,所以就直接返回了,根本没有给MyClassLoader机会加载,所以才没有调用自定义的类加载器MyClassLoader。
如果想要加载的话,那么就到编译生成的Person.class移动到桌面reflect/Person.class下,执行结果:
use custom classloader...
class reflect.Person
reflect.MyClassLoader@135fbaa4
结果发现,自定义的类加载起作用了,其实就是上述的流程中的第7步,找到了相应的类的二进制文件,加载了进来。