引:相信大家对于Java类加载的认识最开始都是来自于《深入理解Java虚拟机》,可能觉得就一个双亲委派机制嘛,没什么东西!但是具体到实际应用,对于类加载器却有所不同,今天我们就跟着Tomcat源码来分析一下它的类加载机制!
前言
这个说到Tomcat源码,在看源码之前,我们都需要先理解它的架构与一些设计原理,如果图快,可以从最后的参考中快速消化一下Tomcat的架构,但是如果是想系统看一下看Tomcat的全貌,我建议可以好好去看看《深入剖析Tomcat》,虽然他对应的版本对于现在来说有点老了,但是核心思想是不变的,相信很多人看完之后会和我一样有一种恍然大悟的感觉。关于怎么看Tomcat源码也可以在最后的参考中找到相关链接。
Java类加载机制
在说Tomcat的类加载机制之前,我们一定要先看看Java自己的类加载机制,之前自己对《深入理解Java虚拟机》中描述的Java类加载机制一篇总结文章,可以先看看:深入理解JVM9类加载器
Tomcat类加载机制
Tomcat使用类加载器的原因有3条:(待会会在源码中看出)
- 为了在载入类指定某些规则
- 为了缓存已经载入的类
- 为了实现类的预加载,方便使用
设计图
- commonLoader:Tomcat最基本的类加载器,加载路径中的class可以被Tomcat容器本身以及各个Webapp访问;
- catalinaLoader:Tomcat容器私有的类加载器,加载路径中的class对于Webapp不可见;
- sharedLoader:各个Webapp共享的类加载器,加载路径中的class对于所有Webapp可见,但是对于Tomcat容器不可见;
- WebappClassLoader:各个Webapp私有的类加载器,加载路径中的class只对当前Webapp可见;
- JasperLoader: 每一个JSP文件对应一个Jsp类加载器,实现热加载;
源码分析
Tomcat启动
如果看过Tomcat的架构和设计,就应该知道Tomcat的启动时运行main
方法作为入口:
1 | public static void main(String args[]) { |
我们需要知道daemon初始化干了什么:
1 | public void init() throws Exception { |
初始化完三个类加载之后,上面就会让catalinaLoader加载Tomcat所需要的类:
1 | public static void securityClassLoad(ClassLoader loader) throws Exception { |
到这里daemon就被创建完成了,启动也就结束了!
WebappClassLoader创建
上面说了Tomcat类加载机制中的三大加载器,但是对于Tomcat来说WebappClassLoader或许是最关键的,因为应用里的类都靠它加载。看过Tomcat架构的都知道WebappClassLoader是和容器StandardContext绑定在一起的。所以我们跟StandardContext生命周期的启动方法startInternal
进去看看(该方法比较长,省略很多内容,只看关键):
1 | protected synchronized void startInternal() throws LifecycleException { |
到这里,WebappClassLoader就被实例化完成了。
WebappClassLoader类加载
WebappClassLoader
就被实例化完成了,接下来我们就需要看看它是如何加载类的?找到他的loaderClass
方法,ParallelWebappClassLoader
的loadClass
是在其父类WebappClassLoaderBase
中实现的:
1 | public Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException { |
这里总结一下delegate属性为false,即默认的情况下的加载顺序:
- 检查本地缓存
- 如果没有,检查虚拟机缓存
- 如果没有,从AppClassLoader加载,这里会使用Java自己的类加载体系
- 如果没有,则从WebappClassLoader加载(按照WEB-INF/classes、WEB-INF/lib的顺序)
- 如果没有,则从父类加载器加载,由于父类加载器采用默认的委派模式,所以加载顺序是Common、Shared
总结
这里用一到面试题结束:Tomcat的类加载机制是否违反了双亲委托原则?
答案:是,具体怎么破坏了看本文理解。