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

Tomcat源码学习(五)-- Tomcat_7.0.70 类加载体系分析

2016-07-08 00:00 537 查看
摘要: Tomcat源码学习(五)-- Tomcat_7.0.70 类加载体系分析

1、前言

Tomcat遵循J2EE规范,实现了Web容器。Java虚拟机有自己的一套类加载体系,同样Tomcat也有自己的一套类加载体系。

2、概述

首先简单介绍下Java虚拟机的主要的类加载器:



启动类加载器(bootstrap classloader)

它用来加载 Java 的核心库,是用原生代码(本地代码,与平台有关)来实现的,并不继承自java.lang.ClassLoader。这个类加载器负责将存放在<JAVA_HOME>\lib目录中的,或者被-Xbootclasspath参数所指定的路径中的,并且是虚拟机识加的(仅按照文件名识别,如rt.jar,名字不符合的类库即使放在lib目录中也不会被加载)类库加载到虚拟机内存中。启动类加载器无法被Java程序直接引用。

扩展类加载器(extensions classloader)

扩展类加载器是由 Sun 的 ExtClassLoader(sun.misc.Launcher$ExtClassLoader) 实现的。它负责将 < Java_Runtime_Home >/lib/ext 或者由系统变量java.ext.dir 指定位置中的类库加载到内存中

应用程序类加载器(application classloader)

系统类加载器是由 Sun 的 AppClassLoader(sun.misc.Launcher$AppClassLoader)实现的,由于这个类加载器是ClassLoader中getSystemClassLoader()方法的返回值,所以一般也称它为系统类加载器。它根据 Java 应用的类路径(CLASSPATH)来加载 Java 类。开发者可以直接使用这个类加载器,如果应用程序中没有自定义过自己的类加载器,一般情况下这个就是程序默认的类加载器。

用户自定义的类装载器

用户自定义的类装载器是普通的Java对象,它的类必须派生自java.lang.ClassLoader类。ClassLoader中定义的方法为程序为程序提供了访问类装载器机制的接口。此外,对于每一个被装载的类型,Java虚拟机都会为它创建一个java.lang.Class类的实例来代表该类型。和所有其它对象一样,用户自定义的类装载器以有Class类的实例都放在内存中的堆区,而装载的类型信息则都放在方法区。

然后在来一张图简要说明Tomcat的类加载体系(图画的不好):



ClassLoader:Java提供的类加载器抽象类,用户自定义的类加载器需要继承实现

commonLoader:Tomcat最基本的类加载器,加载路径中的class可以被Tomcat容器本身以及各个Webapp访问

catalinaLoader:Tomcat容器私有的类加载器,加载路径中的class对于Webapp不可见

sharedLoader:各个Webapp共享的类加载器,加载路径中的class对于所有Webapp可见,但是对于Tomcat容器不可见

WebappClassLoader:各个Webapp私有的类加载器,加载路径中的class只对当前Webapp可见

3、分析

commonLoader、catalinaLoader和sharedLoader在Tomcat容器初始化的一开始,即调用Bootstrap的init方法时创建。catalinaLoader会被设置为Tomcat主线程的线程上下文类加载器,并且使用catalinaLoader加载Tomcat容器自身容器下的class。Bootstrap的init方法的部分代码如下:

/**
* Initialize daemon.
*/
public void init()
throws Exception
{
setCatalinaHome();
setCatalinaBase();

initClassLoaders();

Thread.currentThread().setContextClassLoader(catalinaLoader);

SecurityClassLoad.securityClassLoad(catalinaLoader);
.....
}

initClassLoaders方法:

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) {
handleThrowable(t);
log.error("Class loader creation threw exception", t);
System.exit(1);
}
}

创建类加载器的createClassLoader方法的实现:

