二. 初始化
1. 首先是Bootstrap的#init()操作。
public void init() throws Exception { // 设定Catalina setCatalinaHome(); setCatalinaBase(); // 初始化ClassLoader initClassLoaders(); // 设置线程的上下文的ClassLoader Thread.currentThread().setContextClassLoader(catalinaLoader); SecurityClassLoad.securityClassLoad(catalinaLoader); // 实例化Catalina对象 if (log.isDebugEnabled()) log.debug("Loading startup class"); Class startupClass = catalinaLoader.loadClass("org.apache.catalina.startup.Catalina"); Object startupInstance = startupClass.newInstance(); 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; }
init方法主要是对Catalina环境的设定和ClassLoader的初始化,下面来一个一个的查看:
① Catalina的环境设定:
这里主要是setCatalinaHome()和setCatalinaBase()这两个方法
private void setCatalinaHome() { if (System.getProperty("catalina.home") != null) return; File bootstrapJar = new File(System.getProperty("user.dir"), "bootstrap.jar"); if (bootstrapJar.exists()) { try { System.setProperty("catalina.home", (new File(System.getProperty("user.dir"), "..")).getCanonicalPath()); } catch (Exception e) { // Ignore System.setProperty("catalina.home", System.getProperty("user.dir")); } } else { System.setProperty("catalina.home", System.getProperty("user.dir")); } }
这个方法主要是对catalina.home的设定。
private void setCatalinaBase() { if (System.getProperty("catalina.base") != null) return; if (System.getProperty("catalina.home") != null) System.setProperty("catalina.base", System.getProperty("catalina.home")); else System.setProperty("catalina.base", System.getProperty("user.dir")); }
这个方法主要是对catalina.base的设定。
② 初始化ClassLoader
ClassLoader在Tomcat的启动中是比较重要的部分,我在Apache Tomcat的网站上找到了一些关于Tomcat ClassLoader的说明,简单的介绍下。
这个是Tomcat的ClassLoader的继承结构
Bootstrap
|
System
|
Common
/ \
Webapp1 Webapp2 ...
Bootstrap是JVM提供的ClassLoader,它主要负责载入$JAVA_HOME/jre/lib/ext下的类文件。
System主要负责载入CLASSPATH下的类文件,这些类对Tomcat容器内部的类和Web应用程序都可见。
Common主要载入$CATALINA_HOME/lib下面的类文件,也是同时对Tomcat容器内部的类和Web应用程序都可见。
WebappX载入每一个Web应用程序的类文件,包括/WEB-INF/classes下的类文件和/WEB-INF/lib下的jar文件。并且WebApp之间是不可见的。
而Tomcat5.5的ClassLoader结构是与Tomcat6.0有所不同的
Bootstrap
|
System
|
Common
/ \
Catalina Shared
/ \
Webapp1 Webapp2 ...
在Common下边还有Catalina和Shared两个ClassLoader(Tomcat6.0也是有的,只是默认没有使用)。Catalina负责载入Tomcat内部可见而WebApp不可见的类文件。Shared负责载入所有WebApp都可见的类文件。
原文的网址是: http://tomcat.apache.org/tomcat-6.0-doc/class-loader-howto.html
http://tomcat.apache.org/tomcat-5.5-doc/class-loader-howto.html
简单介绍过之后,看一下#initClassLoaders()方法,此方法中实现了ClassLoader的初始化:
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(); } // 父loader是commonloader catalinaLoader = createClassLoader("server", commonLoader); // 父loader是commonloader sharedLoader = createClassLoader("shared", commonLoader); } catch (Throwable t) { log.error("Class loader creation threw exception", t); System.exit(1); } }
这个方法很容易看懂,就是对3个ClassLoader的初始化。其中调用了createClassLoader()方法,这个也是比较重要的,下面来看下:
private ClassLoader createClassLoader(String name, ClassLoader parent) throws Exception { // 获取与ClassLoader相关的属性值,可能为common.loader, server.loader, shared.loader // 定义在org/apache/catalina/startup/catalina.properties String value = CatalinaProperties.getProperty(name + ".loader"); // catalinaLoader和sharedLoader默认没有配置值,所以默认使用父loader,就是Common loader if ((value == null) || (value.equals("")))// 如果没有定义,那么使用父loader return parent; // 路径位置 ArrayList repositoryLocations = new ArrayList(); // 路径类型 ArrayList repositoryTypes = new ArrayList(); int i; // ClassLoader将载入的路径是用","分割的 StringTokenizer tokenizer = new StringTokenizer(value, ","); while (tokenizer.hasMoreElements()) { // 类文件路径 String repository = tokenizer.nextToken(); // 是否对目录进行过替换 boolean replace = false; String before = repository; // 对"${catalina.home}"进行替换 while ((i = repository.indexOf(CATALINA_HOME_TOKEN)) >= 0) { replace = true; if (i > 0) {// 如果"${catalina.home}"不是在字符串首 repository = repository.substring(0, i) + getCatalinaHome() + repository.substring(i + CATALINA_HOME_TOKEN.length()); } else {// 如果"${catalina.home}"在字符串首 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 " + replace); // Check for a JAR URL repository try { // url用于测试这个目录是否是合法的URL,如果不是,会报出异常,跳到后边。 URL url = new URL(repository); // URL 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()); // jar文件目录 repositoryLocations.add(repository); repositoryTypes.add(ClassLoaderFactory.IS_GLOB); } else if (repository.endsWith(".jar")) { // 单独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 classLoader = ClassLoaderFactory.createClassLoader(locations, types, parent); // MBean server,我不懂JMX~~ MBeanServer mBeanServer = null; if (MBeanServerFactory.findMBeanServer(null).size() > 0) { mBeanServer = (MBeanServer) MBeanServerFactory.findMBeanServer(null).get(0); } else { mBeanServer = MBeanServerFactory.createMBeanServer(); } ObjectName objectName = new ObjectName("Catalina:type=ServerClassLoader,name=" + name); mBeanServer.registerMBean(classLoader, objectName); return classLoader; }
关键点给标上了一些注释,其中返回ClassLoader的那一行值得关注一下,
ClassLoader classLoader = ClassLoaderFactory.createClassLoader(locations, types, parent);
自然,要看一下ClassLoaderFactory#createClassLoader()到底做了什么。
public static ClassLoader createClassLoader(String locations[], Integer types[], ClassLoader parent) throws Exception { if (log.isDebugEnabled()) log.debug("Creating new class loader"); // Construct the "class path" for this class loader ArrayList list = new ArrayList(); // 确保locations和types不为空且长度相同 if (locations != null && types != null && locations.length == types.length) { // 查看每一个location for (int i = 0; i < locations.length; i++) { String location = locations[i]; if (types[i] == IS_URL) {// URL URL url = new URL(location); if (log.isDebugEnabled()) log.debug(" Including URL " + url); list.add(url); } else if (types[i] == IS_DIR) {// 目录 File directory = new File(location); directory = new File(directory.getCanonicalPath()); if (!directory.exists() || !directory.isDirectory() || !directory.canRead()) continue; URL url = directory.toURI().toURL(); if (log.isDebugEnabled()) log.debug(" Including directory " + url); list.add(url); } else if (types[i] == IS_JAR) {// 单独的jar文件 File file = new File(location); file = new File(file.getCanonicalPath()); if (!file.exists() || !file.canRead()) continue; URL url = file.toURI().toURL(); if (log.isDebugEnabled()) log.debug(" Including jar file " + url); list.add(url); } else if (types[i] == IS_GLOB) {// jar目录 File directory = new File(location); if (!directory.exists() || !directory.isDirectory() || !directory.canRead()) continue; if (log.isDebugEnabled()) log.debug(" Including directory glob " + directory.getAbsolutePath()); 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]); file = new File(file.getCanonicalPath()); if (!file.exists() || !file.canRead()) continue; if (log.isDebugEnabled()) log.debug(" Including glob jar file " + file.getAbsolutePath()); URL url = file.toURI().toURL(); list.add(url); } } } } // 类文件的URL列表 URL[] array = (URL[]) list.toArray(new URL[list.size()]); if (log.isDebugEnabled()) for (int i = 0; i < array.length; i++) { log.debug(" location " + i + " is " + array[i]); } // 继承自URLClassLoader StandardClassLoader classLoader = null; if (parent == null) classLoader = new StandardClassLoader(array); else classLoader = new StandardClassLoader(array, parent); return (classLoader); }
至此,ClassLoader生成完毕,并将需要载入的类文件路径传给了构造函数。
初始化ClassLoader之后,调用了Thread.currentThread().setContextClassLoader(catalinaLoader);这一句包含的内容也很有趣,下面来研究一下:
虽然有些本末倒置之嫌,但还是要介绍一下ClassLoader的相关内容:
与ClassLoader相关的概念主要有4个,分别是基本的Classloader,自定义Classloader,Caller Classloader,当前线程的上下文Classloader,下面一一介绍:
基本的Classloader
⑴最基本的Classloader是Bootstrap Classloader和System Classloader(或者说AppClassLoader)
很多内容借鉴自 http://fyjava.iteye.com/blog/198119
Bootstrap Classloader
这个Classloader装载Java虚拟机提供的基本运行时刻类($JAVA_HOME/jre/lib),还包括放置在系统扩展目录($ JAVA_HOME/jre/lib/ext)内的JAR文件中的类。这个Classloader是java程序最顶层的Classloader,只有它没有父Classloader。如果你将一个自己写的类或第三方jar包放进$JAVA_HOME/jre/lib/ext目录中,那么它将被 Bootstrap Classloader装载。
System Classloader
System Classloader通常负责装载系统环境变量CLASSPATH中设置的类。
⑵自定义Classloader
在编写应用代码的时候,常常有需要动态加载类和资源,比如显式的调用classLoader.loadClass(“ClassName”),虽然直接使用 ClassLoader.getSystemClassLoader(),可以得到SystemlassLoader来完成这项任务。但是,由于 System Classloader是JVM创建的Classloader,它的职责有限,只适合于普通的java应用程序,在很多复杂场景中不能满足需求,比如在应用服务器中。这时候就需要自行实现一个Classloader的子类。
⑶Caller Classloader
Caller Classloader指的是当前所在的类装载时使用的Classloader,它可能是System Classloader,也可能是一个自定义的Classloader,这里,我们都称之为Caller Classloader。我们可以通过getClass().getClassLoader()来得到Caller Classloader。例如,存在A类,是被AClassLoader所加载,A.class.getClassLoader()为AClassLoader的实例,它就是A.class的Caller Classloader。
如果在A类中使用new关键字,或者Class.forName(String className)和Class.getResource(String resourceName)方法,那么这时也是使用Caller Classloader来装载类和资源。比如在A类中初始化B类:
/**
* A.java
*/
...
public void foo() {
B b = new B();
b.setName("b");
}
那么,B类由当前Classloader,也就是AClassloader装载。同样的,修改上述的foo方法,其实现改为:
Class clazz = Class.forName("foo.B");最终获取到的clazz,也是由AClassLoader所装载。
那么,如何使用指定的Classloader去完成类和资源的装载呢?或者说,当需要去实例化一个Caller Classloader和它的父Classloader都不能装载的类时,怎么办呢?
一个很典型的例子是JAXP,当使用xerces的SAX实现时,我们首先需要通过rt.jar中的 javax.xml.parsers.SAXParserFactory.getInstance()得到xercesImpl.jar中的 org.apache.xerces.jaxp.SAXParserFactoryImpl的实例。由于JAXP的框架接口的class位于 JAVA_HOME/lib/rt.jar中,由Bootstrap Classloader装载,处于Classloader层次结构中的最顶层,而xercesImpl.jar由低层的Classloader装载,也就是说SAXParserFactoryImpl是在SAXParserFactory中实例化的,如前所述,使用SAXParserFactory的Caller Classloader(这里是Bootstrap Classloader)是完成不了这个任务的。
这时,我们就需要了解一下线程上下文Classloader了。
⑷线程上下文Classloader
每个线程都有一个关联的上下文Classloader。如果使用new Thread()方式生成新的线程,新线程将继承其父线程的上下文Classloader。如果程序对线程上下文Classloader没有任何改动的话,程序中所有的线程将都使用System Classloader作为上下文Classloader。
当使用Thread.currentThread().setContextClassLoader(classloader)时,线程上下文 Classloader就变成了指定的Classloader了。此时,在本线程的任意一处地方,调用Thread.currentThread(). getContextClassLoader(),都可以得到前面设置的Classloader。
回到JAXP的例子,假设 xercesImpl.jar只有AClassLoader能装载,现在A.class内部要使用JAXP,但是A.class却不是由 AClassLoader或者它的子Classloader装载的,那么在A.class中,应该这样写才能正确得到xercesImpl的实现:
AClassLoader aClassLoader = new AClassLoader(parent);
Thread.currentThread().setContextClassLoader(aClassLoader);
SAXParserFactory factory = SAXParserFactory.getInstance();
...
JAXP这时就可以通过线程上下文Classloader装载xercesImpl的实现类了,当然,还有一个前提是在配制文件或启动参数中指定了使用xerces作为JAXP的实现。下面是JAXP中的代码片断:
ClassLoader cl = Thread.currentThread().getContextClassLoader();
…
Class providerClass = cl.loadClass(className);
…
③ 然后,回到Bootstrap#init()方法中来,载入了ClassLoader后,执行了下述代码
if (log.isDebugEnabled()) log.debug("Loading startup class"); Class startupClass = catalinaLoader.loadClass("org.apache.catalina.startup.Catalina"); Object startupInstance = startupClass.newInstance(); // 调用org.apache.catalina.startup.Catalina#setParentClassLoader(),并将前面讲到的Shared ClassLoader作为参数传递给它。 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引用Catalina对象。 catalinaDaemon = startupInstance;
setParentClassLoader()这个方法的定义倒是很简单,如下所示:
public void setParentClassLoader(ClassLoader parentClassLoader) { this.parentClassLoader = parentClassLoader; }
具体Catalina的parentClassLoader有什么作用看到这里还无法知道。
至此,init()方法就简单看完了,下面来看一下Bootstrap#load(String[] arguments)方法。
发表评论
-
出现java.lang.UnsupportedClassVersionError 错误的原因
2010-08-16 23:11 837出现java.lang.UnsupportedClassVer ... -
Tomcat请求处理(七) - Servlet实例的调用
2009-05-07 11:36 1092Tomcat请求处理中Servlet实例的调用是和Filter ... -
请求在Tomcat中传到了CoyoteAdapter的#service()方法中后,就要准备进入Pi
2009-05-07 11:35 1759首先,来看一下Servlet的载入过程。 具体是在org.ap ... -
Tomcat请求处理(五) -- 请求在容器间的流动
2009-05-07 11:34 1383请求在Tomcat中传到了CoyoteAdapter的#ser ... -
Tomcat请求处理(四) -- Request, Response, 和Pipeline
2009-05-07 11:33 12481. Request和Response 当处理请求的时候,To ... -
Tomcat请求处理(三) -- coyote请求处理
2009-05-07 11:32 1274在上一篇文章文章中,Tomcat的请求处理到了JIoEndpo ... -
Tomcat请求处理(二) -- 请求处理框架
2009-05-07 11:30 944书接上文。 当Tomcat的Acceptor监听到有请求到来时 ... -
Tomcat请求处理(一) -- 服务器端口监听
2009-05-07 11:29 1345其实tomcat在哪个类中监听请求的代码很容易找到: 在org ... -
Tomcat启动部分源代码分析(五) -- 应用程序加载
2009-05-07 11:28 1161前面所叙述的tomcat启动 ... -
Tomcat启动部分源代码分析(四) -- 开启容器
2009-05-07 11:27 1162四. 开启容器 最后是Bootstrap#start()方法的 ... -
Tomcat启动部分源代码分析(三) -- 载入
2009-05-07 11:23 1146二. 载入 2. Bootstrap的#Bootstrap#l ... -
Tomcat启动部分源代码分析(一) -- 概览
2009-05-07 11:17 1387一. 概览 本文所涉及的Tomcat为6.0版本。 Tomca ... -
Tomcat的Session管理(二) - Session后台处理
2009-05-07 10:10 935Tomcat会开启一个后台线程每隔一段时间检查Session的 ... -
Tomcat的Session管理(一) - Session的生成
2009-05-07 10:02 1458Session对象的创建一般是源于这样的一条语句: Sessi ...
相关推荐
旦容器初始化已经结束,调时就会出现异常:2022/2/23 下午8:40Tomcat 源代码调试笔记 - 看不见的 Shell我看了下这个 if 之后的语句,并
毕业设计 源代码 初始化数据库 二、技术实现 后台框架:springboot, mybatis, jsp 前台框架:adminlte 数据库:MySQL 开发环境:JDK、idea、Tomcat 三、启动说明 修改数据库ip 直接运行 ZqdglxtApplication 在...
基于 SpringBoot 花卉花店管理系统 毕业设计 源代码 初始化数据库 一、项目简介 这是一套基于JavaWeb的管理系统,主要针对计算机相关专业的正在做毕设的学生和需要项目实战练习的Java学习者。 包含:项目源码、...
一、项目简介 这是一套基于JavaWeb的客房 旅店 管理系统,主要针对计算机相关专业的正在做毕设的学生和需要项目实战练习的Java学习者。...基于 SpringBoot 客房 旅店 管理系统 毕业设计 源代码 初始化数据库
8.3 关联spring源代码87 8.4 小结89 第9章 创建Spring MVC之器90 9.1 整体结构介绍90 9.2 HttpServletBean93 9.3 FrameworkServlet95 9.4 DispatcherServlet100 9.5 小结107 第10章 Spring MVC之用108 10.1...
二、技术实现 后台框架:springboot, mybatis, jsp 前台框架:adminlte 数据库:MySQL 开发环境:JDK、idea、Tomcat 三、启动说明 修改数据库ip 直接运行 GzglxtApplication 在浏览器访问 127.0.0.1:8080 即可 ...
2. 将项目源代码复制到ECLIPSE的工程目录,在eclipse中导入项目,通过 update context definition将项目部署到tomcat5.5中。 项目编译成功后,完成了数据库的初始化! 3. 重新启动TOMCAT 5.5 系统的运行地址: ...
二、技术实现 后台框架:springboot, mybatis, jsp 前台框架:adminlte 数据库:MySQL 开发环境:JDK、idea、Tomcat 三、启动说明 修改数据库ip 直接运行 JwglApplication 在浏览器访问 127.0.0.1:8080 即可 用户名...
二、Linux的优势------------------------------------------------------------------------------------ Linux的安装----------------------------------------------------------------------------------------...
Calendar万年历 1个目标文件 EJB 模拟银行ATM流程及操作源代码 6个目标文件,EJB来模拟银行ATM机的流程及操作:获取系统属性,初始化JNDI,取得Home对象的引用,创建EJB对象,并将当前的计数器初始化,调用每一个...
1.首先使用spring初始化器新建一个工程: 2.取好自己的工程名,并且选择==打包方式为war==的形式: 3.工程自动创建的两个类 4.创建webapp、WEB-INF、web.xml 二、使用外部Tomcat 三、测试是否支持jsp controller ...
酒店管理系统》的源代码和论文等资料,都是由学生提供... 介绍思路 1 《酒店管理系统》 资源目录介绍 2 安装配置 1)前期准备 a、安装好MySQL数据库,用户名root,密码root b、安装配置java环境(JDK1.7+,当前...
读者应该确保计算机上安装有SQL Server 2000服务器,启动SQL Server服务后,打开查询分析器,然后执行本章目录下的db.sql文件自动生成并初始化数据库。 2.将本章的代码(文件夹news)拷贝到Tomcat服务器安装目录的...
1. 初始化数据库 在本地MySQL中创建自己的schema,执行初始化数据库的脚本 [`hupu-spider/src/main/resources/db.sql`](https://github.com/CrowHawk/MagicToe/blob/master/hupu-spider/src/main/resources/db.sql) ...
基于JSP毕业设计-教师档案管理系统(源代码+论文).zip 教师档案管理系统是一个典型的高校档案管理系统,其开发过程主要包括后台数据库的建立和维护以及前端应用程序的开发两个方面。对于前者要求建立起数据一致性和...
2)初始化数据库 a、创建db_jiudian数据库 b、执行数据库脚本文件,初始化数据库 3)部署酒店系统 4)启动测试 3 功能介绍 1)基本操作 2)客户管理 3)客房管理 4)菜品管理 5)餐桌预定 6)餐饮消费...
errorInit.java 演示变量初始化错误的程序 integerExample.java 演示各种整型变量的使用 isPrime.java 判断素数 leapYearByIf.java 用if语句判断闰年 leapYearByLogical.java 用逻辑表达式判断闰年 lowToUpper...
《酒店管理系统》的源代码和论文等资料,都是由学生提供... 介绍思路 1 《酒店管理系统》 资源目录介绍 2 安装配置 1)前期准备 a、安装好MySQL数据库,用户名root,密码root b、安装配置java环境(JDK1.7+,...
2.初始不了SessionFactory 可能是二级缓存初始化问题,去掉二级缓存即可(在hibernate.配置文件中)(没多大用) 3. hibernate配置文件和applicationContent中也要修改数据库用户名和密码 因为这个是网站,所以源...