设为首页 收藏本站
查看: 310|回复: 0

[经验分享] 1、Tomcat启动

[复制链接]

尚未签到

发表于 2017-1-19 06:25:36 | 显示全部楼层 |阅读模式
  [转]http://dev.csdn.net/htmls/27/27225.html
    1.入口类及main方法
  tomcat的入口类是org.apache.catalina.startup.BootStrap,进入其main方法,可以看到它首先实例化,然后进行初始化。

Java代码 DSC0000.gif   DSC0001.png DSC0002.gif




  • if (daemon == null) {   

  •    daemon = new Bootstrap();   

  •    try {   

  •     //因为在启动时设置了catalina.home属性,所以System.getProperty("catalina.home")可以直接获取到这个属性值.   
  •        daemon.init();   

  •    } catch (Throwable t) {   
  •        t.printStackTrace();   

  •        return;   
  •    }   



if (daemon == null) { daemon = new Bootstrap(); try { //因为在启动时设置了catalina.home属性,所以System.getProperty("catalina.home")可以直接获取到这个属性值. daemon.init(); } catch (Throwable t) { t.printStackTrace(); return; } }

  2.init方法
  init方法首先设置catalina的家目录,这里调用了两个方法
  setCatalinaHome();
setCatalinaBase();

Java代码  




  • private void setCatalinaHome() {   
  •   

  •        if (System.getProperty("catalina.home") != null)   

  •            return;   
  •        File bootstrapJar =    

  •            new File(System.getProperty("user.dir"), "bootstrap.jar");   

  •        if (bootstrapJar.exists()) {   

  •            try {   
  •                System.setProperty   

  •                    ("catalina.home",    

  •                     (new File(System.getProperty("user.dir"), ".."))   
  •                     .getCanonicalPath());   

  •            } catch (Exception e) {   

  •                // Ignore   

  •                System.setProperty("catalina.home",   

  •                                   System.getProperty("user.dir"));   
  •            }   

  •        } else {   

  •         System.getProperty("catalina.config");   
  •        }   
  •   
  •    }   



private void setCatalinaHome() { if (System.getProperty("catalina.home") != null) return; File bootstrapJar = new File(System.getProperty("user.dir"), "bootstrap.jar"); if (bootstrapJar.exists()) { try { System.setProperty ("catalina.home", (new File(System.getProperty("user.dir"), "..")) .getCanonicalPath()); } catch (Exception e) { // Ignore System.setProperty("catalina.home", System.getProperty("user.dir")); } } else { System.getProperty("catalina.config"); } } 

  在前一篇文件中我讲到在启动tomcat时,会添加一个参数-Dcatalina.home="**************************",这个的意思是说为系统添加一个属性,相当于System.setProperty("catalina.home","**************************"),可见此方法在执行到第一句时无意外的话就会返回了,因为catalina.home己经有值了。对于另一个方法,也很明确

Java代码  




  • private void setCatalinaBase() {   
  •   

  •         if (System.getProperty("catalina.base") != null)   

  •             return;   

  •         if (System.getProperty("catalina.home") != null)   

  •             System.setProperty("catalina.base",   

  •                                System.getProperty("catalina.home"));   

  •         else  

  •             System.setProperty("catalina.base",   

  •                                System.getProperty("user.dir"));   
  •   
  •     }  



private void setCatalinaBase() { if (System.getProperty("catalina.base") != null) return; if (System.getProperty("catalina.home") != null) System.setProperty("catalina.base", System.getProperty("catalina.home")); else System.setProperty("catalina.base", System.getProperty("user.dir")); }

  如果catalina.home有值的话就把它给catalina.base
  设置好家目录后,就开始初始化类加载器,这个时候前面设置的家目录就开始起作用了。
  这个方法会被调用,initClassLoaders()它本身没什么,它又接着调用createClassLoader()方法,在这个方法里会去读conf/catalina.properties里面的内容

Java代码  




  • String value = CatalinaProperties.getProperty(name + ".loader");  



String value = CatalinaProperties.getProperty(name + ".loader");

  这里的CatalinaProperties会首先执行一个static方法loadProperties(),用于加载conf/catalina.properties里面的内容,
  先得到catalina.properties的输入流,然后加载,最后将所有的属性添加到系统属性中

Java代码  




  • File home = new File(getCatalinaBase());   

  •                File conf = new File(home, "conf");   

  •                File properties = new File(conf, "catalina.properties");   

  •                is = new FileInputStream(properties);   
  •   
  •   

  • properties = new Properties();   
  •                properties.load(is);   
  •                is.close();  



File home = new File(getCatalinaBase()); File conf = new File(home, "conf"); File properties = new File(conf, "catalina.properties"); is = new FileInputStream(properties); properties = new Properties(); properties.load(is); is.close();


Java代码  




  • Enumeration enumeration = properties.propertyNames();   

  •         while (enumeration.hasMoreElements()) {   
  •             String name = (String) enumeration.nextElement();   
  •             String value = properties.getProperty(name);   

  •             if (value != null) {   

  •                 System.setProperty(name, value);//一个一个添加到系统属性当中   
  •             }   
  •         }  



Enumeration enumeration = properties.propertyNames(); while (enumeration.hasMoreElements()) { String name = (String) enumeration.nextElement(); String value = properties.getProperty(name); if (value != null) { System.setProperty(name, value);//一个一个添加到系统属性当中 } }

  接下来通过反射机制得到Catalina类的实例:

Java代码  




  • Class startupClass =   
  •             catalinaLoader.loadClass   

  •             ("org.apache.catalina.startup.Catalina");   
  •         Object startupInstance = startupClass.newInstance();  



Class startupClass = catalinaLoader.loadClass ("org.apache.catalina.startup.Catalina"); Object startupInstance = startupClass.newInstance();

  3.开启执行启动方法
  找到main方法中的

Java代码  




  • daemon.setAwait(true);   
  •                daemon.load(args);   
  •                daemon.start();  



daemon.setAwait(true); daemon.load(args); daemon.start();

  这几个方法内部都是通过反射机制调用Catalina的相关方法实现,这里我们主要看start方法,

Java代码  




  • public void start()   

  •         throws Exception {   

  •         if( catalinaDaemon==null ) init();   

  • //这里的catalinaDaemon是一个Catalina类的实例,在init方法中可以看出来。   

  •         Method method = catalinaDaemon.getClass().getMethod("start", (Class [] )null);   

  •         method.invoke(catalinaDaemon, (Object [])null);   
  •   
  •     }  



public void start() throws Exception { if( catalinaDaemon==null ) init(); //这里的catalinaDaemon是一个Catalina类的实例,在init方法中可以看出来。 Method method = catalinaDaemon.getClass().getMethod("start", (Class [] )null); method.invoke(catalinaDaemon, (Object [])null); }

  可以看出此方法最后调用了Catalina实例的start方法。
  进入start方法,可以看到它首先会执行load方法,load方法首先会去获取conf/server.xml配置文件的路径,

Java代码  




  • file = configFile();   

  •             inputStream = new FileInputStream(file);   

  •             inputSource = new InputSource("file://" + file.getAbsolutePath());   

  • //上面得到路径   
  •   
  • inputSource.setByteStream(inputStream);   

  •             digester.push(this);   
  •             digester.parse(inputSource);   
  •             inputStream.close();   

  • //下面负责解析server.xml并同时实例化Server  



file = configFile(); inputStream = new FileInputStream(file); inputSource = new InputSource("file://" + file.getAbsolutePath()); //上面得到路径 inputSource.setByteStream(inputStream); digester.push(this); digester.parse(inputSource); inputStream.close(); //下面负责解析server.xml并同时实例化Server

  实例化是通过Digester类完成,它可以实现xml与javaBean之间的转化,想了解的可以参考这里
  http://software.ccidnet.com/art/322/20021125/31671_2.html
  最后调用server的start方法启动tomcat

Java代码  




  • if (server instanceof Lifecycle) {   

  •             try {   
  •                 ((Lifecycle) server).start();   

  •             } catch (LifecycleException e) {   

  •                 log.error("Catalina.start: ", e);   
  •             }   
  •         }  

  总结:
1.反射的大量使用
  无论是classloader,还是具体的某个类,都是通过反射实现初始化和方法调用的。思路是:配置文件->系统属性->解析属性字符串->得到类路径->反射,得到类->方法调用
  这样可以使服务器的实现类很容易被替换,降低耦合性,减少将来代码的改动,的确是一种优秀的设计模式思想
  启动类的配置文件比较简单,都是简单的“键-值”,所以直接用了StringToken解析字符串。后面的代码牵涉到xml配置文件时,必定会换成xml解析器。但总体思路应该类似
  2.JMX的使用
  Bootstrap的JMX用得很少,仅仅对类装载器进行mbean注册,相信后面会有更多JMX的身影
   
  org.apache.catalina.startup.ClassLoaderFactory
顾名思义,是tomcat专门用来产生classloader的工厂类
  包括三个重载方法createClassLoader,内容都大同小异,无非是根据传入的类路径参数,产生一个相应的类装载器
  tomcat的类路径分为:包含class文件的目录、jar文件、包含jar的目录和URL
  以Bootstrap类用到的createClassLoader方法为例
  参数为:(String locations[],Integer types[], ClassLoader parent)
locations即为类路径的字符串表示,types为对应的类路径的类型,这两个数组的长度肯定是相同的,parant即为父classloader
  方法的处理逻辑很简单,即根据每个location的类型,对location字符串做处理,如取得绝对路径、遍历目录、判断是不是jar文件等等
  从代码可以看到,ClassLoaderFactory产生的classloader其实都是org.apache.catalina.loader.StandardClassLoader,这个类装载器留待以后研究
  org.apache.catalina.startup.Catalina
Bootstrap中,是通过start方法启动Catalina的
  Catalina中包含一个server实例,对应server.xml中的server元素。如果server实现了Lifecycle,start首先将server实例作为生命周期对象进行启动。然后,在JDK的Runtime中注册一个Shutdownhook。最后,如果指定了await参数,则让Catalina停止
  start方法代码如下:
  public void start() {
          if (server == null) {
            load();
        }
          long t1 = System.nanoTime();
        // Start the new server
        if (server instanceof Lifecycle) {
            try {
                ((Lifecycle) server).start();
            } catch (LifecycleException e) {
                log.error("Catalina.start: ", e);
            }
        }
          long t2 = System.nanoTime();
        if(log.isInfoEnabled())
            log.info("Server startup in " + ((t2 - t1) / 1000000) + " ms");
          try {
            // Register shutdown hook
            if (useShutdownHook) {
                if (shutdownHook == null) {
                    shutdownHook = new CatalinaShutdownHook();
                }
                Runtime.getRuntime().addShutdownHook(shutdownHook);
            }
        } catch (Throwable t) {
            // This will fail on JDK 1.2. Ignoring, as Tomcat can run
            // fine without the shutdown hook.
        }
          if (await) {
            await();
            stop();
        }
      }
  Shutdownhook是一个Thread对象,用于给JVM在收到停止命令时(例如Ctrl C),运行这些Thread执行收尾工作,具体用法可以参考JAVA文档
  CatalinaShutdownHook在这里就是执行Catalina.stop方法,所以在Catalina.stop方法里面,会手动删除Runtime中的CatalinaShutdownHook,以防止调用两次stop,如下:
  // Remove the ShutdownHook first so that server.stop()
            // doesn't get invoked twice
            if (useShutdownHook) {
                Runtime.getRuntime().removeShutdownHook(shutdownHook);
            }
   
  Catalina也有个main函数,不过里头调用的方法都是已废弃的,估计是遗留代码
  main函数主要是对命令行参数进行解析,比如可以取代默认的server.xml路径,其余工作主要交给load和start方法
  load方法
按以下顺序读取配置文件server.xml,通过Digester类进行解析
1.configFile字段指明的配置文件“conf/server.xml”
2.Catalina的类装载器的getResource方法(具体原理不清楚,似乎很神奇。和装载类的机制一样,propagate方式,父装载器优先)
3.最后是从内置的catalina.jar的server-embed.xml读入配置
  一旦读取成功,则后面的不读取,优先级依次降低
load方法最后进行配置和server的初始化,调用server.initialize方法
  其中,Digester类似乎很神奇很强大,可以根据一定的规则,对一个xml源文件进行解析,然后执行相应的动作,留待以后研究。这里有一篇文章可以参考
  http://software.ccidnet.com/pub/article/c322_a31671_p2.html
   
  stop方法
  // Shut down the server
        if (server instanceof Lifecycle) {
            try {
               ((Lifecycle) server).stop();
            } catch (LifecycleException e) {
                log.error("Catalina.stop", e);
            }
        }
  stopserver方法
比起stop方法要复杂得多,不是简单地调用server.stop,而是通过创建一个Digester,根据配置文件的定义进行动作;然后,以非常“原始”的方法:建立到server的socket连接,以字节流的方式,一个个字符地发送tomcat的“SHUTDOWN”指令,如下:
  // Stop the existing server
       try {
           String hostAddress = InetAddress.getByName("localhost").getHostAddress();
           Socket socket = new Socket(hostAddress, server.getPort());
           OutputStream stream = socket.getOutputStream();
           String shutdown = server.getShutdown();
           for (int i = 0; i < shutdown.length(); i++)
               stream.write(shutdown.charAt(i));
           stream.flush();
           stream.close();
           socket.close();
       } catch (IOException e) {
           log.error("Catalina.stop: ", e);
           System.exit(1);
       }
   
  补充
在看完StandardServer后,回到Catalina,现在对于Catalina的stop和stopServer方法的区别又多了一些理解。
  关于StandardServer的笔记:
  Catalina的stop方法,其实是调用StandardServer.stop,进而停止StandardServer内部的各个Service,是名副其实的真正“停止”服务器
  而stopServer方法,其实是构造一个socket连接到StandardServer的await方法打开的端口上,并发送SHUTDOWN命令。如前面所述,await只是起到阻塞作用,接受到SHUTDOWN命令后则await结束阻塞,并返回。具体要做哪些操作,还得由调用await的代码来决定。
  现在先姑且认为,Catalina的stop方法是单纯的、Catalinal自己定义的一个停止服务器的函数,里面的实现都是封装在Catalina内部的,完成的工作就是停止服务器的socket和相关的Service服务,外人无法改动。
  假如有第三方希望在停止server之前做点别的事情(比如释放特定的资源、或者通知其他组件),那么可以利用stopServer方法,待到await返回后,先完成自己要做的事情,最后再回头调用Catalina.stop(),完成最终的“停止” 。
  通过这样的设计,tomcat给了外部代码,或者说基于tomcat的二次开发人员很大的灵活性,不必拘泥于tomcat本身的实现,也有利于tomcat自身的功能扩展。
  本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/wangchengsi/archive/2008/09/20/2966850.aspx

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-330307-1-1.html 上篇帖子: 配置tomcat服务器 下篇帖子: tomcat相关知识备忘
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表