tomcat原理解析(二):整体架构

一 整体结构

前面tomcat实现原理(一)里面描述了整个tomcat接受一个http请求的简单处理,这里面我们讲下整个tomcat的架构,以便对整体结构有宏观的了解。tomat里面由很多个容器结合在一起,主要有server,service,context,host,engine,wrapper,connector这7个容器来组装。当然了tomcat里面还有其它容器这里就不一一列举,因为我只看重点的。这7个容器存着父子关系,即可以通过当前容器找自己的父容器和自己的子容器。说到这我画了一个简单的结构图,让这种关系更加直观。如下:

根据图可以看出server是最外层的一个容器,它里面可以包含了Service容器,Service容器里面又包含了Connector和Engine,Engine容器里面又包含了Host,Host容器包含Context,Context容器包含Wrapper容器。就好比一个人有最外层的躯体,躯体里面有心脏,肺,胃,肾脏等等很多个个功能器件。

二 组件描述

这里主要通过tomcat的源码来分析这7个容器是通过什么样的机制来组织在一起的,相互的包含的关系怎么处理。

1.server组件

server就像一个架子,很多其它子容器都装配在这个架子上。同时这个容器又暴露了一些入口对外提供服务。比如初始化服务,启动服务,停止服务等。源码中的server是一个接口,如下图:

查看Server接口类它还继承着Lifecycle接口,详情如下图:


Lifecycle接口控制着各个组件的生命周期,比如接口中的抽象方法init()初始化,start()启动,stop()停止,destory()销毁。 Server的标准实现类为StandardServer,细读StandardServer里面的接口addService,findServices的实现代码,可以看出这Service和 Server有着关联,Server可以有多个Service以数组的形式保存在其中。

[html] view plain copy

  1. public void addService(Service service) {
  2. service.setServer(this);
  3. synchronized (services) {
  4. Service results[] = new Service[services.length + 1];
  5. System.arraycopy(services, 0, results, 0, services.length);
  6. results[services.length] = service;
  7. services = results;
  8. if (getState().isAvailable()) {
  9. try {
  10. service.start();
  11. } catch (LifecycleException e) {
  12. // Ignore
  13. }
  14. }
  15. // Report this property change to interested listeners
  16. support.firePropertyChange("service", null, service);
  17. }
  18. }<span style="font-family:Microsoft YaHei;font-size:12px;">
  19. </span>

1.将设置进来的Service 对象设置父组件为Server

2.重新构建了一个Service数组,然后将老的Service拷贝到新的数组中,同时追加新的Service组件

3.根据当前组件的生命状态,判断新加入的组件是否需要启动

2.Service组件

service接口它也继承Lifecycle接口。 前面说过了service组件的父组件是server组件,结构如下图:

它的标准实现是StandardService类,查看接口抽象方法addConnector的具体实现可以看出通过它可以给service容器添加connector容器,这里的connector就主要负责接收浏览器客户端发过来的http请求,仔细看里面的源码可以发现它接到请求时生成了一个socket对象,并将这个socket对象放入了一个线程中,同时线程会存入一个线程池来处理这次响应,并根据请求的详情信息来定位响应资源,响应的资源通过Engine容器给出。它就像浏览器和 Engine容器的桥梁,具体如何响应请求的会通过后面的章节来细说。先看是怎么将connector容器绑定到service中。 如下面代码:

[html] view plain copy

  1. public void addConnector(Connector connector) {
  2. synchronized (connectors) {
  3. connector.setService(this);
  4. Connector results[] = new Connector[connectors.length + 1];
  5. System.arraycopy(connectors, 0, results, 0, connectors.length);
  6. results[connectors.length] = connector;
  7. connectors = results;
  8. if (getState().isAvailable()) {
  9. try {
  10. ((Lifecycle) connector).start();
  11. } catch (LifecycleException e) {
  12. log.error("Connector.start", e);
  13. }
  14. }
  15. // Report this property change to interested listeners
  16. support.firePropertyChange("connector", null, connector);
  17. }
  18. }

