原文 http://tyrion.iteye.com/blog/1912290

前一篇文章里最后看到Bootstrap的main方法最后会调用org.apache.catalina.startup.Catalina对象的load和start两个方法,那么就来看看这两个方法里面到底做了些什么。

load方法:

  1. /**
  2. * Start a new server instance.
  3. */
  4. public void load() {
  5. long t1 = System.nanoTime();
  6. initDirs();
  7. // Before digester - it may be needed
  8. initNaming();
  9. // Create and execute our Digester
  10. Digester digester = createStartDigester();
  11. InputSource inputSource = null;
  12. InputStream inputStream = null;
  13. File file = null;
  14. try {
  15. file = configFile();
  16. inputStream = new FileInputStream(file);
  17. inputSource = new InputSource(file.toURI().toURL().toString());
  18. } catch (Exception e) {
  19. if (log.isDebugEnabled()) {
  20. log.debug(sm.getString("catalina.configFail", file), e);
  21. }
  22. }
  23. if (inputStream == null) {
  24. try {
  25. inputStream = getClass().getClassLoader()
  26. .getResourceAsStream(getConfigFile());
  27. inputSource = new InputSource
  28. (getClass().getClassLoader()
  29. .getResource(getConfigFile()).toString());
  30. } catch (Exception e) {
  31. if (log.isDebugEnabled()) {
  32. log.debug(sm.getString("catalina.configFail",
  33. getConfigFile()), e);
  34. }
  35. }
  36. }
  37. // This should be included in catalina.jar
  38. // Alternative: don't bother with xml, just create it manually.
  39. if( inputStream==null ) {
  40. try {
  41. inputStream = getClass().getClassLoader()
  42. .getResourceAsStream("server-embed.xml");
  43. inputSource = new InputSource
  44. (getClass().getClassLoader()
  45. .getResource("server-embed.xml").toString());
  46. } catch (Exception e) {
  47. if (log.isDebugEnabled()) {
  48. log.debug(sm.getString("catalina.configFail",
  49. "server-embed.xml"), e);
  50. }
  51. }
  52. }
  53. if (inputStream == null || inputSource == null) {
  54. if  (file == null) {
  55. log.warn(sm.getString("catalina.configFail",
  56. getConfigFile() + "] or [server-embed.xml]"));
  57. } else {
  58. log.warn(sm.getString("catalina.configFail",
  59. file.getAbsolutePath()));
  60. if (file.exists() && !file.canRead()) {
  61. log.warn("Permissions incorrect, read permission is not allowed on the file.");
  62. }
  63. }
  64. return;
  65. }
  66. try {
  67. inputSource.setByteStream(inputStream);
  68. digester.push(this);
  69. digester.parse(inputSource);
  70. } catch (SAXParseException spe) {
  71. log.warn("Catalina.start using " + getConfigFile() + ": " +
  72. spe.getMessage());
  73. return;
  74. } catch (Exception e) {
  75. log.warn("Catalina.start using " + getConfigFile() + ": " , e);
  76. return;
  77. } finally {
  78. try {
  79. inputStream.close();
  80. } catch (IOException e) {
  81. // Ignore
  82. }
  83. }
  84. getServer().setCatalina(this);
  85. // Stream redirection
  86. initStreams();
  87. // Start the new server
  88. try {
  89. getServer().init();
  90. } catch (LifecycleException e) {
  91. if (Boolean.getBoolean("org.apache.catalina.startup.EXIT_ON_INIT_FAILURE")) {
  92. throw new java.lang.Error(e);
  93. } else {
  94. log.error("Catalina.start", e);
  95. }
  96. }
  97. long t2 = System.nanoTime();
  98. if(log.isInfoEnabled()) {
  99. log.info("Initialization processed in " + ((t2 - t1) / 1000000) + " ms");
  100. }
  101. }

这个110行的代码看起来东西挺多,把注释、异常抛出、记录日志、流关闭、非空判断这些放在一边就会发现实际上真正做事的就这么几行代码:

  1. Digester digester = createStartDigester();
  2. inputSource.setByteStream(inputStream);
  3. digester.push(this);
  4. digester.parse(inputSource);
  5. getServer().setCatalina(this);
  6. getServer().init();

做的事情就两个,一是创建一个Digester对象,将当前对象压入Digester里的对象栈顶,根据inputSource里设置的文件xml路径及所创建的Digester对象所包含的解析规则生成相应对象,并调用相应方法将对象之间关联起来。二是调用Server接口对象的init方法。

