(转)JVM——自定义类加载器
背景:为什么要自定义,如何自定义,实现过程
转载:http://blog.csdn.net/SEU_Calvin/article/details/52315125
0. 为什么需要自定义类加载器
网上的大部分自定义类加载器文章,几乎都是贴一段实现代码,然后分析一两句自定义ClassLoader的原理。但是我觉得首先得把为什么需要自定义加载器这个问题搞清楚,因为如果不明白它的作用的情况下,还要去学习它显然是很让人困惑的。
首先介绍自定义类的应用场景:
(1)加密:Java代码可以轻易的被反编译,如果你需要把自己的代码进行加密以防止反编译,可以先将编译后的代码用某种加密算法加密,类加密后就不能再用Java的ClassLoader去加载类了,这时就需要自定义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;
}
ps:下面的总结,我觉得是最清晰的总结
双亲委派模型的工作过程如下:
(1)当前类加载器从自己已经加载的类中查询是否此类已经加载,如果已经加载则直接返回原来已经加载的类。
(2)如果没有找到,就去委托父类加载器去加载(如代码c = parent.loadClass(name, false)所示)。父类加载器也会采用同样的策略,查看自己已经加载过的类中是否包含这个类,有就返回,没有就委托父类的父类去加载,一直到启动类加载器。因为如果父加载器为空了,就代表使用启动类加载器作为父加载器去加载。
(3)如果启动类加载器加载失败(例如在$JAVA_HOME/jre/lib里未查找到该class),会使用拓展类加载器来尝试加载,继续失败则会使用AppClassLoader来加载,继续失败则会抛出一个异常ClassNotFoundException,然后再调用当前加载器的findClass()方法进行加载。
双亲委派模型的好处:
(1)主要是为了安全性,避免用户自己编写的类动态替换 Java的一些核心类,比如 String。
(2)同时也避免了类的重复加载,因为 JVM中区分不同类,不仅仅是根据类名,相同的 class文件被不同的 ClassLoader加载就是不同的两个类。
2. 自定义类加载器
(1)从上面源码看出,调用loadClass时会先根据委派模型在父加载器中加载,如果加载失败,则会调用当前加载器的findClass来完成加载。(ps:在所有加载器找不到的情况下,就会调用用户自定义的类加载器来加载)
(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){
int i = fc.read(by);
if (i == 0 || i == -1)
break;
by.flip();
wbc.write(by);
by.clear();
}
fis.close();
return baos.toByteArray();
}
}
2.3 在主函数里使用
MyClassLoader mcl = new MyClassLoader();
Class<?> clazz = Class.forName("People", true, mcl);
Object obj = clazz.newInstance(); System.out.println(obj);
System.out.println(obj.getClass().getClassLoader());//打印出我们的自定义类加载器
2.4 运行结果
至此关于自定义ClassLoader的内容总结完毕。
(转)JVM——自定义类加载器的更多相关文章
- JVM自定义类加载器加载指定classPath下的所有class及jar
一.JVM中的类加载器类型 从Java虚拟机的角度讲,只有两种不同的类加载器:启动类加载器和其他类加载器. 1.启动类加载器(Boostrap ClassLoader):这个是由c++实现的,主要负责 ...
- JVM——自定义类加载器
)以上两种情况在实际中的综合运用:比如你的应用需要通过网络来传输 Java 类的字节码,为了安全性,这些字节码经过了加密处理.这个时候你就需要自定义类加载器来从某个网络地址上读取加密后的字节代码,接着 ...
- JVM 自定义类加载器在复杂类情况下的运行分析
一.自定义类加载器在复杂类情况下的运行分析 1.使用之前创建的类加载器 public class MyTest16 extends ClassLoader{ private String classN ...
- JVM 自定义类加载器
一.创建自定义类加载器 package com.example.jvm.classloader; import java.io.ByteArrayOutputStream; import java.i ...
- Java虚拟机JVM学习06 自定义类加载器 父委托机制和命名空间的再讨论
Java虚拟机JVM学习06 自定义类加载器 父委托机制和命名空间的再讨论 创建用户自定义的类加载器 要创建用户自定义的类加载器,只需要扩展java.lang.ClassLoader类,然后覆盖它的f ...
- jvm(1)类的加载(二)(自定义类加载器)
[深入Java虚拟机]之四:类加载机制 1,从Java虚拟机的角度,只存在两种不同的类加载器: 1,启动类加载器:它使用C++实现(这里仅限于Hotspot,也就是JDK1.5之后默认的虚拟机,有其他 ...
- JVM之类加载器下篇
除了自定义的类加载之外,jvm存在三种类加载器,并以一种父委托的加载机制进行加载. --启动类加载器,又称根加载器,是一个native的方法,使用c++实现.在java中我们用null标识,用于加载j ...
- java类加载器学习2——自定义类加载器和父类委托机制带来的问题
一.自定义类加载器的一般步骤 Java的类加载器自从JDK1.2开始便引入了一条机制叫做父类委托机制.一个类需要被加载的时候,JVM先会调用他的父类加载器进行加载,父类调用父类的父类,一直到顶级类加载 ...
- 【深入理解JVM】类加载器与双亲委派模型
原文链接:http://blog.csdn.net/u011080472/article/details/51332866,http://www.cnblogs.com/lanxuezaipiao/p ...
随机推荐
- Redis未授权访问漏洞的利用及防护
Redis未授权访问漏洞的利用及防护 什么是Redis未授权访问漏洞? Redis在默认情况下,会绑定在0.0.0.0:6379.如果没有采取相关的安全策略,比如添加防火墙规则.避免其他非信任来源IP ...
- Node 系列之url模块
引入 url: const url = require("url"); 用于URL解析.处理等操作的解决方案 1.url.parse(urlStr[, parseQueryStri ...
- linux第四次读书笔记
第四章:进程调度 一.多任务 1.非抢占式多任务 进程会一直执行直到自己主动停止运行(这一步骤称为让步) 2.抢占式多任务 Linux/Unix使用的是抢占式的方式:强制的挂起进程的动作就叫做抢占.进 ...
- C# wkhtmltopdf 将html转pdf(详解)
https://www.cnblogs.com/louby/p/905198.html转自,看文章只放了代码看起来云里雾里的,在此做些解析 使用说明: 1.首先呢,得安装下软件,地址下面有链接,文件里 ...
- struts引入s标签
<%@ taglib prefix="s" uri="/struts-tags"%>
- SSH框架开发蛋糕房管理系统之质量属性
SSH框架开发蛋糕房管理系统之质量属性 我要开发的系统是基于ssh框架的蛋糕房管理系统.本系统前台提供的主要功能是在线预定蛋糕,本店管理员拥有最高权限,包括收银管理,设备管理,日常销售管理,蛋糕定制管 ...
- Spring整合SpringMVC
整合:把在springMVC配置文件中的spring提取出来整合为另一份配置文件 希望: 1).Spring的配置文件只是用来配置和业务逻辑有关的功能(数据源.事务控制.切面....) 2).Spri ...
- pandas获取当前时间
datetime.now()用于获取当前的日期和时间 print pd.datetime.now() #encoding:utf8 import pandas as pd print("(p ...
- 玩弄 python 正则表达式
这里记录一个我常用的模型,每次久了不使用正则就会忘记. 记得最好玩的一句关于正则表达式的话就是 当你想到一件事情可以用正则表达式解决的时候 现在你就面临了两个问题了. python里面使用了re模块对 ...
- 将ubuntu14.04 从mysql从5.5删除之后安装5.7遇到的一些问题(本篇不讨论热升级)
五一放假实在无聊 继续玩弄新的服务器.发现有台mysql版本实在有点老,估计是akiho直接使用 apt-get install mysql-server ,然后又没有更新到最新的源,然后无脑安装了5 ...