🐺 Tomcat 类加载过程
2022年10月10日
- frame
🐺 Tomcat 类加载过程
1. 源码
类图
public abstract class WebappClassLoaderBase extends URLClassLoader
implements Lifecycle, InstrumentableClassLoader, WebappProperties, PermissionCheck {
// ....
/**
* bootstrap class loader
*/
private ClassLoader javaseClassLoader;
}
1) loadClass(String name)
/**
* 根据类的二进制名称 加载类
*
* @param name The binary name of the class to be loaded
*
* @exception ClassNotFoundException if the class was not found
*/
@Override
public Class<?> loadClass(String name) throws ClassNotFoundException {
return loadClass(name, false);
}
/**
* 根据 特定名称 查找类,如果找不到,抛出 ClassNotFoundException 异常
*
* 1. 若该类已经被加载过,调用 findLoadedClass(String) 返回该类
* 2. delegate == true 说明指定使用 双亲委派 策略,调用 loadClass() 递归调用返回 所需类
* 3. 在指定的本地目录中查找 该类,找到即可返回
* 4. 使用 双亲委派 策略兜底
*
* @param name The binary name of the class to be loaded
* @param resolve 若无 true 说明需要回收该类
*
* @exception ClassNotFoundException if the class was not found
*/
@Override
public Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
synchronized (JreCompat.isGraalAvailable() ? this : getClassLoadingLock(name)) {
if (log.isDebugEnabled()) {
log.debug("loadClass(" + name + ", " + resolve + ")");
}
Class<?> clazz = null;
// Log access to stopped class loader
checkStateForClassLoading(name);
// (0) 判断 在本地内存中 是否加载过该类
clazz = findLoadedClass0(name);
if (clazz != null) {
if (log.isDebugEnabled()) {
log.debug(" Returning class from cache");
}
if (resolve) {
resolveClass(clazz);
}
return clazz; // 本地内存中加载过,直接返回
}
// (0.1) 判断 内存中 是否加载过该类,这里的内存应该指的是 系统类加载器的内存
clazz = JreCompat.isGraalAvailable() ? null : findLoadedClass(name);
if (clazz != null) {
if (log.isDebugEnabled()) {
log.debug(" Returning class from cache");
}
if (resolve) {
resolveClass(clazz);
}
return clazz; // 内存中加载过,直接返回
}
// (0.2) 尝试使用 bootstrap class loader 加载,防止被应用程序类覆盖
String resourceName = binaryNameToPath(name, false);
ClassLoader javaseLoader = getJavaseClassLoader();
boolean tryLoadingFromJavaseLoader;
try {
// Use getResource as it won't trigger an expensive
// ClassNotFoundException if the resource is not available from
// the Java SE class loader. However (see
// https://bz.apache.org/bugzilla/show_bug.cgi?id=58125 for
// details) when running under a security manager in rare cases
// this call may trigger a ClassCircularityError.
// See https://bz.apache.org/bugzilla/show_bug.cgi?id=61424 for
// details of how this may trigger a StackOverflowError
// Given these reported errors, catch Throwable to ensure any
// other edge cases are also caught
URL url;
if (securityManager != null) {
PrivilegedAction<URL> dp = new PrivilegedJavaseGetResource(resourceName);
url = AccessController.doPrivileged(dp);
} else {
url = javaseLoader.getResource(resourceName);
}
tryLoadingFromJavaseLoader = (url != null);
} catch (Throwable t) {
// Swallow all exceptions apart from those that must be re-thrown
ExceptionUtils.handleThrowable(t);
// The getResource() trick won't work for this class. We have to
// try loading it directly and accept that we might get a
// ClassNotFoundException.
tryLoadingFromJavaseLoader = true;
}
if (tryLoadingFromJavaseLoader) { // 从 系统类加载器 加载有可能会成功
try {
clazz = javaseLoader.loadClass(name);
if (clazz != null) {
if (resolve) {
resolveClass(clazz);
}
return clazz; // 成功被 系统类加载器加载,返回
}
} catch (ClassNotFoundException e) {
// Ignore
}
}
// (0.5) 权限验证 - 如果存在的话
if (securityManager != null) {
int i = name.lastIndexOf('.');
if (i >= 0) {
try {
securityManager.checkPackageAccess(name.substring(0,i));
} catch (SecurityException se) {
String error = sm.getString("webappClassLoader.restrictedPackage", name);
log.info(error, se);
throw new ClassNotFoundException(error, se);
}
}
}
boolean delegateLoad = delegate || filter(name, true); // 是否采用 双亲委派 策略
// (1) 如果要求采用 双亲委派
if (delegateLoad) {
if (log.isDebugEnabled()) {
log.debug(" Delegating to parent classloader1 " + parent);
}
try {
clazz = Class.forName(name, false, parent);
if (clazz != null) {
if (log.isDebugEnabled()) {
log.debug(" Loading class from parent");
}
if (resolve) {
resolveClass(clazz);
}
return clazz;
}
} catch (ClassNotFoundException e) {
// Ignore
}
}
// (2) 否则,搜索 本地目录
if (log.isDebugEnabled()) {
log.debug(" Searching local repositories");
}
try {
clazz = findClass(name);
if (clazz != null) {
if (log.isDebugEnabled()) {
log.debug(" Loading class from local repository");
}
if (resolve) {
resolveClass(clazz);
}
return clazz; // 成功找到,返回
}
} catch (ClassNotFoundException e) {
// Ignore
}
// (3) 上面都无法成功,此时,无条件使用 双亲委派 策略
if (!delegateLoad) {
if (log.isDebugEnabled()) {
log.debug(" Delegating to parent classloader at end: " + parent);
}
try {
clazz = Class.forName(name, false, parent);
if (clazz != null) {
if (log.isDebugEnabled()) {
log.debug(" Loading class from parent");
}
if (resolve) {
resolveClass(clazz);
}
return clazz;
}
} catch (ClassNotFoundException e) {
// Ignore
}
}
}
throw new ClassNotFoundException(name);
}
/**
* 本地内存查找该类
*
* @param name The binary name of the resource to return
* @return a loaded class
*/
protected Class<?> findLoadedClass0(String name) {
String path = binaryNameToPath(name, true);
ResourceEntry entry = resourceEntries.get(path);
if (entry != null) {
return entry.loadedClass;
}
return null;
}
// 获取系统类加载器
protected ClassLoader getJavaseClassLoader() {
return javaseClassLoader;
}
2) findClass(String name)
/**
* 在本地目录中查找类
*
* @param name The binary name of the class to be loaded
*
* @exception ClassNotFoundException if the class was not found
*/
@Override
public Class<?> findClass(String name) throws ClassNotFoundException {
if (log.isDebugEnabled()) {
log.debug(" findClass(" + name + ")");
}
checkStateForClassLoading(name);
// (1) 权限验证
if (securityManager != null) {
int i = name.lastIndexOf('.');
if (i >= 0) {
try {
if (log.isTraceEnabled()) {
log.trace(" securityManager.checkPackageDefinition");
}
securityManager.checkPackageDefinition(name.substring(0,i));
} catch (Exception se) {
if (log.isTraceEnabled()) {
log.trace(" -->Exception-->ClassNotFoundException", se);
}
throw new ClassNotFoundException(name, se);
}
}
}
// Ask our superclass to locate this class, if possible
// (throws ClassNotFoundException if it is not found)
Class<?> clazz = null;
try {
if (log.isTraceEnabled()) {
log.trace(" findClassInternal(" + name + ")");
}
try {
if (securityManager != null) {
PrivilegedAction<Class<?>> dp = new PrivilegedFindClassByName(name);
clazz = AccessController.doPrivileged(dp);
} else {
clazz = findClassInternal(name); // 在 Web 应用 目录下查找类
}
} catch(AccessControlException ace) {
log.warn(sm.getString("webappClassLoader.securityException", name, ace.getMessage()), ace);
throw new ClassNotFoundException(name, ace);
} catch (RuntimeException e) {
if (log.isTraceEnabled()) {
log.trace(" -->RuntimeException Rethrown", e);
}
throw e;
}
// Web 应用 目录下没有找到该类
if ((clazz == null) && hasExternalRepositories) {
try {
clazz = super.findClass(name); // 交给 父加载器,即 AppClassLoader 查找该类
} catch(AccessControlException ace) {
log.warn(sm.getString("webappClassLoader.securityException", name, ace.getMessage()), ace);
throw new ClassNotFoundException(name, ace);
} catch (RuntimeException e) {
if (log.isTraceEnabled()) {
log.trace(" -->RuntimeException Rethrown", e);
}
throw e;
}
}
// AppClassLoader 也没有找到,抛出异常
if (clazz == null) {
if (log.isDebugEnabled()) {
log.debug(" --> Returning ClassNotFoundException");
}
throw new ClassNotFoundException(name);
}
} catch (ClassNotFoundException e) {
if (log.isTraceEnabled()) {
log.trace(" --> Passing on ClassNotFoundException");
}
throw e;
}
// Return the class we have located
if (log.isTraceEnabled()) {
log.debug(" Returning class " + clazz);
}
if (log.isTraceEnabled()) {
ClassLoader cl;
if (Globals.IS_SECURITY_ENABLED){
cl = AccessController.doPrivileged(new PrivilegedGetClassLoader(clazz));
} else {
cl = clazz.getClassLoader();
}
log.debug(" Loaded by " + cl.toString());
}
return clazz;
}
3) 热加载
- 本质是通过一个后台线程做周期性任务,定时检测类文件的变化
- 若有变化,就重新加载该类
public abstract class ContainerBase extends LifecycleMBeanBase implements Container {
// ...
/**
* Private runnable class to invoke the backgroundProcess method
* of this container and its children after a fixed delay.
*/
protected class ContainerBackgroundProcessor implements Runnable {
@Override
public void run() {
processChildren(ContainerBase.this);
}
protected void processChildren(Container container) {
ClassLoader originalClassLoader = null;
try {
if (container instanceof Context) {
Loader loader = ((Context) container).getLoader();
// Loader will be null for FailedContext instances
if (loader == null) {
return;
}
// Ensure background processing for Contexts and Wrappers
// is performed under the web app's class loader
originalClassLoader = ((Context) container).bind(false, null);
}
container.backgroundProcess();
Container[] children = container.findChildren();
// 遍历子容器,递归调用本方法,保证所有子容器均被处理
for (Container child : children) {
// > 0 说明子容器有自己的后台线程,无需父容器调用它的 processChildren(child) 方法
// <= 0 说明有必要递归调用该方法
if (child.getBackgroundProcessorDelay() <= 0) {
processChildren(child);
}
}
} catch (Throwable t) {
ExceptionUtils.handleThrowable(t);
log.error(sm.getString("containerBase.backgroundProcess.error"), t);
} finally {
if (container instanceof Context) {
((Context) container).unbind(false, originalClassLoader);
}
}
}
}
// ...
}