Анализ исходного кода Tomcat8 (1)

задняя часть исходный код Tomcat

Анализ исходного кода Tomcat8

Общая архитектура Tomcat

  • 在这里插入图片描述

Соединитель: откройте сокет и отслеживайте запросы клиентов и возвращайте данные ответа; Контейнер: отвечает за обработку конкретных запросов;

Служба отвечает за поддержку нескольких Соединителей и Контейнера, поэтому запросы от Соединителя могут обрабатываться только Контейнером, поддерживаемым Службой, к которой он принадлежит;

Engine: представляет весь движок сервлета Хост: указывает на виртуальный хост Контекст: представляет приложение оболочка: представляет собой сервлет

Создание исходного кода Tomcat

  1. Ссылка для загрузки программного обеспечения Tomcat и файла с исходным кодом:tomcat.apache.org/download-80…
  • 在这里插入图片描述
  1. Создайте папку каталога tomcat для хранения загруженных файлов, а затем создайте файл pom.xml. Затем используйте идею, чтобы открыть каталог tomcat.
  • 在这里插入图片描述
  • файл pom.xml выглядит следующим образом
<?xml version="1.0" encoding="UTF-8"?>

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.apache.tomcat</groupId>
    <artifactId>tomcat</artifactId>
    <version>1.0-SNAPSHOT</version>

    <name>tomcat</name>
    <url>http://www.example.com</url>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>1.7</maven.compiler.source>
        <maven.compiler.target>1.7</maven.compiler.target>
    </properties>

    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.easymock</groupId>
            <artifactId>easymock</artifactId>
            <version>3.4</version>
        </dependency>
        <dependency>
            <groupId>org.apache.ant</groupId>
            <artifactId>ant</artifactId>
            <version>1.10.1</version>
        </dependency>
        <dependency>
            <groupId>wsdl4j</groupId>
            <artifactId>wsdl4j</artifactId>
            <version>1.6.2</version>
        </dependency>
        <dependency>
            <groupId>javax.xml</groupId>
            <artifactId>jaxrpc</artifactId>
            <version>1.1</version>
        </dependency>
        <dependency>
            <groupId>org.eclipse.jdt</groupId>
            <artifactId>org.eclipse.jdt.core</artifactId>
            <version>3.13.0</version>
        </dependency>
        <dependency>
            <groupId>org.eclipse.jdt.core.compiler</groupId>
            <artifactId>ecj</artifactId>
            <version>4.5.1</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.6.1</version>
                <configuration>
                    <encoding>UTF-8</encoding>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>
  1. Настройки в идее такие, как показано ниже:
  • 在这里插入图片描述
  • 在这里插入图片描述
  1. Наконец начните. Путь успешного запуска при запуске:http://localhost:8080/
  • 在这里插入图片描述

Анализ исходного кода Tomcat

1. Фаза инициализации Tomcat
# 1.Bootstrap启动类的main方法
public static void main(String args[]) {
  if (daemon == null) {
        //实例化BootStrap
        Bootstrap bootstrap = new Bootstrap();
        try {
            //初始化BootStrap
            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")) {
            //当命令是start,执行下面操作
            daemon.setAwait(true); //为了让tomcat在关闭端口阻塞监听关闭命令
            daemon.load(args);     //实际上调用catalina.load()方法,初始化server,service,engine,executor,connector
            daemon.start();        //实际上调用catalina.start()方法,启动server,service,engine,executor,connector;Host,Context,Wrapper
            if (null == daemon.getServer()) {
                System.exit(1);
            }
        } 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);
    }
}

# 2.初始化BootStrap
public void init() throws Exception {
    //初始化类加载器
    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");
    //加载Catalina类
    Class<?> startupClass = catalinaLoader.loadClass("org.apache.catalina.startup.Catalina");
    //使用Catalina类的构造方法,创建Catalina实例对象
    Object startupInstance = startupClass.getConstructor().newInstance();

    //执行Catalina的setParentClassLoader方法,设置父类加载器
    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
    catalinaDaemon = startupInstance;
}

# 3.初始化类加载器
private void initClassLoaders() {
    try {
        //commonLoader的父类加载器就设置为null,即打破了双亲委派机制。
        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();
        }
        //catalinaLoader和sharedLoader,的父类加载器设置为commonLoader
        catalinaLoader = createClassLoader("server", commonLoader);
        sharedLoader = createClassLoader("shared", commonLoader);
    } catch (Throwable t) {
        handleThrowable(t);
        log.error("Class loader creation threw exception", t);
        System.exit(1);
    }
}

# 4.daemon.load(args);初始化操作
private void load(String[] arguments)
    throws Exception {

    // Call the load() method
    String methodName = "load";
    Object param[];
    Class<?> paramTypes[];
    if (arguments==null || arguments.length==0) {
        paramTypes = null;
        param = null;
    } else {
        paramTypes = new Class[1];
        paramTypes[0] = arguments.getClass();
        param = new Object[1];
        param[0] = arguments;
    }
    //执行catalina的load()方法。
    Method method =
        catalinaDaemon.getClass().getMethod(methodName, paramTypes);
    if (log.isDebugEnabled())
        log.debug("Calling startup class " + method);
    method.invoke(catalinaDaemon, param);

}

Как видно из приведенного выше кода, запись класса запуска tomcat является основным методом класса Bootstrap. Как видно, здесь есть несколько важных шагов: 1. Инициализируйте Bootstrap, в основном для создания объектов Catalina путем отражения; инициализируйте загрузчик класса tomcat 2. Вызовите метод загрузки Bootstrap, чтобы инициализировать компоненты, связанные с tomcat, что фактически вызывает метод загрузки каталины.

  • Выполняется метод catalina.load() с последующим анализом отслеживания исходного кода.
/**
 * 会去初始化一些资源,优先加载conf/server.xml,找不到再去加载server-embed.xml;
 * 此外,load方法还会初始化Server
 */
public void load() {
    if (loaded) {
        return;
    }
    loaded = true;
    long t1 = System.nanoTime();

    //初始化目录
    initDirs();

    //初始化命名空间
    initNaming();

    //解析器,解析Server.xml文件
    Digester digester = createStartDigester();

    //读取Server.xml文件
    InputSource inputSource = null;
    InputStream inputStream = null;
    File file = null;
    try {
        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);
            }
        }
       
        try {
            inputSource.setByteStream(inputStream);
            digester.push(this);

            //开始解析Server.xml文件(重点)
            digester.parse(inputSource);
        } 
    }

    //设置server的Catalina等信息
    getServer().setCatalina(this);
    getServer().setCatalinaHome(Bootstrap.getCatalinaHomeFile());
    getServer().setCatalinaBase(Bootstrap.getCatalinaBaseFile());

    // Stream redirection
    initStreams();

    // Start the new server
    try {

        //初始化Server,这个init方法是父类LifecycleBase的方法(重点)
        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");
    }
}

Метод catalina.load в основном выполняет две важные операции: 1. Разберите файл server.xml. 2. Инициализируйте сервер.

  • 在这里插入图片描述
  • Выполняемый метод getServer().init() инициализирует Сервер, анализ исходного кода
# 1.LifecycleBase的init()方法
@Override
public final synchronized void init() throws LifecycleException {
    if (!state.equals(LifecycleState.NEW)) {
        invalidTransition(Lifecycle.BEFORE_INIT_EVENT);
    }

    try {
        setStateInternal(LifecycleState.INITIALIZING, null, false);
        //调用子类的initInternal方法(重要)
        initInternal();
        setStateInternal(LifecycleState.INITIALIZED, null, false);
    } catch (Throwable t) {
        ExceptionUtils.handleThrowable(t);
        setStateInternal(LifecycleState.FAILED, null, false);
        throw new LifecycleException(
                sm.getString("lifecycleBase.initFail",toString()), t);
    }
}

