读Tomcat的源码也算是有一段时间了吧,感觉读的也是断断续续的,这次写一篇比较综合性的吧,来看看Tomcat的整体ClassLoader体系的设计。。。。
在具体的涉及到源码之前,先来一张图来整体的描述一下整体的结构吧:
这张图在以前的文章应该也出现过。。。首先整个Tomcat的classLoader分为了两条线,左边的一条线为catalinaLoader,这个是Tomcat服务器专用的,用于加载Tomcat服务器本身的class,右边的一条线则为web应用程序用的,每一个web应用程序都有自己专用的WebappClassLoader,用于加载属于自己应用程序的资源,例如/web-inf/lib下面的jar包,classes里面的class文件。。。
然后上面也体现了整体的classLoader的双亲继承关系。。。。
好啦,接下来来开始进入代码部分吧。。在整个tomcat的启动入口部分bootstrap对象的main函数中,代码如下:
Bootstrap bootstrap = new Bootstrap(); //这里创建当前Bootstarp类型的对象 try { bootstrap.init(); //初始化,这里有创建classLoader,其实这里主要是创建org.apache.catalina.startup.Catalina对象并调用setParentClassLoader设置classLoader,用的是shareLoader } catch (Throwable t) { handleThrowable(t); t.printStackTrace(); return; } daemon = bootstrap; //保存当前引用到静态变量
这里主要是对bootstrap的初始化,这里面就会涉及到commonLoader,catalinaLoader与sharedLoader的创建,来看代码吧:
//初始化当前的tomcat后台,主要是创建org.apache.catalina.startup.Catalina对象,并且设置它的classLoader为catalinaLoader public void init() throws Exception { initClassLoaders(); //先初始化classLoader,包括common,catalina以及shared Thread.currentThread().setContextClassLoader(catalinaLoader); //设置当前线程classLoader SecurityClassLoad.securityClassLoad(catalinaLoader); //安全classLoader? // Load our startup class and call its process() method if (log.isDebugEnabled()) log.debug("Loading startup class"); Class<?> startupClass = catalinaLoader.loadClass //这里加载catalina类型是用catalinaloader来加载的 ("org.apache.catalina.startup.Catalina"); //获取org.apache.catalina.startup.Catalina类型 Object startupInstance = startupClass.newInstance(); //创建org.apache.catalina.startup.Catalina对象 // 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; //传进这个classLoader Method method = startupInstance.getClass().getMethod(methodName, paramTypes); method.invoke(startupInstance, paramValues); //调用刚刚创建的org.apache.catalina.startup.Catalina对象的setParentClassLoader设置classLoader,shareloader catalinaDaemon = startupInstance; //将这个启动的实例保存起来,这里引用的是catalina的类型的对象 }
这里有几个比较重要的部分吧,首先调用了initClassLoader方法来创建了上面提到的三个classLoader,然后这里还要注意,将当前的线程classLoader设置为了catalinaLoader,这个待会会看到具体干嘛用。。。接着就是调用创建的catalina对象的setParent方法,将sharedLoader传进去,这个也很重要。。一会就知道了。。。
这里还有一个比较重要的地方,catalina的class是用catalinaLoader加载的。。。
好了,先暂时搁置catalina部分的内容,来看看initClassLoader方法做了啥吧:
//初始化classLoader,这里分别创建了3个classLoader,common,catalina和sharedLoader,其中common没有父亲,另外两个的父亲是common private void initClassLoaders() { try { commonLoader = createClassLoader("common", null); //创建common的classloader java.net.URLClassLoader 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); //java.net.URLClassLoader sharedLoader = createClassLoader("shared", commonLoader); //java.net.URLClassLoader } catch (Throwable t) { handleThrowable(t); log.error("Class loader creation threw exception", t); System.exit(1); } }
这里就可以看到创建了commonLoader,catalinaLoader与sharedLoader,而且可以很清楚的看到他们之间的双亲结构。。。这里我们来看看createClassLoader方法是怎么搞的吧:
//具体的创建classLoader的方法,第一个参数是当前要创建的loader的名字,第二个是这个loader的双亲loader的父亲 private ClassLoader createClassLoader(String name, ClassLoader parent) throws Exception { //首先获取要载入的资源路径 String value = CatalinaProperties.getProperty(name + ".loader"); //"${catalina.base}/lib","${catalina.base}/lib/*.jar","${catalina.home}/lib","${catalina.home}/lib/*.jar" if ((value == null) || (value.equals(""))) //如果没有这个classLoader特定的资源,那么就用parent就好了 return parent; value = replace(value); //这里主要是获取当前classLoader加载资源的路径 List<Repository> repositories = new ArrayList<>(); String[] repositoryPaths = getPaths(value); //将里面的路径区分开来 for (String repository : repositoryPaths) { //遍历所有的要载入的资源路径 // Check for a JAR URL repository /* * G:\work eclipse workspace\tomcat8/lib G:\work eclipse workspace\tomcat8/lib/*.jar G:\work eclipse workspace\tomcat8/lib G:\work eclipse workspace\tomcat8/lib/*.jar */ try { @SuppressWarnings("unused") URL url = new URL(repository); // 创建路径的url引用 repositories.add( new Repository(repository, RepositoryType.URL)); //将他们加入repositories continue; } catch (MalformedURLException e) { // Ignore } // Local repository if (repository.endsWith("*.jar")) { //如果是*.jar结尾的,那么只取前面的文件夹路径就好了 repository = repository.substring (0, repository.length() - "*.jar".length()); repositories.add( new Repository(repository, RepositoryType.GLOB)); } else if (repository.endsWith(".jar")) { //如果是jar结尾的,那么表示是个jar包 repositories.add( new Repository(repository, RepositoryType.JAR)); } else { repositories.add( new Repository(repository, RepositoryType.DIR)); //其他的就是路径了 } } return ClassLoaderFactory.createClassLoader(repositories, parent); //创建classLoder }
第一个参数是要创建的classLoader的名字,第二个是该classLoader的父亲,对于commonLoader,它的父亲为null,这里首先做的是获取指定要创建的classLoader的配置信息,其实这里也就是该classLoder要载入的资源。。
这里也可以看到,如果classLoader没有自己要特定载入的资源的话,那么将不会创建,直接用parent就好了。。。
有的时候,对于sharedLoader与catalinaLoader就没有特定要创建的。。。在这种情况下就catalina,sharedLoader与commonLoader就为同一个对象。。。
当然对于最顶层的commonLoader,它是由自己要载入的资源的,路径如下: //"${catalina.base}/lib","${catalina.base}/lib/*.jar","${catalina.home}/lib","${catalina.home}/lib/*.jar"
这也就是commonLoader要载入的主要的资源路径,因此也可以知道主要就是载入Tomcat服务器根路径下lib文件夹里面的资源。。。。最后调用ClassLoaderFactory的createClassLoader方法的时候将要载入的资源的路径引用穿件去就好了。。最终创建的classLoader其实就是类库的URLClassLoader,也就是在创建URLClassLoader对象的时候,将刚刚提到的资源的url引用传入就好了。。。。
那么到这里为止,commonLoader,catalinaLoader与sharedLoader的创建就算是比较清楚了吧。。。
接到开始的话题,在bootStrap对象的初始化方法中将当前的线程classLoader设置为了catalinaLoader,然后将创建的catalina对象的parentClassLoader设置为了sharedLoader。。。嗯。。记住。。。
好啦,接下来进入catalina对象部分,我们知道bootstrap对象的启动,实际上是调用catalina对象的start方法来具体工作的。。那么接下来进入catalina对象的start方法吧:
//创建一个tomcatServer的实例,在bootstrap对象中的启动会调用catalina的start方法来启动tomcat public void start() { if (getServer() == null) { load(); //加载server,这里其实主要是解析xml文件,读取里面的元素定义,用于生成server对象,并且初始化server对象 } if (getServer() == null) { log.fatal("Cannot start server. Server instance is not configured."); return; } long t1 = System.nanoTime(); // Start the new server try { getServer().start(); //调用server的start,最终启动tomcat服务器,其实server要做的是启动里面的service } catch (LifecycleException e) { log.fatal(sm.getString("catalina.serverStartFail"), e); try { getServer().destroy(); } catch (LifecycleException e1) { log.debug("destroy() failed for failed Server ", e1); } return; } long t2 = System.nanoTime(); if(log.isInfoEnabled()) { log.info("Server startup in " + ((t2 - t1) / 1000000) + " ms"); } // Register shutdown hook if (useShutdownHook) { if (shutdownHook == null) { shutdownHook = new CatalinaShutdownHook(); } Runtime.getRuntime().addShutdownHook(shutdownHook); // If JULI is being used, disable JULI‘s shutdown hook since // shutdown hooks run in parallel and log messages may be lost // if JULI‘s hook completes before the CatalinaShutdownHook() LogManager logManager = LogManager.getLogManager(); if (logManager instanceof ClassLoaderLogManager) { ((ClassLoaderLogManager) logManager).setUseShutdownHook( false); } } if (await) { await(); //阻塞当前线程 stop(); } }
这部分要做的事情,其实是创建server对象,然后启动就好了。。。然后catalina对象将会阻塞当前线程。。
那么来看看具体是怎么创建Server对象的吧:
//用于创建server对象 public void load() { long t1 = System.nanoTime(); initDirs(); //初始化一些目录参数 // Before digester - it may be needed initNaming(); // Create and execute our Digester Digester digester = createStartDigester(); //创建处理xml文件的对象,并会生成相应的处理规则,例如如何创建server对象,主要是处理server.xml InputSource inputSource = null; InputStream inputStream = null; File file = null; try { file = configFile(); //获取server.xml inputStream = new FileInputStream(file); //读取server.xml inputSource = new InputSource(file.toURI().toURL().toString()); } catch (Exception e) { if (log.isDebugEnabled()) { log.debug(sm.getString("catalina.configFail", file), e); } } if (inputStream == null) { try { inputStream = getClass().getClassLoader() .getResourceAsStream(getConfigFile()); inputSource = new InputSource (getClass().getClassLoader() .getResource(getConfigFile()).toString()); } catch (Exception e) { if (log.isDebugEnabled()) { log.debug(sm.getString("catalina.configFail", getConfigFile()), e); } } } // This should be included in catalina.jar // Alternative: don‘t bother with xml, just create it manually. if( inputStream==null ) { try { inputStream = getClass().getClassLoader() .getResourceAsStream("server-embed.xml"); inputSource = new InputSource (getClass().getClassLoader() .getResource("server-embed.xml").toString()); } catch (Exception e) { if (log.isDebugEnabled()) { log.debug(sm.getString("catalina.configFail", "server-embed.xml"), e); } } } if (inputStream == null || inputSource == null) { if (file == null) { log.warn(sm.getString("catalina.configFail", getConfigFile() + "] or [server-embed.xml]")); } else { log.warn(sm.getString("catalina.configFail", file.getAbsolutePath())); if (file.exists() && !file.canRead()) { log.warn("Permissions incorrect, read permission is not allowed on the file."); } } return; } try { inputSource.setByteStream(inputStream); digester.push(this); //这里先将当前对象放到Digester的栈底,但会server生成之后会调用当前对象的setServer方法来保存起来 digester.parse(inputSource); } catch (SAXParseException spe) { log.warn("Catalina.start using " + getConfigFile() + ": " + spe.getMessage()); return; } catch (Exception e) { log.warn("Catalina.start using " + getConfigFile() + ": " , e); return; } finally { try { inputStream.close(); } catch (IOException e) { // Ignore } } getServer().setCatalina(this); //设置sever的catalina对象 getServer().setCatalinaHome(Bootstrap.getCatalinaHomeFile()); //设置server的目录 getServer().setCatalinaBase(Bootstrap.getCatalinaBaseFile()); // Stream redirection initStreams(); // Start the new server try { getServer().init(); //初始化创建的server对象,其实这里主要是初始化service } catch (LifecycleException e) { if (Boolean.getBoolean("org.apache.catalina.startup.EXIT_ON_INIT_FAILURE")) { throw new java.lang.Error(e); } else { log.error("Catalina.start", e); } } long t2 = System.nanoTime(); if(log.isInfoEnabled()) { log.info("Initialization processed in " + ((t2 - t1) / 1000000) + " ms"); } }
这里其实主要就是创建Digester对象,然后用它来解析conf/server.xml文件,根据配置的信息来创建相应的对象,例如server对象,因此这个Digester就算是很重要的啦。。。来看看它的创建吧:
protected Digester createStartDigester() { long t1=System.currentTimeMillis(); // Initialize the digester Digester digester = new Digester(); //这个对象用于解析处理xml文件 digester.setValidating(false); digester.setRulesValidation(true); HashMap<Class<?>, List<String>> fakeAttributes = new HashMap<>(); ArrayList<String> attrs = new ArrayList<>(); attrs.add("className"); fakeAttributes.put(Object.class, attrs); digester.setFakeAttributes(fakeAttributes); digester.setUseContextClassLoader(true); //将useContextClassLoader参数设置为true,那么待会将会用预先保存的线程classLoader来载入class,这里其实就是catalinaloader // Configure the actions we will be using digester.addObjectCreate("Server", "org.apache.catalina.core.StandardServer", "className"); //创建一个创建server对象的规则 digester.addSetProperties("Server"); //创建一个设置属性的规则,那么创建完了server对象之后就会设置配置文件后面的属性 digester.addSetNext("Server", "setServer", "org.apache.catalina.Server"); //当server元素执行完了之后,执行的操作。。。这里其实是调用前面那个对象的方法,这里其实就是调用catalina对象的setServer方法 digester.addObjectCreate("Server/GlobalNamingResources", "org.apache.catalina.deploy.NamingResourcesImpl"); //创建全局名字资源 digester.addSetProperties("Server/GlobalNamingResources"); digester.addSetNext("Server/GlobalNamingResources", "setGlobalNamingResources", "org.apache.catalina.deploy.NamingResourcesImpl"); //其实这里实在server对象上面设置resource digester.addObjectCreate("Server/Listener", null, // MUST be specified in the element "className"); //根据xml文件配置来创建listener digester.addSetProperties("Server/Listener"); //设置listener对象配置的属性 digester.addSetNext("Server/Listener", "addLifecycleListener", "org.apache.catalina.LifecycleListener"); //这里其实是在server对象上面添加lifecyclelistener digester.addObjectCreate("Server/Service", "org.apache.catalina.core.StandardService", "className"); //创建service对象 digester.addSetProperties("Server/Service"); //设置service的属性 digester.addSetNext("Server/Service", "addService", //在server对象上面调用addService方法 "org.apache.catalina.Service"); //在server上面添加调用addService方法 //为service创建listener,不一定有 digester.addObjectCreate("Server/Service/Listener", null, // MUST be specified in the element "className"); //根据配置文件参数来创建service的listener digester.addSetProperties("Server/Service/Listener"); digester.addSetNext("Server/Service/Listener", "addLifecycleListener", "org.apache.catalina.LifecycleListener"); //Executor //创建service的executor,这个不一定有 digester.addObjectCreate("Server/Service/Executor", "org.apache.catalina.core.StandardThreadExecutor", "className"); digester.addSetProperties("Server/Service/Executor"); digester.addSetNext("Server/Service/Executor", "addExecutor", "org.apache.catalina.Executor"); //为servie添加connector规则 digester.addRule("Server/Service/Connector", new ConnectorCreateRule()); //这个规则会创建connector,而且如果有属性执行executor的话,还会设置connector的executor digester.addRule("Server/Service/Connector", //<Connector port="8080" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443"/> new SetAllPropertiesRule(new String[]{"executor"})); //这里会将所有的属性都设置,除了executor digester.addSetNext("Server/Service/Connector", "addConnector", "org.apache.catalina.connector.Connector"); //在service上面添加这个connector //这里是设置connector的listener digester.addObjectCreate("Server/Service/Connector/Listener", null, // MUST be specified in the element "className"); digester.addSetProperties("Server/Service/Connector/Listener"); digester.addSetNext("Server/Service/Connector/Listener", "addLifecycleListener", "org.apache.catalina.LifecycleListener"); // Add RuleSets for nested elements digester.addRuleSet(new NamingRuleSet("Server/GlobalNamingResources/")); digester.addRuleSet(new EngineRuleSet("Server/Service/")); //engine元素的定义的处理,这里主要是创建eingie digester.addRuleSet(new HostRuleSet("Server/Service/Engine/")); //engine里面host的定义 digester.addRuleSet(new ContextRuleSet("Server/Service/Engine/Host/")); //一些context的配置处理 addClusterRuleSet(digester, "Server/Service/Engine/Host/Cluster/"); digester.addRuleSet(new NamingRuleSet("Server/Service/Engine/Host/Context/")); // When the ‘engine‘ is found, set the parentClassLoader. digester.addRule("Server/Service/Engine", new SetParentClassLoaderRule(parentClassLoader)); //如果有发现engine,那么设置container(Engine对象)的parentClassloader,shareloader addClusterRuleSet(digester, "Server/Service/Engine/Cluster/"); long t2=System.currentTimeMillis(); if (log.isDebugEnabled()) { log.debug("Digester for server.xml created " + ( t2-t1 )); } return (digester); }
这里可以其实主要就是对于server.xml文件的处理规则,有几点需要搞清楚:
(1) digester.setUseContextClassLoader(true);这句代码,将会对Digester进行设置,待会创建对象的时候加载class的时候将会用到当前线程的classLoader,这个在前面已经强调,当前线程classLoader被设置为了catalinaLoader
(2)来看看是如何创建对象的吧,这里对于对象的创建将会创建一个ObjectCreateRule规则,这里来看看这个规则是怎么处理的吧:
//当遇到需要创建元素的element的时候要做的事情 @Override public void begin(String namespace, String name, Attributes attributes) throws Exception { // Identify the name of the class to instantiate String realClassName = className; if (attributeName != null) { String value = attributes.getValue(attributeName); //看是否有指定的属性 if (value != null) { realClassName = value; } } if (digester.log.isDebugEnabled()) { digester.log.debug("[ObjectCreateRule]{" + digester.match + "}New " + realClassName); } if (realClassName == null) { throw new NullPointerException("No class name specified for " + namespace + " " + name); } // Instantiate the new object and push it on the context stack Class<?> clazz = digester.getClassLoader().loadClass(realClassName); //用classloader来载入class Object instance = clazz.newInstance(); //创建这个对象 digester.push(instance); //放入digester }
这里其实主要就是调用classLoader的loadClass方法来加载class,然后创建对象。。。那么这里用的是什么classLoader呢。。?来看看:
public ClassLoader getClassLoader() { if (this.classLoader != null) { return (this.classLoader); } if (this.useContextClassLoader) { //这里一般都是用这里,线程classLoader,在bootstarp里面设置为catalinaLoader ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); if (classLoader != null) { return (classLoader); } } return (this.getClass().getClassLoader()); }
到这里就很清楚了吧,前面已经提到了,这里将会采用当前线程classLoader,也就是catalinaLoader,这里也就可以知道,对于Server对象所有东西的创建,其class的都是用的catalinaLoader。。
嗯。。。这也就是为啥说catalinaLoader是Tomcat服务器专用的了,整个Tomcat服务器的重要对象创建用到的loader都是它。。。
(3)注意一句代码: digester.addRule("Server/Service/Engine",
new SetParentClassLoaderRule(parentClassLoader)); //如果有发现engine,那么设置container(Engine对象)的parentClassloader,shareloader
它创建的规则将会将创建的engine对象的parentClassLoader设置为当前catalina对象的parentClassLoader,前面就已经说过了,当前catalina对象的parentClassLoader被设置为了sharedLoader,那么表示engine对象的parentClassLoader也会是sharedLoader。。。
(4)我们来看看创建host对象的用到的规则:
//创建host对象 digester.addObjectCreate(prefix + "Host", "org.apache.catalina.core.StandardHost", //创建host对象的配置 "className"); digester.addSetProperties(prefix + "Host"); digester.addRule(prefix + "Host", new CopyParentClassLoaderRule()); //会将host的parentClassloader设置为engine的,engine被设置为sharedloader
这里可以看到在host的创建中加了一个CopyParentClassLoaderRule规则,它的作用是将当前对象的parentClassLoader设置为上一层对象的parentClassLoader,host外面就是engine,那么可以知道将会将host的parentClassLoader也设置为sharedLoader。。。
好了,到这里catalina部分对classLoader的处理就差不多了。。。
到现在位置,我们知道在tomcat的启动的时候,载入tomcat系统相关的额class都是采用的catalinaLoader
然后还有在catalina对象中将parentClassLoader设置为了sharedLoader,然后engine的parentClassLoader也被设置为了sharedLoader,然后host对象的parentClassLoader也被设置为了sharedLoader,嗯,这个为以后context部分的内容埋下了伏笔。。。
好了,接下来就开始进入Context部分吧,我们知道一个Context基本上就代表了web应用程序,前面提到,每一个web应用程序都有自己专有的classLoader,那么接下来就来看看这个是如何创建的吧。。。在StandardContext的startInternal方法中,我们可以看到如下的代码:
//loader部分 if (getLoader() == null) { //这一步用于创建loader对象 WebappLoader webappLoader = new WebappLoader(getParentClassLoader()); //这里用于创建当前context用的classLoader,这里会将parent设置为sharedclassLoader webappLoader.setDelegate(getDelegate()); setLoader(webappLoader); //保存创建的loader }
这里是创建了WebappLoader对象,然后注意看这里传入了当前context对象的parentClassLoader,那么我们来看看这个方法:
public ClassLoader getParentClassLoader() { if (parentClassLoader != null) { return (parentClassLoader); } if (getPrivileged()) { return this.getClass().getClassLoader(); } else if (parent != null) { //一般都是使用parent的parentClassLoader,也就是host的,host将会设置为与engine一样,也就是sharedLoader return (parent.getParentClassLoader()); } return (ClassLoader.getSystemClassLoader()); }
由于在创建context对象的时候并没有指定parentClassLoader,所以这里返回的将是context对象的parent的parentClassLoader,。我们知道context的parent是host对象,而host对象的parentclassLoader已经被设置为了sharedLoader,所以在这里创建webapploader的时候穿进去的构造参数其实也就是sharedLoader。。。
来看看WebappLoader的构造函数吧:
public WebappLoader(ClassLoader parent) { super(); this.parentClassLoader = parent; //parentClassLoader,一般都是sharedLoader }
嗯,这里好像也就是主要将传进来的sharedLoader保存起来。。那么在来看看Webapploader对象是如何启动的吧:
//启动webapploader 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 = createClassLoader(); //创建classLoader org.apache.catalina.loader.WebappClassLoader classLoader.setResources(context.getResources()); //设置资源目录 classLoader.setDelegate(this.delegate); // Configure our repositories setClassPath(); //设置classPath setPermissions(); ((Lifecycle) classLoader).start(); //启动classLoader,里面主要是加载classes以及lib下的代码 String contextName = context.getName(); //获取当前context的名字 if (!contextName.startsWith("/")) { contextName = "/" + contextName; } ObjectName cloname = new ObjectName(context.getDomain() + ":type=WebappClassLoader,host=" + context.getParent().getName() + ",context=" + contextName); Registry.getRegistry(null, null) .registerComponent(classLoader, cloname, null); //在jmx上面注册 } catch (Throwable t) { t = ExceptionUtils.unwrapInvocationTargetException(t); ExceptionUtils.handleThrowable(t); log.error( "LifecycleException ", t ); throw new LifecycleException("start: ", t); } setState(LifecycleState.STARTING); }
这里可以知道WebappLoader有一个classLoader的属性,将会在启动的时候创建,创建完了之后还会涉及到为这个classLoader设置资源,然后启动这个classLoader。。。
好啦,那就先来看看是如何创建的吧:
//创建webappclassLoader,这里会将sharedLoader设置为parent private WebappClassLoader createClassLoader() throws Exception { Class<?> clazz = Class.forName(loaderClass); //获取要创建的classLoader的class引用 org.apache.catalina.loader.WebappClassLoader WebappClassLoader classLoader = null; if (parentClassLoader == null) { parentClassLoader = context.getParentClassLoader(); //获取context的parentClassLoader,这里是sharedLoader } Class<?>[] argTypes = { ClassLoader.class }; Object[] args = { parentClassLoader }; Constructor<?> constr = clazz.getConstructor(argTypes); //获取构造函数,这里需要传递一个parentClassLoader,其实这里的双亲loader就是sharedLoader classLoader = (WebappClassLoader) constr.newInstance(args); return classLoader; }
这里就很明白了,创建了一个WebappClassLoader对象,并且将其的双亲loader设置为了sharedLoader。。。。
其实到这里整个tomcat的整体的classLoader就算了解的差不多了。。
最后再来看看WebappClassLoader是怎么启动的吧:
//其实主要是加载classes与lib下的代码,jar啥的 public void start() throws LifecycleException { WebResource classes = resources.getResource("/WEB-INF/classes"); //获取/WEB-INF/classes目录的资源引用 if (classes.isDirectory() && classes.canRead()) { addURL(classes.getURL()); //将该资源添加到当前classLoader的资源库 } WebResource[] jars = resources.listResources("/WEB-INF/lib"); //这里是获取lib文件夹 for (WebResource jar : jars) { //遍历所有的资源 if (jar.getName().endsWith(".jar") && jar.isFile() && jar.canRead()) { addURL(jar.getURL()); // 将资源加入到classLoader的资源库 jarModificationTimes.put( jar.getName(), Long.valueOf(jar.getLastModified())); } } started = true; String encoding = null; try { encoding = System.getProperty("file.encoding"); } catch (SecurityException e) { return; } if (encoding.indexOf("EBCDIC")!=-1) { needConvert = true; } }
这个代码看起来应该就很熟悉吧,加载/WEB-INF/classes以及/WEB-INF/lib下面的资源。。。
好啦。。classLoader部分就算完事了。。。可以看出tomcat8中对classLoader的处理比jetty6中还是要细致一些的。。。
Tomcat源码分析之ClassLoader部分的设计详细分析,布布扣,bubuko.com
Tomcat源码分析之ClassLoader部分的设计详细分析
原文:http://blog.csdn.net/fjslovejhl/article/details/21328347