目录
Java 类加载机制概述
类加载流程
验证 ClassLoader
自定义类加器
class 文件准备
NetworkClassLoader
Java 类加载机制概述
1、Java 虚拟机使用 Java 类的流程为首先将开发者编写的 Java 源代码.java文件编译成 Java 字节码.class文件然后类加载器会读取 .class 文件并转换成 java.lang.Class 的实例。有了该 Class 实例后Java 虚拟机可以利用 newInstance 之类的方法创建其真正对象了。
2、ClassLoader 是 Java 提供的类加载器绝大多数的类加载器都继承自 ClassLoader它们被用来加载不同来源的 Class 文件。
public abstract class java.lang.ClassLoader extends Object { }
.class 文件来源
1、既然类加载器(ClassLoader)读取字节码文件(.class文件)那么.class文件有哪些来源呢
1最常见的是开发者在应用程序中编写的类这些类位于项目目录下会由.java文件自动编译成.class文件
2Java 内部自带的核心类如 java.lang、java.math、java.io 等包内部的类位于 $JAVA_HOME/jre/lib/ 目录下如 java.lang.String 类就是定义在 $JAVA_HOME/jre/lib/rt.jar 文件里jar文件就是打包编译好的.class文件集合
3Java 核心扩展类位于 $JAVA_HOME/jre/lib/ext 目录下开发者也可以把自己编写的类打包成 jar 文件放入该目录下
4最后一种是动态加载远程的 .class 文件。
不同的类加载器加载不同的 .class
1、针对上面四种来源的类文件(.class文件)分别有不同的加载器负责加载
1级别最高的 Java 核心类即$JAVA_HOME/jre/lib 中的核心 jar 文件这些类是 Java 运行的基础类由一个名为 BootstrapClassLoader 加载器负责加载它也被称作 根加载器引导加载器。注意 BootstrapClassLoader 比较特殊它不继承 ClassLoader而是由 JVM 内部实现
2Java 核心扩展类即 $JAVA_HOME/jre/lib/ext 目录下的 jar 文件这些类由 ExtensionClassLoader 负责加载它也被称作 扩展类加载器。用户如果把自己开发的 jar 文件放在这个目录也会被 ExtClassLoader 加载
3开发者在项目中编写的类这些类由 AppClassLoader 加载器进行加载它也被称作 系统类加载器
4如果想远程加载如本地文件网络下载的方式则必须要自己自定义一个 ClassLoader复写其中的 findClass() 方法才能得以实现。
2、因此可以总结出 Java 中提供了至少四种 ClassLoader 来分别加载不同来源的 Class。
类加载流程
1、不同的 ClassLoader 加载不同来源的类当查找一个类时优先BootstrapClassLoader遍历最高级别的 Java 核心类然后ExtensionClassLoader 再去遍历 Java 核心扩展类最后 AppClassLoader 再遍历用户自定义类整个遍历过程是一旦找到就立即停止遍历。在 Java 中这种实现方式也称作 双亲委托。
2、可以将 BootstrapClassLoader 想象为核心高层领导人 ExtClassLoader 想象为中层干部 AppClassLoader 想象为普通公务员。每次需要加载一个类先获取一个系统加载器 AppClassLoader 的实例ClassLoader.getSystemClassLoader()然后向上级层层请求由最上级优先去加载如果上级觉得这些类不属于核心类就可以下放到各子级负责人去自行加载。
验证 ClassLoader
/*** Created by Administrator on 2018/6/21 0021.* 音乐播放器*/public class MusicPlayer {public static void main(String[] args) {/**获取类加载器对象*/ClassLoader classLoader MusicPlayer.class.getClassLoader();/**获取类加载器名称*/System.out.println(classLoader.getClass().getName());//输出sun.misc.Launcher$AppClassLoaderClassLoader classLoader_ext classLoader.getParent();System.out.println(classLoader_ext.getClass().getName());//输出sun.misc.Launcher$ExtClassLoaderClassLoader classLoader_boot classLoader_ext.getParent();System.out.println(classLoader_boot);//输出null}}
1、可以发现 ExtClassLoader 确实是 AppClassLoader 的双亲不过却没有看到 BootstrapClassLoader因为 BootstrapClassLoader 比较特殊它是由 JVM 内部实现的所以 ExtClassLoader.getParent() null。
自定义类加器
1、自定义类加载器允许 JVM 在运行时可以从本地磁盘或网络上动态加载自定义类(.class文件)这使得开发者可以动态修复某些有问题的类热更新代码。
2、下面来实现一个网络类加载器这个加载器可以从网络上动态下载 .class 文件并加载到虚拟机中使用。
3、自定义类加载器流程如下
1自定义 NetworkClassLoader 类继承 ClassLoader
2实现 ClassLoader 类的 findClass() 方法。注意ClassLoader 自己提供了 loadClass()它会基于双亲委托机制去搜索某个 class当搜索不到时会调用自身的 findClass()如果直接复写loadClass()那还要实现双亲委托机制
3在 findClass() 方法里要从网络上下载一个 .class 文件然后转化成 Class 对象供虚拟机使用。
class 文件准备
class 文件除了放在网络上访问同理也可以放在本地磁盘。
/*** Created by Administrator on 2018/6/21 0021.* 音乐播放器*/public class MusicPlayer {/*** 模拟播放音乐*/public void player() {System.out.println("music is player ...");}/*** 音乐直播** param musicName歌曲名称* return*/public String musicLivePlayer(String musicName) {return "music " musicName " player is success";}}
NetworkClassLoader
import java.io.ByteArrayOutputStream;import java.io.IOException;import java.io.InputStream;import java.lang.reflect.InvocationTargetException;import java.lang.reflect.Method;import java.net.MalformedURLException;import java.net.URL;/*** Created by Administrator on 2018/6/21 0021.* 自定义网络类加载器---继承ClassLoader*/public class NetworkClassLoader extends ClassLoader {/*** 必须重写findClass方法** param name* return* throws ClassNotFoundException*/Overrideprotected Class findClass(String name) throws ClassNotFoundException {if (name null || "".equals(name.trim())) {return null;}/**下载网络上的Class文件返回字节数组*/byte[] classData downloadClassData(name);if (classData null || classData.length < 0) {super.findClass(name);} else {/**调用ClassLoader重载的defineClass方法* 将一个 byte 数组转换为 Class 类的实例。必须分析 Class然后才能使用它。* name所需要的类的二进制名称如果不知道此名称则该参数为 null* classData已经包含了class信息name设置为null也是可以的* 当name不为null时必须是目标类的全路径如org.xyjt.filter.MusicPlayer*/return defineClass(null, classData, 0, classData.length);}return null;}/*** 下载网络上的Class文件** param name .class文件名也是目标类的名字如MusicPlayer* return 返回字节数组*/public byte[] downloadClassData(String name) {try {/*** path拼接完成后如http://localhost:8080/webProject/javaClass/MusicPlayer.class* 这个路径只有确保网络上能访问到此.class文件即可为了方便立即才写死在这*/String path "http://localhost:8080/webProject/javaClass/" name ".class";URL url new URL(path);InputStream inputStream url.openStream();/**用一个字节数组输出流来接收* 边读边存都是常用的IO操作*/ByteArrayOutputStream byteArrayOutputStream new ByteArrayOutputStream();int buffSize 2048;byte[] buffer new byte[buffSize];int bytesRead;while ((bytesRead inputStream.read(buffer)) ! -1) {byteArrayOutputStream.write(buffer, 0, bytesRead);}/**最后返回字节数组*/return byteArrayOutputStream.toByteArray();} catch (MalformedURLException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}return null;}public static void main(String[] args) {try {/**调用的目标.class文件的名称*/String className "MusicPlayer";/**用自定义类加载器类加载此类* loadClass方法会使用双亲委托当找不到时它默认就是调用上面的findClass方法* 当然也可以直接networkClassLoader.findClass("className)*/NetworkClassLoader networkClassLoader new NetworkClassLoader();/**aClass就是网络上的动态加载的org.xyjt.filter.MusicPlayer类*/Class aClass networkClassLoader.loadClass(className);/** 这里肯定输出的是自定义累加器而不是AppClassLoader、ExtClassLoader、BootstrapClassLoader*/System.out.println("1、"aClass.getClassLoader().getClass().getName());System.out.println("2、"aClass.getName());/**调用MusicPlayer方法*/Method playerMethod aClass.getMethod("player", null);playerMethod.invoke(aClass.newInstance(),null);Method musicLivePlayerMethod aClass.getMethod("musicLivePlayer", String.class);Object result musicLivePlayerMethod.invoke(aClass.newInstance(), "千年等一回");System.out.println("3、"result);} catch (ClassNotFoundException e) {e.printStackTrace();} catch (InvocationTargetException e) {e.printStackTrace();} catch (NoSuchMethodException e) {e.printStackTrace();} catch (InstantiationException e) {e.printStackTrace();} catch (IllegalAccessException e) {e.printStackTrace();}}}