日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

tomcat(8)载入器

發(fā)布時(shí)間:2023/12/3 编程问答 24 豆豆
生活随笔 收集整理的這篇文章主要介紹了 tomcat(8)载入器 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
【0】README 0.0)本文部分描述轉(zhuǎn)自“深入剖析tomcat”,旨在學(xué)習(xí)?tomcat(8)載入器 的基礎(chǔ)知識(shí); 0.1)一個(gè)標(biāo)準(zhǔn)web 應(yīng)用程序中的載入器:簡單來說就是 tomcat中的載入器; 0.2)servlet容器需要實(shí)現(xiàn)一個(gè)自定義的載入器,而不能簡單地使用系統(tǒng)的類載入器的原因:(干貨——為什么servlet容器要實(shí)現(xiàn)一個(gè)自定義的載入器) 0.2.1)原因1:因?yàn)閟ervlet容器不應(yīng)該完全信任它正在運(yùn)行的servlet類; 0.2.2)原因2:如果使用系統(tǒng)類的載入器載入某個(gè)servlet類所使用的全部類,那么servlet就能夠訪問所有的類,包括當(dāng)前運(yùn)行的java 虛擬機(jī)中環(huán)境變量CLASSPATH指明的路徑下的所有的類和庫,這是非常危險(xiǎn)的;因?yàn)?servlet應(yīng)該只允許載入 WEB-INF/classes目錄及其子目錄下的類,和部署到 WEB-INF/lib 目錄下的類(類庫); 0.2.3)原因3:為了提供自動(dòng)重載的功能,即當(dāng) WEB-INF/classes 目錄或 WEB-INF/lib目錄下的類發(fā)生變化時(shí),web 應(yīng)用程序會(huì)重新載入這些類。在tomcat的載入器的實(shí)現(xiàn)中, 類載入器使用一個(gè)額外的線程來不斷檢查servlet 類和其它類的文件的時(shí)間戳。 0.3)在Catalina中: 載入器是 org.apache.catalina.Loader接口的實(shí)例; 若要支持自動(dòng)重載功能, 則載入器必須實(shí)現(xiàn) org.apache.catalina.loader.Reloader 接口; 0.4)intro to 兩個(gè)術(shù)語 0.4.1)倉庫(repository):倉庫表示類載入器會(huì)在哪里搜索要載入的類; 0.4.2)資源(resource):而資源指的是一個(gè)類載入器中的 DirContext對(duì)象,它的文件根路徑指的就是 上下文的文件根路徑; 0.5)for complete source code, please visit?https://github.com/pacosonTang/HowTomcatWorks/tree/master/chapter8/chapter8
【1】?java的類載入器 0)intro to 類載入器: 每次創(chuàng)建java類的實(shí)例時(shí),都必須先將類載入到內(nèi)存中, java虛擬機(jī)使用類載入器來載入需要的類。一般case下, 類載入器會(huì)在一些java 核心類庫,以及環(huán)境變量 classpath 中指明的目錄中 搜索相關(guān)類。如果在這些位置都找不到要載入的類,就會(huì)拋出 java.lang.ClassNotFoundException 異常; 1)從J2SE 1.2 開始, jvm 使用了3種類載入器來載入所需要的類:分別是引導(dǎo)類載入器(bootstrap class loader), 擴(kuò)展類載入器(extension?class loader) 和 系統(tǒng)類載入器(system?class loader)。而 引導(dǎo)類載入器是 擴(kuò)展類載入器的父親,?擴(kuò)展類載入器是 系統(tǒng)類載入器的父親。(干貨——jvm 使用了3種類載入器來載入所需要的類
2)3種載入器的詳細(xì)描述:(干貨——3種載入器的 spec intro) 2.1)引導(dǎo)類載入器: 用于引導(dǎo)啟動(dòng) jvm。當(dāng)調(diào)用 javax.exe 是, 就會(huì)啟動(dòng)引導(dǎo)類載入器。引導(dǎo)類載入器是使用本地代碼來實(shí)現(xiàn)的, 因?yàn)樗脕磔d入運(yùn)行 jvm 所需要的類, 以及所有的 java 核心類。如 java.lang 包 和 java.io 包下的類。啟動(dòng)類載入器會(huì)在 rt.jar 和 i18n.jar 等java 包中搜索要載入的類。 2.2) 擴(kuò)展類載入器: 負(fù)責(zé)載入標(biāo)準(zhǔn)擴(kuò)展目錄中的類。sum 公司的 jvm 的標(biāo)準(zhǔn)擴(kuò)展目錄是 /jdk/jre/lib/ext/; 2.3)系統(tǒng)類載入器:是默認(rèn)的類載入器, 他會(huì)搜索在環(huán)境變量 CLASSPATH 中指明的路徑和 JAR 文件; 3)jvm 使用的是哪種類載入器呢? 3.1)答案在于 類載入器的代理模型。 3.2)載入一個(gè)類 的steps(每當(dāng)需要載入一個(gè)類 的時(shí)候):(干貨中的干貨——載入一個(gè)類 的steps) step1)首先調(diào)用 系統(tǒng)類載入器,但并不會(huì)立即載入這個(gè)類; step2)相反,他會(huì)將載入類的任務(wù)交給其父類載入器——擴(kuò)展類載入器; step3)而擴(kuò)展類載入器也會(huì)將載入任務(wù)交給其父類載入器——引導(dǎo)類載入器; 3.3)因此,引導(dǎo)類載入器會(huì)首先執(zhí)行載入某個(gè)類的任務(wù)。接下來有3種cases: case1)如果引導(dǎo)類載入器找不到需要載入的類,那么擴(kuò)展類載入器會(huì)嘗試 載入該類; case2)如果擴(kuò)展類載入器也找不到該類,就輪到系統(tǒng)類載入器繼續(xù)執(zhí)行載入任務(wù); case3)如果系統(tǒng)類載入器也找不到這個(gè)類,拋出 java.lang.ClassNotFoundException 異常;
3.4)為什么要這么做? 代理模型的重要用途就是為了 解決 類載入過程中的安全問題;(干貨——代理模型的重要用途 3.5)看個(gè)荔枝: 當(dāng)程序的某個(gè)地方調(diào)用了 自定義的 java.lang.Object 類時(shí), 系統(tǒng)類載入器會(huì)將載入工作 委托給 擴(kuò)展類載入器,繼而會(huì)被交給 引導(dǎo)類載入器。 引導(dǎo)類載入器搜索其 核心庫, 找到標(biāo)準(zhǔn)的 java.lang.Object 類, 并將之實(shí)例化。 結(jié)果是, 自定義的 java.lang.Object 類并沒有被載入(這正是我們想要的)。
4)關(guān)于 java 中類載入機(jī)制的一件重要事情是, 可以通過繼承抽象類 java.lang.ClassLoader 類 編寫自己的類載入器。而 tomcat 要使用自定義類載入器的原因有3條(reasons):(干貨——tomcat 要使用自定義類載入器的原因) r1)為了在載入類中指定某些規(guī)則; r2)為了緩存已經(jīng)載入的類; r3)為了實(shí)現(xiàn)類的預(yù)載入,方便使用;
【2】Loader接口 0)載入web 應(yīng)用程序中需要的servlet類及其相關(guān)類需要遵循的一些rules:(干貨——web app中用到的servlet需要遵循的一些rules) r1)應(yīng)用程序中的 servlet 只能引用部署在 WEB-INF/classes 目錄及其子目錄下的類; r2)但是,servlet類不能訪問其他路徑中的類,即使這些類包含在運(yùn)行當(dāng)前的Tomcat的 jvm 的 classpath 環(huán)境變量中; r3)此外, servlet類只能訪問 WEB-INF/lib 目錄下的庫,其他目錄中的類庫不能訪問; 1)Tomcat載入器指的是web 應(yīng)用程序載入器,而不僅僅指 類載入器:(干貨——tomcat載入器不僅僅是類載入器) 1.1)載入器必須實(shí)現(xiàn) org.apache.catalina.Loader接口; 1.2)載入器的實(shí)現(xiàn)中,會(huì)使用一個(gè) 自定義類載入器: 它是 org.apache.catalina.loader.WebappClassLoader類的一個(gè)實(shí)例;(Loader接口的getClassLoader() 方法來獲取) 2)Loader接口定義的對(duì)倉庫集合的操作:(Tomcat中的倉庫就是WEB-INFO/classes 目錄和 WEB-INF/lib 目錄 2.1)intro to 倉庫:一個(gè)web app的倉庫指的是,其 WEB-INFO/classes 目錄和 WEB-INF/lib 目錄,這兩個(gè)目錄作為倉庫添加到 載入器中; 2.2)addRepository方法和 findRepositories() 方法:添加一個(gè)新倉庫和返回所有倉庫集合(數(shù)組對(duì)象); 3)Tomcat的載入器通常會(huì)與一個(gè) Context級(jí)別的servlet容器相關(guān)聯(lián); 4)自動(dòng)重載:如果Context 容器中的一個(gè)或多個(gè)類被修改了, 載入器也可以支持對(duì)類的自動(dòng)重載; 4.1)Loader接口使用modified() 方法來支持類的自動(dòng)重載:如果倉庫中的一個(gè)或多個(gè)類被修改了,那么modified() 方法會(huì)返回true,才能提供自動(dòng)重載的支持; 4.2)載入器類本身并不會(huì)自動(dòng)重載: 它會(huì)調(diào)用 Context接口(容器)的reload() 方法來實(shí)現(xiàn); 4.3)setReloadable() 和 getReloadable() 方法:用來指明是否支持載入器的自動(dòng)重載; 4.4)自動(dòng)重載的default case:默認(rèn)情況下是 禁用了自動(dòng)重載的功能的,要想啟動(dòng)Context容器的自動(dòng)重載功能,需要再 server.xml 文件中添加一個(gè) Context元素,如下所示:(干貨——默認(rèn)case下,Context容器的自動(dòng)重載功能是closed,通過如下方式啟用自動(dòng)重載功能) <Context path="/myApp" docBase="myApp" debug="0" reloadable="true" /> 4.5)載入器的實(shí)現(xiàn)會(huì)指明是否要委托給一個(gè)父類載入器;(Loader接口中聲明了 getDelegate()方法 和 setDelegate()方法) 4.6)org.apache.catalina.Loader的聲明代碼如下: package org.apache.catalina; import java.beans.PropertyChangeListener;public interface Loader {public ClassLoader getClassLoader();public Container getContainer();public void setContainer(Container container);public DefaultContext getDefaultContext();public void setDefaultContext(DefaultContext defaultContext);public boolean getDelegate();public void setDelegate(boolean delegate);public String getInfo();public boolean getReloadable();public void setReloadable(boolean reloadable); public void addPropertyChangeListener(PropertyChangeListener listener); public void addRepository(String repository); public String[] findRepositories(); public boolean modified(); public void removePropertyChangeListener(PropertyChangeListener listener); } 5)Catalina提供了 org.apache.catalina.loader.WebappLoader類作為 Loader接口的實(shí)現(xiàn):?WebappLoader 對(duì)象中使用 org.apache.catalina.loader.WebappClassLoader 類的實(shí)例作為其類載入器,該類繼承自 java.net.URLClassLoader類;(干貨——WebappClassLoader類很重要,已經(jīng)提及過兩次了 6)Loader接口及其實(shí)現(xiàn)類的URL類圖如下:
Attention)當(dāng)與某個(gè)載入器相關(guān)聯(lián)的容器(如 Context)需要使用某個(gè) servlet類時(shí),即當(dāng)該類的某個(gè)方法被調(diào)用時(shí), 容器會(huì)先調(diào)用載入器的 getClassLoader() 方法來獲取類載入器的實(shí)例。然后,容器會(huì)調(diào)用類 載入器的 loadClass() 方法來載入這個(gè)servlet類;
【3】Reloader接口 1)intro to Reloader接口:為了支持類的自動(dòng)重載功能,類載入器實(shí)現(xiàn)需要實(shí)現(xiàn) org.apache.catalina.loader.Reloader接口; 2)最重要的方法:modified方法,其作用是,如果web app 中的某個(gè)servlet 或相關(guān)類被修改了,modified方法會(huì)返回true;(干貨——Reloader接口的最重要的方法modified方法)
【4】 WebappLoader類(web 應(yīng)用程序載入器, 負(fù)責(zé)載入web 應(yīng)用程序中所使用到的類) 1)如上述的UML類圖所示,WebappLoader類實(shí)現(xiàn) Runnable接口,當(dāng)調(diào)用WebappLoader類的start方法時(shí),會(huì)完成以下幾項(xiàng)重要工作(works):
w1)創(chuàng)建一個(gè)類載入器; w2)設(shè)置倉庫; w3)設(shè)置類路徑; w4)設(shè)置訪問權(quán)限; w5)啟動(dòng)一個(gè)新線程來支持自動(dòng)重載; (下面的內(nèi)容將分別對(duì)以上works 進(jìn)行 intro) public void start() throws LifecycleException {// org.apache.catalina.loader.WebappLoader.start(),為了看其 outline,我沒有對(duì)該method做刪減// Validate and update our current component stateif (started)throw new LifecycleException(sm.getString("webappLoader.alreadyStarted"));if (debug >= 1)log(sm.getString("webappLoader.starting"));lifecycle.fireLifecycleEvent(START_EVENT, null);started = true;if (container.getResources() == null)return;// Register a stream handler factory for the JNDI protocolURLStreamHandlerFactory streamHandlerFactory =new DirContextURLStreamHandlerFactory();try {URL.setURLStreamHandlerFactory(streamHandlerFactory);} catch (Throwable t) {// Ignore the error here.}// Construct a class loader based on our current repositories listtry {classLoader = createClassLoader(); // 創(chuàng)建一個(gè)類載入器,下面是設(shè)置類載入器classLoader.setResources(container.getResources()); classLoader.setDebug(this.debug);classLoader.setDelegate(this.delegate);for (int i = 0; i < repositories.length; i++) {classLoader.addRepository(repositories[i]);}// Configure our repositoriessetRepositories(); // 設(shè)置倉庫setClassPath(); // 設(shè)置類路徑setPermissions(); // 設(shè)置訪問權(quán)限if (classLoader instanceof Lifecycle)((Lifecycle) classLoader).start(); // Binding the Webapp class loader to the directory contextDirContextURLStreamHandler.bind((ClassLoader) classLoader, this.container.getResources());} catch (Throwable t) {throw new LifecycleException("start: ", t);}// Validate that all required packages are actually availablevalidatePackages();// Start our background thread if we are reloadableif (reloadable) {log(sm.getString("webappLoader.reloading"));try {threadStart(); // 啟動(dòng)一個(gè)新線程來支持自動(dòng)重載} catch (IllegalStateException e) {throw new LifecycleException(e);}}}
【4.1】創(chuàng)建類載入器 1)WebappLoader類提供了 getLoaderClass() 方法 和 setLoaderClass() 方法來獲取或改變 其私有變量的 loaderClass 的值; 1.1)該私有變量保存了一個(gè)字符串類型的值,指明了類載入器所要載入的類的名字;(干貨——私有變量loaderClass指明了類載入器所要載入的類的名字) 1.2)默認(rèn)情況下: 變量loadClass的值是 org.apache.catalina.loader.WebappClassLoader ; 1.3)也可以通過繼承 WebappClassLoader 類的方式實(shí)現(xiàn)自己的類載入器,然后調(diào)用 setLoaderClass() 方法強(qiáng)制WebappLoader實(shí)例使用 自定義類載入器。否則的話,在它啟動(dòng)時(shí),WebappLoader 類會(huì)調(diào)用其 私有方法 createClassLoader() 方法來創(chuàng)建 默認(rèn) 的 ?類載入器; Attention) A1)可以不使用 WebappClassLoader 類的實(shí)例,而使用其他類的實(shí)例作為類載入器; A2)createClassLoader()方法的返回值是: WebappLoader類型的, 因此,如果自定義類型沒有繼承自 WebappClassLoader l類,createClassLoader方法就會(huì)拋出一個(gè)異常; private WebappClassLoader createClassLoader()throws Exception { // org.apache.catalina.loader.WebappLoader.createClassLoader()Class clazz = Class.forName(loaderClass);WebappClassLoader classLoader = null; // highlight line.if (parentClassLoader == null) {// Will cause a ClassCast is the class does not extend WCL, but// this is on purpose (the exception will be caught and rethrown)classLoader = (WebappClassLoader) clazz.newInstance();} else {Class[] argTypes = { ClassLoader.class };Object[] args = { parentClassLoader };Constructor constr = clazz.getConstructor(argTypes);classLoader = (WebappClassLoader) constr.newInstance(args);}return classLoader;} 【4.2】設(shè)置倉庫 private void setRepositories() { // org.apache.catalina.loader.WebappLoader.setRepositories()if (!(container instanceof Context))return;ServletContext servletContext =((Context) container).getServletContext();if (servletContext == null)return;// Loading the work directoryFile workDir =(File) servletContext.getAttribute(Globals.WORK_DIR_ATTR);if (workDir == null)return;log(sm.getString("webappLoader.deploy", workDir.getAbsolutePath()));DirContext resources = container.getResources();// Setting up the class repository (/WEB-INF/classes), if it existsString classesPath = "/WEB-INF/classes";DirContext classes = null;try {Object object = resources.lookup(classesPath);if (object instanceof DirContext) {classes = (DirContext) object;}} catch(NamingException e) {// Silent catch: it's valid that no /WEB-INF/classes collection// exists}if (classes != null) {File classRepository = null;String absoluteClassesPath =servletContext.getRealPath(classesPath);if (absoluteClassesPath != null) {classRepository = new File(absoluteClassesPath);} else {classRepository = new File(workDir, classesPath);classRepository.mkdirs();copyDir(classes, classRepository);}log(sm.getString("webappLoader.classDeploy", classesPath,classRepository.getAbsolutePath()));// Adding the repository to the class loaderclassLoader.addRepository(classesPath + "/", classRepository);}// Setting up the JAR repository (/WEB-INF/lib), if it existsString libPath = "/WEB-INF/lib";classLoader.setJarPath(libPath);DirContext libDir = null;// Looking up directory /WEB-INF/lib in the contexttry {Object object = resources.lookup(libPath);if (object instanceof DirContext)libDir = (DirContext) object;} catch(NamingException e) {// Silent catch: it's valid that no /WEB-INF/lib collection// exists}if (libDir != null) {boolean copyJars = false;String absoluteLibPath = servletContext.getRealPath(libPath);File destDir = null;if (absoluteLibPath != null) {destDir = new File(absoluteLibPath);} else {copyJars = true;destDir = new File(workDir, libPath);destDir.mkdirs();}// Looking up directory /WEB-INF/lib in the contexttry {NamingEnumeration enum = resources.listBindings(libPath);while (enum.hasMoreElements()) {Binding binding = (Binding) enum.nextElement();String filename = libPath + "/" + binding.getName();if (!filename.endsWith(".jar"))continue;// Copy JAR in the work directory, always (the JAR file// would get locked otherwise, which would make it// impossible to update it or remove it at runtime)File destFile = new File(destDir, binding.getName());log(sm.getString("webappLoader.jarDeploy", filename,destFile.getAbsolutePath()));Resource jarResource = (Resource) binding.getObject();if (copyJars) {if (!copy(jarResource.streamContent(),new FileOutputStream(destFile)))continue;}JarFile jarFile = new JarFile(destFile);classLoader.addJar(filename, jarFile, destFile);}} catch (NamingException e) {// Silent catch: it's valid that no /WEB-INF/lib directory// exists} catch (IOException e) {e.printStackTrace();} }} 【4.3】設(shè)置類路徑 1)設(shè)置類路徑的任務(wù)是:通過在 start()方法中調(diào)用 setClassPath方法完成的。該方法會(huì)在servlet上下文中 為 Jasper JSP 編譯器設(shè)置一個(gè)字符串形式的屬性來指明類路徑信息; private void setClassPath() { // org.apache.catalina.loader.WebappLoader.setClassPath()// Validate our current state informationif (!(container instanceof Context))return;ServletContext servletContext =((Context) container).getServletContext();if (servletContext == null)return;StringBuffer classpath = new StringBuffer();// Assemble the class path information from our class loader chainClassLoader loader = getClassLoader();int layers = 0;int n = 0;while ((layers < 3) && (loader != null)) {if (!(loader instanceof URLClassLoader))break;URL repositories[] =((URLClassLoader) loader).getURLs();for (int i = 0; i < repositories.length; i++) {String repository = repositories[i].toString();if (repository.startsWith("file://"))repository = repository.substring(7);else if (repository.startsWith("file:"))repository = repository.substring(5);else if (repository.startsWith("jndi:"))repository =servletContext.getRealPath(repository.substring(5));elsecontinue;if (repository == null)continue;if (n > 0)classpath.append(File.pathSeparator);classpath.append(repository);n++;}loader = loader.getParent();layers++;}// Store the assembled class path as a servlet context attributeservletContext.setAttribute(Globals.CLASS_PATH_ATTR,classpath.toString());} 【4.4】設(shè)置訪問權(quán)限 1)若運(yùn)行Tomcat時(shí),使用了安全管理器:則 setPermissions() 方法會(huì)為 類載入器設(shè)置訪問相關(guān)目錄的權(quán)限; private void setPermissions() {// org.apache.catalina.loader.WebappLoader.setPermission()if (System.getSecurityManager() == null)return;if (!(container instanceof Context))return;// Tell the class loader the root of the contextServletContext servletContext =((Context) container).getServletContext();// Assigning permissions for the work directoryFile workDir =(File) servletContext.getAttribute(Globals.WORK_DIR_ATTR);if (workDir != null) {try {String workDirPath = workDir.getCanonicalPath();classLoader.addPermission(new FilePermission(workDirPath, "read,write"));classLoader.addPermission(new FilePermission(workDirPath + File.separator + "-", "read,write,delete"));} catch (IOException e) {// Ignore}} try {URL rootURL = servletContext.getResource("/");classLoader.addPermission(rootURL);String contextRoot = servletContext.getRealPath("/");if (contextRoot != null) {try {contextRoot = (new File(contextRoot)).getCanonicalPath();classLoader.addPermission(contextRoot);} catch (IOException e) {// Ignore}}URL classesURL = servletContext.getResource("/WEB-INF/classes/");classLoader.addPermission(classesURL);URL libURL = servletContext.getResource("/WEB-INF/lib/");classLoader.addPermission(libURL);if (contextRoot != null) {if (libURL != null) {File rootDir = new File(contextRoot);File libDir = new File(rootDir, "WEB-INF/lib/");try {String path = libDir.getCanonicalPath();classLoader.addPermission(path);} catch (IOException e) {}}} else {if (workDir != null) {if (libURL != null) {File libDir = new File(workDir, "WEB-INF/lib/");try {String path = libDir.getCanonicalPath();classLoader.addPermission(path);} catch (IOException e) {}}if (classesURL != null) {File classesDir = new File(workDir, "WEB-INF/classes/");try {String path = classesDir.getCanonicalPath();classLoader.addPermission(path);} catch (IOException e) {}}}}} catch (MalformedURLException e) {}} 【4.5】開啟新線程執(zhí)行類的重新載入 1)WebappLoader類支持自動(dòng)重載功能:如果倉庫中的類被重新編譯了,那么這個(gè)類會(huì)自動(dòng)重新載入,無需重啟tomcat; 2)為了實(shí)現(xiàn)這個(gè)功能:WebappLoader類使用一個(gè)線程周期性地檢查每個(gè)資源的時(shí)間戳。間隔時(shí)間由變量 checkInterval 指定,單位為妙。默認(rèn)case下, checkInterval的值為15,即每隔15秒會(huì)檢查一次是否有文件需要自動(dòng)重新載入。 getCheckInterval方法和setCheckInterval方法 用于獲取和設(shè)置間隔時(shí)間; 3)tomcat4中,WebappLoader類實(shí)現(xiàn) java.lang.Runnable 接口來支持自動(dòng)重載,源代碼如下: 對(duì)以上代碼的分析(Analysis)(run方法中while循環(huán)會(huì)執(zhí)行以下operations): o1)使線程休眠一段時(shí)間,時(shí)長由變量checkInterval指定,以秒為單位; o2)調(diào)用WebappLoader 實(shí)例的類載入器的modified方法檢查已經(jīng)載入的類是否被修改,若沒有類修改,則重新執(zhí)行循環(huán); o3)若某個(gè)已經(jīng)載入的類被修改了,則調(diào)用私有方法 notifyContext(),通知與 WebappLoader實(shí)例關(guān)聯(lián)的 Context容器重新載入相關(guān)類;
Attention)緊接上面的調(diào)用流程圖,last step 是調(diào)用Context容器的reload() 方法,其源代碼如下(本文應(yīng)用程序的Context容器的實(shí)現(xiàn)示例是StandardContext): public synchronized void reload() { //org.apache.catalina.core.StandardContext.reload()// Validate our current component stateif (!started)throw new IllegalStateException(sm.getString("containerBase.notStarted", logName()));// Make sure reloading is enabled// if (!reloadable)// throw new IllegalStateException// (sm.getString("standardContext.notReloadable"));log(sm.getString("standardContext.reloadingStarted"));// Stop accepting requests temporarilysetPaused(true);// Binding threadClassLoader oldCCL = bindThread();// Shut down our session managerif ((manager != null) && (manager instanceof Lifecycle)) {try {((Lifecycle) manager).stop();} catch (LifecycleException e) {log(sm.getString("standardContext.stoppingManager"), e);}}// Shut down the current version of all active servletsContainer children[] = findChildren();for (int i = 0; i < children.length; i++) {Wrapper wrapper = (Wrapper) children[i];if (wrapper instanceof Lifecycle) {try {((Lifecycle) wrapper).stop();} catch (LifecycleException e) {log(sm.getString("standardContext.stoppingWrapper",wrapper.getName()),e);}}}// Shut down application event listenerslistenerStop();// Clear all application-originated servlet context attributesif (context != null)context.clearAttributes();// Shut down filtersfilterStop();if (isUseNaming()) {// StartnamingContextListener.lifecycleEvent(new LifecycleEvent(this, Lifecycle.STOP_EVENT));}// Binding threadunbindThread(oldCCL);// Shut down our application class loaderif ((loader != null) && (loader instanceof Lifecycle)) {try {((Lifecycle) loader).stop();} catch (LifecycleException e) {log(sm.getString("standardContext.stoppingLoader"), e);}}// Binding threadoldCCL = bindThread();// Restart our application class loaderif ((loader != null) && (loader instanceof Lifecycle)) {try {((Lifecycle) loader).start();} catch (LifecycleException e) {log(sm.getString("standardContext.startingLoader"), e);}}// Binding threadunbindThread(oldCCL);// Create and register the associated naming context, if internal// naming is usedboolean ok = true;if (isUseNaming()) {// StartnamingContextListener.lifecycleEvent(new LifecycleEvent(this, Lifecycle.START_EVENT));}// Binding threadoldCCL = bindThread();// Restart our application event listeners and filtersif (ok) {if (!listenerStart()) {log(sm.getString("standardContext.listenerStartFailed"));ok = false;}}if (ok) {if (!filterStart()) {log(sm.getString("standardContext.filterStartFailed"));ok = false;}}// Restore the "Welcome Files" and "Resources" context attributespostResources();postWelcomeFiles();// Restart our currently defined servletsfor (int i = 0; i < children.length; i++) {if (!ok)break;Wrapper wrapper = (Wrapper) children[i];if (wrapper instanceof Lifecycle) {try {((Lifecycle) wrapper).start();} catch (LifecycleException e) {log(sm.getString("standardContext.startingWrapper",wrapper.getName()),e);ok = false;}}}// Reinitialize all load on startup servletsloadOnStartup(children);// Restart our session manager (AFTER naming context recreated/bound)if ((manager != null) && (manager instanceof Lifecycle)) {try {((Lifecycle) manager).start();} catch (LifecycleException e) {log(sm.getString("standardContext.startingManager"), e);}}// Unbinding threadunbindThread(oldCCL);// Start accepting requests againif (ok) {log(sm.getString("standardContext.reloadingCompleted"));} else {setAvailable(false);log(sm.getString("standardContext.reloadingFailed"));}setPaused(false);// Notify our interested LifecycleListenerslifecycle.fireLifecycleEvent(Context.RELOAD_EVENT, null);} 【5】WebappClassLoader類 1)web 應(yīng)用程序中負(fù)責(zé)載入類的類載入器是: org.apache.catalina.loader.WebappLoader類的實(shí)例; 2)考慮到安全性:WebappClassLoader 類不允許載入指定的某些類,這些類的名字存儲(chǔ)在一個(gè)字符串?dāng)?shù)組變量triggers中,當(dāng)前只有一個(gè)元素: private static final String[] triggers = { "javax.servlet.Servlet" }; 3)還有,某些特殊的包及其子包下的類也是不允許載入的,也不會(huì)將載入類的任務(wù)委托給系統(tǒng)類載入器去執(zhí)行: private static final String[] packageTriggers = {"javax","org.xml.sax","org.w3c.dom","org.apache.xerces","org.apache.xalan", }; (下面說明WebappClassLoader類是如何完成待加載類的緩存和載入任務(wù)的。)
【5.1】類緩存 1)為了達(dá)到更好的性能,會(huì)緩存已經(jīng)載入的類:緩存可以在本地執(zhí)行,即可以由WebappClassLoader 實(shí)例來管理它所加載并緩存的類。 1.1)java.lang.ClassLoader類會(huì)維護(hù)一個(gè)Vector對(duì)象,保存已經(jīng)載入的類,防止這些類在不使用時(shí)當(dāng)做垃圾而回收; 2)資源:每個(gè)由 WebappClassLoader 載入的類(無論是在WEB-INF/classes 目錄下還是從某個(gè)JAR 文件內(nèi)作為類文件部署), 都視為資源;資源是 org.apache.catalina.loader.ResourceEntry 類的實(shí)例;(干貨——資源的定義——每個(gè)由 WebappClassLoader 載入的類都是資源 3)資源類ResourceEntry類的源代碼定義為: public class ResourceEntry { // org.apache.catalina.loader.ResourceEntry/*** The "last modified" time of the origin file at the time this class* was loaded, in milliseconds since the epoch.*/public long lastModified = -1;/*** Binary content of the resource.*/public byte[] binaryContent = null;/*** Loaded class.*/public Class loadedClass = null;/*** URL source from where the object was loaded.*/public URL source = null;/*** URL of the codebase from where the object was loaded.*/public URL codeBase = null;/*** Manifest (if the resource was loaded from a JAR).*/public Manifest manifest = null;/*** Certificates (if the resource was loaded from a JAR).*/public Certificate[] certificates = null; }
4)所有已經(jīng)緩存的類會(huì)存儲(chǔ)在一個(gè)名為resourceEntries 的 HashMap 類型的變量中,其key值就是載入的資源名稱。那些載入失敗的類會(huì)被存儲(chǔ)到另一個(gè)名為 notFoundResources 的 HashMap 類型的變量中;
【5.2】載入類(當(dāng)載入類時(shí),WebappClassLoader類要遵守如下rules) r1)因?yàn)樗幸呀?jīng)載入的類都會(huì)緩存起來,所以載入類時(shí)要先檢查本地緩存; r2)若本地緩存中沒有,則檢查上一層緩存,即調(diào)用 java.lang.ClassLoader 類的findLoadedClass() 方法; r3)若兩個(gè)緩存中都沒有,則使用系統(tǒng)的類載入器進(jìn)行加載,防止 web 應(yīng)用程序中的類覆蓋J2EE 的類; r4)若啟用了 SecurityManager,則檢查是否允許載入該類。若該類是禁止載入的類,拋出 ClassNotFoundException異常; r5)若打開標(biāo)志位 delegate,或者待載入的類是屬于包觸發(fā)器中的包名,則調(diào)用父載入器來載入相關(guān)類。如果父載入器是null,則使用系統(tǒng)的類載入器; r6)從當(dāng)前倉庫中載入相關(guān)的類; r7)若當(dāng)前倉庫中沒有需要的類,且標(biāo)志位delegate關(guān)閉,則使用父類載入器。若父類載入器為 null, 則使用系統(tǒng)的類載入器進(jìn)行加載; r8)若仍未找到需要的類,則拋出 ClassNotFoundException 異常;
【5.3】應(yīng)用程序 1)應(yīng)用程序目的是為了說明:如何使用與某個(gè)Context容器相關(guān)聯(lián)的WebappLoader實(shí)例;(干貨——本應(yīng)用程序的目的 2)而Context接口的標(biāo)準(zhǔn)實(shí)現(xiàn):org.apache.catalina.core.StandardContext;(干貨——你也看到了,從本文起,tomcat的Context容器的標(biāo)準(zhǔn)實(shí)現(xiàn)變?yōu)榱薙tandardContext,而不是原來diy出的 SimpleContext) 3)我們需要知道的是:StandardContext類是如何與監(jiān)聽它觸發(fā)的事件(如START_EVENT and STOP_EVENT)的監(jiān)聽器協(xié)同工作的;(干貨——需要理清StandardContext類的工作原理 4)監(jiān)聽器必須實(shí)現(xiàn)接口: org.apache.catalina.lifecycle.LifecycleListener接口;而監(jiān)聽器的一個(gè)實(shí)例是 SimpleContextConfig類; 5)應(yīng)用程序源代碼: public final class Bootstrap {public static void main(String[] args) {//invoke: http://localhost:8080/Modern or http://localhost:8080/Primitive // 為了通知StandardContext 實(shí)例到哪里查找應(yīng)用程序目錄,需要設(shè)置一個(gè)名為"catalina.base"的系統(tǒng)屬性,其值為"user.dir"屬性的值;System.setProperty("catalina.base", System.getProperty("user.dir"));Connector connector = new HttpConnector(); //實(shí)例化默認(rèn)連接器Wrapper wrapper1 = new SimpleWrapper(); // 為兩個(gè)servlet類創(chuàng)建兩個(gè)Wrapper實(shí)例;wrapper1.setName("Primitive");wrapper1.setServletClass("PrimitiveServlet");Wrapper wrapper2 = new SimpleWrapper();wrapper2.setName("Modern");wrapper2.setServletClass("ModernServlet");// 創(chuàng)建StandardContext 的一個(gè)實(shí)例,設(shè)置應(yīng)用程序路徑和上下文的文檔根路徑Context context = new StandardContext();// StandardContext's start method adds a default mappercontext.setPath("/myApp");context.setDocBase("myApp");// 上面的代碼在功能上等同于下面的在server.xml 文件中的配置// <Context path="/myApp" docBase="myApp" />context.addChild(wrapper1); // 將兩個(gè)Wrapper實(shí)例添加到Context容器中context.addChild(wrapper2);// 為它們設(shè)置訪問路徑的映射關(guān)系,這樣Context 容器就能夠定位到他們// context.addServletMapping(pattern, name); context.addServletMapping("/Primitive", "Primitive");context.addServletMapping("/Modern", "Modern");// add ContextConfig. This listener is important because it configures// StandardContext (sets configured to true), otherwise StandardContext// won't start// 下一步,實(shí)例化一個(gè)監(jiān)聽器,并通過 Context容器注冊它.LifecycleListener listener = new SimpleContextConfig();((Lifecycle) context).addLifecycleListener(listener);// 接著,它會(huì)實(shí)例化WebappLoader類,并將其關(guān)聯(lián)到Context容器.// here is our loaderLoader loader = new WebappLoader();// associate the loader with the Contextcontext.setLoader(loader);// 然后,將Context容器與默認(rèn)連接器相關(guān)聯(lián),調(diào)用默認(rèn)連接器的initialize() and start()方法,// 再調(diào)用 Context容器的 start() 方法,這樣servlet容器準(zhǔn)備就緒,可以處理servlet請求了.connector.setContainer(context);try {connector.initialize();((Lifecycle) connector).start(); ((Lifecycle) context).start(); // attention: 從這一行開始(包括這一行),進(jìn)行spec analysis.// 接下來的幾行代碼僅僅顯示出資源的docBase屬性值和類載入器中所有的倉庫的名字.// now we want to know some details about WebappLoaderWebappClassLoader classLoader = (WebappClassLoader) loader.getClassLoader();System.out.println("Resources' docBase: " + ((ProxyDirContext)classLoader.getResources()).getDocBase());String[] repositories = classLoader.findRepositories();for (int i=0; i<repositories.length; i++) {System.out.println(" repository: " + repositories[i]);}// 最后,用戶輸入任意鍵后,程序退出.// make the application wait until we press a key.System.in.read();((Lifecycle) context).stop();}catch (Exception e) {e.printStackTrace();}} } Conclusion) C1)web 應(yīng)用程序中的載入器,或一個(gè)簡單的載入器,都是 Catalina中最重要的組件;(干貨——載入器是Catalina中最重要的組件) C2)載入器負(fù)責(zé)載入應(yīng)用程序所需要的類,因此會(huì)使用一個(gè)內(nèi)部類載入器:這個(gè)內(nèi)部類載入器是一個(gè)自定義類,tomcat使用這個(gè)自定義的類載入器對(duì) web應(yīng)用程序上下文中要載入的類進(jìn)行一些約束; C3)此外,自定義類載入器可以支持對(duì)載入類的緩存和對(duì)一個(gè)或多個(gè)被修改的類的自動(dòng)重載;
6)打印結(jié)果 E:\bench-cluster\cloud-data-preprocess\HowTomcatWorks\src>java -cp .;lib/servlet.jar;lib/catalina_4_1_24.jar;lib/catalina-5.5.4.jar;lib/naming-resources.jar;lib/naming-common.jar;lib/commons-collectio ns.jar;lib/catalina.jar;E:\bench-cluster\cloud-data-preprocess\HowTomcatWorks\webroot com.tomcat.chapter8.startup.Bootstrap HttpConnector Opening server socket on all host IP addresses HttpConnector[8080] Starting background thread WebappLoader[/myApp]: Deploying class repositories to work directory E:\bench-cluster\cloud-data-preprocess\HowTomcatWorks\src\work\_\_\myApp Starting Wrapper Primitive Starting Wrapper Modern StandardManager[/myApp]: Seeding random number generator class java.security.SecureRandom StandardManager[/myApp]: Seeding of random number generator has been completed Resources' docBase: E:\bench-cluster\cloud-data-preprocess\HowTomcatWorks\src\myApp // this line.Stopping wrapper Primitive Stopping wrapper ModernE:\bench-cluster\cloud-data-preprocess\HowTomcatWorks\src>Attention)這里還差了一個(gè)打印info, 在this line 下面一行: repository: /WEB-INF/classes/ ;

總結(jié)

以上是生活随笔為你收集整理的tomcat(8)载入器的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。