<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
掌握了Java的類載入器和雙親委派機制,現在我們就可以回答正題上來了,Tomcat的類載入器是怎麼設計的?
Web容器有其自身的特殊性,所以在類載入器這塊是不能完全使用JVM的類載入器的雙親委派機制的。在Web容器中我們應該要滿足如下的特性:
隔離性:
部署在同一個Web容器上的兩個Web應用程式所使用的Java類庫可以實現相互隔離。設想一下,兩個Web應用,一個使用了Spring3.0,另一個使用了新的的5.0,應用伺服器使用一個類載入器,Web應用將會因為jar包覆蓋而無法啟動。
靈活性:
Web應用之間的類載入器相互獨立,那麼就能針對一個Web應用進行重新部署,此時Web應用的類載入器會被重建,而且不會影響其他的Web應用。如果採用一個類載入器,類之間的依賴是雜亂複雜的,無法完全移出某個應用的類。
效能:
效能也是一個比較重要的點。部署在同一個Web容器上的兩個Web應用程式所使用的Java類庫可以互相共用。這個需求也很常見,例如,使用者可能有10個使用Spring框架的應用程式部署在同一臺伺服器上,如果把10份Spring分別存放在各個應用程式的隔離目錄中,將會是很大的資源浪費——這主要倒不是浪費磁碟空間的問題,而是指類庫在使用時都要被載入到Web容器的記憶體,如果類庫不能共用,虛擬機器器的方法區就會很容易出現過度膨脹的風險。
明白了Web容器的類載入器有多個,再來看tomcat的類載入器結構。
首先上張圖,整體看下tomcat的類載入器:
可以看到在原先的java類載入器基礎上,tomcat新增了幾個類載入器,包括3個基礎類載入器和每個Web應用的類載入器,其中3個基礎類載入器可在conf/catalina.properties中設定,具體介紹下:
Common:
以應用類載入器為父類別,是tomcat頂層的公用類載入器,其路徑由conf/catalina.properties中的common.loader指定,預設指向${catalina.base}/lib下的包。
Catalina:
以Common類載入器為父類別,是用於載入Tomcat應用伺服器的類載入器,其路徑由server.loader指定,預設為空,此時tomcat使用Common類載入器載入應用伺服器。
Shared:
以Common類載入器為父類別,是所有Web應用的父類別載入器,其路徑由shared.loader指定,預設為空,此時tomcat使用Common類載入器作為Web應用的父載入器。
Web應用:
以Shared類載入器為父類別,載入/WEB-INF/classes目錄下的未壓縮的Class和資原始檔以及/WEB-INF/lib目錄下的jar包,該類載入器只對當前Web應用可見,對其他Web應用均不可見。
預設情況下,Common、Catalina、Shared類載入器是同一個,但可以設定3個不同的類載入器,使他們各司其職。
首先,Common類載入器複雜載入Tomcat應用伺服器內部和Web應用均可見的類,如Servlet規範相關包和一些通用工具包。
其次,Catalina類載入器負責只有Tomcat應用伺服器內部可見的類,這些類對Web應用不可見。比如,想實現自己的對談儲存方案,而且該方案依賴了一些第三方包,當然是不希望這些包對Web應用可見,這時可以設定server.load,建立獨立的Catalina類載入器。
再次,Shared類複雜載入Web應用共用類,這些類tomcat伺服器不會依賴
首先來看看Tomcat的類載入器的繼承結構
可以看到繼承的結構和我們上面所寫的類載入器的結構不同。
大家需要注意雙親委派機制並不是通過繼承來實現的,而是相互之間組合而形成的。
所以AppClassLoader沒有繼承自 ExtClassLoader,WebappClassLoader也沒有繼承自AppClassLoader。
至於Common ClassLoader ,Shared ClassLoader,Catalina ClassLoader則是在啟動時初始化的三個不同名字的URLClassLoader。
先來看看Bootstrap#init()方法。init方法會呼叫initClassLoaders,同樣也會將Catalina ClassLoader設定到當前執行緒設定到當前執行緒,進入initClassLoaders來看看。
private void initClassLoaders() { try { // 建立 commonLoader catalinaLoader sharedLoader 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(); } // 預設情況下 server.loader 和 shared.loader 都為空則會返回 commonLoader 類載入器 catalinaLoader = createClassLoader("server", commonLoader); sharedLoader = createClassLoader("shared", commonLoader); } catch (Throwable t) { handleThrowable(t); log.error("Class loader creation threw exception", t); System.exit(1); } }
我們可以看到在initClassLoaders()方法中完成了CommonClassLoader, CatalinaClassLoader,和SharedClassLoader的建立,而且進入到createClassLoader方法中。
可以看到這三個基礎類載入器所載入的資源剛好對應conf/catalina.properties中的common.loader,server.loader,shared.loader
3.2 層次結構
Common/Catalina/Shared ClassLoader的建立好了之後就會維護相互之間的組合關係
其實也就是設定了父載入器
原始碼比較長,直接進入到 WebappClassLoaderBase
中的 LoadClass方法
@Override 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); // (0) Check our previously loaded local class cache // 檢查WebappClassLoader中是否載入過此類 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 // 如果第一步沒有找到,則繼續檢查JVM虛擬機器器中是否載入過該類 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 bootstrap class loader, to prevent // the webapp from overriding Java SE classes. This implements // SRV.10.7.2 // 如果前兩步都沒有找到,則使用系統類載入該類(也就是當前JVM的ClassPath)。 // 為了防止覆蓋基礎類實現,這裡會判斷class是不是JVMSE中的基礎類庫中類。 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) 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 = sm.getString("webappClassLoader.restrictedPackage", name); log.info(error, se); throw new ClassNotFoundException(error, se); } } } // 檢查是否 設定了delegate屬性,設定為true,那麼就會完全按照JVM的"雙親委託"機制流程載入類。 boolean delegateLoad = delegate || filter(name, true); // (1) Delegate to our parent if requested 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 // 若是沒有委託,則預設會首次使用WebappClassLoader來載入類。通過自定義findClass定義處理類載入規則。 // findClass()會去Web-INF/classes 目錄下查詢類。 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 // 若是WebappClassLoader在/WEB-INF/classes、/WEB-INF/lib下還是查詢不到class, // 那麼無條件強制委託給System、Common類載入器去查詢該類。 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); }
Web應用類載入器預設的載入順序是:
tomcat提供了delegate屬性用於控制是否啟用java委派模式,預設false(不啟用),當設定為true時,tomcat將使用java的預設委派模式,這時載入順序如下:
以上就是Tomcat Catalina為什麼不new出來原理解析的詳細內容,更多關於Tomcat Catalina原理的資料請關注it145.com其它相關文章!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45