这篇文章主要介绍“tomcat类加载机制是什么”,在日常操作中,相信很多人在tomcat类加载机制是什么问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”tomcat类加载机制是什么”的疑惑有所帮助!接下来,请跟着小编一起来学习吧!
tomcat类加载器的集成体系,包含两部分:
1.上半部分是jdk自带的,包含:启动类加载器,扩展类加载器,应用类加载器,这里不展开讲解。如果想了解,可以查看sun.misc.Launcher.Launcher()和java.lang.ClassLoader.loadClass(String, boolean)源码
2.下半部分是tomcat的自定义类加载器,包含:
a)commonClassLoader:通过${tomcatpath}/conf/catalina.properties中的common.loader配置,通常加载tomcat和webapp共享的class
b) catalinaClassLoader: 通过${tomcatpath}/conf/catalina.properties中的server.loader配置,加载tomcat需要的class,对webapp不可见
c) sharedClassLoader:通过${tomcatpath}/conf/catalina.properties中的shared.loader配置, 加载webapp们共享的class,对tomcat容器不可见
d) webappClassLoader: 这个类加载器,就是加载我们写的应用程序class的。通常说的tomcat打破双亲委派就是说的它
1.初始化自定义加载器
// org.apache.catalina.startup.Bootstrap.initClassLoaders()
private void initClassLoaders() {
try {
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();
}
catalinaLoader = createClassLoader("server", commonLoader);
sharedLoader = createClassLoader("shared", commonLoader);
} catch (Throwable t) {
handleThrowable(t);
log.error("Class loader creation threw exception", t);
System.exit(1);
}
}
2.初始化webappclassloader
org.apache.catalina.loader.WebappLoader.startInternal()
protected void startInternal() throws LifecycleException {
if (log.isDebugEnabled())
log.debug(sm.getString("webappLoader.starting"));
if (context.getResources() == null) {
log.info("No resources for " + context);
setState(LifecycleState.STARTING);
return;
}
// Construct a class loader based on our current repositories list
try {
// 创建classloader,并将父classloader设置成sharedClassLoader
classLoader = createClassLoader();
classLoader.setResources(context.getResources());
// 设置委派模式
classLoader.setDelegate(this.delegate);
// Configure our repositories
setClassPath();
setPermissions();
// 开始类加载
((Lifecycle) classLoader).start();
String contextName = context.getName();
if (!contextName.startsWith("/")) {
contextName = "/" + contextName;
}
ObjectName cloname = new ObjectName(context.getDomain() + ":type=" +
classLoader.getClass().getSimpleName() + ",host=" +
context.getParent().getName() + ",context=" + contextName);
Registry.getRegistry(null, null)
.registerComponent(classLoader, cloname, null);
} catch (Throwable t) {
t = ExceptionUtils.unwrapInvocationTargetException(t);
ExceptionUtils.handleThrowable(t);
log.error( "LifecycleException ", t );
throw new LifecycleException("start: ", t);
}
setState(LifecycleState.STARTING);
}
private WebappClassLoaderBase createClassLoader()
throws Exception {
Class<?> clazz = Class.forName(loaderClass);
WebappClassLoaderBase classLoader = null;
if (parentClassLoader == null) {
parentClassLoader = context.getParentClassLoader();
} else {
context.setParentClassLoader(parentClassLoader);
}
Class<?>[] argTypes = { ClassLoader.class };
Object[] args = { parentClassLoader };
Constructor<?> constr = clazz.getConstructor(argTypes);
classLoader = (WebappClassLoaderBase) constr.newInstance(args);
return classLoader;
}
3.装配应用class文件
// org.apache.catalina.loader.WebappClassLoaderBase.start()
public void start() throws LifecycleException {
state = LifecycleState.STARTING_PREP;
// 扫描 /WEB-INF/classes 下的class
WebResource[] classesResources = resources.getResources("/WEB-INF/classes");
for (WebResource classes : classesResources) {
if (classes.isDirectory() && classes.canRead()) {
localRepositories.add(classes.getURL());
}
}
// 扫描 /WEB-INF/lib 下的jar
WebResource[] jars = resources.listResources("/WEB-INF/lib");
for (WebResource jar : jars) {
if (jar.getName().endsWith(".jar") && jar.isFile() && jar.canRead()) {
localRepositories.add(jar.getURL());
// 记录jar的修改时间,用于热更新
jarModificationTimes.put(
jar.getName(), Long.valueOf(jar.getLastModified()));
}
}
state = LifecycleState.STARTED;
}
4.tomcat热更新:
// org.apache.catalina.loader.WebappLoader.backgroundProcess()
public void backgroundProcess() {
// reloadable:是否开启热更新
// modified():判断jar和class是否有修改(这里会导致cpu瞬间高)
if (reloadable && modified()) {
try {
Thread.currentThread().setContextClassLoader
(WebappLoader.class.getClassLoader());
if (context != null) {
context.reload();
}
} finally {
if (context != null && context.getLoader() != null) {
Thread.currentThread().setContextClassLoader
(context.getLoader().getClassLoader());
}
}
}
}
5.加载一个class文件的过程:
// org.apache.catalina.loader.WebappClassLoaderBase.loadClass(String)
public Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
synchronized (getClassLoadingLock(name)) {
Class<?> clazz = null;
// Log access to stopped class loader
checkStateForClassLoading(name);
// (0) Check our previously loaded local class cache
// 从本地缓存查找
clazz = findLoadedClass0(name);
if (clazz != null) {
if (log.isDebugEnabled())
log.debug(" Returning class from cache");
if (resolve)
resolveClass(clazz);
return clazz;
}
// (0.1) Check our previously loaded class cache
// 从父类类加载器缓存查找
clazz = findLoadedClass(name);
if (clazz != null) {
if (log.isDebugEnabled())
log.debug(" Returning class from cache");
if (resolve)
resolveClass(clazz);
return clazz;
}
// (0.2) Try loading the class with the system class loader, to prevent
// the webapp from overriding Java SE classes. This implements
// SRV.10.7.2
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;
}
// 尝试用加载String类(即启动类加载器)的类加载器加载
if (tryLoadingFromJavaseLoader) {
try {
clazz = javaseLoader.loadClass(name);
if (clazz != null) {
if (resolve)
resolveClass(clazz);
return clazz;
}
} catch (ClassNotFoundException e) {
// Ignore
}
}
// (0.5) Permission to access this class when using a SecurityManager
if (securityManager != null) {
int i = name.lastIndexOf('.');
if (i >= 0) {
try {
securityManager.checkPackageAccess(name.substring(0,i));
} catch (SecurityException se) {
String error = "Security Violation, attempt to use " +
"Restricted Class: " + name;
log.info(error, se);
throw new ClassNotFoundException(error, se);
}
}
}
// 判断是否委派给父类加载器
boolean delegateLoad = delegate || filter(name, true);
// (1) Delegate to our parent if requested
// 委派给父类加载器(sharedClassLoader),符合委派双亲
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) 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 {
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类加载机制是什么”的学习就结束了,希望能够解决大家的疑惑。理论与实践的搭配能更好的帮助大家学习,快去试试吧!若想继续学习更多相关知识,请继续关注亿速云网站,小编会继续努力为大家带来更多实用的文章!
亿速云「云服务器」,即开即用、新一代英特尔至强铂金CPU、三副本存储NVMe SSD云盘,价格低至29元/月。点击查看>>
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。
原文链接:https://my.oschina.net/u/2937247/blog/4690609