Tomcat源码解析:4、Tomcat初始化

Bootstrap

org.apache.catalina.startup.Bootstrap类是Tomcat的入口,当我们想在IDE中运行Tomcat进行调试,可以找到这个类直接运行main方法。

我们看下Bootstrap的main方法

    public static void main(String args[]) {

        if (daemon == null) {
            // 实例化Bootstrap对象
            Bootstrap bootstrap = new Bootstrap();
            try {
            	 // 调用init进行Tomcat初始化,这节主要讲这个
                bootstrap.init();
            } catch (Throwable t) {
                handleThrowable(t);
                t.printStackTrace();
                return;
            }
            daemon = bootstrap;
        } else {
            Thread.currentThread().setContextClassLoader(daemon.catalinaLoader);
        }

        try {
            String command = "start";
            // args数组为空,所以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);
                // Tomcat启动的方法,后面在讲start
                daemon.load(args);
                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);
        }

    }

上面的代码,精简如下

	Bootstrap bootstrap = new Bootstrap();
	/*
	 * 初始化过程
	 */
	bootstrap.init();
	daemon = bootstrap;
	daemon.setAwait(true);
    daemon.load(args);
    /*
	 * 启动过程
	 */
    daemon.start();

这节只讲初始化,所以下面依次讲下init、load方法,start方法下一节在分析

init方法

    public void init() throws Exception {
		
		 /*
		  * 初始化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");
        /*         
         * 通过classLoader反射实例化org.apache.catalina.startup.Catalina类
         */
        Class<?> startupClass = catalinaLoader.loadClass("org.apache.catalina.startup.Catalina");
        Object startupInstance = startupClass.getConstructor().newInstance();

        // Set the shared extensions class loader
        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);
        /*
         * 反射调用Catalina的setParentClassLoader方法
         */
        method.invoke(startupInstance, paramValues);
		
		// 进行变量赋值
        catalinaDaemon = startupInstance;

    }

简单总结下上面init方法,然后依次进行分析

  • initClassLoaders(),初始化ClassLoader,加载lib/*.jar,这些都是Tomcat公共的资源
  • catalinaLoader.loadClass(“org.apache.catalina.startup.Catalina”),反射实例化Catalina类
  • 反射调用Catalina的setParentClassLoader方法
 		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);

initClassLoaders方法

    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();
            }
            catalinaLoader = createClassLoader("server", commonLoader);
            sharedLoader = createClassLoader("shared", commonLoader);
        } catch (Throwable t) {
            handleThrowable(t);
            log.error("Class loader creation threw exception", t);
            System.exit(1);
        }
    }

实际上调用了createClassLoader方法

    private ClassLoader createClassLoader(String name, ClassLoader parent)
        throws Exception {
			
		 /*
		  * 获取conf/catalina.properties文件里的 name.loader的值,这个文件大家可以自己去看
		  */
        String value = CatalinaProperties.getProperty(name + ".loader");
        
        /*
         * 如果value为空,那么直接返回parentClassLoader
         */
        if ((value == null) || (value.equals("")))
            return parent;
		/*
		 * 把value中的${catalina.base}、${catalina.home}替换成真实路径
		 */
        value = replace(value);

        List<Repository> repositories = new ArrayList<>();
		 
		 /*
		  * 取出""双引号或者()里的值,切分成数组
		  */
        String[] repositoryPaths = getPaths(value);

		 /*
		  * 根据path,构造对应的Repository对象
		  */
        for (String repository : repositoryPaths) {
            // 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
                //e.printStackTrace();
            }

            // 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));
            }
        }
			
		 /* 
		  * 创建URLClassLoader,里面的源码就不分析了,感兴趣可以继续看
		  */
        return ClassLoaderFactory.createClassLoader(repositories, parent);
    }

简单总结下initClassLoaders

  • 创建了三个CommonClassLoader、ServerClassLoader、SharedClassLoader
  • 根据conf/catalina.properties目录里的name+“.loader”的路径,来创建ClassLoader
  • 默认情况下server.loader、shared.loader为空,所以ServerClassLoader和SharedClassLoader都是CommonClassLoader
  • 如果server.loader、shared.loader不为空,那么CommonClassLoader就是ServerClassLoader和SharedClassLoader的parentClassLoader

所以init方法,就分析完毕,主要是创建了三个ClassLoader,并且把ClassLoader设置到Catalina中

紧接着来看下load方法