private ClassLoader createClassLoader(String name, ClassLoader parent)
throws Exception {

String value = CatalinaProperties.getProperty(name + ".loader");
if ((value == null) || (value.equals("")))
return parent;

value = replace(value);

List<Repository> repositories = new ArrayList<Repository>();

StringTokenizer tokenizer = new StringTokenizer(value, ",");
while (tokenizer.hasMoreElements()) {
String repository = tokenizer.nextToken().trim();
if (repository.length() == 0) {
continue;
}

// Check for a JAR URL repository
try {
@SuppressWarnings("unused")
URL url = new URL(repository);
repositories.add(
new Repository(repository, RepositoryType.URL));
continue;
} catch (MalformedURLException e) {
// Ignore
}

// Local repository
if (repository.endsWith("*.jar")) {
repository = repository.substring
(0, repository.length() - "*.jar".length());
repositories.add(
new Repository(repository, RepositoryType.GLOB));
} else if (repository.endsWith(".jar")) {
repositories.add(
new Repository(repository, RepositoryType.JAR));
} else {
repositories.add(
new Repository(repository, RepositoryType.DIR));
}
}

return ClassLoaderFactory.createClassLoader(repositories, parent);
}

createClassLoader最终使用ClassLoaderFactory.createClassLoader(locations, types, parent)方法创建ClassLoader。

我们在看SecurityClassLoad.securityClassLoad(catalinaLoader);

public static void securityClassLoad(ClassLoader loader)
throws Exception {

if( System.getSecurityManager() == null ){
return;
}

loadCorePackage(loader);
loadCoyotePackage(loader);
loadLoaderPackage(loader);
loadRealmPackage(loader);
loadServletsPackage(loader);
loadSessionPackage(loader);
loadUtilPackage(loader);
loadValvesPackage(loader);
loadJavaxPackage(loader);
loadConnectorPackage(loader);
loadTomcatPackage(loader);
}

securityClassLoad方法主要加载Tomcat容器所需的class,包括:

Tomcat核心class,即org.apache.catalina.core路径下的class;

org.apache.catalina.loader.WebappClassLoader$PrivilegedFindResourceByName

Tomcat有关session的class,即org.apache.catalina.session路径下的class

Tomcat工具类的class,即org.apache.catalina.util路径下的class

javax.servlet.http.Cookie

Tomcat处理请求的class,即org.apache.catalina.connector路径下的class

Tomcat其它工具类的class,也是org.apache.catalina.util路径下的class

我们以加载Tomcat核心class的loadCorePackage方法为例,查看其实现:

private static final void loadCorePackage(ClassLoader loader)
throws Exception {
final String basePackage = "org.apache.catalina.core.";
loader.loadClass
(basePackage +
"AccessLogAdapter");
loader.loadClass
(basePackage +
"ApplicationContextFacade$1");
loader.loadClass
(basePackage +
"ApplicationDispatcher$PrivilegedForward");
loader.loadClass
(basePackage +
"ApplicationDispatcher$PrivilegedInclude");
loader.loadClass
(basePackage +
"AsyncContextImpl");
loader.loadClass
(basePackage +
"AsyncContextImpl$DebugException");
loader.loadClass
(basePackage +
"AsyncContextImpl$1");
loader.loadClass
(basePackage +
"AsyncListenerWrapper");
loader.loadClass
(basePackage +
"ContainerBase$PrivilegedAddChild");
loader.loadClass
(basePackage +
"DefaultInstanceManager$1");
loader.loadClass
(basePackage +
"DefaultInstanceManager$2");
loader.loadClass
(basePackage +
"DefaultInstanceManager$3");
loader.loadClass
(basePackage +
"DefaultInstanceManager$AnnotationCacheEntry");
loader.loadClass
(basePackage +
"DefaultInstanceManager$AnnotationCacheEntryType");
loader.loadClass
(basePackage +
"ApplicationHttpRequest$AttributeNamesEnumerator");
}

至此为止,我们还没有看到WebappClassLoader。启动StandardContext的时候会创建WebappLoader,StandardContext的方法startInternal的部分代码如下:

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的start方法:

public final synchronized void start() throws LifecycleException {

if (LifecycleState.STARTING_PREP.equals(state) || LifecycleState.STARTING.equals(state) ||
LifecycleState.STARTED.equals(state)) {

if (log.isDebugEnabled()) {
Exception e = new LifecycleException();
log.debug(sm.getString("lifecycleBase.alreadyStarted", toString()), e);
} else if (log.isInfoEnabled()) {
log.info(sm.getString("lifecycleBase.alreadyStarted", toString()));
}

return;
}

if (state.equals(LifecycleState.NEW)) {
init();
} else if (state.equals(LifecycleState.FAILED)) {
stop();
} else if (!state.equals(LifecycleState.INITIALIZED) &&
!state.equals(LifecycleState.STOPPED)) {
invalidTransition(Lifecycle.BEFORE_START_EVENT);
}

setStateInternal(LifecycleState.STARTING_PREP, null, false);

try {
startInternal();//start再次调用了startInternal方法(WebappLoader中的方法)
} catch (Throwable t) {
// This is an 'uncontrolled' failure so put the component into the
// FAILED state and throw an exception.
ExceptionUtils.handleThrowable(t);
setStateInternal(LifecycleState.FAILED, null, false);
throw new LifecycleException(sm.getString("lifecycleBase.startFail", toString()), t);
}

if (state.equals(LifecycleState.FAILED)) {
// This is a 'controlled' failure. The component put itself into the
// FAILED state so call stop() to complete the clean-up.
stop();
} else if (!state.equals(LifecycleState.STARTING)) {
// Shouldn't be necessary but acts as a check that sub-classes are
// doing what they are supposed to.
invalidTransition(Lifecycle.AFTER_START_EVENT);
} else {
setStateInternal(LifecycleState.STARTED, null, false);
}
}

start又调用了startInternal方法,startInternal的实现如下:

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 =
DirContextURLStreamHandlerFactory.getInstance();
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) {
ExceptionUtils.handleThrowable(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.setClearReferencesRmiTargets(
((StandardContext) container).getClearReferencesRmiTargets());
classLoader.setClearReferencesStatic(
((StandardContext) container).getClearReferencesStatic());
classLoader.setClearReferencesStopThreads(
((StandardContext) container).getClearReferencesStopThreads());
classLoader.setClearReferencesStopTimerThreads(
((StandardContext) container).getClearReferencesStopTimerThreads());
classLoader.setClearReferencesHttpClientKeepAliveThread(
((StandardContext) container).getClearReferencesHttpClientKeepAliveThread());
}

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 contextName = ctx.getName();
if (!contextName.startsWith("/")) {
contextName = "/" + contextName;
}
ObjectName cloname = new ObjectName
(MBeanUtils.getDomain(ctx) + ":type=WebappClassLoader,context="
+ contextName + ",host=" + ctx.getParent().getName());
Registry.getRegistry(null, null)
.registerComponent(classLoader, cloname, null);

} catch (Throwable t) {
t = ExceptionUtils.unwrapInvocationTargetException(t);
ExceptionUtils.handleThrowable(t);
log.error( "LifecycleException ", t );
throw new LifecycleException("start: ", t);
}

setState(LifecycleState.STARTING);
}

最后我们看看createClassLoader的实现:

private WebappClassLoaderBase createClassLoader()
throws Exception {

Class<?> clazz = Class.forName(loaderClass);
WebappClassLoaderBase classLoader = null;

if (parentClassLoader == null) {
parentClassLoader = container.getParentClassLoader();
}
Class<?>[] argTypes = { ClassLoader.class };
Object[] args = { parentClassLoader };
Constructor<?> constr = clazz.getConstructor(argTypes);
classLoader = (WebappClassLoaderBase) constr.newInstance(args);

return classLoader;

}

至此Tomcat类加载完毕。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: