tomcat java.ext.dirs_TOMCAT源码阅读总结(1)-TOMCAT的类加载体系

摘要

一直以来想看一下tomcat的源码,可以从头到尾理解一遍在整个web请求的处理过程中,容器完成的功能,容器怎么把一个url的请求的处理递交给了servlet,容器的pipeline是怎么设计的,容器的filter怎么实现的,如何维护session,好多好多的问题,有幸发现一篇文章Tomcat7.0源码分析,让我得以窥见容器的内部实现,从源码之中能按着作者的思路来理解和阅读。读完之后,有了一些理解,但是不是很深刻,借着这个机会,写了这篇总结,温故而知新。读源代码,就是要反复得读,每次读完都会先释然,又会产生新的疑惑,再带着问题重读源码,如此反复,脉络才会越来越清晰。

整个文章按如下几个主题:

tomcat的类加载体系

server.xml文件的加载与解析

生命周期管理

停止与启动服务

请求原理分析

session管理分析

TOMCAT的类加载体系

Tomcat为了webapp之间以及app和容器之间的资源(jar包)隔离,是通过自定义类加载器的方式来实现的。首先我们看一下jdk的类加载器结构树(为了方便起见,将tomcat自定义的类加载器也放上去了)

150acf4073206f39a5eb79cf83425ddc.png

这个粗看会有一点困惑,jdk不是父亲委派加载机制吗?为什么这里的AppClassLoader,ExtClassLoad不是继承关系呢?其实这是一个误解。加载器之间父子关系不是通过类继承的方式,而是通过对象变量的方式,来实现的。

private ClassLoader(Void unused, ClassLoader parent) {

this.parent = parent;

然后在加载类的时候,会尝试先让parent来加载

protected Class> loadClass(String name, boolean resolve)

throws ClassNotFoundException

{

synchronized (getClassLoadingLock(name)) {

// First, check if the class has already been loaded

Class> c = findLoadedClass(name);

if (c == null) {

long t0 = System.nanoTime();

try {

if (parent != null) {

c = parent.loadClass(name, false);

} else {

c = findBootstrapClassOrNull(name);

}

这个就是父亲委托加载机制的由来。之前找不到JDK的ExtClassLoader和AppClassLoader,后来才发现这两个类原来是rt.jar包sum.msic.Launcher的内部类. Bootstrap加载sum.boot.class.path下的类,ExtClassPath加载java.ext.dirs下的类,AppClassLoader加载java.class.path下的类。当然也可以在运行时通过参数( -XBootclasspath , -Djava.ext.dirs , -cp)分别指定。

好了,现在让我们回到tomcat的类加载器上来,看类树上,tomcat自定义了WebappClassLoader和standardClassLoader,简单的看,前者是给web应用用的,后者是给tomcat容器本身用的。我们通过原来来进行分析。

Bootstrap.java

public void init()

throws Exception

{

// Set Catalina path

setCatalinaHome();

setCatalinaBase();

initClassLoaders();

Thread.currentThread().setContextClassLoader(catalinaLoader);

SecurityClassLoad.securityClassLoad(catalinaLoader);

// Load our startup class and call its process() method

if (log.isDebugEnabled())

log.debug("Loading startup class");

//调用Catalina的setParentClassLoader方法,将sharedLoader设置为所有WebappClassLoader的parent

Class> startupClass =

catalinaLoader.loadClass

("org.apache.catalina.startup.Catalina");

Object startupInstance = startupClass.newInstance();

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

Method method =

startupInstance.getClass().getMethod(methodName, paramTypes);

method.invoke(startupInstance, paramValues);

catalinaDaemon = startupInstance;

}

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

}

}

private ClassLoader createClassLoader(String name, ClassLoader parent)

throws Exception {

//获取各个类加载器相应的资源配置文件(common.loader,server.loader,shared.loader)

//这些是具体配置在catalina.properties配置文件中的。以本机配置为例

//common.loader=${catalina.base}/lib,${catalina.base}/lib/*.jar,${catalina.home}/lib,${catalina.home}/lib/*.jar

//server.loader=

//shared.loader=

String value = CatalinaProperties.getProperty(name + ".loader");

if ((value == null) || (value.equals("")))

return parent;

ArrayList repositoryLocations = new ArrayList();

ArrayList repositoryTypes = new ArrayList();

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

}

//回看init方法,对应的securityClassLoad就是使用catalinaLoader完成tomcat核心类的加载的

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

}

此处,完成了commonLoader,catalinaLoader和sharedLoader三个加载器的初始化,他们均是StandardClassLoader的实例,同时我们可以看到这三者之间的关系为

f1b1effdb2bab10eae44c331c0f428fd.png

接下去,我们来看WebappLoader

StandardContext.java

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.java

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 =

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

}

// 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 path = ctx.getPath();

if (path.equals("")) {

path = "/";

}

ObjectName cloname = new ObjectName

(MBeanUtils.getDomain(ctx) + ":type=WebappClassLoader,path="

+ path + ",host=" + ctx.getParent().getName());

Registry.getRegistry(null, null)

.registerComponent(classLoader, cloname, null);

} catch (Throwable t) {

log.error( "LifecycleException ", t );

throw new LifecycleException("start: ", t);

}

setState(LifecycleState.STARTING);

}

private WebappClassLoader createClassLoader()

throws Exception {

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;

}

此处完成了WebappClassLoader的初始化,可见这个类加载器是对应一个Context的,即一个web应用。其parent应该是sharedLoader.

8be7046640ca24726e0717962ae17241.png

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值