创建自定义ClassLoader,绕过双亲委派
1.什么是类加载
通过javac将.java文件编译成.class字节码文件后,则需要将.class加载到JVM中运行,哪么是谁将.class加载到JVM的呢?那就是类加载器啦。
2.类加载器类型
- Bootstrap ClassLoader(启动类加载器):该类加载器由C++实现的。负责加载Java基础类,对应加载的文件是%JRE_HOME/lib/ 目录下的rt.jar、resources.jar、charsets.jar和class等。
- Extension ClassLoader(标准扩展类加载器):继承URLClassLoader。对应加载的文件是%JRE_HOME/lib/ext 目录下的jar和class等。
- App ClassLoader(系统类加载器):继承URLClassLoader。对应加载的应用程序classpath目录下的所有jar和class等。
- CustomClassLoader(用户自定义类加载器):由Java实现。我们可以自定义类加载器,并可以加载指定路径下的class文件。
3.什么是双亲委派机制
双亲委派机制是当类加载器需要加载某一个.class字节码文件时,则首先会把这个任务委托给他的上级类加载器,递归这个操作,如果上级没有加载该.class文件,自己才会去加载这个.class。
4.为什么叫双亲委派机制
双:代表是两两的意思。亲:代表两者之间有着千丝万缕的关系。委派:则是我们个人办不到的事情,委托别人去帮我们完成。总体来说,就是当子类加载器无法完成这件事时,则会委托父加载器去完成,当父加载器说这不是我做的事情时,则该任务又会落回到子类加载器,此时,子类加载器只能自己去完成该事情。通过上面的阐述,我们则可以明白为什么叫双亲委派机制了,两两之间相互委托对方。(以上纯属个人理解,如有错误之处,请指出)
5.双亲委派的作用
①防止加载同一个.class。通过委托去询问上级是否已经加载过该.class,如果加载过了,则不需要重新加载。保证了数据安全。
②保证核心.class不被篡改。通过委托的方式,保证核心.class不被篡改,即使被篡改也不会被加载,即使被加载也不会是同一个class对象,因为不同的加载器加载同一个.class也不是同一个Class对象。这样则保证了Class的执行安全。
创建自定义ClassLoader
MyClassLoader.java 中定义了两个加载class的方法,其中findClass(String name)实现双亲委派逻辑,createClass(String name)绕过双亲委派逻辑
package com.xinxin.classloader; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; public class MyClassLoader extends ClassLoader { private String mLibPath; public MyClassLoader(String path) { mLibPath = path; } /** * 双亲委派逻辑,父加载器读不到Class才会调用此方法 */ @Override protected Class<?> findClass(String name) throws ClassNotFoundException{ byte[] data; try { data = readClassFile( name); return defineClass(name,data,0,data.length); } catch (Exception e) { e.printStackTrace(); } return super.findClass(name); } /** * 绕过双亲委派逻辑,直接获取Class */ public Class<?> createClass(String name) throws Exception{ byte[] data; data = readClassFile(name); return defineClass(name,data,0,data.length); } /** * 读取Class文件 */ private byte[] readClassFile(String name) throws Exception{ String fileName = getFileName(name); File file = new File(mLibPath,fileName); FileInputStream is = new FileInputStream(file); ByteArrayOutputStream bos = new ByteArrayOutputStream(); int len = 0; while ((len = is.read()) != -1) { bos.write(len); } byte[] data = bos.toByteArray(); is.close(); bos.close(); return data; } //获取要加载 的class文件名 private String getFileName(String name) { int index = name.lastIndexOf('.'); if(index == -1){ return name+".class"; }else{ return name.substring(index+1)+".class"; } } }
测试:
static void testClassLoader() throws Exception{ MyClassLoader diskLoader = new MyClassLoader("D:\\tmp\\"); //双亲委派 //Class<?> c = diskLoader.loadClass("com.xinxin.classloader.Student"); //绕过双亲委派 Class<?> c = diskLoader.createClass("com.xinxin.classloader.Student"); if(c != null){ try { Object obj = c.newInstance(); Method method = c.getDeclaredMethod("hello",null); method.invoke(obj, null); } catch (Exception e){ e.printStackTrace(); } } }
我只是偶尔安静下来,对过去的种种思忖一番。那些曾经的旧时光里即便有过天真愚钝,也不值得谴责。毕竟,往后的日子,还很长。不断鼓励自己,
天一亮,又是崭新的起点,又是未知的征程(上校9)
逆水行舟,不进,则退!