Tomcat类加载机制以及Context的初始化
文章目录
概述
本节简单介绍Java虚拟机规范中提到的主要类加载器:
- Bootstrap Loader:加载lib目录下或者System.getProperty(“sun.boot.class.path”)、或者-XBootclasspath所指定的路径或jar
- Extended Loader:加载lib\ext目录下或者System.getProperty(“java.ext.dirs”) 所指定的 路径或jar。例如:java -Djava.ext.dirs=/projects/testproj/classes HelloWorld
- AppClassLoader:加载-cp或者System.getProperty(“java.class.path”)所指定的 路径或jar。在使用Java运行程序时,也可以加上-cp来覆盖原有的Classpath设置,例如: java -cp /projects/testproj/classes HelloWorld
- Tomcat的commonLoader: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"里面的类
- Tomcat的WebappClassLoader:各个Webapp私有的类加载器,加载路径中的class只对当前Webapp可见
tomcat使用的自定义类加载器
tomcat使用了两个自己定义的类加载器,其模型如下
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-f283HSmg-1684648970844)(assets/image-20220724095224325.png)]
源码分析
commonClassLoader初始化
在Bootstrap.init()中调Bootstrap#initClassLoaders进行初始化
private void initClassLoaders() {
try {
// commonLoader的加载路径为common.loader(properties文件配置)
// 加载${catalina.base}/lib,${catalina.base}/lib/*.jar,${catalina.home}/lib,${catalina.home}/lib/*.jar里面的类
commonLoader = createClassLoader("common", null);
if( commonLoader == null ) {
// no config file, default to this loader - we might be in a 'single' env.
commonLoader=this.getClass().getClassLoader();
}
// 加载路径为server.loader,默认为空,父类加载器为commonLoader
catalinaLoader = createClassLoader("server", commonLoader);
// 加载路径为shared.loader,默认为空,父类加载器为commonLoader
sharedLoader = createClassLoader("shared", commonLoader);
} catch (Throwable t) {
handleThrowable(t);
log.error("Class loader creation threw exception", t);
System.exit(1);
}
}
Context的初始化包含ParallerWebAppClassLoader的初始化
加载的类存放的位置
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YuzlnsL6-1684648970846)(assets/image-20220724153842621.png)]
其中:
allResources[1]=mainResources;// /WEB-INF/classes包含的类
allResources[1]=classResources;// /WEB-INF/lib包含的jar中的类
在StandardContext#startInternal()->实例化WebappLoader->WebappLoader.start()->实例化ParallerWebAppClassLoader
先看StandardContext.startInternal()的逻辑
protected synchronized void startInternal() throws LifecycleException {
if(log.isDebugEnabled())
log.debug("Starting " + getBaseName());
// Send j2ee.state.starting notification
if (this.getObjectName() != null) {
Notification notification = new Notification("j2ee.state.starting",
this.getObjectName(), sequenceNumber.getAndIncrement());
broadcaster.sendNotification(notification);
}
setConfigured(false);
boolean ok = true;
// Currently this is effectively a NO-OP but needs to be called to
// ensure the NamingResources follows the correct lifecycle
if (namingResources != null) {
namingResources.start();
}
// Post work directory
postWorkDirectory();
// Add missing components as necessary
if (getResources() == null) {
// (1) Required by Loader
if (log.isDebugEnabled())
log.debug("Configuring default Resources");
try {
// 设置 context 的 Resources 为 StandardRoot
setResources(new StandardRoot(this));
} catch (IllegalArgumentException e) {
log.error(sm.getString("standardContext.resourcesInit"), e);
ok = false;
}
}
if (ok) {
// StandardRoot.startInternal() 初始化要加载allResources包含
// 重要: 1.mainResources: /WEB-INF/classes包含的jar中的类
// 重要: 2.classResources: /WEB-INF/lib包含的jar中的类
// 3.preResources: 是context.xml中定义的 preResources 资源
// 4.postResources: 是context.xml中定义的 postResources资源
// 5.classResources
resourcesStart();
}
if (getLoader() == null) {
// WebappLoader.loaderClass=ParallelWebappClassLoader.class.getName()标识context应用使用的应用类加载器
WebappLoader webappLoader = new WebappLoader(getParentClassLoader());
webappLoader.setDelegate(getDelegate());
setLoader(webappLoader);
}
// An explicit cookie processor hasn't been specified; use the default
if (cookieProcessor == null) {
cookieProcessor = new Rfc6265CookieProcessor();
}
// Initialize character set mapper
getCharsetMapper();
// Validate required extensions
boolean dependencyCheck = true;
try {
dependencyCheck = ExtensionValidator.validateApplication
(getResources(), this);
} catch (IOException ioe) {
log.error(sm.getString("standardContext.extensionValidationError"), ioe);
dependencyCheck = false;
}
if (!dependencyCheck) {
// do not make application available if dependency check fails
ok = false;
}
// Reading the "catalina.useNaming" environment variable
String useNamingProperty = System.getProperty("catalina.useNaming");
if ((useNamingProperty != null)
&& (useNamingProperty.equals("false"))) {
useNaming = false;
}
if (ok && isUseNaming()) {
if (getNamingContextListener() == null) {
NamingContextListener ncl = new NamingContextListener();
ncl.setName(getNamingContextName());
ncl.setExceptionOnFailedWrite(getJndiExceptionOnFailedWrite());
addLifecycleListener(ncl);
setNamingContextListener(ncl);
}
}
// Standard container startup
if (log.isDebugEnabled())
log.debug("Processing standard container startup");
// Binding thread
ClassLoader oldCCL = bindThread();
try {
if (ok) {
// Start our subordinate components, if any
Loader loader = getLoader();
if (loader instanceof Lifecycle) {
// 在WebappLoader.startInternal中实例化类加载器为:ParallelWebappClassLoader
// 设置在WebappLoader.classLoader=ParallelWebappClassLoader之后创建EventListener,Filter,Servlet
((Lifecycle) loader).start();
}
// since the loader just started, the webapp classloader is now
// created.
setClassLoaderProperty("clearReferencesRmiTargets",
getClearReferencesRmiTargets());
setClassLoaderProperty("clearReferencesStopThreads",
getClearReferencesStopThreads());
setClassLoaderProperty("clearReferencesStopTimerThreads",
getClearReferencesStopTimerThreads());
setClassLoaderProperty("clearReferencesHttpClientKeepAliveThread",
getClearReferencesHttpClientKeepAliveThread());
setClassLoaderProperty("clearReferencesObjectStreamClassCaches",
getClearReferencesObjectStreamClassCaches());
setClassLoaderProperty("clearReferencesThreadLocals",
getClearReferencesThreadLocals());
// By calling unbindThread and bindThread in a row, we setup the
// current Thread CCL to be the webapp classloader
unbindThread(oldCCL);
oldCCL = bindThread();
// Initialize logger again. Other components might have used it
// too early, so it should be reset.
logger = null;
getLogger();
Realm realm = getRealmInternal();
if(null != realm) {
if (realm instanceof Lifecycle) {
((Lifecycle) realm).start();
}
// Place the CredentialHandler into the ServletContext so
// applications can have access to it. Wrap it in a "safe"
// handler so application's can't modify it.
CredentialHandler safeHandler = new CredentialHandler() {
@Override
public boolean matches(String inputCredentials, String storedCredentials) {
return getRealmInternal().getCredentialHandler().matches(inputCredentials, storedCredentials);
}
@Override