摘要:加載器之間父子關(guān)系不是通過(guò)類繼承的方式,而是通過(guò)對(duì)象變量的方式,來(lái)實(shí)現(xiàn)的。調(diào)用的方法,將設(shè)置為所有的獲取各個(gè)類加載器相應(yīng)的資源配置文件這些是具體配置在配置文件中的。
摘要
一直以來(lái)想看一下tomcat的源碼,可以從頭到尾理解一遍在整個(gè)web請(qǐng)求的處理過(guò)程中,容器完成的功能,容器怎么把一個(gè)url的請(qǐng)求的處理遞交給了servlet,容器的pipeline是怎么設(shè)計(jì)的,容器的filter怎么實(shí)現(xiàn)的,如何維護(hù)session,好多好多的問(wèn)題,有幸發(fā)現(xiàn)一篇文章Tomcat7.0源碼分析,讓我得以窺見容器的內(nèi)部實(shí)現(xiàn),從源碼之中能按著作者的思路來(lái)理解和閱讀。讀完之后,有了一些理解,但是不是很深刻,借著這個(gè)機(jī)會(huì),寫了這篇總結(jié),溫故而知新。讀源代碼,就是要反復(fù)得讀,每次讀完都會(huì)先釋然,又會(huì)產(chǎn)生新的疑惑,再帶著問(wèn)題重讀源碼,如此反復(fù),脈絡(luò)才會(huì)越來(lái)越清晰。
整個(gè)文章按如下幾個(gè)主題:
tomcat的類加載體系
server.xml文件的加載與解析
生命周期管理
停止與啟動(dòng)服務(wù)
請(qǐng)求原理分析
session管理分析
TOMCAT的類加載體系Tomcat為了webapp之間以及app和容器之間的資源(jar包)隔離,是通過(guò)自定義類加載器的方式來(lái)實(shí)現(xiàn)的。首先我們看一下jdk的類加載器結(jié)構(gòu)樹(為了方便起見,將tomcat自定義的類加載器也放上去了)
這個(gè)粗看會(huì)有一點(diǎn)困惑,jdk不是父親委派加載機(jī)制嗎?為什么這里的AppClassLoader,ExtClassLoad不是繼承關(guān)系呢?其實(shí)這是一個(gè)誤解。加載器之間父子關(guān)系不是通過(guò)類繼承的方式,而是通過(guò)對(duì)象變量的方式,來(lái)實(shí)現(xiàn)的。
private ClassLoader(Void unused, ClassLoader parent) { this.parent = parent;
然后在加載類的時(shí)候,會(huì)嘗試先讓parent來(lái)加載
protected Class> loadClass(String name, boolean resolve) throws ClassNotFoundException { synchronized (getClassLoadingLock(name)) { // First, check if the class has already been loaded Class> c = findLoadedClass(name); if (c == null) { long t0 = System.nanoTime(); try { if (parent != null) { c = parent.loadClass(name, false); } else { c = findBootstrapClassOrNull(name); }
這個(gè)就是父親委托加載機(jī)制的由來(lái)。之前找不到JDK的ExtClassLoader和AppClassLoader,后來(lái)才發(fā)現(xiàn)這兩個(gè)類原來(lái)是rt.jar包sum.msic.Launcher的內(nèi)部類. Bootstrap加載sum.boot.class.path下的類,ExtClassPath加載java.ext.dirs下的類,AppClassLoader加載java.class.path下的類。當(dāng)然也可以在運(yùn)行時(shí)通過(guò)參數(shù)( -XBootclasspath , -Djava.ext.dirs , -cp)分別指定。
好了,現(xiàn)在讓我們回到tomcat的類加載器上來(lái),看類樹上,tomcat自定義了WebappClassLoader和standardClassLoader,簡(jiǎn)單的看,前者是給web應(yīng)用用的,后者是給tomcat容器本身用的。我們通過(guò)原來(lái)來(lái)進(jìn)行分析。
Bootstrap.java public void init() throws Exception { // Set Catalina path setCatalinaHome(); setCatalinaBase(); initClassLoaders(); Thread.currentThread().setContextClassLoader(catalinaLoader); SecurityClassLoad.securityClassLoad(catalinaLoader); // Load our startup class and call its process() method if (log.isDebugEnabled()) log.debug("Loading startup class"); //調(diào)用Catalina的setParentClassLoader方法,將sharedLoader設(shè)置為所有WebappClassLoader的parent Class> startupClass = catalinaLoader.loadClass ("org.apache.catalina.startup.Catalina"); Object startupInstance = startupClass.newInstance(); // Set the shared extensions class loader if (log.isDebugEnabled()) log.debug("Setting startup class properties"); String methodName = "setParentClassLoader"; Class> paramTypes[] = new Class[1]; paramTypes[0] = Class.forName("java.lang.ClassLoader"); Object paramValues[] = new Object[1]; paramValues[0] = sharedLoader; Method method = startupInstance.getClass().getMethod(methodName, paramTypes); method.invoke(startupInstance, paramValues); catalinaDaemon = startupInstance; } 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) { log.error("Class loader creation threw exception", t); System.exit(1); } } private ClassLoader createClassLoader(String name, ClassLoader parent) throws Exception { //獲取各個(gè)類加載器相應(yīng)的資源配置文件(common.loader,server.loader,shared.loader) //這些是具體配置在catalina.properties配置文件中的。以本機(jī)配置為例 //common.loader=${catalina.base}/lib,${catalina.base}/lib/*.jar,${catalina.home}/lib,${catalina.home}/lib/*.jar //server.loader= //shared.loader= String value = CatalinaProperties.getProperty(name + ".loader"); if ((value == null) || (value.equals(""))) return parent; ArrayListrepositoryLocations = new ArrayList (); ArrayList repositoryTypes = new ArrayList (); int i; StringTokenizer tokenizer = new StringTokenizer(value, ","); while (tokenizer.hasMoreElements()) { String repository = tokenizer.nextToken(); // Local repository boolean replace = false; String before = repository; while ((i=repository.indexOf(CATALINA_HOME_TOKEN))>=0) { replace=true; if (i>0) { repository = repository.substring(0,i) + getCatalinaHome() + repository.substring(i+CATALINA_HOME_TOKEN.length()); } else { repository = getCatalinaHome() + repository.substring(CATALINA_HOME_TOKEN.length()); } } while ((i=repository.indexOf(CATALINA_BASE_TOKEN))>=0) { replace=true; if (i>0) { repository = repository.substring(0,i) + getCatalinaBase() + repository.substring(i+CATALINA_BASE_TOKEN.length()); } else { repository = getCatalinaBase() + repository.substring(CATALINA_BASE_TOKEN.length()); } } if (replace && log.isDebugEnabled()) log.debug("Expanded " + before + " to " + repository); // Check for a JAR URL repository try { new URL(repository); repositoryLocations.add(repository); repositoryTypes.add(ClassLoaderFactory.IS_URL); continue; } catch (MalformedURLException e) { // Ignore } if (repository.endsWith("*.jar")) { repository = repository.substring (0, repository.length() - "*.jar".length()); repositoryLocations.add(repository); repositoryTypes.add(ClassLoaderFactory.IS_GLOB); } else if (repository.endsWith(".jar")) { repositoryLocations.add(repository); repositoryTypes.add(ClassLoaderFactory.IS_JAR); } else { repositoryLocations.add(repository); repositoryTypes.add(ClassLoaderFactory.IS_DIR); } } String[] locations = repositoryLocations.toArray(new String[0]); Integer[] types = repositoryTypes.toArray(new Integer[0]); //創(chuàng)建ClassLoader ClassLoader classLoader = ClassLoaderFactory.createClassLoader (locations, types, parent); // Retrieving MBean server MBeanServer mBeanServer = null; if (MBeanServerFactory.findMBeanServer(null).size() > 0) { mBeanServer = MBeanServerFactory.findMBeanServer(null).get(0); } else { mBeanServer = ManagementFactory.getPlatformMBeanServer(); } // Register the server classloader ObjectName objectName = new ObjectName("Catalina:type=ServerClassLoader,name=" + name); mBeanServer.registerMBean(classLoader, objectName); return classLoader; } //回看init方法,對(duì)應(yīng)的securityClassLoad就是使用catalinaLoader完成tomcat核心類的加載的 public static void securityClassLoad(ClassLoader loader) throws Exception { if( System.getSecurityManager() == null ){ return; } loadCorePackage(loader); loadLoaderPackage(loader); loadSessionPackage(loader); loadUtilPackage(loader); loadJavaxPackage(loader); loadCoyotePackage(loader); loadTomcatPackage(loader); }
此處,完成了commonLoader,catalinaLoader和sharedLoader三個(gè)加載器的初始化,他們均是StandardClassLoader的實(shí)例,同時(shí)我們可以看到這三者之間的關(guān)系為
接下去,我們來(lái)看WebappLoader
StandardContext.java protected synchronized void startInternal() throws LifecycleException { //省略前邊代碼 if (getLoader() == null) { WebappLoader webappLoader = new WebappLoader(getParentClassLoader()); webappLoader.setDelegate(getDelegate()); setLoader(webappLoader); } //省略中間代碼 if ((loader != null) && (loader instanceof Lifecycle)) ((Lifecycle) loader).start(); //省略后邊代碼 } WebappLoader.java protected void startInternal() throws LifecycleException { if (log.isDebugEnabled()) log.debug(sm.getString("webappLoader.starting")); if (container.getResources() == null) { log.info("No resources for " + container); setState(LifecycleState.STARTING); return; } // Register a stream handler factory for the JNDI protocol URLStreamHandlerFactory streamHandlerFactory = new DirContextURLStreamHandlerFactory(); if (first) { first = false; try { URL.setURLStreamHandlerFactory(streamHandlerFactory); } catch (Exception e) { // Log and continue anyway, this is not critical log.error("Error registering jndi stream handler", e); } catch (Throwable t) { // This is likely a dual registration log.info("Dual registration of jndi stream handler: " + t.getMessage()); } } // Construct a class loader based on our current repositories list try { classLoader = createClassLoader(); classLoader.setResources(container.getResources()); classLoader.setDelegate(this.delegate); classLoader.setSearchExternalFirst(searchExternalFirst); if (container instanceof StandardContext) { classLoader.setAntiJARLocking( ((StandardContext) container).getAntiJARLocking()); classLoader.setClearReferencesStatic( ((StandardContext) container).getClearReferencesStatic()); classLoader.setClearReferencesStopThreads( ((StandardContext) container).getClearReferencesStopThreads()); classLoader.setClearReferencesStopTimerThreads( ((StandardContext) container).getClearReferencesStopTimerThreads()); classLoader.setClearReferencesThreadLocals( ((StandardContext) container).getClearReferencesThreadLocals()); } for (int i = 0; i < repositories.length; i++) { classLoader.addRepository(repositories[i]); } // Configure our repositories setRepositories(); setClassPath(); setPermissions(); ((Lifecycle) classLoader).start(); // Binding the Webapp class loader to the directory context DirContextURLStreamHandler.bind(classLoader, this.container.getResources()); StandardContext ctx=(StandardContext)container; String path = ctx.getPath(); if (path.equals("")) { path = "/"; } ObjectName cloname = new ObjectName (MBeanUtils.getDomain(ctx) + ":type=WebappClassLoader,path=" + path + ",host=" + ctx.getParent().getName()); Registry.getRegistry(null, null) .registerComponent(classLoader, cloname, null); } catch (Throwable t) { log.error( "LifecycleException ", t ); throw new LifecycleException("start: ", t); } setState(LifecycleState.STARTING); } private WebappClassLoader createClassLoader() throws Exception { Class> clazz = Class.forName(loaderClass); WebappClassLoader classLoader = null; if (parentClassLoader == null) { parentClassLoader = container.getParentClassLoader(); } Class>[] argTypes = { ClassLoader.class }; Object[] args = { parentClassLoader }; Constructor> constr = clazz.getConstructor(argTypes); classLoader = (WebappClassLoader) constr.newInstance(args); return classLoader; }
此處完成了WebappClassLoader的初始化,可見這個(gè)類加載器是對(duì)應(yīng)一個(gè)Context的,即一個(gè)web應(yīng)用。其parent應(yīng)該是sharedLoader.
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://www.ezyhdfw.cn/yun/67795.html
摘要:一般來(lái)說(shuō)會(huì)在項(xiàng)目中的中添加一個(gè),里面配置這個(gè)配置文件在部署時(shí),會(huì)被復(fù)制到中去,并被重新命名為項(xiàng)目名。由于使用的項(xiàng)目中配置了來(lái)加載第三方庫(kù),配置了我自定義加載器后,這些包都無(wú)法加載了。我往開發(fā)機(jī)上打加密補(bǔ)丁的時(shí)候,總是無(wú)故消失,不知道為什么 為了這個(gè)功能提了很多問(wèn)題都沒(méi)能得到解答,最后終于自己搞定了,現(xiàn)在把大體步驟總結(jié)下,供大家參考指正 分三種情況:(1)沒(méi)有配置spring自動(dòng)掃描(2...
時(shí)間:2017年12月01日星期五說(shuō)明:本文部分內(nèi)容均來(lái)自慕課網(wǎng)。@慕課網(wǎng):http://www.imooc.com 教學(xué)源碼:無(wú) 學(xué)習(xí)源碼:https://github.com/zccodere/s... 第一章:課程介紹 1-1 課程介紹 熱部署的使用場(chǎng)景 本地調(diào)式 線上發(fā)布 熱部署的使用優(yōu)點(diǎn) 無(wú)論本地還是線上,都適用 無(wú)需重啟服務(wù)器:提高開發(fā)、調(diào)式效率、提升發(fā)布、運(yùn)維效率、降低運(yùn)維成本 前置...
摘要:標(biāo)準(zhǔn)擴(kuò)展類加載器,它負(fù)責(zé)加載或由系統(tǒng)變量指定位置中的類庫(kù)加載到內(nèi)存中。系統(tǒng)類加載器,它負(fù)責(zé)將類路徑中的類庫(kù)加載到內(nèi)存。 類加載機(jī)制大家應(yīng)該已經(jīng)非常熟悉了,采取雙親委派機(jī)制,當(dāng)加載一個(gè)類時(shí),首先將加載任務(wù)委托給父類加載器,依次遞歸,如果父類加載器可以完成加載任務(wù),就成功返回;如果父類無(wú)法加載,才由自己加載。 雙親委派機(jī)制的作用:防止內(nèi)存中出現(xiàn)多份相同的字節(jié)碼。 其他規(guī)則:1.隱式加載:...
閱讀 988·2021-11-22 13:54
閱讀 2938·2021-09-28 09:36
閱讀 3038·2019-08-30 15:55
閱讀 2005·2019-08-30 15:44
閱讀 597·2019-08-29 12:31
閱讀 2616·2019-08-28 18:18
閱讀 1266·2019-08-26 13:58
閱讀 1498·2019-08-26 13:44