摘要
一直以来想看一下tomcat的源码,可以从头到尾理解一遍在整个web请求的处理过程中,容器完成的功能,容器怎么把一个url的请求的处理递交给了servlet,容器的pipeline是怎么设计的,容器的filter怎么实现的,如何维护session,好多好多的问题,有幸发现一篇文章Tomcat7.0源码分析,让我得以窥见容器的内部实现,从源码之中能按着作者的思路来理解和阅读。读完之后,有了一些理解,但是不是很深刻,借着这个机会,写了这篇总结,温故而知新。读源代码,就是要反复得读,每次读完都会先释然,又会产生新的疑惑,再带着问题重读源码,如此反复,脉络才会越来越清晰。
整个文章按如下几个主题:
- tomcat的类加载体系
- server.xml文件的加载与解析
- 生命周期管理
- 停止与启动服务
- 请求原理分析
- session管理分析
TOMCAT的类加载体系
Tomcat为了webapp之间以及app和容器之间的资源(jar包)隔离,是通过自定义类加载器的方式来实现的。首先我们看一下jdk的类加载器结构树(为了方便起见,将tomcat自定义的类加载器也放上去了)
这个粗看会有一点困惑,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<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 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的实例,同时我们可以看到这三者之间的关系为
接下去,我们来看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.
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。