本文主要是介绍Tomcat源码解析-启动过程分析之主干流程,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
Tomcat启动入口就在脚本startup.sh中,具体脚本可以看tomcat的源码,这个启动脚本主要用来判断环境,
找到catalina.sh脚本路径,将启动参数传递给catalina.sh执行。
catalina.sh start 最终会执行org.apache.catalina.startup.Bootstrap中的main方法,并把start参数传入。
以后分析Tomcat关闭的时候,也是一个套路,最终都会调用到org.apache.catalina.startup.Bootstrap的main方法,并把stop参数传入。
找到catalina.sh脚本路径,将启动参数传递给catalina.sh执行。
catalina.sh start 最终会执行org.apache.catalina.startup.Bootstrap中的main方法,并把start参数传入。
以后分析Tomcat关闭的时候,也是一个套路,最终都会调用到org.apache.catalina.startup.Bootstrap的main方法,并把stop参数传入。
分析main方法:
[Java] 纯文本查看 复制代码
?
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 | /** * 通过提供的脚本启动Tomcat时的主方法和入口点。 * * @param args 要处理的命令行参数 */ public static void main(String args[]) { //main使用的守护进程对象。 synchronized (daemonLock) { //daemon是volatile修饰的Bootstrap对象 if (daemon == null ) { //在init()完成之前不要设置守护进程 Bootstrap bootstrap = new Bootstrap(); try { //初始化守护进程。 bootstrap.init(); } catch (Throwable t) { handleThrowable(t); t.printStackTrace(); return ; } daemon = bootstrap; } else { // W当作为服务运行时,要停止的调用将位于新线程上, // 因此请确保使用了正确的类加载器,以防止出现一系列类未找到异常。直到init()完成 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" )) { //启动操作 //通过反射调用守护进程引用的org.apache.catalina.startup.Catalina实例的setAwait方法 daemon.setAwait( true ); //调用Catalina实例的load方法 daemon.load(args); //start方法 daemon.start(); //反射调用Catalina实例的getServer方法,返回的对象为空时,终止当前运行的Java虚拟机。 if ( null == daemon.getServer()) { System.exit( 1 ); } } else if (command.equals( "stop" )) { //通过反射调用Catalina的stopServer方法。 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 ); } } |
启动过程有两步操作:
1、初始化守护进程,获取类加载器和反射实例化org.apache.catalina.startup.Catalina。
2、根据启动参数start,通过反射,依次执行Catalina实例的setAwait、load、start方法。
1、初始化守护进程,获取类加载器和反射实例化org.apache.catalina.startup.Catalina。
2、根据启动参数start,通过反射,依次执行Catalina实例的setAwait、load、start方法。
下面主要分析Catalina实例的setAwait、load、start方法:
1 setAwait
入参为true
[Java] 纯文本查看 复制代码
?
1 2 3 4 5 6 7 8 | /** * Use await. */ protected boolean await = false ; public void setAwait( boolean b) { await = b; } |
2 load
[Java] 纯文本查看 复制代码
?
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 | /** * 启动一个新的服务器实例。 */ public void load() { //防止重复加载。 if (loaded) { return ; } loaded = true ; long t1 = System.nanoTime(); //创建java.io.tmpdir文件夹 initDirs(); // Before digester - it may be needed //初始化jmx的环境变量 initNaming(); // Create and execute our Digester //创建和配置将用于启动的Digester。 //配置解析server.xml中各个标签的解析类 Digester digester = createStartDigester(); InputSource inputSource = null ; InputStream inputStream = null ; File file = null ; try { //下面一大段都是为了加载conf/server.xml配置文件,失败就加载server-embed.xml ... try { inputSource.setByteStream(inputStream); //把Catalina作为一个顶级容器 digester.push( this ); //解析过程会实例化各个组件,比如Server、Container、Connector等 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在解析xml之后就有值了,这是Server的Catalina信息 getServer().setCatalina( this ); getServer().setCatalinaHome(Bootstrap.getCatalinaHomeFile()); getServer().setCatalinaBase(Bootstrap.getCatalinaBaseFile()); // Stream redirection initStreams(); // Start the new server try { //生命周期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" ); } } |
主要流程:
- 初始化JMX环境变量
- 创建和配置Digester
- 读取配置文件conf/server.xml配置文件,失败就加载server-embed.xml
- 通过Digester解析配置文件,并将当前Catalina作为最顶层容器,解析过程会实例化各种组件
- 设置Server组件的catalina信息
- 调用Server组件的生命周期init方法
解析配置文件,注入catalina的各种组件后面分析。
下面看start方法:
下面看start方法:
3 start
[Java] 纯文本查看 复制代码
?
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 | /** * 启动一个新的服务器实例。 */ public void start() { //如果Server组件不存在,则重新执行load方法 if (getServer() == null ) { load(); } //依然不存在就返回 if (getServer() == null ) { log.fatal( "Cannot start server. Server instance is not configured." ); return ; } long t1 = System.nanoTime(); // Start the new server try { //调用Server的start方法 getServer().start(); } catch (LifecycleException e) { log.fatal(sm.getString( "catalina.serverStartFail" ), e); try { //异常的时候调用Server的destroy方法 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 ) { // Catalina.this.stop(); 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 ); } } // Bootstrap中会设置await为true,其目的在于让tomcat在shutdown端口阻塞监听关闭命令 if (await) { //等待收到正确的关机命令,然后返回。 await(); //停止现有的服务器实例。 stop(); } } |
流程:
- 调用Server组件的start方法,开启一个新的服务。
- 注册关闭钩子
- 让Tomcat在shutdown端口阻塞监听关闭命令
本篇目的就是了解整个Tomcat启动的主干流程,体现在代码层的就是依次执行Catalina实例的setAwait、load、start方法。
其中的load方法中的解析配置文件与注册组件、执行生命周期方法init;
start方法中的开启服务、注册关闭钩子、阻塞监听关闭指令等详细细节,将在后期慢慢分析。
其中的load方法中的解析配置文件与注册组件、执行生命周期方法init;
start方法中的开启服务、注册关闭钩子、阻塞监听关闭指令等详细细节,将在后期慢慢分析。
更多技术资讯可关注:itheimaGZ获取
这篇关于Tomcat源码解析-启动过程分析之主干流程的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!