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

Tomcat的类加载体系

2016-03-30 15:27 597 查看
>>> Java虚拟机规范中提到的主要类加载器:

Bootstrap Loader:加载lib目录下或者System.getProperty(“sun.boot.class.path”)、或者-XBootclasspath所指定的路径或jar。

Extended Loader:加载lib\ext目录下或者System.getProperty(“java.ext.dirs”) 所指定的 路径或jar。在使用Java运行程序时,也可以指定其搜索路径,例如:java -Djava.ext.dirs=d:\projects\testproj\classes HelloWorld。

AppClass Loader:加载System.getProperty("java.class.path")所指定的 路径或jar。在使用Java运行程序时,也可以加上-cp来覆盖原有的Classpath设置,例如: java -cp ./lavasoft/classes HelloWorld。
>>> Tomcat的类加载体系:

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

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

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

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

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

>>> 源码分析

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

/**

* Initialize daemon.

*/

public void init() throws Exception {

// Set Catalina path

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) {

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;

ArrayList<String> repositoryLocations = new ArrayList<String>();

ArrayList<Integer> repositoryTypes = new ArrayList<Integer>();

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]);

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;

}

> 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);

loadLoaderPackage(loader);

loadSessionPackage(loader);

loadUtilPackage(loader);

loadJavaxPackage(loader);

loadCoyotePackage(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 final static void loadCorePackage(ClassLoader loader) throws Exception {

String basePackage = "org.apache.catalina.";

loader.loadClass (basePackage + "core.ApplicationContextFacade$1");

loader.loadClass (basePackage + "core.ApplicationDispatcher$PrivilegedForward");

loader.loadClass (basePackage + "core.ApplicationDispatcher$PrivilegedInclude");

loader.loadClass (basePackage + "core.AsyncContextImpl");

loader.loadClass (basePackage + "core.AsyncContextImpl$AsyncState");

loader.loadClass (basePackage + "core.AsyncContextImpl$DebugException");

loader.loadClass (basePackage + "core.AsyncContextImpl$1");

loader.loadClass (basePackage + "core.AsyncContextImpl$2");

loader.loadClass (basePackage + "core.AsyncListenerWrapper");

loader.loadClass (basePackage + "core.ContainerBase$PrivilegedAddChild");

loader.loadClass (basePackage + "core.DefaultInstanceManager$1");

loader.loadClass (basePackage + "core.DefaultInstanceManager$2");

loader.loadClass (basePackage + "core.DefaultInstanceManager$3");

loader.loadClass (basePackage + "core.DefaultInstanceManager$4");

loader.loadClass (basePackage + "core.DefaultInstanceManager$5");

loader.loadClass (basePackage + "core.ApplicationHttpRequest$AttributeNamesEnumerator");

}

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

/**

* Start this component and implement the requirements

* of {@link LifecycleBase#startInternal()}.

*

* @exception LifecycleException if this component detects a fatal error

* that prevents this component from being used

*/

@Override

protected synchronized void startInternal() throws LifecycleException {

// 省略前边的代码

if (getLoader() == null) {

WebappLoader webappLoader = new WebappLoader(getParentClassLoader());

webappLoader.setDelegate(getDelegate());

setLoader(webappLoader);

}

// 省略中间的代码

// Start our subordinate components, if any

if ((loader != null) && (loader instanceof Lifecycle))

((Lifecycle) loader).start();

// 省略后边的代码

}

从上面代码看到最后会调用WebappLoader的start方法,start又调用了startInternal方法,startInternal的实现如下:

/**

* Start associated {@link ClassLoader} and implement the requirements

* of {@link LifecycleBase#startInternal()}.

*

* @exception LifecycleException if this component detects a fatal error

* that prevents this component from being used

*/

@Override

protected void startInternal() throws LifecycleException {

// 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]);

}

> 最后我们看看createClassLoader的实现:

/**

* Create associated classLoader.

*/

private WebappClassLoader createClassLoader()

throws Exception {

//loaderClass即字符串org.apache.catalina.loader.WebappClassLoader

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;

}

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