在整个Tomcat系列文章讲解之前, 我想说的是虽然整个Tomcat体系比较复杂, 但是Tomcat中的代码并不难读, 只要认真花点功夫, 一定能啃下来.

由于篇幅的原因, 很难把Tomcat所有的知识点都放到同一篇文章中, 我将把Tomcat系列文章分为Tomcat的启动, Tomcat中各模块的介绍和Tomcat中的设计模式三部分, 欢迎阅读与关注.

一:通过idea搭建Tomcat源码阅读环境

  • 首先我们到Tomcat的官网(http://tomcat.apache.org/)上下载Tomcat的源码, 本文分析的Tomcat版本是Tomcat7.0.94.
  • 进入官网后在左边的目录中选择Tomcat7, 然后到页面末尾的源码区进行下载.

  • 下载完成后打开idea, 选择File->Open->选择tomcat的源码目录
  • 然后到项目配置中设置JDK和源码目录. File->Project Structure->project SDK

  • 设置完毕后我们便可以开始愉快的源码阅读之旅了. (eclipse或其他ide搭建环境的思路也是差不多的, 可以摸索一下).

二:startup源码分析

  • 当我们初学tomcat的时候, 肯定先要学习怎样启动tomcat. 在tomcat的bin目录下有两个启动tomcat的文件, 一个是startup.bat, 它用于windows环境下启动tomcat; 另一个是startup.sh, 它用于linux环境下tomcat的启动. 两个文件中的逻辑是一样的, 我们只分析其中的startup.bat.
  • 下面给出startup.bat的源码
@echo off
rem Licensed to the Apache Software Foundation (ASF) under one or more
rem contributor license agreements. See the NOTICE file distributed with
rem this work for additional information regarding copyright ownership.
rem The ASF licenses this file to You under the Apache License, Version 2.0
rem (the "License"); you may not use this file except in compliance with
rem the License. You may obtain a copy of the License at
rem
rem http://www.apache.org/licenses/LICENSE-2.0
rem
rem Unless required by applicable law or agreed to in writing, software
rem distributed under the License is distributed on an "AS IS" BASIS,
rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
rem See the License for the specific language governing permissions and
rem limitations under the License. rem ---------------------------------------------------------------------------
rem Start script for the CATALINA Server
rem --------------------------------------------------------------------------- setlocal rem Guess CATALINA_HOME if not defined
set "CURRENT_DIR=%cd%"
if not "%CATALINA_HOME%" == "" goto gotHome
set "CATALINA_HOME=%CURRENT_DIR%"
if exist "%CATALINA_HOME%\bin\catalina.bat" goto okHome
cd ..
set "CATALINA_HOME=%cd%"
cd "%CURRENT_DIR%"
:gotHome
if exist "%CATALINA_HOME%\bin\catalina.bat" goto okHome
echo The CATALINA_HOME environment variable is not defined correctly
echo This environment variable is needed to run this program
goto end
:okHome set "EXECUTABLE=%CATALINA_HOME%\bin\catalina.bat" rem Check that target executable exists
if exist "%EXECUTABLE%" goto okExec
echo Cannot find "%EXECUTABLE%"
echo This file is needed to run this program
goto end
:okExec rem Get remaining unshifted command line arguments and save them in the
set CMD_LINE_ARGS=
:setArgs
if ""%1""=="""" goto doneSetArgs
set CMD_LINE_ARGS=%CMD_LINE_ARGS% %1
shift
goto setArgs
:doneSetArgs call "%EXECUTABLE%" start %CMD_LINE_ARGS% :end
  • .bat文件中@echo是打印指令, 用于控制台输出信息, rem是注释符.
  • 跳过开头的注释, 我们来到配置CATALINA_HOME的代码段, 执行startup.bat文件首先会设置CATALINA_HOME.
set "CURRENT_DIR=%cd%"
if not "%CATALINA_HOME%" == "" goto gotHome
set "CATALINA_HOME=%CURRENT_DIR%"
if exist "%CATALINA_HOME%\bin\catalina.bat" goto okHome
cd ..
set "CATALINA_HOME=%cd%"
cd "%CURRENT_DIR%"
:gotHome
if exist "%CATALINA_HOME%\bin\catalina.bat" goto okHome
echo The CATALINA_HOME environment variable is not defined correctly
echo This environment variable is needed to run this program
goto end
:okHome
  • 先通过set指令把当前目录设置到一个名为CURRENT_DIR的变量中,
  • 如果系统中配置过CATALINA_HOME则跳到gotHome代码段. 正常情况下我们的电脑都没有配置CATALINA_HOME, 所以往下执行, 把当前目录设置为CATALINA_HOME.
  • 然后判断CATALINA_HOME目录下是否存在catalina.bat文件, 如果存在就跳到okHome代码块.
  • 在okHome中, 会把catalina.bat文件的的路径赋给一个叫EXECUTABLE的变量, 然后会进一步判断这个路径是否存在, 存在则跳转到okExec代码块, 不存在的话会在控制台输出一些错误信息.
  • 在okExec中, 会把setArgs代码块的返回结果赋值给CMD_LINE_ARGS变量, 这个变量用于存储启动参数.
  • setArgs中首先会判断是否有参数, (if ""%1""==""""判断第一个参数是否为空), 如果没有参数则相当于参数项为空. 如果有参数则循环遍历所有的参数(每次拼接第一个参数).
  • 最后执行call "%EXECUTABLE%" start %CMD_LINE_ARGS%, 也就是说执行catalina.bat文件, 如果有参数则带上参数.
  • 总结: startup.bat文件实际上就做了一件事情: 启动catalina.bat.
  • ps: 这样看来, 在windows下启动tomcat未必一定要通过startup.bat, 用catalina.bat start也是可以的.

catalina.bat源码分析

  • 为了不让文章看起来太臃肿, 这里就不先把整个文件贴出来了, 我们逐块代码进行分析.
  • 跳过开头的注释, 我们来到下面的代码段:
setlocal

rem Suppress Terminate batch job on CTRL+C
if not ""%1"" == ""run"" goto mainEntry
if "%TEMP%" == "" goto mainEntry
if exist "%TEMP%\%~nx0.run" goto mainEntry
echo Y>"%TEMP%\%~nx0.run"
if not exist "%TEMP%\%~nx0.run" goto mainEntry
echo Y>"%TEMP%\%~nx0.Y"
call "%~f0" %* <"%TEMP%\%~nx0.Y"
rem Use provided errorlevel
set RETVAL=%ERRORLEVEL%
del /Q "%TEMP%\%~nx0.Y" >NUL 2>&1
exit /B %RETVAL%
:mainEntry
del /Q "%TEMP%\%~nx0.run" >NUL 2>&1
  • 大多情况下我们启动tomcat都没有设置参数, 所以直接跳到mainEntry代码段, 删除了一个临时文件后, 继续往下执行.
rem Guess CATALINA_HOME if not defined
set "CURRENT_DIR=%cd%"
if not "%CATALINA_HOME%" == "" goto gotHome
set "CATALINA_HOME=%CURRENT_DIR%"
if exist "%CATALINA_HOME%\bin\catalina.bat" goto okHome
cd ..
set "CATALINA_HOME=%cd%"
cd "%CURRENT_DIR%"
:gotHome if exist "%CATALINA_HOME%\bin\catalina.bat" goto okHome
echo The CATALINA_HOME environment variable is not defined correctly
echo This environment variable is needed to run this program
goto end
:okHome rem Copy CATALINA_BASE from CATALINA_HOME if not defined
if not "%CATALINA_BASE%" == "" goto gotBase
set "CATALINA_BASE=%CATALINA_HOME%"
  • 可以看到这段代码与startup.bat中开头的代码相似, 在确定CATALINA_HOME下有catalina.bat后把CATALINA_HOME赋给变量CATALINA_BASE.
rem Get standard environment variables
if not exist "%CATALINA_BASE%\bin\setenv.bat" goto checkSetenvHome
call "%CATALINA_BASE%\bin\setenv.bat"
goto setenvDone
:checkSetenvHome
if exist "%CATALINA_HOME%\bin\setenv.bat" call "%CATALINA_HOME%\bin\setenv.bat"
:setenvDone rem Get standard Java environment variables
if exist "%CATALINA_HOME%\bin\setclasspath.bat" goto okSetclasspath
echo Cannot find "%CATALINA_HOME%\bin\setclasspath.bat"
echo This file is needed to run this program
goto end
:okSetclasspath
call "%CATALINA_HOME%\bin\setclasspath.bat" %1
if errorlevel 1 goto end rem Add on extra jar file to CLASSPATH
rem Note that there are no quotes as we do not want to introduce random
rem quotes into the CLASSPATH
if "%CLASSPATH%" == "" goto emptyClasspath
set "CLASSPATH=%CLASSPATH%;"
:emptyClasspath
set "CLASSPATH=%CLASSPATH%%CATALINA_HOME%\bin\bootstrap.jar"
  • 上面这段代码依次执行了setenv.bat和setclasspath.bat文件, 目的是获得CLASSPATH, 相信会Java的同学应该都会在配置环境变量时都配置过classpath, 系统拿到classpath路径后把它和CATALINA_HOME拼接在一起, 最终定位到一个叫bootstrap.jar的文件. 虽然后面还有很多代码, 但是这里必须暂停提示一下: bootstrap.jar将是我们启动tomcat的环境.
  • 接下来从gotTmpdir代码块到noEndorsedVar代码块进行了一些配置, 由于不是主要内容暂且跳过.
echo Using CATALINA_BASE:   "%CATALINA_BASE%"
echo Using CATALINA_HOME: "%CATALINA_HOME%"
echo Using CATALINA_TMPDIR: "%CATALINA_TMPDIR%"
if ""%1"" == ""debug"" goto use_jdk
echo Using JRE_HOME: "%JRE_HOME%"
goto java_dir_displayed
:use_jdk
echo Using JAVA_HOME: "%JAVA_HOME%"
:java_dir_displayed
echo Using CLASSPATH: "%CLASSPATH%" set _EXECJAVA=%_RUNJAVA%
set MAINCLASS=org.apache.catalina.startup.Bootstrap
set ACTION=start
set SECURITY_POLICY_FILE=
set DEBUG_OPTS=
set JPDA= if not ""%1"" == ""jpda"" goto noJpda
set JPDA=jpda
if not "%JPDA_TRANSPORT%" == "" goto gotJpdaTransport
set JPDA_TRANSPORT=dt_socket
:gotJpdaTransport
if not "%JPDA_ADDRESS%" == "" goto gotJpdaAddress
set JPDA_ADDRESS=8000
:gotJpdaAddress
if not "%JPDA_SUSPEND%" == "" goto gotJpdaSuspend
set JPDA_SUSPEND=n
:gotJpdaSuspend
if not "%JPDA_OPTS%" == "" goto gotJpdaOpts
set JPDA_OPTS=-agentlib:jdwp=transport=%JPDA_TRANSPORT%,address=%JPDA_ADDRESS%,server=y,suspend=%JPDA_SUSPEND%
:gotJpdaOpts
shift
:noJpda if ""%1"" == ""debug"" goto doDebug
if ""%1"" == ""run"" goto doRun
if ""%1"" == ""start"" goto doStart
if ""%1"" == ""stop"" goto doStop
if ""%1"" == ""configtest"" goto doConfigTest
if ""%1"" == ""version"" goto doVersion
  • 接下来, 我们能看到一些重要的信息, 其中的重点是:
  • set _EXECJAVA=%_RUNJAVA%, 设置了jdk中bin目录下的java.exe文件路径.
  • set MAINCLASS=org.apache.catalina.startup.Bootstrap, 设置了tomcat的启动类为Bootstrap这个类. (后面会分析这个类)
  • set ACTION=start设置tomcat启动
  • 大家可以留意这些参数, 最后执行tomcat的启动时会用到.
if not ""%1"" == ""jpda"" goto noJpda
set JPDA=jpda
if not "%JPDA_TRANSPORT%" == "" goto gotJpdaTransport
set JPDA_TRANSPORT=dt_socket
:gotJpdaTransport
if not "%JPDA_ADDRESS%" == "" goto gotJpdaAddress
set JPDA_ADDRESS=8000
:gotJpdaAddress
if not "%JPDA_SUSPEND%" == "" goto gotJpdaSuspend
set JPDA_SUSPEND=n
:gotJpdaSuspend
if not "%JPDA_OPTS%" == "" goto gotJpdaOpts
set JPDA_OPTS=-agentlib:jdwp=transport=%JPDA_TRANSPORT%,address=%JPDA_ADDRESS%,server=y,suspend=%JPDA_SUSPEND%
:gotJpdaOpts
shift
:noJpda if ""%1"" == ""debug"" goto doDebug
if ""%1"" == ""run"" goto doRun
if ""%1"" == ""start"" goto doStart
if ""%1"" == ""stop"" goto doStop
if ""%1"" == ""configtest"" goto doConfigTest
if ""%1"" == ""version"" goto doVersion
  • 接着判断第一个参数是否是jpda, 是则进行一些设定. 而正常情况下第一个参数是start, 所以跳过这段代码. 接着会判断第一个参数的内容, 根据判断, 我们会跳到doStart代码段. (有余力的同学不妨看下debug, run等启动方式)
:doStart
shift
if "%TITLE%" == "" set TITLE=Tomcat
set _EXECJAVA=start "%TITLE%" %_RUNJAVA%
if not ""%1"" == ""-security"" goto execCmd
shift
echo Using Security Manager
set "SECURITY_POLICY_FILE=%CATALINA_BASE%\conf\catalina.policy"
goto execCmd
  • 可以看到doStart中无非也是设定一些参数, 最终会跳转到execCmd代码段
:execCmd
rem Get remaining unshifted command line arguments and save them in the
set CMD_LINE_ARGS=
:setArgs
if ""%1""=="""" goto doneSetArgs
set CMD_LINE_ARGS=%CMD_LINE_ARGS% %1
shift
goto setArgs
:doneSetArgs
  • 可以看到这段代码也是在拼接参数, 把参数拼接到一个叫CMD_LINE_ARGS的变量中, 接下来就是catalina最后的一段代码了.
rem Execute Java with the applicable properties
if not "%JPDA%" == "" goto doJpda
if not "%SECURITY_POLICY_FILE%" == "" goto doSecurity
%_EXECJAVA% %LOGGING_CONFIG% %LOGGING_MANAGER% %JAVA_OPTS% %CATALINA_OPTS% %DEBUG_OPTS% -D%ENDORSED_PROP%="%JAVA_ENDORSED_DIRS%" -classpath "%CLASSPATH%" -Dcatalina.base="%CATALINA_BASE%" -Dcatalina.home="%CATALINA_HOME%" -Djava.io.tmpdir="%CATALINA_TMPDIR%" %MAINCLASS% %CMD_LINE_ARGS% %ACTION%
goto end
:doSecurity
%_EXECJAVA% %LOGGING_CONFIG% %LOGGING_MANAGER% %JAVA_OPTS% %CATALINA_OPTS% %DEBUG_OPTS% -D%ENDORSED_PROP%="%JAVA_ENDORSED_DIRS%" -classpath "%CLASSPATH%" -Djava.security.manager -Djava.security.policy=="%SECURITY_POLICY_FILE%" -Dcatalina.base="%CATALINA_BASE%" -Dcatalina.home="%CATALINA_HOME%" -Djava.io.tmpdir="%CATALINA_TMPDIR%" %MAINCLASS% %CMD_LINE_ARGS% %ACTION%
goto end
:doJpda
if not "%SECURITY_POLICY_FILE%" == "" goto doSecurityJpda
%_EXECJAVA% %LOGGING_CONFIG% %LOGGING_MANAGER% %JAVA_OPTS% %JPDA_OPTS% %CATALINA_OPTS% %DEBUG_OPTS% -D%ENDORSED_PROP%="%JAVA_ENDORSED_DIRS%" -classpath "%CLASSPATH%" -Dcatalina.base="%CATALINA_BASE%" -Dcatalina.home="%CATALINA_HOME%" -Djava.io.tmpdir="%CATALINA_TMPDIR%" %MAINCLASS% %CMD_LINE_ARGS% %ACTION%
goto end
:doSecurityJpda
%_EXECJAVA% %LOGGING_CONFIG% %LOGGING_MANAGER% %JAVA_OPTS% %JPDA_OPTS% %CATALINA_OPTS% %DEBUG_OPTS% -D%ENDORSED_PROP%="%JAVA_ENDORSED_DIRS%" -classpath "%CLASSPATH%" -Djava.security.manager -Djava.security.policy=="%SECURITY_POLICY_FILE%" -Dcatalina.base="%CATALINA_BASE%" -Dcatalina.home="%CATALINA_HOME%" -Djava.io.tmpdir="%CATALINA_TMPDIR%" %MAINCLASS% %CMD_LINE_ARGS% %ACTION%
goto end :end
  • 跳过前面两行判断后, 来到了关键语句:
  • %_EXECJAVA% %LOGGING_CONFIG% %LOGGING_MANAGER% %JAVA_OPTS% %CATALINA_OPTS% %DEBUG_OPTS% -D%ENDORSED_PROP%="%JAVA_ENDORSED_DIRS%" -classpath "%CLASSPATH%" -Dcatalina.base="%CATALINA_BASE%" -Dcatalina.home="%CATALINA_HOME%" -Djava.io.tmpdir="%CATALINA_TMPDIR%" %MAINCLASS% %CMD_LINE_ARGS% %ACTION%
  • _EXECJAVA也就是_RUNJAVA, 也就是平时说的java指令, 但在之前的doStart代码块中把_EXECJAVA改为了start "%TITLE%" %_RUNJAVA%, 所以系统会另启一个命令行窗口, 名字叫Tomcat.
  • 在拼接一系列参数后, 我们会看见%MAINCLASS%, 也就是org.apache.catalina.startup.Bootstrap启动类, 拼接完启动参数后, 最后拼接的是%ACTION%, 也就是start.
  • 总结: catalina.bat最终执行了Bootstrap类中的main方法.
  • ps: 分析完整个catalina.bat我们发现我们可以通过设定不同的参数让tomcat以不同的方式运行. 在ide中我们是可以选择debug等模式启动tomcat的, 也可以为其配置参数, 在catalina.bat中我们看到了启动tomcat背后的运作流程.

Tomcat启动流程分析

  • 上面我们从运行startup.bat一路分析来到Bootstrap的启动, 下面我们先对Tomcat中的组件进行一次总览. 由于篇幅的原因, tomcat中各个模块的关系将留到下一篇文章叙述, 这里先给出一张tomcat中各模块的关系图.

  • 从图中我们可以看出tomcat中模块还是挺多的, 那么tomcat是如何启动这些模块的呢? 请看下面这张示意图.

  • 由图中我们可以看到从Bootstrap类的main方法开始, tomcat会以链的方式逐级调用各个模块的init()方法进行初始化, 待各个模块都初始化后, 又会逐级调用各个模块的start()方法启动各个模块. 下面我们通过部分源码看一下这个过程.
  • 首先我们来到Bootstrap类的main方法
 public static void main(String args[]) {

        if (daemon == null) {
// Don't set daemon until init() has completed
Bootstrap bootstrap = new Bootstrap();
try {
bootstrap.init();
} catch (Throwable t) {
handleThrowable(t);
t.printStackTrace();
return;
}
daemon = bootstrap;
} else {
// When running as a service the call to stop will be on a new
// thread so make sure the correct class loader is used to prevent
// a range of class not found exceptions.
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")) {
daemon.setAwait(true);
daemon.load(args);
daemon.start();
if (null == daemon.getServer()) {
System.exit(1);
}
} 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
// 省略
System.exit(1);
}
}
  • 我们可以看到, Bootstrap类首先会创建一个本类对象, 然后调用init()方法进行初始化. 执行完init()方法后会判断启动参数的值, 由于我们采取默认的启动方式, 所以main方法的参数是start, 会进入下面的判断代码块
 else if (command.equals("start")) {
daemon.setAwait(true);
daemon.load(args);
daemon.start();
if (null == daemon.getServer()) {
System.exit(1);
}
  • 可以看到在设置等待后, 调用了本类对象的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);
}
  • 可以看到方法的最后通过反射的方式调用了成员变量catalinaDaemon的load()方法. 通过跟踪源码我们可以看到catalinaDaemon是Bootstrap类的一个私有成员变量
/**
* Daemon reference.
*/
private Object catalinaDaemon = null;
  • 它会在Bootstrap的init()方法中通过反射的方式完成初始化. 下面我们回过头来看init()方法的源码.
public void init()
throws Exception
{ // Set Catalina path
setCatalinaHome();
setCatalinaBase(); 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");
Class<?> startupClass =
catalinaLoader.loadClass
("org.apache.catalina.startup.Catalina");
Object startupInstance = startupClass.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);
method.invoke(startupInstance, paramValues); catalinaDaemon = startupInstance;
}
  • 可以看到init()方法创建了一个Catalina对象, 并把该对象赋给了catalinaDaemon.
  • 再回过头来看之前分享给大家的启动流程图, 对应的是这一块

  • 后面的过程我就不逐一分析了, 你可以按照这张图去跟踪代码进行验证. 最终所有模块都能被初始化并启动.

本篇结束

  • 看到这里相信你应该对tomcat的启动过程已经有一个清晰的认识了, 但是这仅仅是理解tomcat的开始, 下一篇文章开始我将会详细介绍tomcat中的组件, 以及组件间的关系. 了解了各个组件后, tomcat的结构于你而言将不再神秘.

扩展阅读:

详解Tomcat系列(一)-从源码分析Tomcat的启动的更多相关文章

  1. 【Java入门提高篇】Day26 Java容器类详解(八)HashSet源码分析

    前面花了好几篇的篇幅把HashMap里里外外说了个遍,大家可能对于源码分析篇已经讳莫如深了.别慌别慌,这一篇来说说集合框架里最偷懒的一个家伙——HashSet,为什么说它是最偷懒的呢,先留个悬念,看完 ...

  2. 【Java入门提高篇】Day21 Java容器类详解(四)ArrayList源码分析

    今天要介绍的是List接口中最常用的实现类——ArrayList,本篇的源码分析基于JDK8,如果有不一致的地方,可先切换到JDK8后再进行操作. 本篇的内容主要包括这几块: 1.源码结构介绍 2.源 ...

  3. 【Java入门提高篇】Day22 Java容器类详解(五)HashMap源码分析(上)

    准备了很长时间,终于理清了思路,鼓起勇气,开始介绍本篇的主角——HashMap.说实话,这家伙能说的内容太多了,要是像前面ArrayList那样翻译一下源码,稍微说说重点,肯定会让很多人摸不着头脑,不 ...

  4. 【Java入门提高篇】Day24 Java容器类详解(七)HashMap源码分析(下)

    前两篇对HashMap这家伙的主要方法,主要算法做了一个详细的介绍,本篇主要介绍HashMap中默默无闻地工作着的集合们,包括KeySet,values,EntrySet,以及对应的迭代器:HashI ...

  5. 【Java入门提高篇】Day23 Java容器类详解(六)HashMap源码分析(中)

    上一篇中对HashMap中的基本内容做了详细的介绍,解析了其中的get和put方法,想必大家对于HashMap也有了更好的认识,本篇将从了算法的角度,来分析HashMap中的那些函数. HashCod ...

  6. 详解Go语言调度循环源码实现

    转载请声明出处哦~,本篇文章发布于luozhiyun的博客: https://www.luozhiyun.com/archives/448 本文使用的go的源码15.7 概述 提到"调度&q ...

  7. Tomcat处理HTTP请求源码分析(下)

    转载:http://www.infoq.com/cn/articles/zh-tomcat-http-request-2 很多开源应用服务器都是集成tomcat作为web container的,而且对 ...

  8. Tomcat源码分析之—具体启动流程分析

    从Tomcat启动调用栈可知,Bootstrap类的main方法为整个Tomcat的入口,在init初始化Bootstrap类的时候为设置Catalina的工作路径也就是Catalina_HOME信息 ...

  9. SequoiaDB 系列之七 :源码分析之catalog节点

    这一篇紧接着上一篇SequoiaDB 系列之六 :源码分析之coord节点来讲 在上一篇中,分析了coord转发数据包到catalog节点(也有可能是data节点,视情况而定).这一次,我们继续分析上 ...

随机推荐

  1. 用私有构造器或者枚举类型强化Singleton属性

    1.Singleton指仅仅被实例化一次的类.Singleton通常被用来代表那些本质上唯一的系统组件,如窗口管理器或者文件系统.使类称为Singleton会使它的客户端调试变的十分困难,因为无法给S ...

  2. 洛谷P1162 填涂颜色

    题目链接:https://www.luogu.org/problemnew/show/P1162 这道题是LITTLESUN写的第一道BFS哦! 对于这道题的的思路是把封闭图形外边的0标记一边,在最后 ...

  3. dcpromo(server2012不支持)

    dcpromo 编辑 dcpromo命令是一个“开关”命令.如果Windows 2000 Server计算机是成员服务器,则 运行dcpromo命令会安装活动目录,将其升级为域控制器:如果Window ...

  4. IOS开发学习笔记042-UITableView总结2

    一.自定义非等高的cell         如常见的微博界面,有的微博只有文字,有的有文字和图片.这些微博的高度不固定需要重新计算. 这里简单说一下几种方法.前面的步骤和设置等高的cell一样.现在来 ...

  5. Python基础-week06 面向对象编程进阶

    一.反射 1.定义:指的是通过字符串来操作类或者对象的属性 2.为什么用反射? 减少冗余代码,提升代码质量. 3.如何用反射? class People: country='China' def __ ...

  6. PAT——乙级1012

    1012 数字分类 (20 point(s)) 给定一系列正整数,请按要求对数字进行分类,并输出以下 5 个数字: A​1​​ = 能被 5 整除的数字中所有偶数的和: A​2​​ = 将被 5 除后 ...

  7. 商城商品购买数量增减的完美JS效果

    近期在开发一个地方O2O租书项目,使用ASP.NET MVC技术,其中在图书详情页,用户可以输入借阅的数量,这里使用了js来控制数量的增减和校验. 数量一定是数字 点击增减按钮的时候要能自动加1或减1 ...

  8. LAMP第二部分apache的配置

    1. 下载discuz! mkdir /data/wwwcd /data/wwwmv /root/Discuz_X3.2_SC_GBK.zip .wget http://download.comsen ...

  9. eclipse快捷键补全

    Eclipse中 补全快捷键 默认Alt+/ 但是每个人习惯有所不同 我需要来修改自己熟悉的快捷键 windows->preferences->General->keys将Conte ...

  10. POJ 1753 Flip Game(高斯消元+状压枚举)

    Flip Game Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 45691   Accepted: 19590 Descr ...