Tomcat启动流程解析

来源:互联网 发布:淘宝我的小蜜是客服么 编辑:程序博客网 时间:2024/05/21 11:20

一、前言

众所周知,Tomcat是作为一款优秀的web服务容器被广泛应用。Tomcat是基于J2EE规范实现了经典的双亲委派模型的类加载体系。

二、相关概念

1JAVA虚拟机主要的类加载器:

aBootstrap Loader:加载lib目录下或者System.getProperty("sun.boot.class.path")、或者-XBootclasspath所指定的路径或jar

bExtended Loader:加载lib\ext目录下或者System.getProperty("java.ext.dirs")所指定的路径或jar。在使用Java运行程序时,也可以指定其搜索路径,例如:java-Djava.ext.dirs=d:\projects\testproj\classes HelloWorld

cAppClass Loader:加载System.getProperty("java.class.path")所指定的路径或jar。在使用Java运行程序时,也可以加上-cp来覆盖原有的Classpath设置,例如: java -cp ./lavasoft/classes HelloWorld

2、双亲委派模式类加载体系

双亲委派模式类加载体系概念:每个ClassLoader实例都有一个父类加载器的引用(不是继承的关系,是一个包含的关系),虚拟机内置的类加载器(Bootstrap ClassLoader)本身没有父类加载器,但可以用作其它ClassLoader实例的的父类加载器。当一个ClassLoader实例需要加载某个类时,它会试图亲自搜索某个类之前,先把这个任务委托给它的父类加载器,这个过程是由上至下依次检查的,首先由最顶层的类加载器Bootstrap ClassLoader试图加载,如果没加载到,则把任务转交给Extension ClassLoader试图加载,如果也没加载到,则转交给App ClassLoader进行加载,如果它也没有加载得到的话,则返回给委托的发起者,由它到指定的文件系统或网络等URL中加载该类。如果它们都没有加载到这个类时,则抛出ClassNotFoundException异常。否则将这个找到的类生成一个类的定义,并将它加载到内存当中,最后返回这个类在内存中的Class实例对象。

双亲委派模式类加载体系图示:


从上图中可以看出,JVM虚拟机的类加载器执行的顺序为:
BootStrap ClassLoader > Extension ClassLoader > App ClassLoader
,除了上述的类加载器之外,我们也可以定义自己的类加载器,但是这些自定义的类加载器都必须继承java.lang.ClassLoader类,同样Extension ClassLoaderApp ClassLoader也继承这个类。但是Bootstrap ClassLoader不继承自ClassLoader,因为它不是一个普通的Java类,底层由C++编写,已嵌入到了JVM内核当中,当JVM启动后,Bootstrap ClassLoader也随着启动,负责加载完核心类库后,并构造Extension ClassLoaderApp ClassLoader类加载器。

三、tomcat类加载加载机制

1、前言中已经提到tomcat类加载机制是基于J2EE的双亲委派模式,双亲委派模式此处就不再赘述。tomcat在双亲委派模式基础上定制了自己的类加载器,其类加载体系图如下:


从上图片看出tomcat的类加载体系中包含以下几个类加载器:

aClassLoaderJava提供的类加载器抽象类,用户自定义的类加载器需要继承实现;

bcommonLoaderTomcat最基本的类加载器,加载路径中的class可以被Tomcat容器本身以及各个Webapp访问;

ccatalinaLoaderTomcat容器私有的类加载器,加载路径中的class对于Webapp不可见;

dsharedLoader:各个Webapp共享的类加载器,加载路径中的class对于所有Webapp可见,但是对于Tomcat容器不可见;

eWebappClassLoader:各个Webapp私有的类加载器,加载路径中的class只对当前Webapp可见;

四、tomcat启动源码分析(tomcat8为例)

1tomcat启动从执行 org.apache.catalina.startup.Bootstrap.main()方法开始。main方法主要实现的逻辑如下源码:

