Bootstrap

通过Tomcat的启动脚本能够看到启动的入口是在Bootstrap,来看下Bootstrapmain方法,

    /**
* 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

另一点值得注意的是,commonLoaderparentnull,也就是说当commonLoader进行类载入要托付给父类载入器时。将会绕过AppClassLoaderExtClassLoader,直接交给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,今天就先到这吧。

參考资料

最新文章

  1. Could not publish server configuration for Tomcat v6.0 Server at localhost.
  2. cookie和session简单的用法
  3. Oracle 数据库基础学习 (四) group by的使用
  4. 小记sql server临时表与表变量的区别
  5. Hibernate Hql 总结
  6. 50. Pow(x, n) (编程技巧)
  7. javaweb学习总结二十五(response对象的用法一)
  8. 怎么查看其它apk里面的布局代码及资源
  9. selector的理解
  10. [Javascript] Proper use of console.assert in JavaScript
  11. acm poj1260 dp
  12. laravel提示Mcrypt PHP extension required
  13. JavaSE(一) IO类层次关系和各种IO流的用法总结
  14. 【ACM小白成长撸】--贪婪法解硬币找零问题
  15. C语言之猜数游戏
  16. 小甲鱼OD学习第6讲
  17. [wordpress]WordPress地址(URL)错误,修改解决方案
  18. MyBatis进阶(四)
  19. Ubuntu 16.04 LTS 安装 miniconda
  20. PCI9054 总结(讲解非常清楚)

热门文章

  1. Perl初学笔记
  2. wordcontent小结
  3. python code 1_username registration &amp; login
  4. Hello World (记事本+命令行)
  5. hdu4565---So Easy!(矩阵)
  6. [Python] Wikipedia Crawler
  7. sql-server 2005数据库文件恢复(检測到基于一致性的逻辑 I/O 错误)
  8. Python!Are you kidding me?
  9. css+ js 实现圆环时钟
  10. 设置https验证方式