Tomcat研究之ClassLoader
在研究Tomcat之前,一般是借用现有的UML工具分析Tomcat整体结构,但要分析Tomcat的流程就必须从分析Tomcat的StartUp入手。Tomcat的启动是从解析bat文件开始,bat文件最终调用org.apache.catalina.startup.Bootstrap开始类的加载。
一.Tomcat的ClassLoader:
TOMCAT自己的类载入器(ClassLoader)
+---------------------------+
| Bootstrap |
| | |
| System |
| | |
| Common |
| / / |
| Catalina Shared |
+---------------------------+
其中:
- Bootstrap - 载入JVM自带的类和$JAVA_HOME/jre/lib/ext/*.jar
-
System
1.载入
$CATALINA_HOME/bin/bootstrap.jar 初始化Tomcat,执行Main方法
2
. $JAVA_HOME/lib/tools.jar Sun的工具类,包括编译Jsp为Servlet的工具类
-
Common
这个目录下的类虽然对TOMCAT和所有的WEB APP都可见.但是Web App的类不应该
放在这个目录下,所有未打包的Class都在$CATALINA_HOME/common/classes下,所
有打包的jar都在
$CATALINA_HOME/commons/endorsed和$CATALINA_HOME/common/lib下,默认情况会
包含以下几个包:
1.
jndi.jar
JNDI API接口,这个包仅在Java1.2时候装入,1.3以后的版本JDK已
自动装入.
2.
naming-common.jar
JNDI接口实现类,Tomcat用这些类在内存中使用Context.
3.
naming-resources.jar
JNDI实现,Tomcat用它们定位Web App的静态资源.
4. servlet.jar Servlet,Jsp API
5.
xerces.jar
XML解析器,特定的Web App可以在自己的/WEB-INF/lib 中覆盖.
- Catalina 装入Tomcat实现所有接口的类,这些类对Web App是完全不可见的,所有未打包的类在
$CATALINA_HOME/server/classes所有jar包在$CATALINA_HOME/server/lib下.一
般情况该ClassLoader将Load下面几个包:
1. catalina.jar Servlet容器的Tomcat实现包
2.
jakarta-regexp-X.Y.jar
正则表达式,请求过滤时使用
3.
servlets-xxxxx.jar
Servlet支持包
4
. tomcat-coyote.jar Tomcat的Coyote连接实现包
5.
tomcat-jk.jar
Web Server绑定包,允许Tomcat绑定Apache等作为Web Server
6. tomcat-jk2.jar 功能同上
7.
tomcat-util.jar
Tomcat工具类,可能被一些Connector用到
8.
tomcat-warp.jar
用于Apache Server包
- Shared 载入所有WEB APP都可见的类,对TOMCAT不可见. 所有未打包的类在
$CATALINA_HOME/shared/classes所有jar包在$CATALINA_HOME /lib下.
默认情况包含下面几个包:
1.
jasper-compiler.jar
Jsp编译器,编译Jsp为Servlet
2.
jasper-runtime.jar
Jsp(已编译成Servlet)运行支持包
3.
naming-factory.jar
支持Web App使用JNDI的封装包
-WebAppX Web App ClassLoader,当Web App被部署是该ClassLoader被创建.所有class都在
WEB-INF/classes下,所有jar在WEB-INF/lib下.
特别注意WEB APP自己的ClassLoader的实现与众不同:
它先试图从WEB APP自己的目录里载入,如果失败则请求父ClassLoader的代理
这样可以让不同的WEB APP之间的类载入互不干扰.另,Tomcat Server使用的是Catalina
ClassLoader,一般的Web App使用的是WebApp ClassLoader.
二.
org.apache.catalina.startup.Bootstrap
该类是Tomcat的执行入口点,我们着重分析下面两个方法:
1. initClassLoaders,创建ClassLoader层次.
private void initClassLoaders() {
try {
ClassLoaderFactory.setDebug(debug);
//创建common ClassLoader,没有父ClassLoader
commonLoader = createClassLoader("common", null);
//创建catalina ClassLoader,父ClassLoader为common
catalinaLoader = createClassLoader("server", commonLoader);
//创建shared ClassLoader, 父ClassLoader为common
sharedLoader = createClassLoader("shared", commonLoader);
} catch (Throwable t) {
log("Class loader creation threw exception", t);
System.exit(1);
}
}
2.
createClassLoader,负责具体的创建工作
在$CATALINA_HOME/conf/catalina.properties中定义了common,
server,
shared
ClassLoader载入类的路径及一些包的安全权限.
//common载入类的路径
common.loader=${catalina.home}/common/classes,
${catalina.home}/common/endorsed/*.jar,${catalina.home}/common/lib/*.jar
//server载入类的路径
server.loader=${catalina.home}/server/classes,
${catalina.home}/server/lib/*.jar
//shared载入类的路径
shared.loader=${catalina.base}/shared/classes,
${catalina.base}/shared/lib/*.jar
/**
*param name:Load Name
*param parent:父Loader
*classLoader的资源分三种:
*1.未打包的classes,一般是一个目录
*2.打包的jar目录
*3.网络资源,一般是网上的一个jar包 (Applet经常用到这样的loader)
*/
private ClassLoader createClassLoader(String name, ClassLoader parent)
throws Exception {
//从catalina.properties中取得改Loader的配置信息
String value = CatalinaProperties.getProperty(name + ".loader");
if ((value == null) || (value.equals("")))
return parent;
//classes目录
ArrayList unpackedList = new ArrayList();
//jar目录
ArrayList packedList = new ArrayList();
//网络路径指定的包
ArrayList urlList = new ArrayList();
StringTokenizer tokenizer = new StringTokenizer(value, ",");
//当前Loader该装载的类
while (tokenizer.hasMoreElements()) {
String repository = tokenizer.nextToken();
// Check for a JAR URL repository
try {
//如果是网络路径追加url
urlList.add(new URL(repository));
continue;
} catch (MalformedURLException e) {
// Ignore
}
// 本地路径
boolean packed = false;
//${catalina.home}
if (repository.startsWith(CATALINA_HOME_TOKEN)) {
repository = getCatalinaHome()
+ repository.substring(CATALINA_HOME_TOKEN.length());
//${catalina.base}
} else if (repository.startsWith(CATALINA_BASE_TOKEN)) {
repository = getCatalinaBase()
+ repository.substring(CATALINA_BASE_TOKEN.length());
}
/**经过上述操作,把catalina.properties里的路径替换成绝对路径*/
//如果是jar文件路径
if (repository.endsWith("*.jar")) {
packed = true;
repository = repository.substring
(0, repository.length() - "*.jar".length());
}
if (packed) {
packedList.add(new File(repository));
} else {
unpackedList.add(new File(repository));
}
}
File[] unpacked = (File[]) unpackedList.toArray(new File[0]);
File[] packed = (File[]) packedList.toArray(new File[0]);
URL[] urls = (URL[]) urlList.toArray(new URL[0]);
//调用Factory的方法创建ClassLoader
return ClassLoaderFactory.createClassLoader
(unpacked, packed, urls, parent);
}
三.
ClassLoaderFactory
ClassLoaderFactory是用于创建ClassLoader的工厂类,这个类比较简单.
//参数含义不再说明,参看上面的分析
public static ClassLoader createClassLoader(File unpacked[],
File packed[],
URL urls[],
ClassLoader parent)
throws Exception {
if (debug >= 1)
log("Creating new class loader");
// Construct the "class path" for this class loader
ArrayList list = new ArrayList();
// 通过class目录构造file协议的url,并追加的list
if (unpacked != null) {
for (int i = 0; i < unpacked.length; i++) {
File file = unpacked[i];
if (!file.exists() || !file.canRead())
continue;
if (debug >= 1)
log(" Including directory or JAR "
+ file.getAbsolutePath());
URL url = new URL("file", null,
file.getCanonicalPath() + File.separator);
list.add(url.toString());
}
}
//取出所有jar目录里的jar文件,逐一构造url,并追加的list
if (packed != null) {
for (int i = 0; i < packed.length; i++) {
File directory = packed[i];
if (!directory.isDirectory() || !directory.exists() ||
!directory.canRead())
continue;
String filenames[] = directory.list();
for (int j = 0; j < filenames.length; j++) {
String filename = filenames[j].toLowerCase();
if (!filename.endsWith(".jar"))
continue;
File file = new File(directory, filenames[j]);
if (debug >= 1)
log(" Including jar file " + file.getAbsolutePath());
URL url = new URL("file", null,
file.getCanonicalPath());
list.add(url.toString());
}
}
}
//追加网络路径的资源
if (urls != null) {
for (int i = 0; i < urls.length; i++) {
list.add(urls[i].toString());
}
}
//调用StandardClassLoader创建实际的Loader
String array[] = (String[]) list.toArray(new String[list.size()]);
StandardClassLoader classLoader = null;
if (parent == null)
classLoader = new StandardClassLoader(array);
else
classLoader = new StandardClassLoader(array, parent);
classLoader.setDelegate(true);
return (classLoader);
}
四.
StandardClassLoader
StandardClassLoader继承了URLClassLoader,
URLClassLoader类具有从硬盘目录装载类,或从本地或远程装载jar文件的能力.这个类也实现了Reloader接口,提供了自动重新装载类的功能.我们主要看这个类以下及个方法:
1.
构造函数StandardClassLoader
/**
* @param repositories url数组,见上分析
* @param parent 父loader
*/
public StandardClassLoader(String repositories[], ClassLoader parent) {
//调用父类的构造函数
//父类的构造函数将用转换后的repositories生成URLClassPath的实例
//ucp是类成员变量ucp=
new URLClassPath(urls);
// URLClassPath是sun的扩展包,无法继续跟踪
super(convert(repositories), parent);
this.parent = parent;
this.system = getSystemClassLoader();
securityManager = System.getSecurityManager();
if (repositories != null) {
for (int i = 0; i < repositories.length; i++)
//处理url
addRepositoryInternal(repositories[i]);
}
}
2.
addRepositoryInternal
/**
* @param repository 要处理的url
*/
protected void addRepositoryInternal(String repository) {
URLStreamHandler streamHandler = null;
String protocol = parseProtocol(repository);
if (factory != null)
streamHandler = factory.createURLStreamHandler(protocol);
// 当前url是指向本地或网路的jar文件,验证jar的正确性
//下面的代码看似无用其实是在验证jar文件的正确性,如果jar文件错误抛异常中止执行.
if (!repository.endsWith(File.separator) && !repository.endsWith("/")) {
JarFile jarFile = null;
try {
Manifest manifest = null;
//jar协议
if (repository.startsWith("jar:")) {
URL url = new URL(null, repository, streamHandler);
JarURLConnection conn =
(JarURLConnection) url.openConnection();
conn.setAllowUserInteraction(false);
conn.setDoInput(true);
conn.setDoOutput(false);
conn.connect();
jarFile = conn.getJarFile();
//file协议
} else if (repository.startsWith("file://")) {
jarFile = new JarFile(repository.substring(7));
//file
} else if (repository.startsWith("file:")) {
jarFile = new JarFile(repository.substring(5));
//本地路径的jar文件
} else if (repository.endsWith(".jar")) {
URL url = new URL(null, repository, streamHandler);
URLConnection conn = url.openConnection();
JarInputStream jis =
new JarInputStream(conn.getInputStream());
manifest = jis.getManifest();
//其他情况均为错误
} else {
throw new IllegalArgumentException
("addRepositoryInternal: Invalid URL '" +
repository + "'");
&
|
Tomcat研究之ClassLoader
最新推荐文章于 2024-11-06 15:53:45 发布