这里碰到了Digester,有必要介绍一下Digester的一些基础知识。一般来说Java里解析xml文件有两种方式:一种是Dom4J之类将文件全部读取到内存中,在内存里构造一棵Dom树的方式来解析。一种是SAX的读取文件流,在流中碰到相应的xml节点触发相应的节点事件回调相应方法,基于事件的解析方式,优点是不需要先将文件全部读取到内存中。

Digester本身是采用SAX的解析方式,在其上提供了一层包装,对于使用者更简便友好罢了。最早是在struts1里面用的,后来独立出来成为apache的Commons下面的一个单独的子项目。Tomcat里又把它又封装了一层,为了描述方便,直接拿Tomcat里的Digester建一个单独的Digester的例子来介绍。

  1. package org.study.digester;
  2. import java.io.IOException;
  3. import java.io.InputStream;
  4. import java.util.ArrayList;
  5. import java.util.HashMap;
  6. import java.util.List;
  7. import junit.framework.Assert;
  8. import org.apache.tomcat.util.digester.Digester;
  9. import org.xml.sax.InputSource;
  10. public class MyDigester {
  11. private MyServer myServer;
  12. public MyServer getMyServer() {
  13. return myServer;
  14. }
  15. public void setMyServer(MyServer myServer) {
  16. this.myServer = myServer;
  17. }
  18. private Digester createStartDigester() {
  19. // 实例化一个Digester对象
  20. Digester digester = new Digester();
  21. // 设置为false表示解析xml时不需要进行DTD的规则校验
  22. digester.setValidating(false);
  23. // 是否进行节点设置规则校验,如果xml中相应节点没有设置解析规则会在控制台显示提示信息
  24. digester.setRulesValidation(true);
  25. // 将xml节点中的className作为假属性,不必调用默认的setter方法(一般的节点属性在解析时将会以属性值作为入参调用该节点相应对象的setter方法,而className属性的作用是提示解析器用该属性的值来实例化对象)
  26. HashMap<Class<?>, List<String>> fakeAttributes = new HashMap<Class<?>, List<String>>();
  27. ArrayList<String> attrs = new ArrayList<String>();
  28. attrs.add("className");
  29. fakeAttributes.put(Object.class, attrs);
  30. digester.setFakeAttributes(fakeAttributes);
  31. // addObjectCreate方法的意思是碰到xml文件中的Server节点则创建一个MyStandardServer对象
  32. digester.addObjectCreate("Server",
  33. "org.study.digester.MyStandardServer", "className");
  34. // 根据Server节点中的属性信息调用相应属性的setter方法,以上面的xml文件为例则会调用setPort、setShutdown方法,入参分别是8005、SHUTDOWN
  35. digester.addSetProperties("Server");
  36. // 将Server节点对应的对象作为入参调用栈顶对象的setMyServer方法,这里的栈顶对象即下面的digester.push方法所设置的当前类的对象this,就是说调用MyDigester类的setMyServer方法
  37. digester.addSetNext("Server", "setMyServer",
  38. "org.study.digester.MyServer");
  39. // 碰到xml的Server节点下的Listener节点时取className属性的值作为实例化类实例化一个对象
  40. digester.addObjectCreate("Server/Listener", null, "className");
  41. digester.addSetProperties("Server/Listener");
  42. digester.addSetNext("Server/Listener", "addLifecycleListener",
  43. "org.apache.catalina.LifecycleListener");
  44. digester.addObjectCreate("Server/Service",
  45. "org.study.digester.MyStandardService", "className");
  46. digester.addSetProperties("Server/Service");
  47. digester.addSetNext("Server/Service", "addMyService",
  48. "org.study.digester.MyService");
  49. digester.addObjectCreate("Server/Service/Listener", null, "className");
  50. digester.addSetProperties("Server/Service/Listener");
  51. digester.addSetNext("Server/Service/Listener", "addLifecycleListener",
  52. "org.apache.catalina.LifecycleListener");
  53. return digester;
  54. }
  55. public MyDigester() {
  56. Digester digester = createStartDigester();
  57. InputSource inputSource = null;
  58. InputStream inputStream = null;
  59. try {
  60. String configFile = "myServer.xml";
  61. inputStream = getClass().getClassLoader().getResourceAsStream(
  62. configFile);
  63. inputSource = new InputSource(getClass().getClassLoader()
  64. .getResource(configFile).toString());
  65. inputSource.setByteStream(inputStream);
  66. digester.push(this);
  67. digester.parse(inputSource);
  68. } catch (Exception e) {
  69. e.printStackTrace();
  70. } finally {
  71. try {
  72. inputStream.close();
  73. } catch (IOException e) {
  74. // Ignore
  75. }
  76. }
  77. getMyServer().setMyDigester(this);
  78. }
  79. public static void main(String[] agrs) {
  80. MyDigester md = new MyDigester();
  81. Assert.assertNotNull(md.getMyServer());
  82. }
  83. }

