什么时候需要类加载器?
- 想加载非classpath随意路径中的类文件
- 都是通过接口来实现,希望解耦时候,常用在框架设计
- 这些类希望予以隔离,不同应用的同类名都可以加载,不冲突
步骤
- 1.继承ClassLoader父类
- 2.要遵从双亲委派机制,重写findClass方法
注意不是重写loadClass,否则不会走双亲委派的机制 - 3.读取类文件的字节码
- 4.调用父类的defineClass方法来加载类
- 5.使用者调用类加载器器的loadClass方法
需求与实现
我们自己约定的需求如下,我们从外部路径/tmp/myclasspath/加载类
具体实现如下:
public class MyClassLoader extends ClassLoader{ //继承ClassLoader父类 /** * name就是类名称 */ protected Class<?> findClass(String name) throws ClassNotFoundException { String path="/tmp/myclasspath/"+name+".class"; System.out.println("path:"+path); ByteArrayOutputStream os=new ByteArrayOutputStream(); try { Files.copy(Paths.get(path),os); byte[] bytes=os.toByteArray(); //byte[] -> *.class return defineClass(name,bytes,0,bytes.length);//父类中的defineClass加载,如果找不到会调用本类的defineClass进行加载 } catch (IOException e) { e.printStackTrace(); throw new ClassNotFoundException("类文件未找到",e); } } }
代码测试:
public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException { MyClassLoader classLoader=new MyClassLoader(); Class<?> c1=classLoader.loadClass("HelloWorld"); Class<?> c2=classLoader.loadClass("HelloWorld"); System.out.println(c1==c2); c1.newInstance(); //System.out.println(c1.newInstance()); }
我们在路径下面定义下类:
运行结果:
true
小总结
- 自定义加载类的需求主要来自需要从外部加载类运行的时候,我们很常见的场景就是Tomcat的lib类,还有类似rpc调用的时候,类的实现有时候是从网络中传输过来的
- 双亲委派的机制其实就是一个递归调用的机制,父类先寻找加载的类,如若没有找到,则用当前类加载器去加载,这个的结果就是,如果提前加载过了,则不会在继续被加载,保证了类的版本一致性