Java安全之 ClassLoader类加载器

0x00 前言

前面这里抛出一个问题,Java到底是什么类型的编程语言?是编译型?还是解释型?在这个问题是其实一直都都有疑惑,如果说是解释型语言的话,那么为什么需要编译呢?如果说是编译型语言的话,那么在编译完成后,需要JVM去解析才能运行呢?其实两种说法都对,也不对。下面来看看java的一个执行流程就知道是怎么回事了。

0x01 类加载机制

Java中的源码.java后缀文件会在运行前被编译成.class后缀文件,Java类初始化的时候会调用java.lang.ClassLoader加载字节码,.class文件中保存着Java代码经转换后的虚拟机指令,当需要使用某个类时,虚拟机将会加载它的.class文件,并创建对应的class对象,将class文件加载到虚拟机的内存。

在其中其实包含了比较多的内容,下面来看看他的详细执行流程。

具体的实现分为三大步骤:

第一步 加载:

  1. 类加载指的是将class文件读入内存,并为之创建一个java.lang.Class对象,即程序中使用任何类时,系统都会为之建立一个java.lang.Class对象,系统中所有的类都是java.lang.Class的实例。
  2. 类的加载由类加载器完成,JVM提供的类加载器叫做系统类加载器,此外还可以通过继承ClassLoader基类来自定义类加载器。

第二步 连接:

连接阶段负责把类的二进制数据合并到JRE中

其又可分为如下三个阶段:

  1. 验证:确保加载的类信息符合JVM规范,无安全方面的问题。
  2. 准备:为类的静态Field分配内存,并设置初始值。
  3. 解析:将类的二进制数据中的符号引用替换成直接引用。

第三步 初始化:

