这篇文章主要介绍“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类加载机制是什么”的学习就结束了,希望能够解决大家的疑惑。理论与实践的搭配能更好的帮助大家学习,快去试试吧!若想继续学习更多相关知识,请继续关注亿速云网站,小编会继续努力为大家带来更多实用的文章!
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。