Tomcat源代码阅读#1:classloader初始化
Bootstrap
通过Tomcat的启动脚本能够看到启动的入口是在Bootstrap,来看下Bootstrap的main方法,
/**
* Main method and entry point when starting Tomcat via the provided
* scripts.
*
* @param args Command line arguments to be processed
*/
public static void main(String args[]) {
if (daemon == null) {
////////////////////////////////////////
// 1. Bootstrap初始化
////////////////////////////////////////
// Don't set daemon until init() has completed
Bootstrap bootstrap = new Bootstrap();
try {
bootstrap.init();
} catch (Throwable t) {
handleThrowable(t);
t.printStackTrace();
return;
}
daemon = bootstrap;
} else {
// When running as a service the call to stop will be on a new
// thread so make sure the correct class loader is used to prevent
// a range of class not found exceptions.
Thread.currentThread().setContextClassLoader(daemon.catalinaLoader);
}
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);
////////////////////////////////////////
// 2. org.apache.catalina.startup.Catalina#load
////////////////////////////////////////
daemon.load(args);
////////////////////////////////////////
// 3. org.apache.catalina.startup.Catalina#start
////////////////////////////////////////
daemon.start();
} else if (command.equals("stop")) {
daemon.stopServer(args);
} else if (command.equals("configtest")) {
daemon.load(args);
if (null==daemon.getServer()) {
System.exit(1);
}
System.exit(0);
} else {
log.warn("Bootstrap: command \"" + command + "\" does not exist.");
}
} catch (Throwable t) {
// Unwrap the Exception for clearer error reporting
if (t instanceof InvocationTargetException &&
t.getCause() != null) {
t = t.getCause();
}
handleThrowable(t);
t.printStackTrace();
System.exit(1);
}
}
来看下init方法,
/**
* Initialize daemon.
*/
public void init()
throws Exception
{
////////////////////////////////////////
// 1. 设置catalina.home与catalina.base
////////////////////////////////////////
// Set Catalina path
setCatalinaHome();
setCatalinaBase();
////////////////////////////////////////
// 2. classloader初始化
////////////////////////////////////////
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");
////////////////////////////////////////
// 3. 使用catalinaLoader载入Catalina
////////////////////////////////////////
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");
////////////////////////////////////////
// 4. 将Catalina的parentClassLoader设置为sharedLoader
////////////////////////////////////////
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;
}
能够看到须要先初始化ClassLoader。然后才去载入最重要的服务器类。也就是org.apache.catalina.startup.Catalina。有三个ClassLoader,
protected ClassLoader commonLoader = null;
protected ClassLoader catalinaLoader = null;
protected ClassLoader sharedLoader = null;
以下来看下ClassLoader初始化的代码,
private void initClassLoaders() {
try {
////////////////////////////////////////
// commonLoader的parent置为null
////////////////////////////////////////
commonLoader = createClassLoader("common", null);
////////////////////////////////////////
// 假设没有配置common.loader,则commonLoader就是AppClassLoader
////////////////////////////////////////
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) {
handleThrowable(t);
log.error("Class loader creation threw exception", t);
System.exit(1);
}
}
private ClassLoader createClassLoader(String name, ClassLoader parent)
throws Exception {
////////////////////////////////////////
// 从catalina.base/conf/catalina.properties读取配置
////////////////////////////////////////
String value = CatalinaProperties.getProperty(name + ".loader");
////////////////////////////////////////
// 默认情况下catalinaLoader与sharedLoader配置为空
// 所以三个ClassLoader事实上是同一个引用
////////////////////////////////////////
if ((value == null) || (value.equals("")))
return parent;
value = replace(value);
////////////////////////////////////////
// 加入配置的classpath
////////////////////////////////////////
List<Repository> repositories = new ArrayList<Repository>();
StringTokenizer tokenizer = new StringTokenizer(value, ",");
while (tokenizer.hasMoreElements()) {
String repository = tokenizer.nextToken().trim();
if (repository.length() == 0) {
continue;
}
// Check for a JAR URL repository
try {
@SuppressWarnings("unused")
URL url = new URL(repository);
repositories.add(
new Repository(repository, RepositoryType.URL));
continue;
} catch (MalformedURLException e) {
// Ignore
}
// Local repository
if (repository.endsWith("*.jar")) {
repository = repository.substring
(0, repository.length() - "*.jar".length());
repositories.add(
new Repository(repository, RepositoryType.GLOB));
} else if (repository.endsWith(".jar")) {
repositories.add(
new Repository(repository, RepositoryType.JAR));
} else {
repositories.add(
new Repository(repository, RepositoryType.DIR));
}
}
////////////////////////////////////////
// 差点儿相同是new URLClassLoader
////////////////////////////////////////
ClassLoader classLoader = ClassLoaderFactory.createClassLoader
(repositories, 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;
}
ClassLoader的配置放在catalina.properties,
#
#
# 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.base}/lib,${catalina.base}/lib/*.jar,${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=
能够看到仅仅有commonLoader才有配置,所以createClassLoader("server", commonLoader);与createClassLoader("shared", commonLoader);返回的都会是commonLoader。
另一点值得注意的是,commonLoader的parent是null,也就是说当commonLoader进行类载入要托付给父类载入器时。将会绕过AppClassLoader和ExtClassLoader,直接交给BootstrapClassLoader(不明确的同学能够參考这里)。
Catalina
使用catalinaLoader载入了Catalina之后。会调用Catalina#load方法,
public void load() {
long t1 = System.nanoTime();
////////////////////////////////////////
// 设置catalina.home与catalina.base
////////////////////////////////////////
initDirs();
// Before digester - it may be needed
initNaming();
////////////////////////////////////////
// 创建server.xml的解析器
////////////////////////////////////////
// Create and execute our Digester
Digester digester = createStartDigester();
////////////////////////////////////////
// 读取catalina.base/conf/server.xml
////////////////////////////////////////
InputSource inputSource = null;
InputStream inputStream = null;
File file = null;
try {
file = configFile();
inputStream = new FileInputStream(file);
inputSource = new InputSource(file.toURI().toURL().toString());
} catch (Exception e) {
if (log.isDebugEnabled()) {
log.debug(sm.getString("catalina.configFail", file), e);
}
}
if (inputStream == null) {
try {
inputStream = getClass().getClassLoader()
.getResourceAsStream(getConfigFile());
inputSource = new InputSource
(getClass().getClassLoader()
.getResource(getConfigFile()).toString());
} catch (Exception e) {
if (log.isDebugEnabled()) {
log.debug(sm.getString("catalina.configFail",
getConfigFile()), e);
}
}
}
// This should be included in catalina.jar
// Alternative: don't bother with xml, just create it manually.
if( inputStream==null ) {
try {
inputStream = getClass().getClassLoader()
.getResourceAsStream("server-embed.xml");
inputSource = new InputSource
(getClass().getClassLoader()
.getResource("server-embed.xml").toString());
} catch (Exception e) {
if (log.isDebugEnabled()) {
log.debug(sm.getString("catalina.configFail",
"server-embed.xml"), e);
}
}
}
if (inputStream == null || inputSource == null) {
if (file == null) {
log.warn(sm.getString("catalina.configFail",
getConfigFile() + "] or [server-embed.xml]"));
} else {
log.warn(sm.getString("catalina.configFail",
file.getAbsolutePath()));
if (file.exists() && !file.canRead()) {
log.warn("Permissions incorrect, read permission is not allowed on the file.");
}
}
return;
}
////////////////////////////////////////
// 解析server.xml
////////////////////////////////////////
try {
inputSource.setByteStream(inputStream);
digester.push(this);
digester.parse(inputSource);
} catch (SAXParseException spe) {
log.warn("Catalina.start using " + getConfigFile() + ": " +
spe.getMessage());
return;
} catch (Exception e) {
log.warn("Catalina.start using " + getConfigFile() + ": " , e);
return;
} finally {
try {
inputStream.close();
} catch (IOException e) {
// Ignore
}
}
getServer().setCatalina(this);
// Stream redirection
initStreams();
////////////////////////////////////////
// org.apache.catalina.Server初始化
////////////////////////////////////////
// Start the new server
try {
getServer().init();
} catch (LifecycleException e) {
if (Boolean.getBoolean("org.apache.catalina.startup.EXIT_ON_INIT_FAILURE")) {
throw new java.lang.Error(e);
} else {
log.error("Catalina.start", e);
}
}
long t2 = System.nanoTime();
if(log.isInfoEnabled()) {
log.info("Initialization processed in " + ((t2 - t1) / 1000000) + " ms");
}
}
load方法做的事情就是通过server.xml的配置去初始化Server。随后的Catalina#start事实上就是去启动Server。
public void start() {
if (getServer() == null) {
load();
}
if (getServer() == null) {
log.fatal("Cannot start server. Server instance is not configured.");
return;
}
long t1 = System.nanoTime();
////////////////////////////////////////
// 启动org.apache.catalina.Server
////////////////////////////////////////
// Start the new server
try {
getServer().start();
} catch (LifecycleException e) {
log.fatal(sm.getString("catalina.serverStartFail"), e);
try {
getServer().destroy();
} catch (LifecycleException e1) {
log.debug("destroy() failed for failed Server ", e1);
}
return;
}
long t2 = System.nanoTime();
if(log.isInfoEnabled()) {
log.info("Server startup in " + ((t2 - t1) / 1000000) + " ms");
}
// Register shutdown hook
if (useShutdownHook) {
if (shutdownHook == null) {
shutdownHook = new CatalinaShutdownHook();
}
Runtime.getRuntime().addShutdownHook(shutdownHook);
// If JULI is being used, disable JULI's shutdown hook since
// shutdown hooks run in parallel and log messages may be lost
// if JULI's hook completes before the CatalinaShutdownHook()
LogManager logManager = LogManager.getLogManager();
if (logManager instanceof ClassLoaderLogManager) {
((ClassLoaderLogManager) logManager).setUseShutdownHook(
false);
}
}
if (await) {
await();
stop();
}
}
alright,今天就先到这吧。
參考资料
- https://tomcat.apache.org/tomcat-7.0-doc/building.html
- https://tomcat.apache.org/tomcat-7.0-doc/class-loader-howto.html
- https://tomcat.apache.org/tomcat-7.0-doc/architecture/overview.html
- https://tomcat.apache.org/tomcat-7.0-doc/architecture/startup.html
- https://tomcat.apache.org/tomcat-7.0-doc/architecture/requestProcess.html
Tomcat源代码阅读#1:classloader初始化的更多相关文章
- 【转】Tomcat总体结构(Tomcat源代码阅读系列之二)
本文是Tomcat源代码阅读系列的第二篇文章,我们在本系列的第一篇文章:在IntelliJ IDEA 和 Eclipse运行tomcat 7源代码一文中介绍了如何在intelliJ IDEA 和 Ec ...
- 【转】Tomcat源代码阅读系列
在IntelliJ IDEA 和 Eclipse运行tomcat 7源代码(Tomcat源代码阅读系列之一) Tomcat总体结构(Tomcat源代码阅读系列之二) Tomcat启动过程(Tomcat ...
- Tomcat请求处理过程(Tomcat源代码解析五)
watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/ ...
- [转]madwifi无线网卡源代码阅读
转自:http://xiyong8260.blog.163.com/blog/static/66514621200892465922669/ 在我的Doctor课题研究中,基于ARF协议设计了一个改进 ...
- Tomcat源代码解析系列
学web也有一段时间了.为了从底层了解web应用在Tomcat中的执行,决定看一下Tomcat的源代码參见<How Tomcat works> 和大牛博客.对大体架构有了一定的了解, ...
- 非常好!!!Linux源代码阅读——内核引导【转】
Linux源代码阅读——内核引导 转自:http://home.ustc.edu.cn/~boj/courses/linux_kernel/1_boot.html 目录 Linux 引导过程综述 BI ...
- 非常好!!!Linux源代码阅读——环境准备【转】
Linux源代码阅读——环境准备 转自:http://home.ustc.edu.cn/~boj/courses/linux_kernel/0_prepare.html 目录 Linux 系统环境准备 ...
- 非常好!!!Linux源代码阅读——中断【转】
Linux源代码阅读——中断 转自:http://home.ustc.edu.cn/~boj/courses/linux_kernel/2_int.html 目录 为什么要有中断 中断的作用 中断的处 ...
- Java 推荐读物与源代码阅读
Java 推荐读物与源代码阅读 江苏无锡 缪小东 1. Java语言基础 谈到Java ...
随机推荐
- 学习——HTML5
HTML5多用于手机页面制作,因为PC版浏览器大多不兼容,可以通过下面网站查看HTML5浏览器兼容情况: http://www.caniuse.com/#index 一.语义化标签 1.<hea ...
- 【Henu ACM Round #13 C】 Ebony and Ivory
[链接] 我是链接,点我呀:) [题意] 在这里输入题意 [题解] 先求出c-bx的所有可能 ->存在map里面 然后枚举y看看ay在不在map里面 在的话就有解. 这样复杂度是\(O(N*lo ...
- Java Security安全系列文档翻译笔记————KeyStore、密钥、证书、命令行实战
发送方任务: 1.将文档.源代码打包到jar包(这样才干够签名) 2.在keystore中生成相应的Private key和Public key 3.用Private Key对jar包进行签名,这是j ...
- 【Swift初见】Swift数组(二)
在苹果的开发文档中对Array还提供了其它的操作算法: 1.Sort函数: 对数组进行排序.依据指定的排序规则,看以下的代码: var array = [2, 3, 4, 5] array.sort{ ...
- js设计模式--------基本概念的理解
1.闭包,前面已经说过,这里不再做说明 2.封装 对于JS而言,他不像java一样存在私有,公有 ,可以让对象在一些细节方面存在差异,降低他们的耦合程度,对数据做一些约束,我们可以更容易调试,封 ...
- Elasticsearch之marvel(集群管理、监控)插件安装之后的浏览详解
前提 Elasticsearch之插件介绍及安装 https://i.cnblogs.com/posts?categoryid=950999&page=2 (强烈建议,从头开始看) 比如,我 ...
- WebMethod Description
http://www.webxml.com.cn/WebServices/WeatherWebService.asmx https://www.cnblogs.com/wanganyi/p/72202 ...
- 用css让元素隐藏的几种办法
display:none; //能隐藏并不占空间 visibility:hidden; //隐藏但占据空间 opacity:0; position:absolute 移动到不在页面显示的地方
- ZJOI2008骑士
Z国的骑士团是一个很有势力的组织,帮会中汇聚了来自各地的精英.他们劫富济贫,惩恶扬善,受到社会各界的赞扬. 最近发生了一件可怕的事情,邪恶的Y国发动了一场针对Z国的侵略战争.战火绵延五百里,在和平环境 ...
- VMware Vsphere 6.0安装部署 Vsphere ESXi安装
Vsphere ESXi安装 ESXi作为虚拟化环境的Hypervisor层,负责将服务器虚拟成资源池,提供接口供管理组件调用,将下面的iso刻录成光盘或可启动U盘,安装在服务器裸机上: 下载地址请见 ...