Java中的ClassLoader 动态加载机制

来源:互联网 发布:js常见设计模式 编辑:程序博客网 时间:2024/04/29 13:05

前言:

Android中的动态加载机制能更好的优化我们的应用,同时实现动态的更新,这就便于我们管理我们的应用,通过插件化来减轻我们的内存以及CPU消耗,在不发布新版本的情况下能更新某些模块。
当然这里要说的并不是android中的动态加载机制,而是java中的ClassLoader动态加载我们的class,虽然android是基于Dalvik,但是先了解java中JVM怎么来加载我们的class的对于我们以后了解Android中的动态加载机制会有很大的帮助。
于是乎在网上查阅了很多关于类加载的文章了解JVM是如何通过ClassLoader来加载我们的class的。

JVM中的ClassLoadr:

java为我们提供了3个不同的ClassLoadr,分别是:
1.BootstrapClassLoader---主要负责加载java中的核心类库。是用C++代码实现的,在java虚拟机启动后初始化。
2.ExtensionClassLoader---主要负责加载java中的扩展类库。
3.AppClassLoader---用于java我们应用下的classpath中的class以及我们的jar。

JVM通过这些ClassLoadr把我们的class转换成字节码文件存贮到内存中,然后创建一个Class用特定的数据结构来封装他们,接着我们就能通过这个Class来访问其中的方法以及变量了。(这不就是我们的反射机制么,通过Class对象来获取其中的方法或者变量),当然JVM还有个验证的过程,只有通过javac编译来的class才能被ClassLoader所加载。
而ClassLoader是通过双亲委托模式来加载我们的class,就是先通过父类的ClassLoader来加载我们的class,如果父类加载失败,则通过我们的子ClassLoader来加载我们的class。(这里的父类与子类并不是集成关系)
我们可以通过URLClassLoader来动态加载我们的class也可以通过继承ClassLoader来实现我们的动态加载。而我们的ExtensionClassLoader与AppClassLoader都是继承自URLClassLoader。我这里是通过继承一个ClassLoader来实现的,通过获取Class的字节码文件来生成我们的Class,然后通过反射机制来调用我们类中的方法。
ClassLoader的扩展方法介绍:
loadClass 通过指定的二进制名来加载类(这里要把我们的包路径传过去,例如"com.ljx.test.Test")
defineClass 将byte数据转换成我们的Class类的实例
findloadClass 如果某个类加载器已经加载过这个class,则返回该类
.....还有很多这里就不一一例举了。

Test.java 先来创建需要加载的class,路径是F:Test.java,然后通过javac来生成我们的Test.class


这就是我们的Test的代码

MyClassLoader.java 通过继承ClassLoader来创建我们自己的ClassLoader,来加载Test.class

public class MyClassLoader extends ClassLoader {private byte[] results;public MyClassLoader(String pathName) {//拿到class转成的字节码文件results = loadClassFile(pathName);}public static void main(String[] args) {//初始化我们的classloader,同时拿到class所转成的字节码文件MyClassLoader classLoader = new MyClassLoader("F:\\Test.class");try {//这里要把包路径传入进去Class<?> clazz = classLoader.loadClass("com.ljx.yyy.Test");Object o = clazz.newInstance();//通过反射机制调用我们的Test.java中的printToString方法Method method = clazz.getMethod("printToString");method.invoke(clazz.newInstance());System.out.println(o.getClass().getClassLoader().toString());Method[] methods = clazz.getMethods();for (int i = 0; i < methods.length; i++) {//获取类中的方法名字String methodName = methods[i].getName();System.out.println("MethodName : " + methodName);Class<?>[] params = methods[i].getParameterTypes();for (int j = 0; j < params.length; j++) {//获取方法中的参数类型System.out.println("ParamsType : " + params[j].toString());}}} catch (ClassNotFoundException e) {// TODO Auto-generated catch blocke.printStackTrace();} catch (InstantiationException e) {// TODO Auto-generated catch blocke.printStackTrace();} catch (IllegalAccessException e) {// TODO Auto-generated catch blocke.printStackTrace();} catch (IllegalArgumentException e) {// TODO Auto-generated catch blocke.printStackTrace();} catch (SecurityException e) {// TODO Auto-generated catch blocke.printStackTrace();} catch (InvocationTargetException e) {// TODO Auto-generated catch blocke.printStackTrace();} catch (NoSuchMethodException e) {// TODO Auto-generated catch blocke.printStackTrace();}}//把我们的class文件转成字节码,用于classloader动态加载private byte[] loadClassFile(String classPath) {ByteArrayOutputStream bos = new ByteArrayOutputStream();try {FileInputStream fi = new FileInputStream(classPath);BufferedInputStream bis = new BufferedInputStream(fi);byte[] data = new byte[1024 * 256];int ch = 0;while ((ch = bis.read(data, 0, data.length)) != -1) {bos.write(data, 0, ch);}} catch (FileNotFoundException e) {// TODO Auto-generated catch blocke.printStackTrace();} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}return bos.toByteArray();}@Overrideprotected Class<?> loadClass(String arg0, boolean arg1)throws ClassNotFoundException {Class<?> clazz = findLoadedClass(arg0);if (clazz == null) {if (getParent() != null) {try {//这里我们要用父加载器加载如果加载不成功会抛异常clazz = getParent().loadClass(arg0);} catch (Exception e) {//我们自定义的类加载器的父类 sun.misc.Launcher$AppClassLoader@c387f44System.out.println("getParent : " + getParent());//父类的父类 sun.misc.Launcher$ExtClassLoader@659e0bfdSystem.out.println("getParent.getparent : " + getParent().getParent());//父类的父类的父类 为null 也就是我们的Bootstrap ClassLoader 因为它是JVM生成的由C++实现;//所以拿到的是空System.out.println("getParent.getparent.getparent : " + getParent().getParent().getParent());System.out.println("父类ClassLoader加载失败!");}}if (clazz == null) {clazz = defineClass(arg0, results, 0, results.length);}}return clazz;}}
先是获取我们在F:Test.class的字节码,然后通过它来得到我们的class,然后我们就能通过这个class来使用Test这个类中的方法,以下是执行main方法之后的结果:


这样,基本上就实现了从本地加载一个class到我们的项目中,是不是感觉很神奇,通过这种方式我们就能动态的加载不在我们项目中的类,例如从网上获取class的byte来动态更新我们的功能模块,或者动态加载jar中的class来实现我们要实现的功能。
代码中很大一部分是参照了网上的一些例子,当然最主要的还是为了要阐述如何通过ClassLoader来动态加载我们需要加载的类,通过ClassLoader来更好的优化我们的应用。了解了JVM如何来加载class能更好的便于我们理解Android中的动态加载技术;由于技术有限,如果上述有不正确的地方希望见谅。
0 0
原创粉丝点击