# 2.StandardServer的initInternal()方法
@Override
protected void initInternal() throws LifecycleException {

     super.initInternal();

     // Register global String cache
     // Note although the cache is global, if there are multiple Servers
     // present in the JVM (may happen when embedding) then the same cache
     // will be registered under multiple names
     onameStringCache = register(new StringCache(), "type=StringCache");

     //注册JMX
     // Register the MBeanFactory
     MBeanFactory factory = new MBeanFactory();
     factory.setContainer(this);
     onameMBeanFactory = register(factory, "type=MBeanFactory");

     // Register the naming resources
     globalNamingResources.init();

     //获取类加载器
     // Populate the extension validator with JARs from common and shared
     // class loaders
     if (getCatalina() != null) {
         ClassLoader cl = getCatalina().getParentClassLoader();
         // Walk the class loader hierarchy. Stop at the system class loader.
         // This will add the shared (if present) and common class loaders
         while (cl != null && cl != ClassLoader.getSystemClassLoader()) {
             if (cl instanceof URLClassLoader) {
                 URL[] urls = ((URLClassLoader) cl).getURLs();
                 for (URL url : urls) {
                     if (url.getProtocol().equals("file")) {
                         try {
                             File f = new File (url.toURI());
                             if (f.isFile() &&
                                     f.getName().endsWith(".jar")) {
                                 ExtensionValidator.addSystemResource(f);
                             }
                         } catch (URISyntaxException e) {
                             // Ignore
                         } catch (IOException e) {
                             // Ignore
                         }
                     }
                 }
             }
             cl = cl.getParent();
         }
     }
     // Initialize our defined Services
     for (int i = 0; i < services.length; i++) {
         //初始化services(重点)
         services[i].init();
     }
 }

Метод инициализации init() сервера сначала вызовет метод init() родительского класса LifecycleBase, а затем вызовет метод initInternal() подкласса Server. Режим вызова: родительский класс init--->подкласс initInternal, в следующих сервисах, коннекторе, инициализации движка тот же режим.

  • services[i].init(), инициализация служб, анализ исходного кода
# 1.standardService的initInternal()方法
@Override
protected void initInternal() throws LifecycleException {

    super.initInternal();

    //初始化engine(重点)
    if (engine != null) {
        engine.init();
    }

    //初始化线程池
    // Initialize any Executors
    for (Executor executor : findExecutors()) {
        if (executor instanceof JmxEnabled) {
            ((JmxEnabled) executor).setDomain(getDomain());
        }
        executor.init();
    }

    //初始化mapper映射监听器
    // Initialize mapper listener
    mapperListener.init();

    // Initialize our defined Connectors
    synchronized (connectorsLock) {
        for (Connector connector : connectors) {
            try {
                //初始化connector(重点)
                connector.init();
            } catch (Exception e) {
                String message = sm.getString(
                        "standardService.connector.initFailed", connector);
                log.error(message, e);

                if (Boolean.getBoolean("org.apache.catalina.startup.EXIT_ON_INIT_FAILURE"))
                    throw new LifecycleException(message);
            }
        }
    }
}

Из анализа инициализации служб можно получить инициализацию служб, и будут инициализированы механизм и коннектор. (исполнитель, мапперслушатель)

  • engine.init(), инициализация движка, анализ исходного кода
# 1.standardEngine的initInternal()方法
@Override
protected void initInternal() throws LifecycleException {
    // Ensure that a Realm is present before any attempt is made to start
    // one. This will create the default NullRealm if necessary.
    getRealm();
    super.initInternal();
}

# 2.ContainerBase的initInternal()方法
@Override
protected void initInternal() throws LifecycleException {
    BlockingQueue<Runnable> startStopQueue = new LinkedBlockingQueue<>();
    startStopExecutor = new ThreadPoolExecutor(
            getStartStopThreadsInternal(),
            getStartStopThreadsInternal(), 10, TimeUnit.SECONDS,
            startStopQueue,
            new StartStopThreadFactory(getName() + "-startStop-"));
    startStopExecutor.allowCoreThreadTimeOut(true);
    super.initInternal();
}

# 3.LifecycleMBeanBase的initInternal()方法
@Override
protected void initInternal() throws LifecycleException {
    // If oname is not null then registration has already happened via
    // preRegister().
    if (oname == null) {
        mserver = Registry.getRegistry(null, null).getMBeanServer();
        oname = register(this, getObjectNameKeyProperties());
    }
}
  • Connector.init(), инициализировать коннектор, анализ исходного кода
