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.ExceptionlnlnitializerError5.常用的ClassLoader分析5.1.StandardClassLoader是在Bootsharp类的ininClassLoaders方法中创建的,调用ClassLoaderFactory的createClassLoader()方法5.2.StandardClassLoader是代理类5.3.Tomcat容器的加载ClassLoader:若ClassPath没有设置,则是StandardClassLoader,否则是AppClassLoader5.4.servlet类的加载是显示加载方法5.5.servlet的ClassLoader是WebAppClassLoader5.6.所有的servlet都是InstanceManager实例化的5.7.JVM的类加载规范是委托式加载5.8.如果web应用直接放到webapp目录下,那么tomcat就通过StandardClassLoader直接加载6.如何实现自己的ClassLoader6.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.不能动态加载类,是因为对象的状态被保存了,并且被其它对象引用了.解决办法就是创建对象使用后就被释放掉,例如JSPRemark:1.ClassLoader是类加载器2.ClassLoader作用2.1.将Class加载到JVM2.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加载机制