上面是我自己写的一个拿Tomcat里的Digester的工具类解析xml文件的例子,关键方法的调用含义已经在注释中写明,解析的是项目源文件发布的根目录下的myServer.xml文件。

  1. <?xml version='1.0' encoding='utf-8'?>
  2. <Server port="8005" shutdown="SHUTDOWN">
  3. <Listener
  4. className="org.apache.catalina.core.JasperListener" />
  5. <Listener
  6. className="org.apache.catalina.core.ThreadLocalLeakPreventionListener" />
  7. <Service name="Catalina">
  8. <Listener
  9. className="org.apache.catalina.core.ThreadLocalLeakPreventionListener" />
  10. </Service>
  11. </Server>

Digester的使用一般来说有4步:1.实例化一个Digester对象,并在对象里设置相应的节点解析规则。2.设置要解析的文件作为输入源(InputSource),这里InputSource与SAX里的一样,用以表示一个xml实体。3.将当前对象压入栈,4.调用Digester的parse方法解析xml,生成相应的对象。第3步中将当前对象压入栈中的作用是可以保存一个到Digester生成的一系列对象直接的引用,方便后续使用而已,所以不必是当前对象,只要有一个地方存放这个引用即可。

这里有必要说明的是很多文章里按照代码顺序来描述Digester的所谓栈模型来讲述addSetNext方法时的调用对象,实际上我的理解addSetNext方法具体哪个调用对象与XML文件里的节点树形结构相关,当前节点的父节点是哪个对象该对象就是调用对象。可以试验一下把这里的代码顺序打乱仍然可以按规则解析出来,createStartDigester方法只是在定义解析规则,具体解析与addObjectCreate、addSetProperties、addSetNext这些方法的调用顺序无关。Digester自己内部在解析xml的节点元素时增加了一个rule的概念,addObjectCreate、addSetProperties、addSetNext这些方法内部实际上是在添加rule,在最后解析xml时将会根据读取到的节点匹配相应节点路径下的rule,调用内部的方法。关于Tomcat内的Digester的解析原理以后可以单独写篇文章分析一下。

该示例的代码在附件中,将其放入tomcat7的源代码环境中即可直接运行。

看懂了上面的例子Catalina的createStartDigester方法应该就可以看懂了,它只是比示例要处理的节点类型更多,并且增加几个自定义的解析规则,如384行在碰到Server/GlobalNamingResources/节点时将会调用org.apache.catalina.startup.NamingRuleSet类中的addRuleInstances方法添加解析规则。

要解析的XML文件默认会先找conf/server.xml,如果当前项目找不到则通过其他路径找xml文件来解析,这里以默认情况为例将会解析server.xml

  1. <?xml version='1.0' encoding='utf-8'?>
  2. <!--
  3. Licensed to the Apache Software Foundation (ASF) under one or more
  4. contributor license agreements.  See the NOTICE file distributed with
  5. this work for additional information regarding copyright ownership.
  6. The ASF licenses this file to You under the Apache License, Version 2.0
  7. (the "License"); you may not use this file except in compliance with
  8. the License.  You may obtain a copy of the License at
  9. http://www.apache.org/licenses/LICENSE-2.0
  10. Unless required by applicable law or agreed to in writing, software
  11. distributed under the License is distributed on an "AS IS" BASIS,
  12. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. See the License for the specific language governing permissions and
  14. limitations under the License.
  15. -->
  16. <!-- Note:  A "Server" is not itself a "Container", so you may not
  17. define subcomponents such as "Valves" at this level.
  18. Documentation at /docs/config/server.html
  19. -->
  20. <Server port="8005" shutdown="SHUTDOWN">
  21. <!-- Security listener. Documentation at /docs/config/listeners.html
  22. <Listener className="org.apache.catalina.security.SecurityListener" />
  23. -->
  24. <!--APR library loader. Documentation at /docs/apr.html -->
  25. <Listener className="org.apache.catalina.core.AprLifecycleListener" SSLEngine="on" />
  26. <!--Initialize Jasper prior to webapps are loaded. Documentation at /docs/jasper-howto.html -->
  27. <Listener className="org.apache.catalina.core.JasperListener" />
  28. <!-- Prevent memory leaks due to use of particular java/javax APIs-->
  29. <Listener className="org.apache.catalina.core.JreMemoryLeakPreventionListener" />
  30. <Listener className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener" />
  31. <Listener className="org.apache.catalina.core.ThreadLocalLeakPreventionListener" />
  32. <!-- Global JNDI resources
  33. Documentation at /docs/jndi-resources-howto.html
  34. -->
  35. <GlobalNamingResources>
  36. <!-- Editable user database that can also be used by
  37. UserDatabaseRealm to authenticate users
  38. -->
  39. <Resource name="UserDatabase" auth="Container"
  40. type="org.apache.catalina.UserDatabase"
  41. description="User database that can be updated and saved"
  42. factory="org.apache.catalina.users.MemoryUserDatabaseFactory"
  43. pathname="conf/tomcat-users.xml" />
  44. </GlobalNamingResources>
  45. <!-- A "Service" is a collection of one or more "Connectors" that share
  46. a single "Container" Note:  A "Service" is not itself a "Container",
  47. so you may not define subcomponents such as "Valves" at this level.
  48. Documentation at /docs/config/service.html
  49. -->
  50. <Service name="Catalina">
  51. <!--The connectors can use a shared executor, you can define one or more named thread pools-->
  52. <!--
  53. <Executor name="tomcatThreadPool" namePrefix="catalina-exec-"
  54. maxThreads="150" minSpareThreads="4"/>
  55. -->
  56. <!-- A "Connector" represents an endpoint by which requests are received
  57. and responses are returned. Documentation at :
  58. Java HTTP Connector: /docs/config/http.html (blocking & non-blocking)
  59. Java AJP  Connector: /docs/config/ajp.html
  60. APR (HTTP/AJP) Connector: /docs/apr.html
  61. Define a non-SSL HTTP/1.1 Connector on port 8080
  62. -->
  63. <Connector port="8080" protocol="HTTP/1.1"
  64. connectionTimeout="20000"
  65. redirectPort="8443" />
  66. <!-- A "Connector" using the shared thread pool-->
  67. <!--
  68. <Connector executor="tomcatThreadPool"
  69. port="8080" protocol="HTTP/1.1"
  70. connectionTimeout="20000"
  71. redirectPort="8443" />
  72. -->
  73. <!-- Define a SSL HTTP/1.1 Connector on port 8443
  74. This connector uses the JSSE configuration, when using APR, the
  75. connector should be using the OpenSSL style configuration
  76. described in the APR documentation -->
  77. <!--
  78. <Connector port="8443" protocol="HTTP/1.1" SSLEnabled="true"
  79. maxThreads="150" scheme="https" secure="true"
  80. clientAuth="false" sslProtocol="TLS" />
  81. -->
  82. <!-- Define an AJP 1.3 Connector on port 8009 -->
  83. <Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />
  84. <!-- An Engine represents the entry point (within Catalina) that processes
  85. every request.  The Engine implementation for Tomcat stand alone
  86. analyzes the HTTP headers included with the request, and passes them
  87. on to the appropriate Host (virtual host).
  88. Documentation at /docs/config/engine.html -->
  89. <!-- You should set jvmRoute to support load-balancing via AJP ie :
  90. <Engine name="Catalina" defaultHost="localhost" jvmRoute="jvm1">
  91. -->
  92. <Engine name="Catalina" defaultHost="localhost">
  93. <!--For clustering, please take a look at documentation at:
  94. /docs/cluster-howto.html  (simple how to)
  95. /docs/config/cluster.html (reference documentation) -->
  96. <!--
  97. <Cluster className="org.apache.catalina.ha.tcp.SimpleTcpCluster"/>
  98. -->
  99. <!-- Use the LockOutRealm to prevent attempts to guess user passwords
  100. via a brute-force attack -->
  101. <Realm className="org.apache.catalina.realm.LockOutRealm">
  102. <!-- This Realm uses the UserDatabase configured in the global JNDI
  103. resources under the key "UserDatabase".  Any edits
  104. that are performed against this UserDatabase are immediately
  105. available for use by the Realm.  -->
  106. <Realm className="org.apache.catalina.realm.UserDatabaseRealm"
  107. resourceName="UserDatabase"/>
  108. </Realm>
  109. <Host name="localhost"  appBase="webapps"
  110. unpackWARs="true" autoDeploy="true">
  111. <!-- SingleSignOn valve, share authentication between web applications
  112. Documentation at: /docs/config/valve.html -->
  113. <!--
  114. <Valve className="org.apache.catalina.authenticator.SingleSignOn" />
  115. -->
  116. <!-- Access log processes all example.
  117. Documentation at: /docs/config/valve.html
  118. Note: The pattern used is equivalent to using pattern="common" -->
  119. <Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
  120. prefix="localhost_access_log." suffix=".txt"
  121. pattern="%h %l %u %t &quot;%r&quot; %s %b" />
  122. </Host>
  123. </Engine>
  124. </Service>
  125. </Server>

这样经过对xml文件的解析将会产生org.apache.catalina.core.StandardServer、org.apache.catalina.core.StandardService、org.apache.catalina.connector.Connector、org.apache.catalina.core.StandardEngine、org.apache.catalina.core.StandardHost、org.apache.catalina.core.StandardContext等等一系列对象,这些对象从前到后前一个包含后一个对象的引用(一对一或一对多的关系)。

解析完xml之后关闭文件流,接着设置StandardServer对象(该对象在上面解析xml时)的catalina的引用为当前对象,这种对象间的双向引用在Tomcat的很多地方都会碰到。

接下来将调用StandardServer对象的init方法。

上面分析的是Catalina的load方法,上一篇文章里看到Bootstrap类启动时还会调用Catalina对象的start方法,代码如下:

  1. /**
  2. * Start a new server instance.
  3. */
  4. public void start() {
  5. if (getServer() == null) {
  6. load();
  7. }
  8. if (getServer() == null) {
  9. log.fatal("Cannot start server. Server instance is not configured.");
  10. return;
  11. }
  12. long t1 = System.nanoTime();
  13. // Start the new server
  14. try {
  15. getServer().start();
  16. } catch (LifecycleException e) {
  17. log.fatal(sm.getString("catalina.serverStartFail"), e);
  18. try {
  19. getServer().destroy();
  20. } catch (LifecycleException e1) {
  21. log.debug("destroy() failed for failed Server ", e1);
  22. }
  23. return;
  24. }
  25. long t2 = System.nanoTime();
  26. if(log.isInfoEnabled()) {
  27. log.info("Server startup in " + ((t2 - t1) / 1000000) + " ms");
  28. }
  29. // Register shutdown hook
  30. if (useShutdownHook) {
  31. if (shutdownHook == null) {
  32. shutdownHook = new CatalinaShutdownHook();
  33. }
  34. Runtime.getRuntime().addShutdownHook(shutdownHook);
  35. // If JULI is being used, disable JULI's shutdown hook since
  36. // shutdown hooks run in parallel and log messages may be lost
  37. // if JULI's hook completes before the CatalinaShutdownHook()
  38. LogManager logManager = LogManager.getLogManager();
  39. if (logManager instanceof ClassLoaderLogManager) {
  40. ((ClassLoaderLogManager) logManager).setUseShutdownHook(
  41. false);
  42. }
  43. }
  44. if (await) {
  45. await();
  46. stop();
  47. }
  48. }

这里最主要的是调用StandardServer对象的start方法。

经过以上分析发现,在解析xml产生相应一系列对象后会顺序调用StandardServer对象的init、start方法,这里将会涉及到Tomcat的容器生命周期(Lifecycle),关于这点留到下一篇文章中分析。