# 1.connector的initInternal()方法
@Override
protected void initInternal() throws LifecycleException {

    super.initInternal();

    //初始化适配器
    adapter = new CoyoteAdapter(this);
    protocolHandler.setAdapter(adapter);

    //给parseBodyMethodsSet设置一个默认值
    if (null == parseBodyMethodsSet) {
        setParseBodyMethods(getParseBodyMethods());
    }

    if (protocolHandler.isAprRequired() && !AprLifecycleListener.isAprAvailable()) {
        throw new LifecycleException(sm.getString("coyoteConnector.protocolHandlerNoApr",
                getProtocolHandlerClassName()));
    }
    if (AprLifecycleListener.isAprAvailable() && AprLifecycleListener.getUseOpenSSL() &&
            protocolHandler instanceof AbstractHttp11JsseProtocol) {
        AbstractHttp11JsseProtocol<?> jsseProtocolHandler =
                (AbstractHttp11JsseProtocol<?>) protocolHandler;
        if (jsseProtocolHandler.isSSLEnabled() &&
                jsseProtocolHandler.getSslImplementationName() == null) {
            // OpenSSL is compatible with the JSSE configuration, so use it if APR is available
            jsseProtocolHandler.setSslImplementationName(OpenSSLImplementation.class.getName());
        }
    }

    try {
        //初始化protocolHandler(重点)
        protocolHandler.init();
    } catch (Exception e) {
        throw new LifecycleException(
                sm.getString("coyoteConnector.protocolHandlerInitializationFailed"), e);
    }
}

Из анализа инициализации соединителя можно получить инициализацию соединителя, и будет инициализирован обработчик протокола.

  • protocolHandler.init(), инициализация protocolHandler, анализ исходного кода
# 1.AbstractProtocol的init()方法
@Override
public void init() throws Exception {
    if (getLog().isInfoEnabled()) {
        getLog().info(sm.getString("abstractProtocolHandler.init", getName()));
    }

    if (oname == null) {
        // Component not pre-registered so register it
        oname = createObjectName();
        if (oname != null) {
            Registry.getRegistry(null, null).registerComponent(this, oname, null);
        }
    }

    if (this.domain != null) {
        rgOname = new ObjectName(domain + ":type=GlobalRequestProcessor,name=" + getName());
        Registry.getRegistry(null, null).registerComponent(
                getHandler().getGlobal(), rgOname, null);
    }

    String endpointName = getName();
    endpoint.setName(endpointName.substring(1, endpointName.length()-1));
    endpoint.setDomain(domain);

    //初始化endpoint(重点)
    endpoint.init();
}

Из анализа инициализации протоколаHandler мы можем получить инициализацию протоколаHandler, которая будет инициализировать конечную точку.

  • endpoint.init(), инициализация конечной точки, анализ исходного кода
# 1.AbstractEndpoint的init()方法
public void init() throws Exception {
    if (bindOnInit) {
        bind();
        bindState = BindState.BOUND_ON_INIT;
    }
    if (this.domain != null) {
        // Register endpoint (as ThreadPool - historical name)
        oname = new ObjectName(domain + ":type=ThreadPool,name=\"" + getName() + "\"");
        Registry.getRegistry(null, null).registerComponent(this, oname, null);

        ObjectName socketPropertiesOname = new ObjectName(domain +
                ":type=ThreadPool,name=\"" + getName() + "\",subType=SocketProperties");
        socketProperties.setObjectName(socketPropertiesOname);
        Registry.getRegistry(null, null).registerComponent(socketProperties, socketPropertiesOname, null);

        for (SSLHostConfig sslHostConfig : findSslHostConfigs()) {
            registerJmx(sslHostConfig);
        }
    }
}

На этом этап инициализации tomcat завершен. Используется модель цепочки ответственности, которая инициализируется поэтапно. Порядок инициализации компонента: Сервер-->Сервис-->Движок-->Коннектор-->ProtocolHandler-->Конечная точка Видно, что Host, Context и Wrapper еще не начали инициализацию. Они будут инициализированы на этапе запуска tomcat.