load方法

    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;
        }
        Method method =
            catalinaDaemon.getClass().getMethod(methodName, paramTypes);
        if (log.isDebugEnabled())
            log.debug("Calling startup class " + method);
        method.invoke(catalinaDaemon, param);
    }

上面实际上,反射调用了Catalina类的load方法

public void load() {

        if (loaded) {
            return;
        }
        loaded = true;

        long t1 = System.nanoTime();

        initDirs();

        // Before digester - it may be needed
        initNaming();

        // 创建一个Digester XML解析器 后续分一章节介绍
        Digester digester = createStartDigester();
		
        InputSource inputSource = null;
        InputStream inputStream = null;
        File file = null;
        try {
            try {
                /**
                 * 创建server.xml的File对象
                 */
                file = configFile();
                inputStream = new FileInputStream(file);
                /**
                 * 这里使用JDK自带的SAX解析去解析server.xml
                 * 所以构造InputSource 对象
                 */
                inputSource = new InputSource(file.toURI().toURL().toString());
            } catch (Exception e) {
                if (log.isDebugEnabled()) {
                    log.debug(sm.getString("catalina.configFail", file), e);
                }
            }
            /*
             * 如果server.xml文件不存在
             * 那么从ClassLoader中获取conf/server.xml
             */
            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);
                    }
                }
            }

			/*
			 * 如果上面两个文件都获取不到server.xml
			 * 那么从ClassLoader中获取server-embed.xml
			 */
            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);
                    }
                }
            }

			/*
			 * 如果都获取不到server.xml或者server-embed.xml,那么就直接打印异常信息,return
			 */
            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;
            }

            try {            
                inputSource.setByteStream(inputStream);
                // 把当前对象也就是Catalina,压入Digester的栈中,这个很有用,后面再说
                digester.push(this);
                /**
                 * 解析server.xml 并且创建相应的对象包括,列举几个关键的对象
                 * 1、Server:org.apache.catalina.core.StandardServer
                 * 2、Server的几个监听器:
                 *      1、org.apache.catalina.startup.VersionLoggerListener
                 *      2、org.apache.catalina.core.AprLifecycleListener
                 *      3、org.apache.catalina.core.JreMemoryLeakPreventionListener
                 *      4、org.apache.catalina.mbeans.GlobalResourcesLifecycleListener
                 *      5、org.apache.catalina.core.ThreadLocalLeakPreventionListener
                 * 3、Service:org.apache.catalina.core.StandardService
                 * 4、2个Connector,分别为
                 *      1、org.apache.coyote.http11.Http11NioProtocol:处理HTTP请求
                 *      2、org.apache.coyote.http11.Http11AprProtocol:处理AJP请求
                 * 5、Engine:org.apache.catalina.core.StandardEngine
                 * 6、Host:org.apache.catalina.core.StandardHost
                 */
                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 {
            if (inputStream != null) {
                try {
                    inputStream.close();
                } catch (IOException e) {
                    // Ignore
                }
            }
        }
        
        /**
         * 给Server设置Catalina信息
         */
        getServer().setCatalina(this);
        getServer().setCatalinaHome(Bootstrap.getCatalinaHomeFile());
        getServer().setCatalinaBase(Bootstrap.getCatalinaBaseFile());

        // Stream redirection
        initStreams();

        // Start the new server
        try {
            /**
             * 调用Server的init方法,下面都是靠着父组件去驱动子组件初始化
             */
            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");
        }
    }

简单总结下上面的内容:

  • 创建并且初始化Digester对象,用于解析server.xml或者server-embed.xml文件,这个Digester是JDK SAX解析的框架
  • 先获取server.xml文件流,如果获取不到,那么在获取server-embed.xml。如果都获取不到,那么直接return,打印异常信息
  • 使用Digester进行解析server.xml,创建相应的对象
  • 给Server【org.apache.catalina.core.StandardServer】设置Catalina信息,并且调用Server的init方法

下面我们看下Server的init方法,StandardServer类继续了LifecycleMBeanBase,所以实际上调用的是模板方法initInternal,不懂的,可以回去看下我讲的Lifecyle执行机制

protected void initInternal() throws LifecycleException {

		/*
		 * 调用父类的initInternal,把当前对象注册到JMX
		 */
        super.initInternal(); 

		/*
		 * 下面分别把StringCache和MBeanFactory注册到JMX
		 */     
        onameStringCache = register(new StringCache(), "type=StringCache");

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

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

        if (getCatalina() != null) {
            ClassLoader cl = getCatalina().getParentClassLoader();
            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();
            }
        }
        // 调用了Service的init方法,默认只有一个
        for (int i = 0; i < services.length; i++) {
            services[i].init();
        }
    }

