重庆网站建设安全,广州档案馆建设网站,深圳网站建设服务提供商,北流市建设局网站Java ClassLoader是项目开发中Java的关键但很少使用的组件之一。 就我个人而言#xff0c;我从未在任何项目中扩展ClassLoader#xff0c;但是拥有自己的可以自定义Java类加载的ClassLoader的想法让我感到很兴奋。 本文将概述Java类加载#xff0c;然后继续创建自定义ClassL… Java ClassLoader是项目开发中Java的关键但很少使用的组件之一。 就我个人而言我从未在任何项目中扩展ClassLoader但是拥有自己的可以自定义Java类加载的ClassLoader的想法让我感到很兴奋。 本文将概述Java类加载然后继续创建自定义ClassLoader并使用它。 什么是ClassLoader 我们知道Java程序在Java虚拟机JVM上运行。 当我们编译Java类时它会以字节码的形式将其转换为平台和机器无关的已编译程序并将其存储为.class文件。 之后当我们尝试使用类时Java ClassLoader将该类加载到内存中。 Java中内置了三种类型的内置类加载器 Bootstrap类加载器 –它加载JDK内部类通常加载rt.jar和其他核心类例如java.lang。*包类 扩展类加载器 –它从JDK扩展目录通常为$ JAVA_HOME / lib / ext目录中加载类。 系统类加载器 –它从当前类路径加载类这些类可以在使用-cp或-classpath命令行选项调用程序时进行设置。 让我们通过执行以下java程序更好地理解这一点 ClassLoaderTest.java package com.journaldev.classloader;public class ClassLoaderTest {public static void main(String[] args) {System.out.println(class loader for HashMap: java.util.HashMap.class.getClassLoader());System.out.println(class loader for DNSNameService: sun.net.spi.nameservice.dns.DNSNameService.class.getClassLoader());System.out.println(class loader for this class: ClassLoaderTest.class.getClassLoader());System.out.println(com.mysql.jdbc.Blob.class.getClassLoader());}} 上面程序的输出 HashMap的类加载器null DNSNameService的类加载器sun.misc.Launcher$ExtClassLoader51f12c4e 此类的类加载器sun.misc.Launcher$AppClassLoader799134f4 sun.misc.Launcher$AppClassLoader799134f4 如您所见java.util.HashMap ClassLoader作为null来反映Bootstrap ClassLoader而DNSNameService ClassLoader是ExtClassLoader。 由于类本身位于CLASSPATH中因此System ClassLoader会加载它。 当我们尝试加载HashMap时我们的System ClassLoader将其委托给Extension ClassLoader后者再将其委托给找到该类的Bootstrap ClassLoader并将其加载到JVM中。 DNSNameService类遵循相同的过程但是Bootstrap ClassLoader无法定位它因为它位于$ JAVA_HOME / lib / ext / dnsns.jar中因此由扩展类加载器加载。 还有一点要注意的是子类加载器加载的类可以查看其父类加载器加载的类。 因此由System ClassLoader加载的类具有对Extensions和Bootstrap ClassLoader加载的类的可见性。 如果有同级类加载器则它们将无法访问彼此加载的类。 为什么要编写ClassLoader Java默认的ClassLoader可以从本地文件系统中加载文件这在大多数情况下已经足够了。 但是如果在加载类时希望在运行时或从FTP服务器或通过第三方Web服务获取类则必须扩展现有的类加载器。 例如AppletViewers从远程Web服务器加载类。 ClassLoader如何工作 当JVM请求一个类时它通过传递类的完全分类名称来调用ClassLoader的loadClass函数。 loadClass函数调用findLoadedClass方法来检查该类是否已加载。 需要避免多次加载该类。 如果尚未加载该类则它将把请求委派给父ClassLoader以加载该类。 如果父ClassLoader找不到该Class则它将调用findClass方法在文件系统中查找这些类。 创建我们自己的ClassLoader 我们将通过扩展ClassLoader类并覆盖loadClassString name函数来创建自己的ClassLoader。 如果名称以com.journaldev即我们的示例类包开头则将使用我们自己的类加载器加载它否则我们将调用父ClassLoader loadClass方法加载该类。 项目结构如下图所示 CCLoader.java这是我们的自定义类加载器具有以下方法。 1. 专用字节[] loadClassFileData字符串名称 此方法将从文件系统读取类文件到字节数组。 2. 私有类getClassString name 此方法将调用loadClassFileData函数并通过调用父defineClass方法它将生成Class并返回它。 3. public Class loadClass字符串名称 此方法负责加载Class。 如果类名以com.journaldev我们的示例类开头则它将使用getClass方法加载它否则它将调用父loadClass函数来加载它。 4. public CCLoaderClassLoader的父对象 这是负责设置父ClassLoader的构造函数。 CCLoader源代码 import java.io.DataInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;/*** Our Custom Class Loader to load the classes. Any class in the com.journaldev* package will be loaded using this ClassLoader. For other classes, it will* delegate the request to its Parent ClassLoader.**/
public class CCLoader extends ClassLoader {/*** This constructor is used to set the parent ClassLoader*/public CCLoader(ClassLoader parent) {super(parent);}/*** Loads the class from the file system. The class file should be located in* the file system. The name should be relative to get the file location** param name* Fully Classified name of class, for example com.journaldev.Foo*/private Class getClass(String name) throws ClassNotFoundException {String file name.replace(., File.separatorChar) .class;byte[] b null;try {// This loads the byte code data from the fileb loadClassFileData(file);// defineClass is inherited from the ClassLoader class// that converts byte array into a Class. defineClass is Final// so we cannot override itClass c defineClass(name, b, 0, b.length);resolveClass(c);return c;} catch (IOException e) {e.printStackTrace();return null;}}/*** Every request for a class passes through this method. If the class is in* com.journaldev package, we will use this classloader or else delegate the* request to parent classloader.*** param name* Full class name*/Overridepublic Class loadClass(String name) throws ClassNotFoundException {System.out.println(Loading Class name );if (name.startsWith(com.journaldev)) {System.out.println(Loading Class using CCLoader);return getClass(name);}return super.loadClass(name);}/*** Reads the file (.class) into a byte array. The file should be* accessible as a resource and make sure that its not in Classpath to avoid* any confusion.** param name* File name* return Byte array read from the file* throws IOException* if any exception comes in reading the file*/private byte[] loadClassFileData(String name) throws IOException {InputStream stream getClass().getClassLoader().getResourceAsStream(name);int size stream.available();byte buff[] new byte[size];DataInputStream in new DataInputStream(stream);in.readFully(buff);in.close();return buff;}
} CCRun.java 这是带有主要功能的测试类我们在其中创建ClassLoader的对象并使用其loadClass方法加载示例类。 加载该类后我们将使用Java Reflection API来调用其方法。 import java.lang.reflect.Method;public class CCRun {public static void main(String args[]) throws Exception {String progClass args[0];String progArgs[] new String[args.length - 1];System.arraycopy(args, 1, progArgs, 0, progArgs.length);CCLoader ccl new CCLoader(CCRun.class.getClassLoader());Class clas ccl.loadClass(progClass);Class mainArgType[] { (new String[0]).getClass() };Method main clas.getMethod(main, mainArgType);Object argsArray[] { progArgs };main.invoke(null, argsArray);// Below method is used to check that the Foo is getting loaded// by our custom class loader i.e CCLoaderMethod printCL clas.getMethod(printCL, null);printCL.invoke(null, new Object[0]);}} Foo.java和Bar.java 这些是由我们的自定义类加载器加载的测试类。 它们还具有一个printCL方法该方法将被调用以打印已加载该类的ClassLoader。 Foo类将由我们的自定义类加载器加载后者又使用Bar类因此Bar类也将由我们的自定义类加载器加载。 Foo.java源代码 package com.journaldev.cl;public class Foo {static public void main(String args[]) throws Exception {System.out.println(Foo Constructor args[0] args[1]);Bar bar new Bar(args[0], args[1]);bar.printCL();}public static void printCL() {System.out.println(Foo ClassLoader: Foo.class.getClassLoader());}
} Bar.java源代码 package com.journaldev.cl;public class Bar {public Bar(String a, String b) {System.out.println(Bar Constructor a b);}public void printCL() {System.out.println(Bar ClassLoader: Bar.class.getClassLoader());}
} 执行步骤 首先我们将通过命令行编译所有类。 之后我们将通过传递三个参数来运行CCRun类。 第一个参数是Foo类的完全分类名称它将由我们的类加载器加载。 其他两个参数将传递给Foo类的主函数和Bar构造函数。 输出的执行步骤如下所示。 Pankaj $ javac -cp。 com / journaldev / cl / Foo.java Pankaj $ javac -cp。 com / journaldev / cl / Bar.java Pankaj $ javac CCLoader.java Pankaj $ javac CCRun.java CCRun.java:18警告对最后一个参数使用不精确参数类型的varargs方法的非varargs调用 转换为java.lang.Class以进行varargs调用 强制转换为java.lang.Class []进行非可变参数调用并禁止显示此警告 方法printCL clas.getMethod“ printCL”null; ^ 1警告 Pankaj $ java CCRun com.journaldev.cl.Foo 1212 1313 正在加载类“ com.journaldev.cl.Foo” 使用CCLoader加载类 加载类“ java.lang.Object” 加载类“ java.lang.String” 加载类“ java.lang.Exception” 加载类“ java.lang.System” 加载类“ java.lang.StringBuilder” 加载类java.io.PrintStream Foo构造函数1212 1313 加载类“ com.journaldev.cl.Bar” 使用CCLoader加载类 酒吧建设者1212 1313 加载类“ java.lang.Class” 栏类加载器CCLoader 71f6f0bf Foo ClassLoaderCCLoader 71f6f0bf ctk-pcs1313512-2src pk93229 $ 如果仔细查看输出则首先尝试加载com.journaldev.cl.Foo类但是由于扩展了java.lang.Object类因此尝试首先加载它并请求将其委托给CCLoader loadClass方法它到父类。 因此父类加载器正在加载ObjectString和其他Java类。 我们的ClassLoader仅从文件系统加载Foo和Bar类而当我们调用它们的printCL函数时该文件系统将变得很清晰。 请注意我们可以更改loadClassFileData功能以从FTP服务器读取字节数组或者通过调用任何第三方服务来即时获取类字节数组。 参考 Java面试问题从我们的JCG合作伙伴Pankaj 了解和扩展Java ClassLoader 。 相关文章 JDK中的设计模式 Java内存模型–快速概述和注意事项 Java Fork / Join进行并行编程 依赖注入–手动方式 翻译自: https://www.javacodegeeks.com/2011/03/understanding-extending-java.html