怎么自定义一个类加载器?
2 个回答
明白太多道理,反而失去了这个年纪该有的快乐。君莫邪构建自定义类加载器的步骤是创建一个继承自 `java.lang.ClassLoader` 的类,并重写其 `findClass` 方法。通常,`loadClass` 方法可以不重写,而是调用 `findClass` 和 `defineClass` 来加载类。 下面是构建自定义类加载器的基本步骤和示例代码: ### 步骤: 1. **继承 `ClassLoader`**:新建一个类并继承自 `java.lang.ClassLoader`。 2. **重写 `findClass` 方法**:负责寻找并加载指定类的字节码,通常通过读取 `.class` 文件。 3. **调用 `defineClass` 方法**:将字节数组转换为 `Class` 对象。 4. **(可选)重写 `loadClass` 方法**:如需破坏双亲委派机制,重写 `loadClass` 方法,但通常不建议。 ### 示例代码: ```java import java.io.IOException; import java.nio.file.Files; import java.nio.file.Paths; public class CustomClassLoader extends ClassLoader { private String classPath; // 构造方法,传入类的路径 public CustomClassLoader(String classPath) { this.classPath = classPath; } // 重写findClass方法 @Override protected Class<?> findClass(String name) throws ClassNotFoundException { // 生成类文件的路径 String fileName = classPath + name.replace('.', '/') + ".class"; try { // 读取类文件的字节码 byte[] classData = Files.readAllBytes(Paths.get(fileName)); // 调用defineClass方法,将字节码转换为Class对象 return defineClass(name, classData, 0, classData.length); } catch (IOException e) { e.printStackTrace(); throw new ClassNotFoundException(name); } } // 可选重写loadClass方法,破坏双亲委派机制 @Override public Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException { if (name.startsWith("java.")) { return super.loadClass(name, resolve); // 让系统类加载器加载Java核心类 } return findClass(name); // 直接调用findClass加载类,绕过双亲委派 } public static void main(String[] args) throws Exception { // 设置自定义类加载器的路径,加载自定义类 CustomClassLoader customClassLoader = new CustomClassLoader("/path/to/classes/"); // 加载自定义类,并实例化对象 Class<?> customClass = customClassLoader.loadClass("com.example.MyClass"); Object instance = customClass.newInstance(); // 调用类的方法 System.out.println(instance.getClass().getName()); } } ``` ### 代码说明: 1. **`findClass` 方法**:将类的全限定名(如 `com.example.MyClass`)转换为对应的 `.class` 文件路径,并读取该文件的字节码,通过 `defineClass` 将字节码转换为 `Class` 对象。 2. **`loadClass` 方法**:重写此方法可以绕过双亲委派机制。如果类名以 `java.` 开头,使用父类加载器加载核心类,否则直接调用 `findClass`。 3. **自定义加载路径**:通过构造函数将类文件的路径传递给自定义类加载器。 通过这个方法,你可以自由加载自己定义的类或破坏双亲委派机制,加载未通过父类加载器加载的类。发布于 2022-07-04 15:53 •IP属地 新疆举报评论收藏
自己喜欢的东西,就不要问别人好不好。粑粑加载一个类时,一般是调用类加载器的loadClass()方法来加载一个类,loadClass()方法的工作流程如下: 1.先调用findLoadedClass(className)来获取这个类,判断类是否已加载。 2.如果未加载,如果父类加载器不为空,调用父类加载器的loadClass()来加载这个类,父类加载器为空,就调用父类加载器加载这个类。 3.父类加载器加载失败,那么调用该类加载器findClass(className)方法来加载这个类。 所以我们一般自定义类加载器都是继承ClassLoader,重写findClass()方法,来实现类加载,这样不会违背双亲委派类加载机制,也可以通过重写loadClass()方法进行类加载,但是这样会违背双亲委派类加载机制。 ```java public class DelegationClassLoader extends ClassLoader { private String classpath; public DelegationClassLoader(String classpath, ClassLoader parent) { super(parent); this.classpath = classpath; } @Override protected Class<?> findClass(String name) throws ClassNotFoundException { InputStream is = null; try { String classFilePath = this.classpath + name.replace(".", "/") + ".class"; is = new FileInputStream(classFilePath); byte[] buf = new byte[is.available()]; is.read(buf); return defineClass(name, buf, 0, buf.length); } catch (IOException e) { throw new ClassNotFoundException(name); } finally { if (is != null) { try { is.close(); } catch (IOException e) { throw new IOError(e); } } } } public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException, MalformedURLException { sun.applet.Main main1 = new sun.applet.Main(); DelegationClassLoader cl = new DelegationClassLoader("java-study/target/classes/", getSystemClassLoader()); String name = "sun.applet.Main"; Class<?> clz = cl.loadClass(name); Object main2 = clz.newInstance(); System.out.println("main1 class: " + main1.getClass()); System.out.println("main2 class: " + main2.getClass()); System.out.println("main1 classloader: " + main1.getClass().getClassLoader()); System.out.println("main2 classloader: " + main2.getClass().getClassLoader()); ClassLoader itrCl = cl; while (itrCl != null) { System.out.println(itrCl); itrCl = itrCl.getParent(); } } } ```发布于 2022-02-17 00:30 •IP属地 河北举报评论收藏
公众号进入网站体验更佳

