目录
1、如何用自己实现的的类加载器加载自己的类
2、是否可以重复加载同一个类
3、关于SPI和java热替换
==========================================分割线=========================================================
1、实现一个类加载器,并加载一个类
要加载的类Object:
public class Object {
}
类加载器MyClassLoader:
public class MyClassLoader extends ClassLoader { /** * Finds the class with the specified <a href="#name">binary name</a>. * This method should be overridden by class loader implementations that * follow the delegation model for loading classes, and will be invoked by * the {@link #loadClass <tt>loadClass</tt>} method after checking the * parent class loader for the requested class. The default implementation * throws a <tt>ClassNotFoundException</tt>. </p> * * @param name The <a href="#name">binary name</a> of the class * @return The resulting <tt>Class</tt> Object * @throws ClassNotFoundException If the class could not be found * @since 1.2 */ @Override protected Class<?> findClass(String name) throws ClassNotFoundException { System.out.println(1); try { FileInputStream i = new FileInputStream(new File("/Document/Object.class")); byte b[] = new byte[i.available()]; i.read(b); i.close(); return defineClass(name, b, 0, b.length); } catch (Exception e) { e.printStackTrace(); } return null; } }
main函数:
public class ClassLoaderTest { public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException { MyClassLoader myClassLoader = new MyClassLoader(); myClassLoader.loadClass("Object"); System.out.println(myClassLoader.loadClass("Object").getClassLoader()); } }
全部放到Documents目录下运行:
输入结果展示Object的类加载器为AppClassLoader。
分析:
由于我们并没有重写loadClass方法,所有还是双亲委派模型,会去寻找默认的父加载器,即AppClassLoader,因为Object所在的位置为java的classpath,正好符合AppClassLoader的加载规则,则进行加载。需要让Object类不符合AppClassLoader的加载条件。(查看java的classpath,可以通过“System.out.println(System.getProperty(“java.class.path”));”获取。)
改进:
将Object类放到桌面目录,更改MyClassLoader去桌面加载Object类,其他不动:
FileInputStream i = new FileInputStream(new File("/Desktop/Object.class"));
运行结果:
输入结果显示Object的类加载器为MyClassLoader,完成。
2、用自己的类加载器重复加载一个类
main函数更改如下:
public class ClassLoaderTest { public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException { MyClassLoader myClassLoader = new MyClassLoader(); myClassLoader.findClass("Object"); myClassLoader.findClass("Object"); } }
运行结果:
分析:
直接调用两次findClass方法(findClass会直接加载Class,自己在classLoader重写的方法),来模拟加载两次的情况。
加载两次会抛出异常,注意不要认为调用两次loadClass没错误,认为jvm允许重复加载类,因为loadClass内部有判断是否已经加载。
3、SPI和java热替换
SPI即java定义的接口由最顶层类加载器加载,但是由于实现类一般为第三发实现,所以在引用后,由低于顶层类加载器的类加载,这个时候顶层类加载器无法找到该实现类。
为了解决这个问题,引入了线程上下文类加载器。在通过接口调用实现类方法时,通过该加载器加载类,并调用。
该类加载器,每个线程都是独立的,并且默认为AppClassLoader。
热替换在了解后,发现并不是真正的替换原来在jvm永久区使用的Class类,而是用一个新的类加载器加载改动后的该类。然后在以后的使用中,使用这个新生成的Class类。
由于不断的Class类生成,必定会有老旧无用的Class类占永久区的内存空间,不过当该类的实例都被回收,且类没有使用,该类加载器也被回收后,fullGC就会回收Class类。
常见热替换为jsp,每个jsp页面对应一个类加载器,当定时发现jsp有变动后,就新建类加载器加载jsp。
自己理解的,如有错误,欢迎指出。谢谢。