public static void main(String[] args) {    if(daemon == null) {        Bootstrap t = new Bootstrap();        try {            t.init();        } catch (Throwable var3) {            handleThrowable(var3);            var3.printStackTrace();            return;        }        daemon = t;    } else {    Thread.currentThread().setContextClassLoader(daemon.catalinaLoader);    }    try {        String t2 = "start";        if(args.length > 0) {            t2 = args[args.length - 1];        }      ……}

上叙代码主要实现的BootStrap的初始化及设置当前线程的类加载器。

2main访问中调用的init()方法源码如下:

public void init() throws Exception {    this.initClassLoaders();    Thread.currentThread().setContextClassLoader(this.catalinaLoader);    SecurityClassLoad.securityClassLoad(this.catalinaLoader);    if(log.isDebugEnabled()) {        log.debug("Loading startup class");    }    Class startupClass = this.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[]{Class.forName("java.lang.ClassLoader")};    Object[] paramValues = new Object[]{this.sharedLoader};    Method method = startupInstance.getClass().getMethod(methodName, paramTypes);    method.invoke(startupInstance, paramValues);    this.catalinaDaemon = startupInstance;}

上述代码执行的逻辑说明如下:

ainitClassLoaders()方法中初始化了commonLoader,catalinaLoader,sharedLoader这几个类加载器,具体初始化过程可以参看源码

b、设置当前线程的类加载器setContextClassLoader,设置安全的类加载器ecurityClassLoad.securityClassLoad(this.catalinaLoader);

c、通过java反射机制初始化org.apache.catalina.startup.Catalina并执行setParentClassLoader方法

3、同时main方法中还有部分源码如下:

String t2 = "start";if(args.length > 0) {    t2 = args[args.length - 1];}if(t2.equals("startd")) {    args[args.length - 1] = "start";    daemon.load(args);    daemon.start();} else if(t2.equals("stopd")) {    args[args.length - 1] = "stop";    daemon.stop();} else if(t2.equals("start")) {    daemon.setAwait(true);    daemon.load(args);    daemon.start();} else if(t2.equals("stop")) {    daemon.stopServer(args);} else if(t2.equals("configtest")) {    daemon.load(args);    if(null == daemon.getServer()) {        System.exit(1);    }    System.exit(0);

上述代码中的dameon.stopServer(),dameon.start(),dameon.load()都需要通过反射机制调用到org.apache.catalina.startup.Catalina类中相应的方法执行操作。

4org.apache.catalina.startup.Catalina类中封装了所有的tomcat相关逻辑方法。主要的方法有:

aload()方法,主要是加载server.xml配置及初始化相关资源部分源码如下:

public void load() {    long t1 = System.nanoTime();    this.initDirs();    this.initNaming();    Digester digester = this.createStartDigester();    InputSource inputSource = null;    Object inputStream = null;    File file = null;    label257: {        try {            try {                file = this.configFile();

其中server.xml是用Digester这个工具来parse的,这是apachecommon项目。

bstart()方法,主要是用于启动tomcat容器的。部分源码如下:
public void start() {    if(this.getServer() == null) {        this.load();    }    if(this.getServer() == null) {        log.fatal("Cannot start server. Server instance is not configured.");    } else {        long t1 = System.nanoTime();        try {            this.getServer().start();        } catch (LifecycleException var7) {

cstop()方法,主要用于停止tomcat服务,部分源码如下:

public void stop() {    try {        if(this.useShutdownHook) {            Runtime.getRuntime().removeShutdownHook(this.shutdownHook);            LogManager e = LogManager.getLogManager();            if(e instanceof ClassLoaderLogManager) {                ((ClassLoaderLogManager)e).setUseShutdownHook(true);            }        }    } catch (Throwable var3) {        ExceptionUtils.handleThrowable(var3);    }    try {

dstopServer()方法,用于停止tomcat容器,部分源码如下:

public void stopServer(String[] arguments) {    if(arguments != null) {        this.arguments(arguments);    }    Server s = this.getServer();    if(s == null) {        Digester e = this.createStopDigester();        File file = this.configFile();        Throwable var6;        try {            FileInputStream x2 = new FileInputStream(file);            var6 = null;




原创粉丝点击