Tomcat7启动分析(三)Digester的使用(转载)的更多相关文章

  1. FPGA低温不能启动分析(转)

    FPGA低温不能启动分析 现象描述:在给medium板光端机做低温试验时,分别给发送版.接收板断电重新启动,发现有的板子在-40°可以启动,而有些板子在-20°都不能启动,需要升高温度到0°以上才能启 ...

  2. tomcat源码分析(三)一次http请求的旅行-从Socket说起

    p { margin-bottom: 0.25cm; line-height: 120% } tomcat源码分析(三)一次http请求的旅行 在http请求旅行之前,我们先来准备下我们所需要的工具. ...

  3. mkimage工具 加载地址和入口地址 内核启动分析

    第三章第二节 mkimage工具制作Linux内核的压缩镜像文件,需要使用到mkimage工具.mkimage这个工具位于u-boot-2013. 04中的tools目录下,它可以用来制作不压缩或者压 ...

  4. Android Binder------ServiceManager启动分析

    ServiceManager启动分析   简述: ServiceManager是一个全局的manager.调用了Jni函数,实现addServicew getService checkService ...

  5. 一些有用的javascript实例分析(三)

    原文:一些有用的javascript实例分析(三) 10 输入两个数字,比较大小 window.onload = function () { var aInput = document.getElem ...

  6. Java线程池ThreadPoolExecutor使用和分析(三) - 终止线程池原理

    相关文章目录: Java线程池ThreadPoolExecutor使用和分析(一) Java线程池ThreadPoolExecutor使用和分析(二) - execute()原理 Java线程池Thr ...

  7. 第3阶段——内核启动分析之start_kernel初始化函数(5)

    内核启动分析之start_kernel初始化函数(init/main.c) stext函数启动内核后,就开始进入start_kernel初始化各个函数, 下面只是浅尝辄止的描述一下函数的功能,很多函数 ...

  8. AngularJS标准Web业务流程开发框架—1.AngularJS模块以及启动分析

    前言: AngularJS中提到模块是自定义的模块标准,提到这不得不说AngularJS是框架中的老大哥,思想相当的前卫..在这框架满天横行的时代,AngularJS有些思想至今未被超越,当然仁者见仁 ...

  9. 《转》深入理解Activity启动流程(三)–Activity启动的详细流程1

    本文原创作者:Cloud Chou. 出处:本文链接 本系列博客将详细阐述Activity的启动流程,这些博客基于Cm 10.1源码研究. 深入理解Activity启动流程(一)--Activity启 ...

随机推荐

  1. 01 mysql

    Sql语句: Structured Query Language, 结构化查询语言 分类: DDL (数据定义语句) 数据定义语言 - Data Definition Language 用来定义数据库 ...

  2. python------面向对象介绍之多态实例

    一. 多态 一种接口,多种实现. 多态性(polymorphisn)是允许你将父对象设置成为和一个或更多的他的子对象相等的技术,赋值之后,父对象就可以根据当前赋值给它的子对象的特性以不同的方式运作. ...

  3. AangularJS入门总结三

    (参考的资料) 1. 数据绑定的原理: (1)  $watch 队列: 在DOM中每次绑定一些东西,就会往$watch队列中插入一条$watch: 每一个绑定到了DOM上的数据都会生成一个$watch ...

  4. 下面有关 JAVA 异常类的描述,说法正确的有()

    都是Throwable的子类: 1.Exception(异常) :是程序本身可以处理的异常. 2.Error(错误): 是程序无法处理的错误.这些错误表示故障发生于虚拟机自身.或者发生在虚拟机试图执行 ...

  5. LeetCode - Top K Frequent Words

    Given a non-empty list of words, return the k most frequent elements. Your answer should be sorted b ...

  6. 使用patroni 解决hasura graphql-engine pg 数据库ha的问题

    环境准备 机器pg 数据库地址修改为haproxy 的ip地址,端口是haproxy的tcp 端口,配置比较简单 hasura graphql-engine docker-compose versio ...

  7. 使用netlify-statuskit 进行系统业务状态报告

    netlify-statuskit 是netlify 团队开源的一款类似github status 的脚手架website,使用此工具 我们可以对于我们系统模块进行报告,同时对于故障时,我们可以进行故 ...

  8. 怎样优化app,看Facebook怎样做

    周四,Facebook Engineering blog 发表了一篇名为<Improving Facebook on Android>博文.博文从四个方面(Performance,Data ...

  9. 两个int(32位)整数m和n的二进制表达中,有多少个位(bit)不同

    思路:利用&用算加右移的方法来提取二进制中的每一位数,然后进行比较,查看是否相同. #include<stdio.h> #include<stdlib.h> int m ...

  10. DOM Access and Manipulation JS 操纵DOM

    JS 操纵DOM 有两种很简单的方式: 如果知道ID 的情况下. 我们可以使用 document.getElementById 我们还可以使用 document.getElementById(&quo ...