1.给当前入去的Connector主键设置父容器service.
2.根据原有Connector集合的大小加上新加入的容器,从新构建了一个Connector数组。
3.将旧的数据拷贝到新的容器数组中,再添加新加的容器
4.然后根据当前容器的生命状态判断是否要启动新加入的容器
这里存放connector组件为什么用数组而不用List来存放不是太明白。感觉跟server管理service组件操作一样。看完所有的抽象接口是不是感觉有些奇怪,按照前面给的整体架构图怎么没看到给Service添加Engine组件的入口。查看Engine的标准实现类StandardEngine其实是实现了Container接口的,所以这里应该是通过setContainer接口设置进去的,其源码如下:

[html] view plain copy

  1. public void setContainer(Container container) {
  2. Container oldContainer = this.container;
  3. if ((oldContainer != null) && (oldContainer instanceof Engine))
  4. ((Engine) oldContainer).setService(null);
  5. this.container = container;
  6. if ((this.container != null) && (this.container instanceof Engine))
  7. ((Engine) this.container).setService(this);
  8. if (getState().isAvailable() && (this.container != null)) {
  9. try {
  10. this.container.start();
  11. } catch (LifecycleException e) {
  12. // Ignore
  13. }
  14. }
  15. if (getState().isAvailable() && (oldContainer != null)) {
  16. try {
  17. oldContainer.stop();
  18. } catch (LifecycleException e) {
  19. // Ignore
  20. }
  21. }
  22. // Report this property change to interested listeners
  23. support.firePropertyChange("container", oldContainer, this.container);
  24. }

1.将原有的Container(Engine)对象 的父对象置为空
2.同时将旧的Container对象覆盖掉,变成新注入的Container对象
3.同时根据当前组件的生命周期来判断是否将新注入的容器启动

3.Engine组件
Engine接口类为该组件的抽象接口,它继承Container接口跟前面描述的两个组件似乎有些不一样,查看源码后发现Container接口也继承了Lifecycle接口。Engine结构如下图:

它的标准实现是StandardEngine类,查setService接口的具体实现,service组件设置进Engine组件中,让两者之间关联。怎么没有看到添加子组件的入口呢?我们查看StandardEngine实现类中,它继承了ContainerBase类,而这个类实现了Container接口。在StandardEngine类中看到了addChild方法的实现 如下代码:

[html] view plain copy

  1. @Override
  2. public void addChild(Container child) {
  3. if (!(child instanceof Host))
  4. throw new IllegalArgumentException
  5. (sm.getString("standardEngine.notHost"));
  6. super.addChild(child);
  7. }<span style="font-family:Microsoft YaHei;font-size:12px;">
  8. </span>

通过这个方法来为Engine添加子组件

1.判断当前传入的组件是否是Host类型的,否则抛异常

2.调用服务类中的addChild方法,同时将child变量传递到父类中处理,我们继续看父类ContainerBase中的代码。

[html] view plain copy

  1. public void addChild(Container child) {
  2. if (Globals.IS_SECURITY_ENABLED) {
  3. PrivilegedAction<Void> dp =
  4. new PrivilegedAddChild(child);
  5. AccessController.doPrivileged(dp);
  6. } else {
  7. addChildInternal(child);
  8. }
  9. }
  10. private void addChildInternal(Container child) {
  11. if( log.isDebugEnabled() )
  12. log.debug("Add child " + child + " " + this);
  13. synchronized(children) {
  14. if (children.get(child.getName()) != null)
  15. throw new IllegalArgumentException("addChild:  Child name ‘" +
  16. child.getName() +
  17. "‘ is not unique");
  18. child.setParent(this);  // May throw IAE
  19. children.put(child.getName(), child);
  20. // Start child
  21. if ((getState().isAvailable() ||
  22. LifecycleState.STARTING_PREP.equals(getState())) &&
  23. startChildren) {
  24. boolean success = false;
  25. try {
  26. child.start();
  27. success = true;
  28. } catch (LifecycleException e) {
  29. log.error("ContainerBase.addChild: start: ", e);
  30. throw new IllegalStateException
  31. ("ContainerBase.addChild: start: " + e);
  32. } finally {
  33. if (!success) {
  34. children.remove(child.getName());
  35. }
  36. }
  37. }
  38. fireContainerEvent(ADD_CHILD_EVENT, child);
  39. }
  40. }

