03-tomcat的类加载器
tomcat类加载器结构图:
1)bootstrap ClassLoader:它用来加载 Java 的核心类,是用原生代码来实现的,并不继承自 java.lang.ClassLoader,负责加载$JAVA_HOME中jre/lib/rt.jar里所有的class,由C++实现,不是ClassLoader子类。
2)extension ClassLoader:它负责加载JRE的扩展目录,lib/ext或者由java.ext.dirs系统属性指定的目录中的JAR包的类。由Java语言实现,父类加载器为null。
3)application ClassLoader:它负责将系统类路径(CLASSPATH)中指定的类库加载到内存中。
4)common ClassLoader:Tomcat最基本的类加载器,加载路径中的class可以被Tomcat容器本身以及各个Webapp访问,位于 c a t a l i n a . b a s e / l i b , {catalina.base}/lib, catalina.base/lib,{catalina.base}/lib/.jar, c a t a l i n a . h o m e / l i b , {catalina.home}/lib, catalina.home/lib,{catalina.home}/lib/.jar
5)catalina ClassLoader:Tomcat容器私有的类加载器,加载路径中的class对于Webapp不可见。
6)shared ClassLoader: 各个Webapp共享的类加载器,加载路径中的class对于所有Webapp可见,但是对于Tomcat容器不可见
7)webapp ClassLoader: 各个Webapp私有的类加载器,加载路径中的class只对当前Webapp可见,从而保证各个web应用之间相互隔离。
8)jsp ClassLoader: 而JasperLoader的加载范围仅仅是这个JSP文件所编译出来的那一个.Class文件,它出现的目的就是为了被丢弃:当Web容器检测到JSP文件被修改时,会替换掉目前的
JasperLoader的实例,并通过再建立一个新的Jsp类加载器来实现JSP文件的HotSwap功能。
加载器初始化逻辑位于bootstrap.init()
1. initClassLoaders()
private void initClassLoaders() {
commonLoader = createClassLoader("common", null);
if( commonLoader == null ) {
commonLoader=this.getClass().getClassLoader();
}
catalinaLoader = createClassLoader("server", commonLoader);
sharedLoader = createClassLoader("shared", commonLoader);
}
这里面初始化了commonLoader, catalinaLoader, sharedLoader,分别从catalina.properties中读取common.loader, server.loader, shared.loader配置获取各自的类加载路径:
/*
common.loader=
c
a
t
a
l
i
n
a
.
b
a
s
e
/
l
i
b
,
{catalina.base}/lib,
catalina.base/lib,{catalina.base}/lib/.jar,
c
a
t
a
l
i
n
a
.
h
o
m
e
/
l
i
b
,
{catalina.home}/lib,
catalina.home/lib,{catalina.home}/lib/.jar
server.loader=
shared.loader=
*/
同时从上面的代码可以知道catalinaLoader和sharedLoader的父加载器为commonLoader。
2. Thread.currentThread().setContextClassLoader(catalinaLoader); 设置当前线程上下文环境classLoader为catalinaLoader。
3. SecurityClassLoad.securityClassLoad(catalinaLoader); catalinaLoader加载需要的类。
public static void securityClassLoad(ClassLoader loader)
throws Exception {
loadCorePackage(loader);
loadCoyotePackage(loader);
loadLoaderPackage(loader);
loadRealmPackage(loader);
loadSessionPackage(loader);
loadUtilPackage(loader);
loadValvesPackage(loader);
loadJavaxPackage(loader);
loadConnectorPackage(loader);
loadTomcatPackage(loader);
}
4. 通过反射设置Catalina.java的父加载器为sharedLoader
5. webappLoader的创建在StandardContext的startInternal()中。
if (getLoader() == null) {
WebappLoader webappLoader = new WebappLoader(getParentClassLoader());
webappLoader.setDelegate(getDelegate());
setLoader(webappLoader); //设置当前Context的loader
}
if ((loader != null) && (loader instanceof Lifecycle))
((Lifecycle) loader).start(); //调用当前Context的loader.start()
在01-tomcat架构简介中介绍过一个Context就是一个web应用上下文,所以这里就是在给当前应用设置classLoader。
6. 在((Lifecycle) loader).start()中调用WebappLoader.startInternal()来使用webloader加载web应用路径的资源。
protected void startInternal() throws LifecycleException {
URLStreamHandlerFactory streamHandlerFactory =
DirContextURLStreamHandlerFactory.getInstance();
try {
classLoader = createClassLoader(); //创建webappClassLoader对象
classLoader.setResources(container.getResources()); //设置资源
classLoader.setDelegate(this.delegate);
classLoader.setSearchExternalFirst(searchExternalFirst);
if (container instanceof StandardContext) {
classLoader.setAntiJARLocking(
((StandardContext) container).getAntiJARLocking());
classLoader.setClearReferencesStatic(
((StandardContext) container).getClearReferencesStatic());
classLoader.setClearReferencesStopThreads(
((StandardContext) container).getClearReferencesStopThreads());
classLoader.setClearReferencesStopTimerThreads(
((StandardContext) container).getClearReferencesStopTimerThreads());
classLoader.setClearReferencesHttpClientKeepAliveThread(
((StandardContext) container).getClearReferencesHttpClientKeepAliveThread());
}
for (int i = 0; i < repositories.length; i++) {
classLoader.addRepository(repositories[i]);
}
setRepositories(); //加载资源里面的类,jar等
setClassPath(); //设置classpath属性到servletcontext中
setPermissions(); //设置权限
((Lifecycle) classLoader).start();
DirContextURLStreamHandler.bind(classLoader,
this.container.getResources());
StandardContext ctx=(StandardContext)container;
String contextName = ctx.getName();
if (!contextName.startsWith("/")) {
contextName = "/" + contextName;
}
ObjectName cloname = new ObjectName
(MBeanUtils.getDomain(ctx) + ":type=WebappClassLoader,context="
+ contextName + ",host=" + ctx.getParent().getName());
Registry.getRegistry(null, null)
.registerComponent(classLoader, cloname, null);
} catch (Throwable t) {
}
setState(LifecycleState.STARTING);
}