类加载最后阶段,若该类具有超类,则对其进行初始化,执行静态初始化器和静态初始化成员变量(如前面只初始化了默认值的static变量将会在这个阶段赋值,成员变量也将被初始化

双亲委托机制

当一个类加载器查找class和resource时,是通过“委托模式”进行的,它首先会判断这个class是不是已经加载成功,如果没有加载的话它并不是自己进行查找,而是先通过父加载器,然后递归下去,直到Bootstrap ClassLoader,如果Bootstrap classloader找到了,直接返回,如果没有找到,则一级一级返回,最后是由自身去查找这些对象;这种机制就叫做双亲委托。

其他的方式也可以去加载类的二进制数据:

  1. 1.从本地文件系统加载class文件。
  2. 2.JAR包中加载class文件,如JAR包的数据库启驱动类。
  3. 3.通过网络加载class文件。
  4. 4.把一个Java源文件动态编译并执行加载。

0x02 ClassLoader类加载器

前面提到过编译成class字节码后的文件,会使用类加载器加载字节码。也就是说在java中所有的类都会通过加载器进行加载才能运行。在JVM类加载器中最顶层的是Bootstrap ClassLoader(引导类加载器)Extension ClassLoader(扩展类加载器)App ClassLoader(系统类加载器)AppClassLoader是默认的类加载器,如果类加载时我们不指定类加载器的情况下,默认会使用AppClassLoader加载类。

ClassLoader类 核心方法:

  1. 1.loadClass(String className),根据名字加载一个类。
  2. 2.defineClass(String name, byte[] b, int off, int len),将一个字节流定义为一个类。
  3. 3.findClass(String name),查找一个类。
  4. 4.findLoadedClass(String name),在已加载的类中,查找一个类。
  5. 5.resolveClass(链接指定的Java类)

0x03 自定义Classloader加载class文件

在ClassLoader中有四个很重要实用的方法loadClass()、findLoadedClass()、findClass()、defineClass(),可以用来创建属于自己的类的加载方式;比如我们需要动态加载一些东西,或者从C盘某个特定的文件夹加载一个class文件,又或者从网络上下载class主内容然后再进行加载等。分三步搞定:

  1. 1、编写一个类继承ClassLoader抽象类;
  2. 2、重写findClass()方法;
  3. 3、在findClass()方法中调用defineClass()方法即可实现自定义ClassLoader

下面来编写一个test类

  1. package com.test;
  2. public class test {
  3. public String method(){
  4. return "hello,world";
  5. }
  6. }

编写完成后使用javac进行编译为class字节码文件

  1. javac .\test.java

会发现多出一个class字节码文件,那么我们需要再对其转换为byte类型,方便后面使用类加载器进行加载执行。

  1. import sun.misc.IOUtils;
  2. import java.io.FileInputStream;
  3. import java.io.FileNotFoundException;
  4. import java.io.IOException;
  5. import java.io.InputStream;
  6. import java.util.Arrays;
  7. public class ulits {
  8. public static void main(String[] args) throws IOException {
  9. InputStream fis = new FileInputStream("test.class");
  10. byte[] bytes = IOUtils.readFully(fis, -1, false);
  11. System.out.println(Arrays.toString(bytes));
  12. }
  13. }

自定义加载器类:

  1. package com.test;
  2. import java.lang.reflect.Method;
  3. public class classloadertest extends ClassLoader{
  4. private static String testclassname= "com.test.test";
  5. //转换byte后的字节码
  6. private static byte[] classbytes= new byte[]{-54, -2, -70, -66, 0, 0, 0, 52, 0, 29, 10, 0, 6, 0, 15, 9, 0, 16, 0, 17, 8, 0, 18, 10, 0, 19, 0, 20, 7, 0, 21, 7, 0, 22, 1, 0, 6, 60, 105, 110, 105, 116, 62, 1, 0, 3, 40, 41, 86, 1, 0, 4, 67, 111, 100, 101, 1, 0, 15, 76, 105, 110, 101, 78, 117, 109, 98, 101, 114, 84, 97, 98, 108, 101, 1, 0, 4, 109, 97, 105, 110, 1, 0, 22, 40, 91, 76, 106, 97, 118, 97, 47, 108, 97, 110, 103, 47, 83, 116, 114, 105, 110, 103, 59, 41, 86, 1, 0, 10, 83, 111, 117, 114, 99, 101, 70, 105, 108, 101, 1, 0, 9, 116, 101, 115, 116, 46, 106, 97, 118, 97, 12, 0, 7, 0, 8, 7, 0, 23, 12, 0, 24, 0, 25, 1, 0, 17, -23, -114, -75, -47, -122, -18, -108, -111, -23, -114, -76, -26, -124, -84, -27, -89, -101, 7, 0, 26, 12, 0, 27, 0, 28, 1, 0, 13, 99, 111, 109, 47, 116, 101, 115, 116, 47, 116, 101, 115, 116, 1, 0, 16, 106, 97, 118, 97, 47, 108, 97, 110, 103, 47, 79, 98, 106, 101, 99, 116, 1, 0, 16, 106, 97, 118, 97, 47, 108, 97, 110, 103, 47, 83, 121, 115, 116, 101, 109, 1, 0, 3, 111, 117, 116, 1, 0, 21, 76, 106, 97, 118, 97, 47, 105, 111, 47, 80, 114, 105, 110, 116, 83, 116, 114, 101, 97, 109, 59, 1, 0, 19, 106, 97, 118, 97, 47, 105, 111, 47, 80, 114, 105, 110, 116, 83, 116, 114, 101, 97, 109, 1, 0, 7, 112, 114, 105, 110, 116, 108, 110, 1, 0, 21, 40, 76, 106, 97, 118, 97, 47, 108, 97, 110, 103, 47, 83, 116, 114, 105, 110, 103, 59, 41, 86, 0, 33, 0, 5, 0, 6, 0, 0, 0, 0, 0, 2, 0, 1, 0, 7, 0, 8, 0, 1, 0, 9, 0, 0, 0, 29, 0, 1, 0, 1, 0, 0, 0, 5, 42, -73, 0, 1, -79, 0, 0, 0, 1, 0, 10, 0, 0, 0, 6, 0, 1, 0, 0, 0, 3, 0, 9, 0, 11, 0, 12, 0, 1, 0, 9, 0, 0, 0, 37, 0, 2, 0, 1, 0, 0, 0, 9, -78, 0, 2, 18, 3, -74, 0, 4, -79, 0, 0, 0, 1, 0, 10, 0, 0, 0, 10, 0, 2, 0, 0, 0, 5, 0, 8, 0, 6, 0, 1, 0, 13, 0, 0, 0, 2, 0, 14};
  7. @Override
  8. protected Class<?> findClass(String name) throws ClassNotFoundException {
  9. //只处理com.test.test类
  10. if (name.equals(testclassname)) {
  11. //将一个字节流定义为一个类。
  12. return defineClass(testclassname, classbytes, 0, classbytes.length);
  13. }
  14. return super.findClass(name);
  15. }
  16. public static void main(String[] args) throws Exception {
  17. //创建加载器
  18. classloadertest classloadertest = new classloadertest();
  19. //使用我们自定义的类去加载testclassname
  20. Class aClass = classloadertest.loadClass(testclassname);
  21. //反射创建test类对象
  22. Object o = aClass.newInstance();
  23. //反射获取method方法
  24. Method method = o.getClass().getMethod("method");
  25. //反射去调用执行method方法
  26. String str = (String) method.invoke(o);
  27. System.out.println(str);
  28. }
  29. }

可以看到我们class中的method方法执行了。

总结

  1. 1java文件编译成class字节码文件
  2. 2、转换字节码文件为byte类型,目的方便存储,加载执行
  3. 3、自定义一个类加载器类,自定义处理流程(findClass),实现对象的调用。过程为创建对象,加载class,反射创建自定义class的对象,反射获取需要执行的方法,执行方法。

参考文章

  1. https://javasec.org/javase/ClassLoader/
  2. https://blog.csdn.net/javazejian/article/details/73413292
  3. https://blog.csdn.net/CNAHYZ/article/details/82219210
  4. https://blog.csdn.net/briblue/article/details/54973413
  5. https://blog.csdn.net/xyang81/article/details/7292380

0x04 结尾

对于这篇文章,对于没了解过类加载器的来说还是比较吃力的,比如我。在文中有些地方写的也比较模糊,所以贴了几个不错的文章在上面,作为参考。

Java安全之 ClassLoader类加载器的更多相关文章

  1. 进阶Java编程(11)ClassLoader类加载器【待完成】

    1,ClassLoader类加载器简介 在Java里面提供一个系统的环境变量:ClassPath,这个属性的作用主要是在JVM进程启动的时候进行类加载路径的定义,在JVM里面可以根据类加载器而后进行指 ...

  2. Java URLClassLoader 和 ClassLoader类加载器

    开始:看名字都带有ClassLoader,叫做类加载器,事实上是可以理解为动态的加载类,不过,也不是只能加载类,也可以加载其他形式的文件,比如说.properties属性文件. 区别:其实在两个类加载 ...

  3. Java中对于ClassLoader类加载器 嵌套了深度技术的价值

    关于Java技术是一种不断兴起的编程语言,对于ClassLoader 是 Java 届最为神秘的技术之一,无数人被它伤透了脑筋,摸不清门道究竟在哪里.本文我带你彻底吃透 ClassLoader,让你甚 ...

  4. 【原创】Java基础之ClassLoader类加载器简介

    classloader简介 1 classloader层次结构(父子关系) Bootstrap(ClassLoader) ExtClassLoader AppClassLoader XXXClassL ...

  5. ClassLoader类加载器 & Java类加载机制 & 破坏双亲委托机制

    ClassLoader类加载器 Java 中的类加载器大致可以分成两类: 一类是系统提供的: 引导类加载器(Bootstrap classloader):它用来加载 Java 的核心库(如rt.jar ...

  6. 万万没想到,面试中,连 ClassLoader类加载器 也能问出这么多问题…..

    1.类加载过程 类加载时机 「加载」 将类的.class文件中的二进制数据读入到内存中,将其放在运行时数据区的方法区内,然后在内存上创建一个java.lang.Class对象用来封装类在方法区内的数据 ...

  7. 【Java虚拟机3】类加载器

    前言 Java虚拟机设计团队有意把类加载阶段中的"通过一个类的全限定名来获取描述该类的二进制字节流"这个动作放到Java虚拟机外部去实现,以便让应用程序自己决定如何去获取所需的类. ...

  8. java ClassLoader类加载器

    原文 首先来了解一下字节码和class文件的区别: 我们知道,新建一个java对象的时候,JVM要将这个对象对应的字节码加载到内存中,这个字节码的原始信息存放在classpath(就是我们新建Java ...

  9. java 反射,类的加载过程以及Classloader类加载器

    首先自定义一个类Person package reflection; public class Person { private String name; public int age; public ...

随机推荐

  1. C# 简单的对称加密

    const string KEY_64 = "HuidTeac";//注意了,是8个字符 const string IV_64 = "HuidTeac"; pu ...

  2. C++ 保存读取二进制

    一.保存二进制 #include <iostream> #include <fstream> int main(){ float* output = new float[100 ...

  3. 信号量-Semaphore、SemaphoreSlim

    核心类:Semaphore,通过int数值来控制线程个数. * 通过观察构造函数 public Semaphore(int initialCount, int maximumCount);: * in ...

  4. spring的异常处理

    出自于:https://blog.csdn.net/he90227/article/details/46309297   ---- 利用Spring进行统一异常处理的两种方式. 原文:https:// ...

  5. MySQL时间戳、字符串、日期

    1.时间转字符串:date_format(date, format) SELECT date_format(now(), '%Y-%m-%d') 2.时间转时间戳:unix_timestamp() S ...

  6. jQuery中的基本过滤选择器(四、三)::first、:last、:not() ... ...

    <!DOCTYPE html> <html> <head> <title>基本过滤选择器</title> <meta http-equ ...

  7. Hibernate之检索方式

    时间:2017-1-22 16:09 --检索方式Hibernate中提供了以下几种检索对象的方式:    *   导航对象图检索方式        根据已经加载额对象导航到其他对象.        ...

  8. 多线程Synchronized的两种锁

    Synchronized的作用: 能够保证在同一时刻最多只有一个线程执行该段代码,以达到保证并发安全的效果 Synchronized的两个用法: 1)对象锁 包括方法锁(默认锁对象为this当前实例对 ...

  9. php常用的函数

    addslashes: 字符串加入斜线.bin2hex: 二进位转成十六进位.chop: 去除连续空白.chr: 返回序数值的字符.chunk_split: 将字符串分成小段.convert_cyr_ ...

  10. 手机端rem简单配置相关

    手机端rem简单配置相关 1 <!DOCTYPE html> 2 <html xmlns="http://www.w3.org/1999/xhtml"> 3 ...