Java面试题自定义加载器

自定义类加载器为什么需要自定义类加载器

网上的大部分自定义类加载器文章,几乎都是贴一段实现代码,然后分析一两句自定义ClassLoader

原理。但是我觉得首先得把为什么需要自定义加载器这个问题搞清楚,因为如果不明白它的作用的情况

下,还要去学习它显然是很让人困惑的。

首先介绍自定义类的应用场景:

1)加密:Java代码可以轻易的被反编译,如果你需要把自己的代码进行加密以防止反编译,可以先将

编译后的代码用某种加密算法加密,类加密后就不能再用JavaClassLoader去加载类了,这时就需要自

定义ClassLoader在加载类的时候先解密类,然后再加载。

2)从非标准的来源加载代码:如果你的字节码是放在数据库、甚至是在云端,就可以自定义类加载

器,从指定的来源加载类。

3)以上两种情况在实际中的综合运用:比如你的应用需要通过网络来传输 Java 类的字节码,为了安

全性,这些字节码经过了加密处理。这个时候你就需要自定义类加载器来从某个网络地址上读取加密后

的字节代码,接着进行解密和验证,最后定义出在Java虚拟机中运行的类。

1. 双亲委派模型

在实现自己的ClassLoader之前,我们先了解一下系统是如何加载类的,那么就不得不介绍双亲委派模

型的实现过程。

//双亲委派模型的工作过程源码

protected synchronized Class<?> loadClass(String name, boolean resolve) throws

ClassNotFoundException{

// First, check if the class has already been loaded

Class c = findLoadedClass(name);

if (c == null) {

try {

if (parent != null) {

c = 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

//子加载器进行类加载

c = findClass(name);

}

}

if (resolve) {

//判断是否需要链接过程,参数传入

resolveClass(c);

}return c;

}

双亲委派模型的工作过程如下:

1)当前类加载器从自己已经加载的类中查询是否此类已经加载,如果已经加载则直接返回原来已经加

载的类。

2)如果没有找到,就去委托父类加载器去加载(如代码c = parent.loadClass(name, false)所示)。

父类加载器也会采用同样的策略,查看自己已经加载过的类中是否包含这个类,有就返回,没有就委托

父类的父类去加载,一直到启动类加载器。因为如果父加载器为空了,就代表使用启动类加载器作为父

加载器去加载。

3)如果启动类加载器加载失败(例如在$JAVA_HOME/jre/lib里未查找到该class),则会抛出一个异

ClassNotFoundException,然后再调用当前加载器的findClass()方法进行加载。

双亲委派模型的好处:

1)主要是为了安全性,避免用户自己编写的类动态替换 Java的一些核心类,比如 String

2)同时也避免了类的重复加载,因为 JVM中区分不同类,不仅仅是根据类名,相同的 class文件被不

同的 ClassLoader加载就是不同的两个类。

2. 自定义类加载器

1)从上面源码看出,调用loadClass时会先根据委派模型在父加载器中加载,如果加载失败,则会调

用当前加载器的findClass来完成加载。

2)因此我们自定义的类加载器只需要继承ClassLoader,并覆盖findClass方法,下面是一个实际例

子,在该例中我们用自定义的类加载器去加载我们事先准备好的class文件。

2.1自定义一个People.java类做例子

public class People {

//该类写在记事本里,在用javac命令行编译成class文件,放在d盘根目录下

private String name;

public People() {}

public People(String name) {

this.name = name;

}

public String getName() {

return name;

}

public void setName(String name) {

this.name = name;

}

public String toString() {

return "I am a people, my name is " + name;}

}

2.2自定义类加载器

自定义一个类加载器,需要继承ClassLoader类,并实现findClass方法。其中defineClass方法可以把二

进制流字节组成的文件转换为一个java.lang.Class(只要二进制字节流的内容符合Class文件规范)。

import java.io.ByteArrayOutputStream;

import java.io.File;

import java.io.FileInputStream;

import java.nio.ByteBuffer;

import java.nio.channels.Channels;

import java.nio.channels.FileChannel;

import java.nio.channels.WritableByteChannel;

public class MyClassLoader extends ClassLoader

{

public MyClassLoader()

{

}

public MyClassLoader(ClassLoader parent)

{

super(parent);

}

protected Class<?> findClass(String name) throws ClassNotFoundException

{

File file = new File("D:/People.class");

try{

byte[] bytes = getClassBytes(file);

//defineClass方法可以把二进制流字节组成的文件转换为一个java.lang.Class

Class<?> c = this.defineClass(name, bytes, 0, bytes.length);

return c;

}

catch (Exception e)

{

e.printStackTrace();

}

return super.findClass(name);

}

private byte[] getClassBytes(File file) throws Exception

{

// 这里要读入.class的字节,因此要使用字节流

FileInputStream fis = new FileInputStream(file);

FileChannel fc = fis.getChannel();

ByteArrayOutputStream baos = new ByteArrayOutputStream();

WritableByteChannel wbc = Channels.newChannel(baos);

ByteBuffer by = ByteBuffer.allocate(1024);

while (true){2.3在主函数里使用

2.4运行结果

至此关于自定义ClassLoader的内容总结完毕。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

老了敲不动了

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值