学习参考的文章链接
protected ClassLoader() {
this(checkCreateClassLoader(), getSystemClassLoader());
}
private ClassLoader(Void unused, ClassLoader parent) {
//把自定义类加载器的父类加载器设置为SystemClassLoader
this.parent = parent;
.../
}
protected ClassLoader() {
this(checkCreateClassLoader(), getSystemClassLoader());
}
private ClassLoader(Void unused, ClassLoader parent) {
//把自定义类加载器的父类加载器设置为SystemClassLoader
this.parent = parent;
.../
}
最后需要区分的一点就是启动类加载的加载器不一定就是加载类的加载器,而是最后调用defineClass的那个加载器才是最后加载类的那个加载器。
protected Class<?> loadClass(String name, boolean resolve)
//resolve如果是true就需要进行解析操作
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) {
//当前类加载器去加载这个类
long t1 = System.nanoTime();
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;
}
}
@CallerSensitive
public static Class<?> forName(String className)
throws ClassNotFoundException {
//获取调用者的类加载器
Class<?> caller = Reflection.getCallerClass();
return forName0(className, true, ClassLoader.getClassLoader(caller), caller);
}
common.loader="${catalina.base}/lib","${catalina.base}/lib/*.jar","${catalina.home}/lib","${catalina.home}/lib/*.jar"
server.loader=
shared.loader=
public static void main(String[] args) {
synchronized(daemonLock) {
if (daemon == null) {
Bootstrap bootstrap = new Bootstrap();
try {
bootstrap.init();
} catch (Throwable var5) {
handleThrowable(var5);
var5.printStackTrace();
return;
}
daemon = bootstrap;
} else {
Thread.currentThread().setContextClassLoader(daemon.catalinaLoader);
}
}
}
public void init() throws Exception {
//创建tomcat三个类加载器
this.initClassLoaders();
//线程上下文类加载器设置为catalinaLoader,也就是容器的类加载器
Thread.currentThread().setContextClassLoader(this.catalinaLoader);
SecurityClassLoad.securityClassLoad(this.catalinaLoader);
....
}
private void initClassLoaders() {
try {
//创建tomcat的三个重要的classLoader
this.commonLoader = this.createClassLoader("common", (ClassLoader)null);
if (this.commonLoader == null) {
this.commonLoader = this.getClass().getClassLoader();
}
this.catalinaLoader = this.createClassLoader("server", this.commonLoader);
this.sharedLoader = this.createClassLoader("shared", this.commonLoader);
} catch (Throwable var2) {
handleThrowable(var2);
log.error("Class loader creation threw exception", var2);
System.exit(1);
}
}
//刚好加载了三个类都是之前配置文件上面的,那三个
private ClassLoader createClassLoader(String name, ClassLoader parent) throws Exception {
String value = CatalinaProperties.getProperty(name + ".loader");
if (value != null && !value.equals("")) {
value = this.replace(value);
List<Repository> repositories = new ArrayList();
String[] repositoryPaths = getPaths(value);
String[] arr$ = repositoryPaths;
int len$ = repositoryPaths.length;
for(int i$ = 0; i$ < len$; ++i$) {
...//解析并且获取类加载器下面的路径
}
return ClassLoaderFactory.createClassLoader(repositories, parent);
} else {
return parent;
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wZKNPdAd-1635326345834)(…/…/…/…/…/AppData/Roaming/Typora/typora-user-images/image-20211027152508382.png)]
那么整个源码能够看出来jvm的类加载的方式。
那么这里就会问到为啥要这样干啊?直接双亲委派就完事了?
原因就是直接双亲委派就不能够让web应用的不同版本第三方库共存,只能允许一个存在,因为父类加载器只会加载一次这个类。而类是否相同的唯一鉴别标准就是加载类的加载器是不是相同,如果不相同那么就两个类就算类名同最后实际上是不同的。也就是说不同类加载器加载类都是不同的类,这样就解决了tomcat的隔离问题(web容器与web应用还有就是web应用之间的隔离)。
public Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
synchronized(this.getClassLoadingLock(name)) {
if (log.isDebugEnabled()) {
log.debug("loadClass(" + name + ", " + resolve + ")");
}
Class<?> clazz = null;
this.checkStateForClassLoading(name);
//先从自己缓存中查找
clazz = this.findLoadedClass0(name);
if (clazz != null) {
...
}
//然后再从parent的缓存查找是否存在这个类
clazz = this.findLoadedClass(name);
if (clazz != null) {
.....
}
//如果缓存没有先从system类的加载器来加载。
String resourceName = this.binaryNameToPath(name, false);
ClassLoader javaseLoader = this.getJavaseClassLoader();
//是否需要parent来进行代理
boolean delegateLoad = this.delegate || this.filter(name, true);
//如果是true那么就是父类加载器先去加载
if (delegateLoad) {
label211: {
if (log.isDebugEnabled()) {
log.debug(" Delegating to parent classloader1 " + this.parent);
}
try {
//通过父类先去加载
clazz = Class.forName(name, false, this.parent);
....
} catch (ClassNotFoundException var16) {
break label211;
}
return var10000;
}
}
....
//如果父类记载失败那么就调用自身的加载
try {
clazz = this.findClass(name);
if (clazz != null) {
.....
var10000 = clazz;
return var10000;
}
} catch (ClassNotFoundException var17) {
}
....
//如果本类加载器找不到就会委托父类加载器去加载。
try {
clazz = Class.forName(name, false, this.parent);
if (clazz != null) {
....
var10000 = clazz;
return var10000;
}
} catch (ClassNotFoundException var14) {
}
throw new ClassNotFoundException(name);
}
protected WebappClassLoaderBase() {
super(new URL[0]);
this.state = LifecycleState.NEW;
ClassLoader p = this.getParent();
if (p == null) {
p = getSystemClassLoader();
}
this.parent = p;
//获取String的classLoader
ClassLoader j = String.class.getClassLoader();
if (j == null) {
for(j = getSystemClassLoader(); j.getParent() != null; j = j.getParent()) {
}
}
//给javaseClassLoader赋值,赋值的内容就是bootstrap类加载器
this.javaseClassLoader = j;
this.securityManager = System.getSecurityManager();
if (this.securityManager != null) {
this.refreshPolicy();
}
}
最后做一个总结,写这篇文章的时候参考了很多博客,因为这部分只看书真的太难了,博客很多地方都带上源码,最直观的方式就是自己去把tomcat的部分源码拉到idea去看一看,对于jvm的类加载部分会有更深刻的理解。