博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
tomcat启动类org.apache.catalina.startup.Bootstrap(转自C
阅读量:7045 次
发布时间:2019-06-28

本文共 5361 字,大约阅读时间需要 17 分钟。

hot3.png

Java代码  
收藏代码
  1. org.apache.catalina.startup.Bootstrap  
  2. 第一步自然从main函数开始,我们可以从中看出bootstrap的初始化过程   
  3.   
  4. main函数  
  5. 首先是调用init方法 ,然后根据main的参数,比如start、startd、stop、stopd等等,启动或停止服务器   
  6. ps:start和startd的区别,感觉像是前者用于重新启动   
  7.   
  8. init方法   
  9. 设置系统属性:setCatalinaHome和setCatalinaBase,都是利用System.setProperty方法来实现。user.dir属性指向一个目录,catalina.home属性指向user.dir目录下的bootstrap.jar文件,该文件不存在则指向user.dir本身。catalina.base和catalina.home一样   
  10.   
  11. 初始化classloader   
  12. 创建了三个classloader:common,server,shared ,以server为例,创建过程是:   
  13.   
  14. 查找server.loader系统变量,其中包含了以”,“分隔的文件路径(可能是URL、JAR、目录),称为repository。对repository做一些处理后(主要是替换其中的其他环境变量名,以得到完整的绝对路径),利用同一个包里面的另一个类ClassLoaderFactory,来真正产生classloader 。最后将生成的classloader作为mbean保存   
  15.   
  16. Thread.currentThread().setContextClassLoader(catalinaLoader) 把生成的catalinaLoader作为当前线程上下文的classloader   
  17.   
  18. 利用反射机制,正式载入org.apache.catalina.startup.Catalina类,创建类的实例,并调用该类的setParentClassLoader方法。最后,将该实例赋值给Bootstrap类的catalinaDaemon对象(这个对象类型是Object),完成初始化   
  19.   
  20. 回到main函数   
  21. 假设命令参数为start,则依次调用bootstrap的setAwait、load、start方法,这三个方法原理都一样,都是通过反射,实际调用刚才初始化生成的catalinaDaemon的同名方法   
  22.   
  23. 例:start方法中的反射代码  
  24.   
  25. Method method = catalinaDaemon.getClass().getMethod("start", (Class [] )null);   
  26. method.invoke(catalinaDaemon, (Object [])null);  
  27.   
  28. Bootstrap类剩下的几个方法:stop、stopServer,都是和start、load一样,通过反射调用catalinaDaemon的同名方法(其实就是org.apache.catalina.startup.Catalina类)   
  29.   
  30. 总结:   
  31. 1.反射的大量使用   
  32.   
  33. 无论是classloader,还是具体的某个类,都是通过反射实现初始化和方法调用的。思路是:配置文件->系统属性->解析属性字符串->得到类路径->反射,得到类->方法调用   
  34.   
  35. 这样可以使服务器的实现类很容易被替换,降低耦合性,减少将来代码的改动,的确是一种优秀的设计模式思想   
  36.   
  37. 启动类的配置文件比较简单,都是简单的“键-值”,所以直接用了StringToken解析字符串。后面的代码牵涉到xml配置文件时,必定会换成xml解析器。但总体思路应该类似   
  38.   
  39. 2.JMX的使用   
  40.   
  41. Bootstrap的JMX用得很少,仅仅对类装载器进行mbean注册,相信后面会有更多JMX的身影  
  42.   
  43.    
  44.   
  45. org.apache.catalina.startup.ClassLoaderFactory  
  46. 顾名思义,是tomcat专门用来产生classloader的工厂类   
  47.   
  48. 包括三个重载方法createClassLoader,内容都大同小异,无非是根据传入的类路径参数,产生一个相应的类装载器   
  49.   
  50. tomcat的类路径分为:包含class文件的目录、jar文件、包含jar的目录和URL   
  51.   
  52. 以Bootstrap类用到的createClassLoader方法为例   
  53.   
  54. 参数为:(String locations[],Integer types[], ClassLoader parent)   
  55. locations即为类路径的字符串表示,types为对应的类路径的类型,这两个数组的长度肯定是相同的,parant即为父classloader   
  56.   
  57. 方法的处理逻辑很简单,即根据每个location的类型,对location字符串做处理,如取得绝对路径、遍历目录、判断是不是jar文件等等   
  58.   
  59. 从代码可以看到,ClassLoaderFactory产生的classloader其实都是org.apache.catalina.loader.StandardClassLoader,这个类装载器留待以后研究  
  60.   
  61. org.apache.catalina.startup.Catalina  
  62. Bootstrap中,是通过start方法启动Catalina的   
  63.   
  64. Catalina中包含一个server实例,对应server.xml中的server元素。如果server实现了Lifecycle,start首先将server实例作为生命周期对象进行启动。然后,在JDK的Runtime中注册一个Shutdownhook。最后,如果指定了await参数,则让Catalina停止  
  65.   
  66. start方法代码如下:  
  67.   
  68. public void start() {   
  69.   
  70.         if (server == null) {   
  71.             load();   
  72.         }   
  73.   
  74.         long t1 = System.nanoTime();   
  75.         // Start the new server   
  76.         if (server instanceof Lifecycle) {   
  77.             try {   
  78.                 ((Lifecycle) server).start();   
  79.             } catch (LifecycleException e) {   
  80.                 log.error("Catalina.start: ", e);   
  81.             }   
  82.         }   
  83.   
  84.         long t2 = System.nanoTime();   
  85.         if(log.isInfoEnabled())   
  86.             log.info("Server startup in " + ((t2 - t1) / 1000000) + " ms");   
  87.   
  88.         try {   
  89.             // Register shutdown hook   
  90.             if (useShutdownHook) {   
  91.                 if (shutdownHook == null) {   
  92.                     shutdownHook = new CatalinaShutdownHook();   
  93.                 }   
  94.                 Runtime.getRuntime().addShutdownHook(shutdownHook);   
  95.             }   
  96.         } catch (Throwable t) {   
  97.             // This will fail on JDK 1.2. Ignoring, as Tomcat can run   
  98.             // fine without the shutdown hook.   
  99.         }   
  100.   
  101.         if (await) {   
  102.             await();   
  103.             stop();   
  104.         }   
  105.   
  106.     }  
  107.   
  108. Shutdownhook是一个Thread对象,用于给JVM在收到停止命令时(例如Ctrl C),运行这些Thread执行收尾工作,具体用法可以参考JAVA文档   
  109.   
  110. CatalinaShutdownHook在这里就是执行Catalina.stop方法,所以在Catalina.stop方法里面,会手动删除Runtime中的CatalinaShutdownHook,以防止调用两次stop,如下:  
  111.   
  112. // Remove the ShutdownHook first so that server.stop()   
  113.             // doesn't get invoked twice   
  114.             if (useShutdownHook) {   
  115.                 Runtime.getRuntime().removeShutdownHook(shutdownHook);   
  116.             }  
  117.   
  118.    
  119.   
  120. Catalina也有个main函数,不过里头调用的方法都是已废弃的,估计是遗留代码  
  121.   
  122. main函数主要是对命令行参数进行解析,比如可以取代默认的server.xml路径,其余工作主要交给load和start方法   
  123.   
  124. load方法   
  125. 按以下顺序读取配置文件server.xml,通过Digester类进行解析   
  126. 1.configFile字段指明的配置文件“conf/server.xml”   
  127. 2.Catalina的类装载器的getResource方法(具体原理不清楚,似乎很神奇。和装载类的机制一样,propagate方式,父装载器优先)   
  128. 3.最后是从内置的catalina.jar的server-embed.xml读入配置   
  129.   
  130. 一旦读取成功,则后面的不读取,优先级依次降低   
  131. load方法最后进行配置和server的初始化,调用server.initialize方法   
  132.   
  133. 其中,Digester类似乎很神奇很强大,可以根据一定的规则,对一个xml源文件进行解析,然后执行相应的动作,留待以后研究。这里有一篇文章可以参考  
  134.   
  135. http://software.ccidnet.com/pub/article/c322_a31671_p2.html   
  136.   
  137.    
  138.   
  139. stop方法  
  140.   
  141. // Shut down the server   
  142.         if (server instanceof Lifecycle) {   
  143.             try {   
  144.                ((Lifecycle) server).stop();   
  145.             } catch (LifecycleException e) {   
  146.                 log.error("Catalina.stop", e);   
  147.             }   
  148.         }  
  149.   
  150. stopserver方法   
  151. 比起stop方法要复杂得多,不是简单地调用server.stop,而是通过创建一个Digester,根据配置文件的定义进行动作;然后,以非常“原始”的方法:建立到server的socket连接,以字节流的方式,一个个字符地发送tomcat的“SHUTDOWN”指令,如下:  
  152.   
  153. // Stop the existing server   
  154.        try {   
  155.            String hostAddress = InetAddress.getByName("localhost").getHostAddress();   
  156.            Socket socket = new Socket(hostAddress, server.getPort());   
  157.            OutputStream stream = socket.getOutputStream();   
  158.            String shutdown = server.getShutdown();   
  159.            for (int i = 0; i < shutdown.length(); i++)   
  160.                stream.write(shutdown.charAt(i));   
  161.            stream.flush();   
  162.            stream.close();   
  163.            socket.close();   
  164.        } catch (IOException e) {   
  165.            log.error("Catalina.stop: ", e);   
  166.            System.exit(1);   
  167.        }  
  168.   
  169.    
  170.   
  171. 补充  
  172. 在看完StandardServer后,回到Catalina,现在对于Catalina的stop和stopServer方法的区别又多了一些理解。  
  173.   
  174. 关于StandardServer的笔记:  
  175.   
  176. Catalina的stop方法,其实是调用StandardServer.stop,进而停止StandardServer内部的各个Service,是名副其实的真正“停止”服务器   
  177.   
  178. 而stopServer方法,其实是构造一个socket连接到StandardServer的await方法打开的端口上,并发送SHUTDOWN命令。如前面所述,await只是起到阻塞作用,接受到SHUTDOWN命令后则await结束阻塞,并返回。具体要做哪些操作,还得由调用await的代码来决定。   
  179.   
  180. 现在先姑且认为,Catalina的stop方法是单纯的、Catalinal自己定义的一个停止服务器的函数,里面的实现都是封装在Catalina内部的,完成的工作就是停止服务器的socket和相关的Service服务,外人无法改动。   
  181.   
  182. 假如有第三方希望在停止server之前做点别的事情(比如释放特定的资源、或者通知其他组件),那么可以利用stopServer方法,待到await返回后,先完成自己要做的事情,最后再回头调用Catalina.stop(),完成最终的“停止” 。  
  183.   
  184. 通过这样的设计,tomcat给了外部代码,或者说基于tomcat的二次开发人员很大的灵活性,不必拘泥于tomcat本身的实现,也有利于tomcat自身的功能扩展。  
  185.   

转载于:https://my.oschina.net/weiweiblog/blog/468727

你可能感兴趣的文章
js语句执行顺序
查看>>
Android短信开发相关信息记录
查看>>
jdk堆、栈的分析工具--jmap、jstack
查看>>
target和currentTarget的区别
查看>>
RSpec控制器测试重构
查看>>
split,tr,cut,tac
查看>>
Activity的四种加载方式
查看>>
Babel的register特性使用
查看>>
用Flash如何制作360度产品展示
查看>>
lsb_release -a 查询系统版本
查看>>
Apache运维架构之Apache+PHP
查看>>
Python中filter、map、reduce、lambda 的用法
查看>>
我的友情链接
查看>>
Android PackageInstaller 静默安装的实现
查看>>
springboot+vue的前后端分离与合并方案
查看>>
Integer.toBinaryString() 的用法
查看>>
放开那个程序员!
查看>>
dataguard 创建物理standby
查看>>
Android帧缓冲区(Frame Buffer)硬件抽象层(HAL)模块Gralloc的实现原理分析(6)...
查看>>
内核挂钩调试记录
查看>>