做个笔记,马克一下。
原理(大概)
类加载器的分类
- 启动类加载器(Bootstrap ClassLoader):
- 主要负责加载
<JAVA_HOME>\lib
目录,或是-Xbootclasspath参数指定的路径中的类库到虚拟机内存中。 System.getProperty("sun.boot.class.path")
- 主要负责加载
- 扩展类加载器(Extension ClassLoader):
- 主要负责加载
<JAVA_HOME>\lib\ext
目录,或者被java.ext.dirs系统变量所指定的路径中的所有类库。 System.getProperty("java.ext.dirs")
- 主要负责加载
- 应用程序类加载器/系统类加载器(Application ClassLoader):
- 主要负责加载ClassPath路径上的类库,是默认的类加载器。
System.getProperty("java.class.path")
类的加载
- 类加载器的双亲委派模型
- 不多说了,启动类加载器优先级最高,它找到了加载类就是加载的它指定的那个了。扩展类加载器次之,系统类加载器最次。
关于Xbootclasspath
参数
- -Xbootclasspath:
- 完全取代基本核心的Java class 搜索路径。要重新写所有Java 核心class
- -Xbootclasspath/a:
- 后缀在核心class搜索路径后面
- -Xbootclasspath/p:
- 前缀在核心class搜索路径前面
自定义类加载器
继承自java.lang.ClassLoader
类并覆写对应的方法即可。
有趣的用途(大概)
Hook某些java应用
比如,现在我要hook原生的输出方法java.io.PrintStream.println()
。
当我调用这个方法的时候,会输出hhhhhhh再加上换行符。
- 代码框架如下:
- 对
java.io.PrintStream
的改动如下,其它原样复制 -
入口测试类:
package test; public class TestPrint { public static void main(String[] args) { System.out.println("----TestPrint begin----"); System.out.println(); System.out.println("----TestPrint end----"); } }
- 接下来不多说,直接放结果
- java -jar test.jar
- java -Xbootclasspath/p:test.jar -jar test.jar
- java -jar test.jar
动态编译/加载(刷题实现原理?
怎样做一个像LeetCode那样的Java刷题功能呢?
- 一个简单的方法是:
- 将提交的题目编译
.class
文件到当前ClassPath路径下 - 系统类加载器直接加载类
- 通过反射调用约定的方法,得到结果
-
比较预期和实际结果,返回响应
- 下面是一个例子:来自java实现动态编译并动态加载
public void complierAndRun() { try { System.out.println(System.getProperty("user.dir")); // 动态编译 JavaCompiler javac = ToolProvider.getSystemJavaCompiler(); int status = javac.run(null, null, null, "-d", System.getProperty("user.dir") + "\\target\\classes", "D:/test/AlTest.java"); if (status != 0) { System.out.println("没有编译成功!"); } // 通过系统类加载器直接加载类 Class clz = Class.forName("AlTest"); // 通过反射动态执行 Object o = clz.newInstance(); Method method = clz.getDeclaredMethod("sayHello"); // method.invoke() :静态方法第一个参数可为null,第二个参数为实际传参 String result = (String) method.invoke(o); System.out.println(result); } catch (Exception e) { logger.error("test", e); } }
- 将提交的题目编译
- 当用户/题目较多时,显然需要再变更一下,稍微再复杂一点:
- 将提交的题目编译
.class
文件到指定路径下 - 自定义ClassLoader加载类
- 通过反射调用约定的方法,得到结果
-
比较预期和实际结果,返回响应
- 下面是大概的要点:
public class MyClassLoader extends ClassLoader { protected Class<?> findClass(String name) throws ClassNotFoundException { // 读取name对应.class文件到b byte[] b = null; ... return defineClass(name, b, 0, b.length); } public static void main(String[] args) { ... try { // 重载ClassLoader类 MyClassLoader loader = new MyClassLoader(); Class<?> c = loader.findClass(className);// 类名字 c.getMethod(methodName).invoke(c.newInstance());// 方法名字 } catch (Exception e) { e.printStackTrace(); } } }
- 将提交的题目编译