1.ClassLoader类结构分析
1.1.ClassLoader的几个方法
1.1.1.defineClass(byte[],int,int) //将byte字节流解析成JVM能够识别的Class对象
1.1.2.findClass(String) //实现类的加载规则,从而取得加载类的字节码
1.1.3.loadClass(String) //获取类的Class对象,例如:this.getClass().getClassLoader().getloadClass("x.class")
1.1.4.resolveClass(Class<?>)
2.ClassLoader的等级加载机制
2.1.上级委托接待机制
2.2.JVM平台提供三层ClassLoader,分为两种类型
2.2.1.Bootsharp ClassLoader:加载JVM自身工作需要的类
2.2.2.ExtClassLoader:此类加载器只是JVM自身的一部分,服务特定目标在System.getProperty("java.net.dirs")
2.2.3.AppClassLoader:加载某个路径下的类,System.getProperty("java.class.path")
2.3.自己的类加载器根据getSystemClassLoader()方法获取的是AppClassLoader类加载器
2.4.JVM加载class文件到内存有两种方式
2.4.1.隐式加载:通过JVM自动加载需要的类到内存,例如引用某个类,当JVM解析的时候,没有发现此类在内存,那么就会自动加载到内存
2.4.2.显示加载:通过代码调用,例如this.getClass().getClassLoader().getloadClass()
3.如何加载class文件
3.1.找到.class文件并把这个文件包含的字节码加载到内存中
3.1.1.加载字节码到内存
3.1.1.1.在URLClassoLoader()中通过一个URLClassPath类帮助取得要加载的class文件字节流
3.1.1.2.读取它的byte字节流,通过调用defineClass()方法来创建类对象
3.2.验证与解析
3.2.1.字节码验证:保证格式正确,行为正确
3.2.2.类准备:在这个阶段准备代表每个类中定义的字段、方法和实现接口所必需的数据结构
3.2.3.解析:在这个阶段类装入器装入类所引用的其他所有类.可以用许多方式引用类,如超类、接口、字段、方法签名、方法中使用的本地变量
3.3.初始化Class对象:在类中包含的静态初始化器都被执行,在这一阶段末尾静态字段被初始化为默认值
4.常见加载类的错误分析
4.1.ClassNotFoundException(文件不存在)
4.1.1.显示加载一个类的方式
4.1.1.1.通过类Class中的forName()方法
4.1.1.2.通过类ClassLoader中的loadClass()方法
4.1.1.3.通过类ClassLoader中的findSystemClass()方法
4.2.NoClassDefFoundError(找不到类)
4.3.UnsatisfiedLinkError(lib删除)
4.4.ClassCastExceptio(强制类型转换错误)
4.5.ExceptionlnlnitializerError
5.常用的ClassLoader分析
5.1.StandardClassLoader是在Bootsharp类的ininClassLoaders方法中创建的,调用ClassLoaderFactory的createClassLoader()方法
5.2.StandardClassLoader是代理类
5.3.Tomcat容器的加载ClassLoader:若ClassPath没有设置,则是StandardClassLoader,否则是AppClassLoader
5.4.servlet类的加载是显示加载方法
5.5.servlet的ClassLoader是WebAppClassLoader
5.6.所有的servlet都是InstanceManager实例化的
5.7.JVM的类加载规范是委托式加载
5.8.如果web应用直接放到webapp目录下,那么tomcat就通过StandardClassLoader直接加载
6.如何实现自己的ClassLoader
6.1.自定义ClassLoader应用的情况
6.1.1.在自定义路径下查找自定义的class类文件
6.1.2.对自己需要加载的类做特殊处理,比如将类经过加密后再传输,在加载到JVM之前需要对类的字节码再解密 //保证网络输出类的安全性
6.1.3.可以定义类的实现机制,检查类是否修改,如果修改,则重新加载这个类,从而实现热部署
7.实现类的热部署
7.1.判断JVM表示一个类是否是同一个类的两个条件
7.1.1.看这个类的完整类名是否一样(包括所在的包)
7.1.2.加载这个类的ClassLoader是否是同一个 //同一个ClassLoader类的不同实例是实现热部署的关键
8.Java应不应该动态加载类
8.1.根据JVM的工作机制,Java不可以动态加载类
8.2.JVM的设计原则
8.2.1.对象的引用关系只有对象的创建者持有和使用
8.2.2.JVM不可以干预对象的引用关系
8.2.3.JVM不知道对象是怎么被使用的,因为JVM并不知道对象的运行时类型而只知道编译时类型
8.2.4.不能动态加载类,是因为对象的状态被保存了,并且被其它对象引用了.解决办法就是创建对象使用后就被释放掉,例如JSP
Remark:
1.ClassLoader是类加载器
2.ClassLoader作用
2.1.将Class加载到JVM
2.2.审查每个类应该由谁加载
2.3.将Class字节码重新解析成JVM统一要求的对象格式
热部署示例
package com.ninemax.utils.ClassLoader;
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.InputStream;
/**
* Created by AckMan on 2016/8/31.
*/
public class ClassReloader extends ClassLoader {
private String classPath;
String classname = "compile.Yufa";
public ClassReloader(String classPath){
this.classPath = classPath;
}
/**
* 创建类对象
* @param name
* @return
* @throws ClassNotFoundException
*/
protected Class<?> findClass(String name)throws ClassNotFoundException{
byte[] classData = getData(name);
if(classData == null){
throw new ClassNotFoundException();
}else{
return defineClass(classname,classData,0,classData.length);
}
}
// 构建数据
private byte[] getData(String className){
String path = classPath + className;
try {
InputStream is = new FileInputStream(path);
ByteArrayOutputStream stream = new ByteArrayOutputStream();
byte[] buffer = new byte[2048];
int num = 0;
while ((num = is.read(buffer))!=-1){
stream.write(buffer, 0, num);
}
return stream.toByteArray();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
public static void main(String args[]){
try{
String path = "F:/Workspaces/IdeaUI_15/darker/target/classes/compile/";
ClassReloader c = new ClassReloader(path);
Class r = c.findClass("Yufa.class");
System.out.println(r.newInstance());
ClassReloader c2 = new ClassReloader(path);
//Class r2 = c.findClass("Yufa.class");
Class r2 = c2.findClass("Yufa.class");
System.out.println(r2.newInstance());
}catch (Exception e){
e.printStackTrace();
}
}
}
运行结果
若采用同一个ClassLoader实例,则运行结果
ClassLoader关系图
WebappClassLoader加载机制