Tomcat源码分析之ClassLoader部分的设计详细分析

时间:2022-04-06 15:55:32

读Tomcat的源码也算是有一段时间了吧,感觉读的也是断断续续的,这次写一篇比较综合性的吧,来看看Tomcat的整体ClassLoader体系的设计。。。。

在具体的涉及到源码之前,先来一张图来整体的描述一下整体的结构吧:

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中还是要细致一些的。。。