这里同样是调用Service的init方法,这个Service是StandardService,它也继承了LifecycleMBeanBase,所以也是调用了它的模板方法initInternal

protected void initInternal() throws LifecycleException {
	
		/*
		 * 同样调用父类的initInternal,把当前对象注册到JMX
		 */
        super.initInternal();
		
		/*
		 * 调用Engine的init方法
		 */
        if (engine != null) {
            engine.init();
        }

       /*
        * 如果用户有在server.xml的Service标签下配置Executor
        * 那么会以此调用每个Executor的init方法
        * 这个Executor是Tomcat是继承了JUC里的Executor,
        * 并且继承了Lifecycle,让其拥有生命周期,
        * 实现类是StandardThreadExecutor
        */
        for (Executor executor : findExecutors()) {
            if (executor instanceof JmxEnabled) {
                ((JmxEnabled) executor).setDomain(getDomain());
            }
            executor.init();
        }

 
        mapperListener.init();

        // Initialize our defined Connectors
        synchronized (connectorsLock) {
        	/*
        	 * 循环调用Connector的init方法
        	 * 默认存在2个Connector,1个是处理AJP,1个是处理Http
        	 */
            for (Connector connector : connectors) {
                try {
                    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);
                }
            }
        }
    }

简单总结下StandardService的initInternal方法:

  • 调用 super.initInternal(),把自身对象注册到JMX
  • 调用Engine的init方法,进行Engine初始化
  • 如果有配置自定义Executor,那么调用findExecutors(),并且遍历Executor集合,一次调用Executor的init方法
  • 遍历Connector集合,依次调用Connector的init方法

紧接着我们看下Engine的init方法,也就是StandardEngine的initInternal

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();
}

很简单,只是获取Realm和把自身注册到JMX,并且调用了父类的initInternal()方法

ContainerBase
    protected void initInternal() throws LifecycleException {
        BlockingQueue<Runnable> startStopQueue = new LinkedBlockingQueue<>();
        /*
         * 创建了一个线程池,默认coreThread=1而且maxThread=1,这个线程池的作用
         * 主要是调用子组件的start方法
         */
        startStopExecutor = new ThreadPoolExecutor(
                getStartStopThreadsInternal(),
                getStartStopThreadsInternal(), 10, TimeUnit.SECONDS,
                startStopQueue,
                new StartStopThreadFactory(getName() + "-startStop-"));
        /*
         * 设置ThreadPoolExecutor.allowCoreThreadTimeOut属性为true
         * 当线程池中没有Task达到10s,它会回收coreThread
         */
        startStopExecutor.allowCoreThreadTimeOut(true);
        super.initInternal();
    }

