Tomcat类加载分为四部分(不是步骤):
1.BootStrap 启动类加载
2.System 系统类加载 CATALINA_HOME/conf
3.Common 通用类加载 CATALINA_HOME/lib
4.Webapp 应用类加载 WEB-INF/lib和WEB-INF/classes
当Tomcat启动时,步骤如下:
1.BootStrap,加载JVM运行基本的类,以及标准扩展类(jre/lib/ext中的类)
2.System,加载catalina.bat/catalina.sh中指定位置的类,如bootstrap.jar、tomcat-juli.jar等
3.Webapp,每个应用在部署后,都会创建一个唯一的类加载器,该类加载器会加载位于WEB-INF/lib下的jar文件和WEB-INF/classes下的class文件(先classes文件夹再lib文件夹)
4.Common,加载Tomcat使用以及应用通用的一些类,位于CATALINA_HOME/lib下,比如servlet-api.jar
Tomcat中通过扩展URLClassLoader来实现自己的类加载器
Tomcat作为一个java web容器,也有自己的类加载机制,通过自定义的类加载机制以实现共享类库的抽取、不同web应用之间的资源隔离和热加载等功能。除了一些java自身的类加载器之外,Tomcat实现的主要类加载器有:Common ClassLoader、Catalina ClassLoader、Shared ClassLoader以及WebApp ClassLoader。
其中,Common、Catalina、Shared类加载器是URLClassLoader类的一个实例,只是它们的类加载路径不一样,在catalina.properties配置文件中进行配置(common.loader、server.loader、shared.loader)。
WebAppClassLoader继承自WebAppClassLoaderBase,基本所有的逻辑都在WebAppClassLoaderBase中实现了。
而WebAppClassLoaderBase、ExtClassLoader、AppClassLoader都继承自URLClassLoader,因此可以看出,Tomcat所有的类加载器都以URLClassLoader为基础进行扩展。
在默认配置中,Common、Catalina、Shared类加载器都是同一个对象,即commonLoader(因为Catalina、Shared的默认加载路径为空,直接返回父类加载器commonLoader)
一个Web应用对应着一个StandardContext实例,每个web应用都拥有独立的web应用类加载器(WebAppClassLoader),这个类加载器在StandardContext.startInternal()中被构造出来
Tomcat的类加载机制违反了双亲委托原则,对于一些未加载的非基础类(Object、String等),各个web应用自己的类加载器会优先加载,加载不到再交给commonClassLoader走双亲委托,针对某个类的具体加载逻辑位于WebAppClassLoaderBase.loadClass()方法中:
1.先在本地缓存中查找是否已经加载过该类(对于一些已经加载了的类,会被缓存在resourceEntries这个数据结构中),如果已经加载就返回;
2.让系统类加载器(AppClassLoader)尝试加载该类,主要是为了防止一些基础类会被web中的类覆盖,如果加载到就返回;
3.前两步都没加载到目标类,那么web应用的类加载器将自行加载(先加载WEB-INF/classes再加载WEB-INF/lib),如果加载到就返回;
4.最后还是加载不到的话,委托父类加载器(Common ClassLoader)去加载。
第3第4两个步骤的顺序已经违反了双亲委托机制,除了Tomcat外,JDBC、JNDI、Thread.currentThread().setContextClassLoader()等都一样违反了双亲委托。
另外,开发者也会因为粗心而犯下面的错误:
1.在CATALINA_HOME/lib以及WEB-INF/lib中放置了不同版本的jar包,此时就会导致某些情况下报加载不到类的错误;
2.如果多个应用使用同一jar包文件,当放置了多份,就可能导致多个应用间出现类加载不到的错误
原文地址:https://www.cnblogs.com/yuanfei1110111/p/10136801.html