Tomacat类加载机制
tomcat中提供了多种类加载器,以便web应用程序部署到tomcat中可以共享类加载器也可以加载各自应用中WEB-INF的lib目录下面导入的类。在Tomcat启动的时候就会初始化它当中的类加载器。
在Tomcat的目录结构中有/common/* /server/* /shared/*三个目录结构来存放tomcat应用程序能用到的类库,以及/WEB-INF/*应用程序自身用到的类库。下图是tomcat中类加载器示意图,红色框选的是区别于JVM的类加载器,根据名字用来加载对应目录的类库
/common/* :类库可以被Tomcat和所有的web应用程序使用 ommonClassLoader
/server/* :被Tomcat使用,对于web程序不可见 CatalinaClassLoader
/shared/* :被web程序使用,对Tomcat不可见 SharedClassLoader
/WEB-INF/* 仅当前web程序可见类库 WebAppClassLoad
从示意图可以看出,common类加载器加载的类可以被CatalinaClassLoader和SharedClassLoader使用,而CatalinaClassLoader和SharedClassLoader加载的类与对方隔离,WebAppClassLoad和JsperClassLoader一般会有多个实例且各个实例之间互相隔离,每一个web应用都会对应一个WebApp类加载器,每一个Jsp对应一个Jsp类加载器
但是在Tomcat的6.x版本中,把/common/* /server/* /shared/*三个目录合并为一个lib目录
并且在Tomcat\apache-tomcat-8.5.32\conf\catalina.properties中只配置了common类加载器,CatalinaClassLoader和SharedClassLoader为空,看到用common类加载器加载了lib中的全部类库,当然可以设置server.loader和shared.loader重新启用Tomcat5.x
Tomcat类加载过程
(1)在Tomcat启动时,会创建一系列的类加载器,在其主类Bootstrap的初始化过程中,会先初始化classloader,然后将其绑定到Thread中。
(2)其中initClassLoaders方法,会根据catalina.properties的配置,创建相应的classloader。由于默认只配置了common.loader属性,所以其中只会创建一个出来
(3)然后,当一个应用启动的时候,会为其创建对应的WebappClassLoader。此时会将commonClassLoader设置为其parent
(4)在加载时,与java双亲委托机制不同,默认是子优先,也就是先用子加载器加载但是保证Java的基础类不允许其重新加载,以及servlet-api也不允许重新加载。
抛出的问题
这里抛出一个问题,如果有10个web应用程序都是用Spring来进行组织个管理的,可以把 Spring放到Common或shared目录下让这些程序共享。spring要对用户程序进行管理,当然也要能访问到程序自己的类库,就是方法WEB-INF/lib下面的类库,那么被common类加载或者shared类加载器加载的spring如何访问那些类库呢?或者说如何调用WebApp类加载器呢?
当然就是前片所说的使用线程上下文类加载器,当我们在spring的配置文件中通过类来创建对象,spring都是使用线程上下文类加载器来加载了,默认设置为WebAppClassLoader,所以当在web应用程序内,spring通过线程上下文类加载器使用WebAppClassLoader来加载bean