类加载器的层次结构(树状结构)
• 引导类加载器(bootstrap class loader)
– 它用来加载 Java 的核心库(JAVA_HOME/jre/lib/rt.jar,或sun.boot.class.path 路径下的 内容),是用原生代码来实现的,并不继承自java.lang.ClassLoader。
– 加载扩展类和应用程序类加载器。并指定他们的父类加载器。
• 扩展类加载器(extensions class loader)
– 用来加载 Java 的扩展库(JAVA_HOME/jre/ext/*.jar,或java.ext.dirs路径下的内 容) 。 Java 虚拟机的实现会提供一个扩展库目录。该类加载器在此目录里面查找并加载 Java 类。
– 由sun.misc.Launcher$ExtClassLoader实现
应用程序类加载器(application class loader)
– 它根据 Java 应用的类路径(classpath, java.class.path 路径下的内容)来加载 Java 类。 一般来说,Java 应用的类都是由它来完成加载的。
– 由sun.misc.Launcher$AppClassLoader实现
• 自定义类加载器
– 开发人员可以通过继承 java.lang.ClassLoader类的方式 实现自己的类加载器,以满足一些特殊的需求。
自定义文件系统类加载器
JDK的类加载机制是代理模式中的双亲委派机制:
• 就是某个特定的类加载器在接到加载类的请求时,首先将加载任务委 托给父类加 载器,依次追溯,直到最高的爷爷辈的,如果父类加载器 可以完成类加载任务,就成功返回;只有父类加载器无法完成此加载 任务时,才自己去加载。
– 双亲委托机制是为了保证 Java 核心库的类型安全。
• 这种机制就保证不会出现用户自己能定义 java.lang.Object 类的情况。
– 类加载器除了用于加载类,也是安全的最基本的屏障。
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
/**
* 自定义文件系统类加载器
* @author
*
*/
public class FileSystemClassLoader extends ClassLoader {
//com.jmdf.test.User --> d:/myjava/ com/jmdf/test/User.class
private String rootDir;
public FileSystemClassLoader(String rootDir){
this.rootDir = rootDir;
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
Class<?> c = findLoadedClass(name);
//应该要先查询有没有加载过这个类。如果已经加载,则直接返回加载好的类。如果没有,则加载新的类。
if(c!=null){
return c;
}else{
ClassLoader parent = this.getParent();
try {
c = parent.loadClass(name); //委派给父类加载
} catch (Exception e) {
// e.printStackTrace();
}
if(c!=null){
return c;
}else{
byte[] classData = getClassData(name);
if(classData==null){
throw new ClassNotFoundException();
}else{
c = defineClass(name, classData, 0,classData.length);
}
}
}
return c;
}
private byte[] getClassData(String classname){ //com.jmdf.test.User d:/myjava/ com/jmdf/test/User.class
String path = rootDir +"/"+ classname.replace('.', '/')+".class";
// IOUtils,可以使用它将流中的数据转成字节数组
InputStream is = null;
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try{
is = new FileInputStream(path);
byte[] buffer = new byte[1024];
int temp=0;
while((temp=is.read(buffer))!=-1){
baos.write(buffer, 0, temp);
}
return baos.toByteArray();
}catch(Exception e){
e.printStackTrace();
return null;
}finally{
try {
if(is!=null){
is.close();
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if(baos!=null){
baos.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
测试自定义的FileSystemClassLoader
-d . 是编译类后生成文件后创建包路径下的.class文件
先找到.java类文件路径:d:/myjava
/**
* 测试自定义的FileSystemClassLoader
* @author
*
*/
public class Test {
public static void main(String[] args) throws Exception{
FileSystemClassLoader loader = new FileSystemClassLoader("d:/myjava");
FileSystemClassLoader loader2 = new FileSystemClassLoader("d:/myjava");
Class<?> c = loader.loadClass("com.jmdf.test.HelloWorld");
Class<?> c2 = loader.loadClass("com.jmdf.test.HelloWorld");
Class<?> c3 = loader2.loadClass("com.jmdf.test.HelloWorld");
Class<?> c4 = loader2.loadClass("java.lang.String");
Class<?> c5 = loader2.loadClass("com.jmdf.test.Demo01");
System.out.println(c.hashCode());
System.out.println(c2.hashCode());
System.out.println(c3.hashCode()); //同一个类,被不同的加载器加载,JVM认为也是不相同的类
System.out.println(c4.hashCode());
System.out.println(c4.getClassLoader()); //引导类加载器
System.out.println(c3.getClassLoader()); //自定义的类加载器
System.out.println(c5.getClassLoader()); //系统默认的类加载器
}
}
网络类加载器
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
/**
* 网络类加载器
* @author
*
*/
public class NetClassLoader extends ClassLoader {
//com.jmdf.test.User --> www.jmdf.cn/myjava/ com/jmdf/test/User.class
private String rootUrl;
public NetClassLoader(String rootUrl){
this.rootUrl = rootUrl;
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
Class<?> c = findLoadedClass(name);
//应该要先查询有没有加载过这个类。如果已经加载,则直接返回加载好的类。如果没有,则加载新的类。
if(c!=null){
return c;
}else{
ClassLoader parent = this.getParent();
try {
c = parent.loadClass(name); //委派给父类加载
} catch (Exception e) {
// e.printStackTrace();
}
if(c!=null){
return c;
}else{
byte[] classData = getClassData(name);
if(classData==null){
throw new ClassNotFoundException();
}else{
c = defineClass(name, classData, 0,classData.length);
}
}
}
return c;
}
private byte[] getClassData(String classname){ //com.jmdf.test.User d:/myjava/ com/jmdf/test/User.class
String path = rootUrl +"/"+ classname.replace('.', '/')+".class";
// IOUtils,可以使用它将流中的数据转成字节数组
InputStream is = null;
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try{
URL url = new URL(path);
is = url.openStream();
byte[] buffer = new byte[1024];
int temp=0;
while((temp=is.read(buffer))!=-1){
baos.write(buffer, 0, temp);
}
return baos.toByteArray();
}catch(Exception e){
e.printStackTrace();
return null;
}finally{
try {
if(is!=null){
is.close();
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if(baos!=null){
baos.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
/**
* 加载文件系统中加密后的class字节码的类加载器
* @author
*
*/
public class DecrptClassLoader extends ClassLoader {
//com.jmdf.test.User --> d:/myjava/ com/jmdf/test/User.class
private String rootDir;
public DecrptClassLoader(String rootDir){
this.rootDir = rootDir;
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
Class<?> c = findLoadedClass(name);
//应该要先查询有没有加载过这个类。如果已经加载,则直接返回加载好的类。如果没有,则加载新的类。
if(c!=null){
return c;
}else{
ClassLoader parent = this.getParent();
try {
c = parent.loadClass(name); //委派给父类加载
} catch (Exception e) {
// e.printStackTrace();
}
if(c!=null){
return c;
}else{
byte[] classData = getClassData(name);
if(classData==null){
throw new ClassNotFoundException();
}else{
c = defineClass(name, classData, 0,classData.length);
}
}
}
return c;
}
private byte[] getClassData(String classname){ //com.jmdf.test.User d:/myjava/ com/jmdf/test/User.class
String path = rootDir +"/"+ classname.replace('.', '/')+".class";
// IOUtils,可以使用它将流中的数据转成字节数组
InputStream is = null;
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try{
is = new FileInputStream(path);
int temp = -1;
while((temp=is.read())!=-1){
baos.write(temp^0xff); //取反操作,相当于解密
}
return baos.toByteArray();
}catch(Exception e){
e.printStackTrace();
return null;
}finally{
try {
if(is!=null){
is.close();
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if(baos!=null){
baos.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
加密工具类
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
/**
* 加密工具类
* @author
*
*/
public class EncrptUtil {
public static void main(String[] args) {
encrpt("d:/myjava/HelloWorld.class","d:/myjava/temp/HelloWorld.class");
}
public static void encrpt(String src, String dest){
FileInputStream fis = null;
FileOutputStream fos = null;
try {
fis = new FileInputStream(src);
fos = new FileOutputStream(dest);
int temp = -1;
while((temp=fis.read())!=-1){
fos.write(temp^0xff); //取反操作
}
} catch (Exception e) {
e.printStackTrace();
}finally{
try {
if(fis!=null){
fis.close();
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if(fos!=null){
fos.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}