紧接着我们看下Connector的initInternal方法,首先我们看下Connector的构造方法


    public Connector(String protocol) {
    	/*
    	 * 如果protocol是HTTP/1.1,那么会设置protocolHandlerClassName = org.apache.coyote.http11.Http11NioProtocol
    	 * 如果是AJP/1.3,那么会设置protocolHandlerClassName = org.apache.coyote.ajp.AjpAprProtocol
    	 */
        setProtocol(protocol);
        // Instantiate protocol handler
        ProtocolHandler p = null;
        try {
        	/*
        	 * 根据上面设置的protocolHandlerClassName,反射实例化对象
        	 */
            Class<?> clazz = Class.forName(protocolHandlerClassName);
            p = (ProtocolHandler) clazz.getConstructor().newInstance();
        } catch (Exception e) {
            log.error(sm.getString(
                    "coyoteConnector.protocolHandlerInstantiationFailed"), e);
        } finally {
            this.protocolHandler = p;
        }

        if (Globals.STRICT_SERVLET_COMPLIANCE) {
            uriCharset = StandardCharsets.ISO_8859_1;
        } else {
            uriCharset = StandardCharsets.UTF_8;
        }
    }
    public void setProtocol(String protocol) {

        boolean aprConnector = AprLifecycleListener.isAprAvailable() &&
                AprLifecycleListener.getUseAprConnector();

        if ("HTTP/1.1".equals(protocol) || protocol == null) {
            if (aprConnector) {
                setProtocolHandlerClassName("org.apache.coyote.http11.Http11AprProtocol");
            } else {
                setProtocolHandlerClassName("org.apache.coyote.http11.Http11NioProtocol");
            }
        } else if ("AJP/1.3".equals(protocol)) {
            if (aprConnector) {
                setProtocolHandlerClassName("org.apache.coyote.ajp.AjpAprProtocol");
            } else {
                setProtocolHandlerClassName("org.apache.coyote.ajp.AjpNioProtocol");
            }
        } else {
            setProtocolHandlerClassName(protocol);
        }
    }
	/*
	 * 创建了一个Nio2Endpoint类,这个类主要负责端口监听,请求的处理
	 * 
	 */
    public Http11Nio2Protocol() {
        super(new Nio2Endpoint());
    }
    protected void initInternal() throws LifecycleException {
		
		/*
		 * 把自身注册到JMX
		 */
        super.initInternal();

        /*
         * 实例化一个CoyoteAdapter适配器,
         * 把Request、Response转为HttpServletRequest、HttpServletResponse
         */
        adapter = new CoyoteAdapter(this);
        protocolHandler.setAdapter(adapter);

        // Make sure parseBodyMethodsSet has a default
        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的init方法
        	 *  以HTTP/1.1为例,也就是org.apache.coyote.http11.Http11NioProtocol
        	 */
            protocolHandler.init();
        } catch (Exception e) {
            throw new LifecycleException(
                    sm.getString("coyoteConnector.protocolHandlerInitializationFailed"), e);
        }
    }

紧接着调用org.apache.coyote.http11.Http11NioProtocol的init方法,实际上调用的是父类org.apache.coyote.AbstractProtocol的init


    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的init,这里以Nio2Endpoint为例,
		 * 这个对象是在创建Http11Nio2Protocol的构造函数里创建的,上面有说
		 */
        endpoint.init();
    }

调用Http11Nio2Protocol的init方法,实际上也是调用父类org.apache.tomcat.util.net.AbstractEndpoint的init

    public void init() throws Exception {
        if (bindOnInit) {
        	/*
        	 * bind()方法很关键,主要就是绑定端口
        	 */
            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);

            for (SSLHostConfig sslHostConfig : findSslHostConfigs()) {
                registerJmx(sslHostConfig);
            }
        }
    }
Nio2Endpoint
    public void bind() throws Exception {

        // Create worker collection
        /*
         * 如果没有在server.xml没有在service标签下配置自定义的Executor,
         * 那么会调用createExecutor(),创建默认的Executor线程池
         */
        if ( getExecutor() == null ) {
            createExecutor();
        }
        if (getExecutor() instanceof ExecutorService) {
            threadGroup = AsynchronousChannelGroup.withThreadPool((ExecutorService) getExecutor());
        }
        // AsynchronousChannelGroup currently needs exclusive access to its executor service
        if (!internalExecutor) {
            log.warn(sm.getString("endpoint.nio2.exclusiveExecutor"));
        }
	
		/*
		 * 这里很熟悉的NIO编程了,鉴定指定端口
		 */
        serverSock = AsynchronousServerSocketChannel.open(threadGroup);
        socketProperties.setProperties(serverSock);
        InetSocketAddress addr = (getAddress()!=null?new InetSocketAddress(getAddress(),getPort()):new InetSocketAddress(getPort()));
        serverSock.bind(addr,getAcceptCount());

        // Initialize thread count defaults for acceptor, poller
        if (acceptorThreadCount != 1) {
            // NIO2 does not allow any form of IO concurrency
            acceptorThreadCount = 1;
        }

        // Initialize SSL if needed
        initialiseSsl();
    }

到此为止,Tomcat初始化完毕。

总结

简单总结下Tomcat初始化,主要分为几点

  • 创建3个ClassLoader,分别为CommonClassLoader、ServerClassLoader、SharedClassLoader。这几个ClassLoader默认都是CommonClassLoader,他们加载了Tomcat公共的Jar,比如lib下的所有的jar
  • 根据server.xml的配置创建对应的组件,比如Server、Service、Engine等等组件
  • Tomcat初始化特点:每个组件都是在父组件里去调用初始化,以此循环去初始化各个组件,以HTTP协议为例,最后到Nio2Endpoint绑定端口成功后就结束。

所以你会发现,Tomcat初始化时,并没有去加载War项目,它是在调用start()启动的时候才去加载项目。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值