最终它调用了addChildInternal方法来处理。

child.setParent(this);
children.put(child.getName(), child);
1.这里传入host子组件的同时,给host子组件设置了父组件
2.将子组件保存在children变量中,这里的children其实就是个HashMap对象。
看到这里也就明白了,所有继承ContainerBase类的组件在添加子组件都是依赖父类中的MAP对象来保存的.

4.Host组件
Host类为该组件的接口定义,也继承了Container类,它的标准实现是StandardHost类,继承ContainerBase类。host结构如下图所示:

因为该标准实现类StandardHost也继承了ContainerBase类,所以该组件的子组件添加也依赖父类中的Map来保存,跟Engine组件管理子组件是一样的。每一个HOST相当于一个主机,并负责展开和运行多个部署进去的war包。每个主机下面有多个部署的应用,一个应用就对应着一个Context。通过context将多个应用分隔开。所以host的子组件就是context,在通过ContainerBase类的addChild方法来添加context子容器的同时也给它设置了父容器host对象。

5.Context容器

context接口继承了Container,StandardContext类为是Context的标准实现,它也继承了ContainerBase类,所以给改容器注入子容器也是通过containerBase类中的addaddChild来处理的。Context容器代表着一个servlet容器,它为servlet运行提供环境。context管理着里面servlet实例,servlet实例在context中以Wrapper形式存在。那么一个http请求是怎么找到对应的servlet实例的呢?后面章节再来详细描述

6.Wrapper容器

Wrapper接口类也继承Container类,它的标准实现类是StandardWrapper,也继承了ContainerBase类。因为Wrapper容器是最底层的容器了,所以它不存在子容器。Wrapper 代表一个Servlet,它负责管理一个 Servlet,包括的 Servlet的装载、初始化、执行以及资源回收。

三 组件相关类结构

上面小节描述将各个容器编织到一起,阅读相关容器的代码结构可以整理出如下类的继承关系。从类的关系图可以看出所有的容器都实现了Lifecycle接口,该接口定义了控制着tomcat的初始化,启动,停止,销毁等抽象操作。具体的实现都留给了子类实现。

      

仔细查看上面的结构图,可以发现LifecycleBase比较陌生,在前面章节没怎么提到过。LifecycleBase类实现了Lifecycle接口,它实现了Lifecycle接口中的init()start()stop()destroy()。我们看看LifecycleBase中的类是怎么实现的,源码如下:

