开始之前,我们首先需要了解一下几个基本的知识点;
1.tomcat中类加载器的结构与关系。
这里,我引用tomcat文档的一个简图来说明一下, 有兴趣深究的朋友,可以去翻看tomcat的文档,理解更多信息.
(tomcat5.5)
(由于参考的是tomcat6.0的源代码,这里纠正一下类加载器的图(如下),以免给别的朋友造成误解,同时也多谢asialee给出的提醒)
(tomcat6.0)
2.每种类加载器分别加载什么资源:
这些内容,可以在tomcat文档的 Class Loader HOW-TO 找到.
这里我要说明的是, 在tomcat中,这些内容是记录在哪里的。既(程序怎么让tomcat知道,需要加载哪些类)
答案是-----
其通过一个配置文件来指定的:(catalina.properties),这个文件默认存放在
tomcat路径下的 bin/bootstrap.jar中。
如图
打开文件,其内容如下:
- # Licensed to the Apache Software Foundation (ASF) under one or more
- # contributor license agreements. See the NOTICE file distributed with
- # this work for additional information regarding copyright ownership.
- # The ASF licenses this file to You under the Apache License, Version2.0
- # (the "License"); you may not use this file except in compliance with
- # the License. You may obtain a copy of the License at
- #
- # http://www.apache.org/licenses/LICENSE-2.0
- #
- # Unless required by applicable law or agreed to in writing, software
- # distributed under the License is distributed on an "AS IS" BASIS,
- # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- # See the License for the specific language governing permissions and
- # limitations under the License.
- #
- # List of comma-separated packages that start with or equal this string
- # will cause a security exception to be thrown when
- # passed to checkPackageAccess unless the
- # corresponding RuntimePermission ("accessClassInPackage."+package) has
- # been granted.
- package.access=sun.,org.apache.catalina.,org.apache.coyote.,org.apache.tomcat.,org.apache.jasper.,sun.beans.
- #
- # List of comma-separated packages that start with or equal this string
- # will cause a security exception to be thrown when
- # passed to checkPackageDefinition unless the
- # corresponding RuntimePermission ("defineClassInPackage."+package) has
- # been granted.
- #
- # by default, no packages are restrictedfor definition, and none of
- # the class loaders supplied with the JDK call checkPackageDefinition.
- #
- package.definition=sun.,java.,org.apache.catalina.,org.apache.coyote.,org.apache.tomcat.,org.apache.jasper.
- #
- #
- # List of comma-separated paths defining the contents of the "common"
- # classloader. Prefixes should be used to define what is the repository type.
- # Path may be relative to the CATALINA_HOME or CATALINA_BASE path or absolute.
- # If left as blank,the JVM system loader will be used as Catalina's "common"
- # loader.
- # Examples:
- # "foo": Add this folder as aclass repository
- # "foo/*.jar": Add all the JARs of the specified folder asclass
- # repositories
- # "foo/bar.jar": Add bar.jar as aclass repository
- common.loader=${catalina.home}/lib,${catalina.home}/lib/*.jar
- #
- # List of comma-separated paths defining the contents of the "server"
- # classloader. Prefixes should be used to define what is the repository type.
- # Path may be relative to the CATALINA_HOME or CATALINA_BASE path or absolute.
- # If left as blank, the "common" loader will be used as Catalina's"server"
- # loader.
- # Examples:
- # "foo": Add this folder as aclass repository
- # "foo/*.jar": Add all the JARs of the specified folder asclass
- # repositories
- # "foo/bar.jar": Add bar.jar as aclass repository
- server.loader=
- #
- # List of comma-separated paths defining the contents of the "shared"
- # classloader. Prefixes should be used to define what is the repository type.
- # Path may be relative to the CATALINA_BASE path or absolute. If left as blank,
- # the "common" loader will be used as Catalina's"shared" loader.
- # Examples:
- # "foo": Add this folder as aclass repository
- # "foo/*.jar": Add all the JARs of the specified folder asclass
- # repositories
- # "foo/bar.jar": Add bar.jar as aclass repository
- # Please note that for single jars, e.g. bar.jar, you need the URL form
- # starting with file:.
- shared.loader=
- #
- # String cache configuration.
- tomcat.util.buf.StringCache.byte.enabled=true
- #tomcat.util.buf.StringCache.char.enabled=true
- #tomcat.util.buf.StringCache.trainThreshold=500000
- #tomcat.util.buf.StringCache.cacheSize=5000
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# List of comma-separated packages that start with or equal this string
# will cause a security exception to be thrown when
# passed to checkPackageAccess unless the
# corresponding RuntimePermission ("accessClassInPackage."+package) has
# been granted.
package.access=sun.,org.apache.catalina.,org.apache.coyote.,org.apache.tomcat.,org.apache.jasper.,sun.beans.
#
# List of comma-separated packages that start with or equal this string
# will cause a security exception to be thrown when
# passed to checkPackageDefinition unless the
# corresponding RuntimePermission ("defineClassInPackage."+package) has
# been granted.
#
# by default, no packages are restricted for definition, and none of
# the class loaders supplied with the JDK call checkPackageDefinition.
#
package.definition=sun.,java.,org.apache.catalina.,org.apache.coyote.,org.apache.tomcat.,org.apache.jasper.
#
#
# List of comma-separated paths defining the contents of the "common"
# classloader. Prefixes should be used to define what is the repository type.
# Path may be relative to the CATALINA_HOME or CATALINA_BASE path or absolute.
# If left as blank,the JVM system loader will be used as Catalina's "common"
# loader.
# Examples:
# "foo": Add this folder as a class repository
# "foo/*.jar": Add all the JARs of the specified folder as class
# repositories
# "foo/bar.jar": Add bar.jar as a class repository
common.loader=${catalina.home}/lib,${catalina.home}/lib/*.jar
#
# List of comma-separated paths defining the contents of the "server"
# classloader. Prefixes should be used to define what is the repository type.
# Path may be relative to the CATALINA_HOME or CATALINA_BASE path or absolute.
# If left as blank, the "common" loader will be used as Catalina's "server"
# loader.
# Examples:
# "foo": Add this folder as a class repository
# "foo/*.jar": Add all the JARs of the specified folder as class
# repositories
# "foo/bar.jar": Add bar.jar as a class repository
server.loader=
#
# List of comma-separated paths defining the contents of the "shared"
# classloader. Prefixes should be used to define what is the repository type.
# Path may be relative to the CATALINA_BASE path or absolute. If left as blank,
# the "common" loader will be used as Catalina's "shared" loader.
# Examples:
# "foo": Add this folder as a class repository
# "foo/*.jar": Add all the JARs of the specified folder as class
# repositories
# "foo/bar.jar": Add bar.jar as a class repository
# Please note that for single jars, e.g. bar.jar, you need the URL form
# starting with file:.
shared.loader=
#
# String cache configuration.
tomcat.util.buf.StringCache.byte.enabled=true
#tomcat.util.buf.StringCache.char.enabled=true
#tomcat.util.buf.StringCache.trainThreshold=500000
#tomcat.util.buf.StringCache.cacheSize=5000
此文件,下面会有详细的介绍.
OK,到此,我们初步了解到tomcat关于类加载器的一些知识。 下面来详细看看,tomcat内部是怎么来初始化这些类加载器的吧.
首先, 我们知道, java程序都需要一个入口(main方法), 而在tomcat中,这个入口在
org.apache.catalina.startup.Bootstrap 这个类中。
看其结构:
定位到方法内部:
- public staticvoid main(String args[]) {
- if (daemon == null) {
- daemon = new Bootstrap();
- try {
- //初始化资源 (今天来了解的.)
- daemon.init();
- } catch (Throwable t) {
- t.printStackTrace();
- return;
- }
- }
- try {
- //默认为启动
- String command = "start";
- if (args.length > 0) {
- command = args[args.length - 1];
- }
- if (command.equals("startd")) {
- args[args.length - 1] ="start";
- daemon.load(args);
- daemon.start();
- } else if (command.equals("stopd")) {
- args[args.length - 1] ="stop";
- daemon.stop();
- } else if (command.equals("start")) {
- //设置标识
- daemon.setAwait(true);
- daemon.load(args);
- //开启
- daemon.start();
- } else if (command.equals("stop")) {
- daemon.stopServer(args);
- } else {
- log.warn("Bootstrap: command \"" + command +"\" does not exist.");
- }
- } catch (Throwable t) {
- t.printStackTrace();
- }
- }
public static void main(String args[]) {
if (daemon == null) {
daemon = new Bootstrap();
try {
//初始化资源 (今天来了解的.)
daemon.init();
} catch (Throwable t) {
t.printStackTrace();
return;
}
}
try {
//默认为启动
String command = "start";
if (args.length > 0) {
command = args[args.length - 1];
}
if (command.equals("startd")) {
args[args.length - 1] = "start";
daemon.load(args);
daemon.start();
} else if (command.equals("stopd")) {
args[args.length - 1] = "stop";
daemon.stop();
} else if (command.equals("start")) {
//设置标识
daemon.setAwait(true);
daemon.load(args);
//开启
daemon.start();
} else if (command.equals("stop")) {
daemon.stopServer(args);
} else {
log.warn("Bootstrap: command \"" + command + "\" does not exist.");
}
} catch (Throwable t) {
t.printStackTrace();
}
}
在tomcat启动之前, 需要初始化一些系统资源, 初始化的详细工作都定义在init()方法内部了。
OK,我们继续追踪一下。 定位到init()方法中.\
- public void init()
- throws Exception
- {
- // Set Catalina path 设置catalina基本路径
- 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");
- 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;
- }
public void init()
throws Exception
{
// Set Catalina path 设置catalina基本路径
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");
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;
}
可以看到, 上面的代码中,用来初始化类加载器、验证类加载器、
以及使用类加载器来加载类"org.apache.catalina.startup.Catalina"等等操作。
这篇文章,主要来探讨一下,tomcat初始化类加载器的方式, ,所以,我们追踪到方法initClassLoaders()中:
这里主要介绍一下,下面的流程, tomcat会调用initClassLoaders()方法。
用来初始化common ,catalina,shared三种类加载器,而这个操作是通过方法
createClassLoader(String name, ClassLoader parent)来完成的。
而后2个都属于common的子级,
所以下面给出2个方法的源代码(其中相关信息,都以注释给出):
- /**
- * 初始化类加载器:
- * 加载三种:
- * common.
- * / \
- * catalina shared.
- */
- private void initClassLoaders() {
- try {
- //创建common类加载器
- 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();
- }
- //创建catalina类加载器,指定其父级别的加载器为commonLoader.
- catalinaLoader = createClassLoader("server", commonLoader);
- //创建sharedLoader类加载器,指定其父级别的加载器为commonLoader.
- sharedLoader = createClassLoader("shared", commonLoader);
- } catch (Throwable t) {
- log.error("Class loader creation threw exception", t);
- System.exit(1);
- }
- }
/**
* 初始化类加载器:
* 加载三种:
* common.
* / \
* catalina shared.
*/
private void initClassLoaders() {
try {
//创建common类加载器
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();
}
//创建catalina类加载器,指定其父级别的加载器为commonLoader.
catalinaLoader = createClassLoader("server", commonLoader);
//创建sharedLoader类加载器,指定其父级别的加载器为commonLoader.
sharedLoader = createClassLoader("shared", commonLoader);
} catch (Throwable t) {
log.error("Class loader creation threw exception", t);
System.exit(1);
}
}
- /**
- * 创建类加载器
- *
- * @param name
- * @param parent 指定上一级别的类加载器
- * @return
- * @throws Exception
- */
- private ClassLoader createClassLoader(String name, ClassLoader parent)
- throws Exception {
- //这里以common为例: 从catalina.properties中获取common.loader 类加载信息
- //如:
- //common.loader=${catalina.home}/lib,${catalina.home}/lib/*.jar
- 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;
- //是否含有"${catalina.home}"
- while ((i=repository.indexOf(CATALINA_HOME_TOKEN))>=0) {
- replace=true;
- if (i>0) {
- //替换成tomcat路径 替换后的形式如下: c:/opensource/tomcat5/lib.
- repository = repository.substring(0,i) + getCatalinaHome()
- + repository.substring(i+CATALINA_HOME_TOKEN.length());
- } else {
- repository = getCatalinaHome()
- + repository.substring(CATALINA_HOME_TOKEN.length());
- }
- }
- //是否含有"${catalina.base}"
- 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 {
- URL url=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 = (String[]) repositoryLocations.toArray(new String[0]);
- Integer[] types = (Integer[]) 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 =
- (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;
- }
/**
* 创建类加载器
*
* @param name
* @param parent 指定上一级别的类加载器
* @return
* @throws Exception
*/
private ClassLoader createClassLoader(String name, ClassLoader parent)
throws Exception {
//这里以common为例: 从catalina.properties中获取common.loader 类加载信息
//如:
//common.loader=${catalina.home}/lib,${catalina.home}/lib/*.jar
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;
//是否含有"${catalina.home}"
while ((i=repository.indexOf(CATALINA_HOME_TOKEN))>=0) {
replace=true;
if (i>0) {
//替换成tomcat路径 替换后的形式如下: c:/opensource/tomcat5/lib.
repository = repository.substring(0,i) + getCatalinaHome()
+ repository.substring(i+CATALINA_HOME_TOKEN.length());
} else {
repository = getCatalinaHome()
+ repository.substring(CATALINA_HOME_TOKEN.length());
}
}
//是否含有"${catalina.base}"
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 {
URL url=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 = (String[]) repositoryLocations.toArray(new String[0]);
Integer[] types = (Integer[]) 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 =
(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;
}
到这里,我们可以确定,tomcat文档中的类加载器之间关系是准确的,并非凭空说的。
到这里,我们可能对于createClassLoader()方法的
- CatalinaProperties.getProperty(name + ".loader");
CatalinaProperties.getProperty(name + ".loader");
有点疑问, 到底tomcat是如果通过配置文件来获取需要初始化类加载器的相关信息的呢/
前面我们看到 catalina.properties中记录了tomcat三种类加载器中分别需要加载一些什么类的信息。
而CatalinaProperties类正是用来解析此文件的,以告诉tomcat,哪种类加载器,加载哪些类。
我们来看看这个类的源代码:
- /**
- * Utility class to read the bootstrap Catalina configuration.
- * 读取tomcat 的配置文件 catalina.properties
- * @author Remy Maucherat
- * @version $Revision: 467222 $ $Date: 2006-10-24 11:17:11 +0800 (星期二, 24 十月 2006) $
- */
- public class CatalinaProperties {
- // ------------------------------------------------------- Static Variables
- private static org.apache.juli.logging.Log log=
- org.apache.juli.logging.LogFactory.getLog( CatalinaProperties.class );
- private static Properties properties =null;
- static {
- loadProperties();
- }
- // --------------------------------------------------------- Public Methods
- /**
- * Return specified property value.
- */
- public static String getProperty(String name) {
- return properties.getProperty(name);
- }
- /**
- * Return specified property value.
- */
- public static String getProperty(String name, String defaultValue) {
- return properties.getProperty(name, defaultValue);
- }
- // --------------------------------------------------------- Public Methods
- /**
- * 加载配置信息
- * Load properties.
- */
- private staticvoid loadProperties() {
- InputStream is = null;
- Throwable error = null;
- //第一步: 从系统变量中查找
- try {
- //getConfigUrl()方法的内容为: System.getProperty("catalina.config");
- String configUrl = getConfigUrl();
- if (configUrl != null) {
- is = (new URL(configUrl)).openStream();
- }
- } catch (Throwable t) {
- // Ignore
- }
- //第二步:再从tomcat的conf目录下去找
- if (is == null) {
- try {
- File home = new File(getCatalinaBase());
- File conf = new File(home,"conf");
- File properties = new File(conf,"catalina.properties");
- is = new FileInputStream(properties);
- } catch (Throwable t) {
- // Ignore
- }
- }
- //还没找到: 则从类路径中加载.
- if (is == null) {
- try {
- is = CatalinaProperties.class.getResourceAsStream
- ("/org/apache/catalina/startup/catalina.properties");
- } catch (Throwable t) {
- // Ignore
- }
- }
- // 到这里的话,如果找到了,就将配置文件中加载过来
- if (is != null) {
- try {
- properties = new Properties();
- properties.load(is);
- is.close();
- } catch (Throwable t) {
- error = t;
- }
- }
- if ((is == null) || (error !=null)) {
- // Do something
- log.warn("Failed to load catalina.properties", error);
- // That's fine - we have reasonable defaults.
- properties=new Properties();
- }
- //将配置文件的key-value 设置为 系统变量.
- // Register the properties as system properties
- Enumeration enumeration = properties.propertyNames();
- while (enumeration.hasMoreElements()) {
- String name = (String) enumeration.nextElement();
- String value = properties.getProperty(name);
- if (value != null) {
- System.setProperty(name, value);
- }
- }
- }
- /**
- * Get the value of the catalina.home environment variable.
- */
- private static String getCatalinaHome() {
- return System.getProperty("catalina.home",
- System.getProperty("user.dir"));
- }
- /**
- * Get the value of the catalina.base environment variable.
- */
- private static String getCatalinaBase() {
- return System.getProperty("catalina.base", getCatalinaHome());
- }
- /**
- * Get the value of the configuration URL.
- */
- private static String getConfigUrl() {
- return System.getProperty("catalina.config");
- }
- }
/**
* Utility class to read the bootstrap Catalina configuration.
* 读取tomcat 的配置文件 catalina.properties
* @author Remy Maucherat
* @version $Revision: 467222 $ $Date: 2006-10-24 11:17:11 +0800 (星期二, 24 十月 2006) $
*/
public class CatalinaProperties {
// ------------------------------------------------------- Static Variables
private static org.apache.juli.logging.Log log=
org.apache.juli.logging.LogFactory.getLog( CatalinaProperties.class );
private static Properties properties = null;
static {
loadProperties();
}
// --------------------------------------------------------- Public Methods
/**
* Return specified property value.
*/
public static String getProperty(String name) {
return properties.getProperty(name);
}
/**
* Return specified property value.
*/
public static String getProperty(String name, String defaultValue) {
return properties.getProperty(name, defaultValue);
}
// --------------------------------------------------------- Public Methods
/**
* 加载配置信息
* Load properties.
*/
private static void loadProperties() {
InputStream is = null;
Throwable error = null;
//第一步: 从系统变量中查找
try {
//getConfigUrl()方法的内容为: System.getProperty("catalina.config");
String configUrl = getConfigUrl();
if (configUrl != null) {
is = (new URL(configUrl)).openStream();
}
} catch (Throwable t) {
// Ignore
}
//第二步:再从tomcat的conf目录下去找
if (is == null) {
try {
File home = new File(getCatalinaBase());
File conf = new File(home, "conf");
File properties = new File(conf, "catalina.properties");
is = new FileInputStream(properties);
} catch (Throwable t) {
// Ignore
}
}
//还没找到: 则从类路径中加载.
if (is == null) {
try {
is = CatalinaProperties.class.getResourceAsStream
("/org/apache/catalina/startup/catalina.properties");
} catch (Throwable t) {
// Ignore
}
}
// 到这里的话,如果找到了,就将配置文件中加载过来
if (is != null) {
try {
properties = new Properties();
properties.load(is);
is.close();
} catch (Throwable t) {
error = t;
}
}
if ((is == null) || (error != null)) {
// Do something
log.warn("Failed to load catalina.properties", error);
// That's fine - we have reasonable defaults.
properties=new Properties();
}
//将配置文件的key-value 设置为 系统变量.
// Register the properties as system properties
Enumeration enumeration = properties.propertyNames();
while (enumeration.hasMoreElements()) {
String name = (String) enumeration.nextElement();
String value = properties.getProperty(name);
if (value != null) {
System.setProperty(name, value);
}
}
}
/**
* Get the value of the catalina.home environment variable.
*/
private static String getCatalinaHome() {
return System.getProperty("catalina.home",
System.getProperty("user.dir"));
}
/**
* Get the value of the catalina.base environment variable.
*/
private static String getCatalinaBase() {
return System.getProperty("catalina.base", getCatalinaHome());
}
/**
* Get the value of the configuration URL.
*/
private static String getConfigUrl() {
return System.getProperty("catalina.config");
}
}
OK, 到此,tomcat初始化类加载器的过程,我们都已经了解了。
可能看到这里,有的人觉得还是不太理解。 好,让我们来总结一下,这个顺序。
我们按照我们平时常用的操作来看;
1.我要启动tomcat .. (调用Bootstrap的main 方法)
(1)tomcat启动之前,需要加载类,需要类加载器。 于是,它去做初始化工作. -----> init()方法.
(2)init()方法开始工作它再去调用------>initClassLoaders()方法.
(3)发现需要初始化3个类型的类加载器,再调用---> createClassLoader(name,parent) ,告诉它,我要初始化哪种类型的,它的老爸是谁。
(4)通过CatalinaProperties 类去联络catalina.properties,获得,这个哪种类加载器加载哪些类的信息。
(5) 完成初始化,并返回.
2.tomcat 加载其他资源(待续),启动成功……
OK, 文章写完了, 这些内容都是本人自己学习的记录,难免有错误之处,还望大家多提意见,希望能跟各位javaeyer共同交流,达到共同提高的目录。
===========================================
类加载器与Tomcat
Tomcat基于标准的类加载,再加上自己的类加载器就形成了类加载器层次。下图详细描述了该层次结构。
从图中可以看到,它与直接委托模型有些不一样,Common类加载器代理Server类加载器,这意味着Web应用类加载器不能访问内部Server类,而Common类加载器可以。
Tomcat与System类加载器
Tomcat使用默认的System类加载器,但是Tomcat通过下面来设置CLASSPATH环境变量: CATALINA_HOME/bin/bootstrap.jar CATALINA_HOME/bin/tomcat-juli.jar JAVA_HOME/lib/tools.jar 由于Tomcat在上面的文件列表中设置了CLASSPATH环境变量,因此在Tomcat生命周期内,system CLASSPATH变量会忽略。只要用户记住Tomcat拥有自己的classpath, 独立于System的Classpath,那么用户将增加的类放置在“Tomcat Common类加载器”一节中列出的目录中,就不会有任何问题。 Bootstrap.jar文件包含了Tomcat启动时所需的类。Tools.jar包含了javac编译器,用于在运行时编译JSP页面为class文件。Tomcat-juli.jar用于Tomcat的Bootstrap日志中。
Tomcat Common类加载器
下面讲述的是Tomcat的Common类加载器层次结构。Common类加载器加载这些Tomcat和所有Web应用所需要的类时,它从下面路径中加载表7-1中的类文件。 CATALINA_HOME/lib/
JAR文件 描述
annotation-api.jar 包含注释类 catalina.jar Tomcat6服务器中核心实现类
catalina-ant.jar Tomcat Catalina Ant任务
catalina-ha.jar High availability包
Catalina-tribes.jar Tomcat通信包
el-api.jar 表达式语言(EL)API
jasper.jar Jasper2编译和运行,将JSP文件转换成servlets
jasper-el.jar Jasper2表达式语言实现
jasper-jdt.jar Eclipse JDT3.2 Java编译器
jsp-api.jar JSP 2.1 API
servlet-api.jar Servlet 2.5 API
tomcat-coyote.jar Tomcat连接器和有用类的集合
tomcat-dbcp.jar 提供数据库链接池机制和其他对象链接池服务
tomcat-i18n-**.jar 可用的国际化JAR包
尽管开发人员可以引用其所有的API,但是不应允许他们在CATALINA_HOME/lib下放置自己的类和JAR文件。如果开发人员希望所有类和JAR文件对于所有Web应用程序都是可见的,那么应该把它们放置在Shared类加载器能够访问到的位置。注意这个不适用于第三方库,例如数据库驱动。因为Tomcat需要经常访问到这些类。 把自定义类放置在Common类加载器路径下,这是不提倡的,主要因为下面两个原因: 自定义类将会造成与Tomcat的兼容性问题。例如,如果用户将XML解析器放置在这个目录下,它会带来无法修复的程序问题;而将Servlet API的老版本入围在该目录下,也会带来同样的问题。 很容易忘记哪个类JAR文件属于开发人员,哪些属于tomcat自有的。因此维护是很棘手的。
Tomcat web应用类加载器
每个Web应用都有自己的类加载器,它们分别在 CATALINA_HOME/webapps/[webapp]/WEB-INF/lib和classes目录下搜寻JAR文件和类文件,Web应用类加载器有些独特,主要在于以下两点:
第一, 每个Web应用都有自己的类加载器的实例,这意味着Web应用不能看到其他人的类文件。
第二, Web应用类加载器不采用委托模式,取而代之的是,在请求转发到其他类加载器之前,它试图自己首先加载类。这些行为使得Web应用很容易地覆盖Shared和Common.
类加载器下的类。 应该注意的是,Web应用类加载器并不能覆盖掉Java基本类,这也是不可能的。下列是Web应用类加载器唯一例外的时候,它们通常采用委托模式: Javax.* Org.xml.sax.* Org.w3c.dom.* Org.apache.xerces.* Org.apache.xalan.* 如果父类不能加载它们,则Web应用类加载器将自己试图加载它们。
类加载器顺序
为了回顾一下不同的Tomcat类加载器是如何一直协同工作的,下面假设一个单独的应用请求一个类,类加载器将按照下列顺序查找加载:
1) Bootstrap类加载器在Java核心类中搜寻;
2) 这里是不是应该还有一个Extended类加载器在Java的扩展包中搜寻加载相关的类??
3) System类加载器在下列位置中搜寻: a) CATALINA_HOME/bin/bootstrap.jar b) CATALINA_HOME/bin/tomcat-juli.jar c) JAVA_HOME/lib/tools.jar
4) Web应用类加载器在CATALINA_HOME/webapps/[webapp]/WEB-INF/lib和classes目录下搜寻
5) Common类加载器将在CATALINA_HOME/lib目录下搜寻,加载该目录下的所有类和JAR文件。
动态类重载
前面讨论过,只要类加载器加载了一个类,它就将缓存该类。这意味着后面对该类的请求将会获得缓存的拷贝。因此,如果在JVM运行期间,该类在文件系统改变了,则新的类会被忽视。 然而,因为Tomcat使用自己的类加载器加载每个Web应用,所以它只能通过停止Web应用程序,然后使用新的类加载器重载该类,来实现动态类的重载。Web应用初始的类加载器就被孤立了,就会被JVM在空闲时当作垃圾收集。这种消除了当前的类版本部署时,需要重启JVM。 我们可以通过下面两种机制来实现Tomcat重载Web应用: 配置Tomcat搜寻WEB-INF/classes和WEB-INF/lib目录下的改变。 使用Tomcat管理应用程序来显式地重载Web应用。 Tomcat不会告诉它的类加载器转储它们的缓存,然后从磁盘上重载。而是当它检测到改变或者收到显式重载指令时,它会重载和重启所有Web应用。
========================================================================
看完了Java类装载器,我们再来看看应用服务器(Tomcat)对类加载器的使用,每个应用服务器都有一套自己的类加载器体系,从而与Java的类加载器区别开以达到自己与应用程序隔离的目的.
Tomcat的类加载器体系为:Bootstrap--->System--->Common---->Catalina,Shared
Shared------>Webapp1,Webapp2 .....
下面是对每个类加载器的定义:
1.Bootstrap加载器在这里是Java里的Bootstrap和ExtClassLoader的总称,负责加载Java核心包的类,和<Java_Home>/jre/lib/ext目录下的类.通常我们开发人员并不关心.我想只要是java程序这些肯定是必要的
2.System就是系统加载器,一般是AppClassLoader,负责加载ClassPath环境变量设置目录下的值,这个我们开发人员会非常关注,但是在Tomcat里面,虽然用AppClassLoader类加载器,但我们设置的ClassPath对它没有影响(如果有影响,那就麻烦了,将会导致Tomcat运行不稳定),为什么呢,因为tomcat每次启动的时候都会在命令行窗口中都会重新设置Classpath值为:<catalina_Home>/bin/bootstrap.jar和<java_Home>/lib/tools.jar,所以这里面的类一般对应用程序不可见的.除非你设置了
3.Common类加载器负责加载TomcatHOME/common/class下的.Class文件和common/lib中的jar包,这些类可以被Tomcat内核和每个Web应用程序都可以看见,一般放公用的一些重要的类,如servlet.jar等
4.Catalina类加载器从server/classes和server/lib下加载类,Catalina加载的类只对Tomcat服务器内核可见,对Web应用程序不可见,对于运行Tomcat内核的线程,它的上下文类加载器就是Catalina类加载器
5.Shared类加载器负责从share/classes和share/lib中加载类,它加载的类只对所有Web应用程序有效,对Tomcat不可见.
6.WebappX类加载器负责加载Web应用程序的/web-INF/classes和lib目录下的类,只对当前Web应用程序有效,对其他Web应用程序无效,对于运行每个Web应用程序的线程,他们的上下文类加载器就是它们各自的WebappX类加载器
总结:从以上6点可以看出Tomcat提供了完整的类加载器体系,并且分工非常的明确,因而非常访问我们进行Web应用程序的设置和部署.