JVM中的ClassLoader通常采用双亲委派模型,要求除了启动类加载器外,其余的类加载器都应该有自己的父级加载器。这里的父子关系是组合而不是继承(如自定义加载器的父级是app加载器,app加载器的父级是平台加载器,平台类加载器的父级是启动类加载器),这里的父级指的是几个加载器组合在一起。
工作流程如下:
- 类加载器接收到类加载请求后,首先搜索他的内建夹杂其定义的所有“具体模块”;
- 找到合适的模块定义后,将会使用该加载器来加载;
- 如果class没有在加载器定义的具体模块中找到,会委托给父级加载器,直到找到最上层的启动类加载器;
- 如果父级加载器反馈不能完成加载请求,那子的类加载器才自己加载,在自己的classpath找存不存在类,没有继续找子的;
- 在类路径下找到的类将会成为这些加载器的无名模块,指导找不到报错。
双亲委派模型作用
保证java程序的稳定运作,公用且具有一致性(形成搜寻类的链状);安全
实现双亲委派的代码在 java.lang.classloader的loadclass()方法中,如果自定义加载器的话,推荐覆盖实现findclass()方法
如果一个类加载器能加载某个类,称为定义类加载器,所以能成功返回该类的class的类加载器都称为初始类加载器
如果没有指定父加载器,默认是启动类加载器
每个类加载器都有自己的命名空间,命名空间由该加载器及其所有父类加载器所加载的类构成,不同的命名空间,可以出现 类的全路径名 相同的情况(比如一个程序跑两次,就会有两套加载器)
运行时包由同一个类加载器的类构成,只有全路径名和定义类加载器都一样,才属于同一个运行时包,才能实现相互包内可见
破坏双亲委派模型(不能只是子找父,也要有父找子)
接口分为两大类:api(应用程序接口,提供给前端和模块)和spi(只做定义用于扩展)
解决方式:引入线程上下文加载器,可以通过Thread的setContextClassLoader()进行设置,实现父类的加载器使用到了子级的加载器访问的资源。
实现热替换:如OSGI的模块化热部署,他的类加载器就不再严格按照双亲委派模型。