[java] view plain copy

  1. /*
  2. * Licensed to the Apache Software Foundation (ASF) under one or more
  3. * contributor license agreements.  See the NOTICE file distributed with
  4. * this work for additional information regarding copyright ownership.
  5. * The ASF licenses this file to You under the Apache License, Version 2.0
  6. * (the "License"); you may not use this file except in compliance with
  7. * the License.  You may obtain a copy of the License at
  8. *
  9. *      http://www.apache.org/licenses/LICENSE-2.0
  10. *
  11. * Unless required by applicable law or agreed to in writing, software
  12. * distributed under the License is distributed on an "AS IS" BASIS,
  13. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. * See the License for the specific language governing permissions and
  15. * limitations under the License.
  16. */
  17. package org.apache.catalina.util;
  18. import org.apache.catalina.Lifecycle;
  19. import org.apache.catalina.LifecycleException;
  20. import org.apache.catalina.LifecycleListener;
  21. import org.apache.catalina.LifecycleState;
  22. import org.apache.juli.logging.Log;
  23. import org.apache.juli.logging.LogFactory;
  24. import org.apache.tomcat.util.res.StringManager;
  25. /**
  26. * Base implementation of the {@link Lifecycle} interface that implements the
  27. * state transition rules for {@link Lifecycle#start()} and
  28. * {@link Lifecycle#stop()}
  29. */
  30. public abstract class LifecycleBase implements Lifecycle {
  31. private static Log log = LogFactory.getLog(LifecycleBase.class);
  32. private static StringManager sm =
  33. StringManager.getManager("org.apache.catalina.util");
  34. /**
  35. * Used to handle firing lifecycle events.
  36. * TODO: Consider merging LifecycleSupport into this class.
  37. */
  38. private LifecycleSupport lifecycle = new LifecycleSupport(this);
  39. /**
  40. * The current state of the source component.
  41. */
  42. private volatile LifecycleState state = LifecycleState.NEW;
  43. /**
  44. * {@inheritDoc}
  45. */
  46. @Override
  47. public void addLifecycleListener(LifecycleListener listener) {
  48. lifecycle.addLifecycleListener(listener);
  49. }
  50. /**
  51. * {@inheritDoc}
  52. */
  53. @Override
  54. public LifecycleListener[] findLifecycleListeners() {
  55. return lifecycle.findLifecycleListeners();
  56. }
  57. /**
  58. * {@inheritDoc}
  59. */
  60. @Override
  61. public void removeLifecycleListener(LifecycleListener listener) {
  62. lifecycle.removeLifecycleListener(listener);
  63. }
  64. /**
  65. * Allow sub classes to fire {@link Lifecycle} events.
  66. *
  67. * @param type  Event type
  68. * @param data  Data associated with event.
  69. */
  70. protected void fireLifecycleEvent(String type, Object data) {
  71. lifecycle.fireLifecycleEvent(type, data);
  72. }
  73. public synchronized final void init() throws LifecycleException {
  74. if (!state.equals(LifecycleState.NEW)) {
  75. invalidTransition(Lifecycle.INIT_EVENT);
  76. }
  77. //1.实现初始化的方法,最终是调用了当前类的initInternal方法,发现
  78. initInternal抽象的
  79. initInternal();
  80. setState(LifecycleState.INITIALIZED);
  81. }
  82. protected abstract void initInternal() throws LifecycleException;
  83. /**
  84. * {@inheritDoc}
  85. */
  86. @Override
  87. public synchronized final void start() throws LifecycleException {
  88. if (LifecycleState.STARTING_PREP.equals(state) ||
  89. LifecycleState.STARTING.equals(state) ||
  90. LifecycleState.STARTED.equals(state)) {
  91. if (log.isDebugEnabled()) {
  92. Exception e = new LifecycleException();
  93. log.debug(sm.getString("lifecycleBase.alreadyStarted",
  94. toString()), e);
  95. } else if (log.isInfoEnabled()) {
  96. log.info(sm.getString("lifecycleBase.alreadyStarted",
  97. toString()));
  98. }
  99. return;
  100. }
  101. if (state.equals(LifecycleState.NEW)) {
  102. init();
  103. } else if (!state.equals(LifecycleState.INITIALIZED) &&
  104. !state.equals(LifecycleState.STOPPED)) {
  105. invalidTransition(Lifecycle.BEFORE_START_EVENT);
  106. }
  107. setState(LifecycleState.STARTING_PREP);
  108. try {
  109. //2.实现初始化的方法,最终是调用了当前类的startInternal方法,发现startInternal抽象的
  110. startInternal();
  111. } catch (LifecycleException e) {
  112. setState(LifecycleState.FAILED);
  113. throw e;
  114. }
  115. if (state.equals(LifecycleState.FAILED) ||
  116. state.equals(LifecycleState.MUST_STOP)) {
  117. stop();
  118. } else {
  119. // Shouldn‘t be necessary but acts as a check that sub-classes are
  120. // doing what they are supposed to.
  121. if (!state.equals(LifecycleState.STARTING)) {
  122. invalidTransition(Lifecycle.AFTER_START_EVENT);
  123. }
  124. setState(LifecycleState.STARTED);
  125. }
  126. }
  127. /**
  128. * Sub-classes must ensure that:
  129. * <ul>
  130. * <li>the {@link Lifecycle#START_EVENT} is fired during the execution of
  131. *     this method</li>
  132. * <li>the state is changed to {@link LifecycleState#STARTING} when the
  133. *     {@link Lifecycle#START_EVENT} is fired
  134. * </ul>
  135. *
  136. * If a component fails to start it may either throw a
  137. * {@link LifecycleException} which will cause it‘s parent to fail to start
  138. * or it can place itself in the error state in which case {@link #stop()}
  139. * will be called on the failed component but the parent component will
  140. * continue to start normally.
  141. *
  142. * @throws LifecycleException
  143. */
  144. protected abstract void startInternal() throws LifecycleException;
  145. /**
  146. * {@inheritDoc}
  147. */
  148. @Override
  149. public synchronized final void stop() throws LifecycleException {
  150. if (LifecycleState.STOPPING_PREP.equals(state) ||
  151. LifecycleState.STOPPING.equals(state) ||
  152. LifecycleState.STOPPED.equals(state)) {
  153. if (log.isDebugEnabled()) {
  154. Exception e = new LifecycleException();
  155. log.debug(sm.getString("lifecycleBase.alreadyStopped",
  156. toString()), e);
  157. } else if (log.isInfoEnabled()) {
  158. log.info(sm.getString("lifecycleBase.alreadyStopped",
  159. toString()));
  160. }
  161. return;
  162. }
  163. if (state.equals(LifecycleState.NEW)) {
  164. state = LifecycleState.STOPPED;
  165. return;
  166. }
  167. if (!state.equals(LifecycleState.STARTED) &&
  168. !state.equals(LifecycleState.FAILED) &&
  169. !state.equals(LifecycleState.MUST_STOP)) {
  170. invalidTransition(Lifecycle.BEFORE_STOP_EVENT);
  171. }
  172. setState(LifecycleState.STOPPING_PREP);
  173. //3.实现初始化的方法,最终是调用了当前类的stopInternal方法,发现stopInternal抽象的
  174. stopInternal();
  175. if (state.equals(LifecycleState.MUST_DESTROY)) {
  176. // Complete stop process first
  177. setState(LifecycleState.STOPPED);
  178. destroy();
  179. } else {
  180. // Shouldn‘t be necessary but acts as a check that sub-classes are doing
  181. // what they are supposed to.
  182. if (!state.equals(LifecycleState.STOPPING)) {
  183. invalidTransition(Lifecycle.AFTER_STOP_EVENT);
  184. }
  185. setState(LifecycleState.STOPPED);
  186. }
  187. }
  188. /**
  189. * Sub-classes must ensure that:
  190. * <ul>
  191. * <li>the {@link Lifecycle#STOP_EVENT} is fired during the execution of
  192. *     this method</li>
  193. * <li>the state is changed to {@link LifecycleState#STOPPING} when the
  194. *     {@link Lifecycle#STOP_EVENT} is fired
  195. * </ul>
  196. *
  197. * @throws LifecycleException
  198. */
  199. protected abstract void stopInternal() throws LifecycleException;
  200. public synchronized final void destroy() throws LifecycleException {
  201. if (LifecycleState.DESTROYED.equals(state)) {
  202. if (log.isDebugEnabled()) {
  203. Exception e = new LifecycleException();
  204. log.debug(sm.getString("lifecycleBase.alreadyDestroyed",
  205. toString()), e);
  206. } else if (log.isInfoEnabled()) {
  207. log.info(sm.getString("lifecycleBase.alreadyDestroyed",
  208. toString()));
  209. }
  210. return;
  211. }
  212. if (!state.equals(LifecycleState.STOPPED) &&
  213. !state.equals(LifecycleState.FAILED) &&
  214. !state.equals(LifecycleState.NEW)) {
  215. invalidTransition(Lifecycle.DESTROY_EVENT);
  216. }
  217. //4.实现初始化的方法,最终是调用了当前类的destroyInternal方法,发现destroyInternal抽象的
  218. destroyInternal();
  219. setState(LifecycleState.DESTROYED);
  220. }
  221. protected abstract void destroyInternal() throws LifecycleException;
  222. /**
  223. * {@inheritDoc}
  224. */
  225. public LifecycleState getState() {
  226. return state;
  227. }
  228. /**
  229. * Provides a mechanism for sub-classes to update the component state.
  230. * Calling this method will automatically fire any associated
  231. * {@link Lifecycle} event.
  232. *
  233. * @param state The new state for this component
  234. */
  235. protected synchronized void setState(LifecycleState state) {
  236. setState(state, null);
  237. }
  238. /**
  239. * Provides a mechanism for sub-classes to update the component state.
  240. * Calling this method will automatically fire any associated
  241. * {@link Lifecycle} event.
  242. *
  243. * @param state The new state for this component
  244. * @param data  The data to pass to the associated {@link Lifecycle} event
  245. */
  246. protected synchronized void setState(LifecycleState state, Object data) {
  247. if (log.isDebugEnabled()) {
  248. log.debug(sm.getString("lifecycleBase.setState", this, state));
  249. }
  250. this.state = state;
  251. String lifecycleEvent = state.getLifecycleEvent();
  252. if (lifecycleEvent != null) {
  253. fireLifecycleEvent(lifecycleEvent, data);
  254. }
  255. }
  256. private void invalidTransition(String type) throws LifecycleException {
  257. String msg = sm.getString("lifecycleBase.invalidTransition", type,
  258. toString(), state);
  259. throw new LifecycleException(msg);
  260. }
  261. }

