您的位置:首页 > 运维架构 > Tomcat

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

2016-07-03 21:44 573 查看
读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