Tomcat源码初识一 Tomcat整理流程图
Tomcat源码初识二 用文字描述整体流程
Tomcat源码初识三 Tomcat如何实现热加载与热部署
Tomcat源码初识四 Tomcat如何打破双亲委派
Tomcat源码初识五 Tomcat如何处理HTTP请求
java中类加载器加载顺序如下图:
sun.misc.Launcher类中,初始化了ExtClassLoader 加载器与AppClassLoader加载器
public Launcher() {
Launcher.ExtClassLoader var1;
try {
//创建ExtClassLoader
var1 = Launcher.ExtClassLoader.getExtClassLoader();
} catch (IOException var10) {
throw new InternalError("Could not create extension class loader", var10);
}
try {
//创建AppClassLoader
this.loader = Launcher.AppClassLoader.getAppClassLoader(var1);
} catch (IOException var9) {
throw new InternalError("Could not create application class loader", var9);
}
//设置默认类加载器为app类加载器
Thread.currentThread().setContextClassLoader(this.loader);
...
}
ExtClassLoader类是Launcher类中得到内部类,继承自URLClassLoader
调用ExtClassLoader构造函数时,赋值父加载器为null,(null实际上是启动类加载器)
static class ExtClassLoader extends URLClassLoader {
//获取ExtClassLoader(Launcher调用此方法创建ExtClassLoader)
public static Launcher.ExtClassLoader getExtClassLoader() throws IOException {
final File[] var0 = getExtDirs();
try {
return (Launcher.ExtClassLoader)AccessController.doPrivileged(new PrivilegedExceptionAction<Launcher.ExtClassLoader>() {
public Launcher.ExtClassLoader run() throws IOException {
int var1 = var0.length;
for(int var2 = 0; var2 < var1; ++var2) {
MetaIndex.registerDirectory(var0[var2]);
}
return new Launcher.ExtClassLoader(var0);
}
});
} catch (PrivilegedActionException var2) {
throw (IOException)var2.getException();
}
}
//构造函数,创建ExtClassLoader对象,设置父加载器为null(null实际上是启动类加载器)
public ExtClassLoader(File[] var1) throws IOException {
super(getExtURLs(var1), (ClassLoader)null, Launcher.factory);
SharedSecrets.getJavaNetAccess().getURLClassPath(this).initLookupCache(this);
}
//默认加载文件路径
private static File[] getExtDirs() {
String var0 = System.getProperty("java.ext.dirs");
File[] var1;
if (var0 != null) {
StringTokenizer var2 = new StringTokenizer(var0, File.pathSeparator);
int var3 = var2.countTokens();
var1 = new File[var3];
for(int var4 = 0; var4 < var3; ++var4) {
var1[var4] = new File(var2.nextToken());
}
} else {
var1 = new File[0];
}
return var1;
}
...
}
AppClassLoader 类是Launcher类中得到内部类,继承自URLClassLoader
调用AppClassLoader 构造函数时,赋值父加载器为ExtClassLoader
默认加载文件路径为java.class.path
static class AppClassLoader extends URLClassLoader {
//获取getAppClassLoader,Launcher调用此方法创建AppClassLoader,var0为ExtClassLoader对象
public static ClassLoader getAppClassLoader(final ClassLoader var0) throws IOException {
//默认加载文件路径
final String var1 = System.getProperty("java.class.path");
final File[] var2 = var1 == null ? new File[0] : Launcher.getClassPath(var1);
return (ClassLoader)AccessController.doPrivileged(new PrivilegedAction<Launcher.AppClassLoader>() {
public Launcher.AppClassLoader run() {
URL[] var1x = var1 == null ? new URL[0] : Launcher.pathToURLs(var2);
return new Launcher.AppClassLoader(var1x, var0);
}
});
}
//构造函数,设置父加载器为ExtClassLoader加载器
AppClassLoader(URL[] var1, ClassLoader var2) {
super(var1, var2, Launcher.factory);
this.ucp.initLookupCache(this);
}
...
}
AppClassLoader 与ExtClassLoader的继承关系如下图:
调用loadClass时,会调用父类ClassLoader的loadClass(这里就是双亲委派代码),代码如下:
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException
{
synchronized (getClassLoadingLock(name)) {
// 查看类是不是已经加载过了
Class<?> c = findLoadedClass(name);
if (c == null) {
//如果没有加载过,开始加载
long t0 = System.nanoTime();
try {
//父加载器不为空,用父加载器加载
if (parent != null) {
c = parent.loadClass(name, false);
} else {
//父加载器为空,用启动类加载器加载
c = findBootstrapClassOrNull(name);
}
} catch (ClassNotFoundException e) {
// ClassNotFoundException thrown if class not found
// from the non-null parent class loader
}
//如果父加载器没有加载到类,用当前类加载器加载
if (c == null) {
// If still not found, then invoke findClass in order
// to find the class.
long t1 = System.nanoTime();
//将类的字节码解析成Class类
c = findClass(name);
// this is the defining class loader; record the stats
sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
sun.misc.PerfCounter.getFindClasses().increment();
}
}
if (resolve) {
resolveClass(c);
}
return c;
}
}
}
经过上面的代码,就能看出来java中类加载的双亲委派是如何实现的,所有类都会先向上试图让最上级父类加载
双亲委派好处:
- 沙箱安全机制,底层类库不允许篡改
- 父类加载过的类,子类不再加载
Tomcat打破双亲委派:
为什么要打破双亲委派:tomcat中会部署多个项目,每个项目可能会出现相同的类名,或者引用的jar包相同但版本不同,这时候不打破双亲委派,一个类只会加载一次,项目就互相之间有冲突,打破双亲委派,可以实现自己的项目加载自己项目的类,互不影响
原理:自定义类加载器,然后重写loadClass,不先由父类加载
- 先检查tomcat缓存中是否已经加载过
- 判断jvm缓存中是否已经加载过
- 尝试使用ExtClassLoader加载
- 如果设置了代理,使用AppClassLoader加载
- 使用当前类加载器加载
- 如果没有设置代理,尝试使用AppClassLoader加载
WebappClassLoaderBase类的继承关系如下图:
WebappClassLoaderBase重写了ClassLoader的loadClass方法,实现代码:
public Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
synchronized (getClassLoadingLock(name)) {
if (log.isDebugEnabled())
log.debug("loadClass(" + name + ", " + resolve + ")");
Class<?> clazz = null;
// Log access to stopped class loader
checkStateForClassLoading(name);
// tomcat自己做的缓存,查看类是否加载过了
clazz = findLoadedClass0(name);
if (clazz != null) {
if (log.isDebugEnabled())
log.debug(" Returning class from cache");
if (resolve)
resolveClass(clazz);
return clazz;
}
// 调用jvm的缓存,判断类是否加载过
clazz = findLoadedClass(name);
if (clazz != null) {
if (log.isDebugEnabled())
log.debug(" Returning class from cache");
if (resolve)
resolveClass(clazz);
return clazz;
}
String resourceName = binaryNameToPath(name, false);
//获取extClassLoader
ClassLoader javaseLoader = getJavaseClassLoader();
...
if (tryLoadingFromJavaseLoader) {
try {
//尝试使用extClassLoader加载类
clazz = javaseLoader.loadClass(name);
if (clazz != null) {
if (resolve)
resolveClass(clazz);
return clazz;
}
} catch (ClassNotFoundException e) {
// Ignore
}
}
...
// (1) Delegate to our parent if requested
if (delegateLoad) {
if (log.isDebugEnabled())
log.debug(" Delegating to parent classloader1 " + parent);
try {
//如果设置了代理(默认没设置),使用appClassLoader加载
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) Search local repositories
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) Delegate to parent unconditionally
if (!delegateLoad) {
if (log.isDebugEnabled())
log.debug(" Delegating to parent classloader at end: " + parent);
try {
//如果没设置代理,并且当前类加载器没有加载到,尝试使用appClassLoader类加载器加载
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);
}
Tomcat源码初识一 Tomcat整理流程图
Tomcat源码初识二 用文字描述整体流程
Tomcat源码初识三 Tomcat如何实现热加载与热部署
Tomcat源码初识四 Tomcat如何打破双亲委派
Tomcat源码初识五 Tomcat如何处理HTTP请求