阅读以上LifecycleBase类中的源码,在第1,2,3,4处分别实现了init(),start(),stop(),destroy()等方法。分析其方法体中内容,实现代码最终还是调用了当前类中的initInternal(),startInternal(),stopInternal(),destroyInternal()等方法,而且这些方法还是抽象的。也就是说在执行这些方法时,最终是执行子类的具体实现。这貌似就是设计模式中的抽象模版方法模式哦!

 

四 总结

阅读到这,大家应该对tomcat的整体架构和各个容器如何交织在一起有了一定的了解。并在第三节我粗略的分析了下容器的初始化,启动,停止,销毁等生命周期的控制,从结构上看使用了抽象模版方法模式,所以掌握23种设计模式还是非常有用的在阅读源码或自己做设计方面 。了解了这些您是否会再思考,这些容器是什么时候或什么事件来触发装配在一起操作的哇!。下一个章节就来说说容器的初始化吧,它会给出前面的答案!

时间: 2024-08-04 22:45:16

tomcat原理解析(二):整体架构的相关文章

MyBatis框架中Mapper映射配置的使用及原理解析(二) 配置篇 SqlSessionFactoryBuilder,XMLConfigBuilder

在 <MyBatis框架中Mapper映射配置的使用及原理解析(一) 配置与使用> 的demo中看到了SessionFactory的创建过程: SqlSessionFactory sessionFactory = null; String resource = "mybatisConfig.xml"; try { sessionFactory = new SqlSessionFactoryBuilder().build(Resources .getResourceAsRea

Request 接收参数乱码原理解析二:浏览器端编码原理

上一篇<Request 接收参数乱码原理解析一:服务器端解码原理>,分析了服务器端解码的过程,那么浏览器是根据什么编码的呢? 1. 浏览器解码 浏览器根据服务器页面响应Header中的“Content-Type: text/html; charset=gb2312”解码.修改web.config中“responseEncoding=utf-8”,发现服务器页面响应Header变成了“Content-Type: text/html; charset=utf8”. <system.web&g

HDFS原理解析(总体架构,读写操作流程)

前言 HDFS 是一个能够面向大规模数据使用的,可进行扩展的文件存储与传递系统.是一种允许文件通过网络在多台主机上分享的文件系统,可让多机器上的多用户分享文件和 存储空间.让实际上是通过网络来访问文件的动作,由程序与用户看来,就像是访问本地的磁盘一般.即使系统中有某些节点脱机,整体来说系统仍然可以持续运作 而不会有数据损失. 一.HDFS体系结构 1.Namenode Namenode是整个文件系统的管理节点.它维护着整个文件系统的文件目录树,文件/目录的元信息和每个文件对应的数据块列表, 接收

HDFS原理解析(总体架构,读写操作流程等)

前言 HDFS 是一个能够面向大规模数据使用的,可进行扩展的文件存储与传递系统.是一种允许文件通过网络在多台主机上分享的文件系统,可让多机器上的多用户分享文件和存储空间.让实际上是通过网络来访问文件的动作,由程序与用户看来,就像是访问本地的磁盘一般.即使系统中有某些节点脱机,整体来说系统仍然可以持续运作而不会有数据损失. 一.HDFS体系结构 1.Namenode Namenode是整个文件系统的管理节点.它维护着整个文件系统的文件目录树,文件/目录的元信息和每个文件对应的数据块列表, 接收用户

tomcat原理解析

                                Tomcat总体结构 Tomcat 的心脏是两个组件:Connector 和 Container,一个 Container 可以选择对应多个 Connector.多个Connector 和一个 Container 就形成了一个 Service,有了 Service 就可以对外提供服务了,但是 Service 还要一个生存的环境,必须要有人能够给她生命.掌握其生死大权,那就非 Server 莫属了.所以整个 Tomcat 的生命周期由

Nmap 源码学习二 整体架构

目录功能: docs :相关文档 libdnet-stripped :开源网络接口库 liblinear:开源大型线性分类库 liblua:开源Lua脚本语言库 libnetutil:基本的网络函数 libpcap:开源抓包库 libpcre:开源正则表达式库 macosx:xcode项目文件 mswin32:vs项目文件 nbase:Nmap封装的基础使用函数库 ncat:netcat网络工具,由Nmap实现 ndiff:比较Nmap扫描结果的实用命令 nmap-update:负责Nmap更新

MyBatis框架中Mapper映射配置的使用及原理解析(三) 配置篇 Configuration

从上文<MyBatis框架中Mapper映射配置的使用及原理解析(二) 配置篇 SqlSessionFactoryBuilder,XMLConfigBuilder> 我们知道XMLConfigBuilder调用parse()方法解析Mybatis配置文件,生成Configuration对象. Configuration类主要是用来存储对Mybatis的配置文件及mapper文件解析后的数据,Configuration对象会贯穿整个Mybatis的执行流程,为Mybatis的执行过程提供必要的配

Flutter系列(三) 整体架构

您好,欢迎关注我的专栏,本篇是关于 Flutter 系列的第三篇,从简单的 Flutter 介绍开始,一步步带你了解进入 Flutter 的世界.你最好有一定的移动开发经验,如果没有也不要担心,在我的专栏底部给我留言,我会尽我的能力给你解答. 上篇文章我们介绍了用 Flutter 开发第一个跨平台应用程序,相信大家一定印象深刻,本篇文章介绍 Flutter 平台的整体架构. 一.核心原则 之前专栏有提到过,Flutter 的SDK中包括一个现代的响应式框架.一个2D渲染引擎.现成的widget和

Tomcat源码分析二:先看看Tomcat的整体架构

Tomcat源码分析二:先看看Tomcat的整体架构 Tomcat架构图 我们先来看一张比较经典的Tomcat架构图: 从这张图中,我们可以看出Tomcat中含有Server.Service.Connector.Container等组件,接下来我们一起去大致的看看这些组件的作用和他们之间的相互联系.在这之前,我们先补充一个知识点,也就是Tomcat它实现的功能点是什么呢?通过查找一些资料,这里参考下极客时间<深入拆解Tomcat_Jetty>中的总结,即Tomcat 要实现 2 个核心功能: