上一章, 实例化组件

接着我们上一章, 实例化组件之后开始, 不要迷路了

// Catalina.java
public void load() {//... 省略实例化部分代码// 把Catalina 设置到 ServergetServer().setCatalina(this);// -Dcatalina.home=catalina-homegetServer().setCatalinaHome(Bootstrap.getCatalinaHomeFile());// -Dcatalina.base=catalina-homegetServer().setCatalinaBase(Bootstrap.getCatalinaBaseFile());// 初始化console 打印器, 只是换了个颜色???, 我把这行代码注释掉也不影响运行 = _ =initStreams();// 初始化server <Server/>try {// 也初始化 我们今天就从这里getServer().init();} catch (LifecycleException e) {if (Boolean.getBoolean("org.apache.catalina.startup.EXIT_ON_INIT_FAILURE")) {throw new java.lang.Error(e);} else {log.error("Catalina.start", e);}}long t2 = System.nanoTime();if(log.isInfoEnabled()) {log.info("Initialization processed in " + ((t2 - t1) / 1000000) + " ms");}
}// getServer().init(); -> 调用了生命周期方法, init()
// org.apache.catalina.util.LifecycleBase
public final synchronized void init() throws LifecycleException {// 我们组件默认实例化以后都是 LifecycleState.NEWif (!state.equals(LifecycleState.NEW)) {invalidTransition(Lifecycle.BEFORE_INIT_EVENT);}try {// 更新组件生命周期的状态, 以及执行一谢监听器, 这个有点重要, 我们了解一下, 在组件启动的时候, 会初始化目录扫码一些其他的setStateInternal(LifecycleState.INITIALIZING, null, false);// 子类实现, 回到ServerinitInternal();// 更新生命周期setStateInternal(LifecycleState.INITIALIZED, null, false);} catch (Throwable t) {handleSubClassException(t, "lifecycleBase.initFail", toString());}
}// org.apache.catalina.util.LifecycleBase
private synchronized void setStateInternal(LifecycleState state, Object data, boolean check)throws LifecycleException {if (log.isDebugEnabled()) {log.debug(sm.getString("lifecycleBase.setState", this, state));}if (check) {//...// 更新状态this.state = state;String lifecycleEvent = state.getLifecycleEvent();if (lifecycleEvent != null) {// 激活监听器, 广播事件, Server 下面有6个监听器, 一个是实例化时默认的命名空间监听器, 还有5个Server.xml 配置的fireLifecycleEvent(lifecycleEvent, data);}}
}// 我们回到生命周期, 看初始化方法// org.apache.catalina.core.StandardServer
@Override
protected void initInternal() throws LifecycleException {super.initInternal();   // 基本生命周期初始化// 全局字符串缓存onameStringCache = register(new StringCache(), "type=StringCache");// 注册一个bean 工厂, 暂时没发现在哪里用了MBeanFactory factory = new MBeanFactory();factory.setContainer(this);onameMBeanFactory = register(factory, "type=MBeanFactory");// 初始化命令空间globalNamingResources.init();// 加载公共jar, 我们默认是没有的// class loadersif (getCatalina() != null) {ClassLoader cl = getCatalina().getParentClassLoader();// Walk the class loader hierarchy. Stop at the system class loader.// This will add the shared (if present) and common class loaderswhile (cl != null && cl != ClassLoader.getSystemClassLoader()) {if (cl instanceof URLClassLoader) {URL[] urls = ((URLClassLoader) cl).getURLs();for (URL url : urls) {if (url.getProtocol().equals("file")) {try {File f = new File (url.toURI());if (f.isFile() &&f.getName().endsWith(".jar")) {ExtensionValidator.addSystemResource(f);}} catch (URISyntaxException e) {// Ignore} catch (IOException e) {// Ignore}}}}cl = cl.getParent();}}// 下一步初始化 Servicefor (Service service : services) {service.init();}
}

server 另外 5个监听器在哪里

组件state进度

实例化 -> 初始化 -> 启动 -> 停止 -> 销毁 -> 错误

Service 初始化

service.init()
由于service 下面没有监听器, 所以我们就不看生命周期方法了, 为什么Server 有监听器我们也没有看呢, 因为博主暂时还没发现有价值的代码, 直接进入 service 的 initInternal()

Service.initInternal()

@Override
protected void initInternal() throws LifecycleException {// 调用父类initInternal, 注册组件, 设置名称super.initInternal();// 引擎初始化if (engine != null) {engine.init();}// 初始化连接, 我们默认没有配置, 所以这里找不到for (Executor executor : findExecutors()) {if (executor instanceof JmxEnabled) {((JmxEnabled) executor).setDomain(getDomain());}executor.init();}mapperListener.init();// 初始化连接器synchronized (connectorsLock) {for (Connector connector : connectors) {try {connector.init();} catch (Exception e) {String message = sm.getString("standardService.connector.initFailed", connector);log.error(message, e);if (Boolean.getBoolean("org.apache.catalina.startup.EXIT_ON_INIT_FAILURE"))throw new LifecycleException(message);}}}
}

初始化线程池, 我们默认都是没有配置的, 配置在 server.xml,默认是注释的

在 Service 初始化的时候, 我们可以看到并没有看到太多的代码, 这些代码都交给了, 两个重要的子组件去执行了,

  • Engine 引擎
  • Connector 连接器

Connector 负责创建socket监听, 封装http请求
Engine 则负责接手http请求, 传递给 Servlet
我们接着讲两个子组件

engine.init()

engine 是怎么来的, 请你仔细看上一章节的, server.xml 解析

在讲之前需要知道一个东西, Engine , Host, Content, 都是继承ContainerBase, 他们在初始化的时候只要调用了父类的 initInternal() 也就是 org.apache.catalina.core.ContainerBase.initInternal(), 都会创建一个线程池,
这个线程池是用来干嘛的, 是用来后台执行任务的, 主线程接着走, 不停顿
下面我们看 engine.initInternal()

@Override
protected void initInternal() throws LifecycleException {// Ensure that a Realm is present before any attempt is made to start// one. This will create the default NullRealm if necessary.getRealm();super.initInternal();}// super.initInternal();它的直接父类也就是 ContainerBase
@Override
protected void initInternal() throws LifecycleException {BlockingQueue<Runnable> startStopQueue = new LinkedBlockingQueue<>();startStopExecutor = new ThreadPoolExecutor(getStartStopThreadsInternal(),getStartStopThreadsInternal(), 10, TimeUnit.SECONDS,startStopQueue,new StartStopThreadFactory(getName() + "-startStop-"));startStopExecutor.allowCoreThreadTimeOut(true);super.initInternal();
}

engine 初始化的什么没什么大动作, 大动作都在启动的时候
现在我们回到service的 initInternal() 看另外一个重要的组件 Connector

connector.init();

为什么 connector 是一个数组, 设计如此, 可以对不同的应用目录, 使用布同的端口, 进来灵活配置, 我们复制一个 Connector , 只改端口, 也是可以运行的, 可以对外提供两个接口访问

同样的 connector 也没有监听器, 所以我们直接进入 initInternal()

@Override
protected void initInternal() throws LifecycleException {// 注册容器, 更新名称super.initInternal();// 创建适配器, http 请求, 最终是通过适配器, 传递给执行引擎的, 也就是容器adapter = new CoyoteAdapter(this);// 协议处理者protocolHandler.setAdapter(adapter);//....try {protocolHandler.init();} catch (Exception e) {throw new LifecycleException(sm.getString("coyoteConnector.protocolHandlerInitializationFailed"), e);}
}
  1. 问题 protocolHandler 它是个啥
    翻译: 协议处理者
    封装http请求的, 设置参数, 过期时间啊, session 啊, cookie啊
  2. 问题 protocolHandler 怎么来的
    那我们就得看它的构造器了, 不用说也是 server.xml 解析, 实例化的, 请看上一章
<Connector port="28080" protocol="HTTP/1.1"connectionTimeout="20000"redirectPort="8443" />// protocol = HTTP/1.1
public Connector(String protocol) {setProtocol(protocol);// Instantiate protocol handlerProtocolHandler p = null;try {// 实例化Class<?> clazz = Class.forName(protocolHandlerClassName);p = (ProtocolHandler) clazz.getConstructor().newInstance();} catch (Exception e) {log.error(sm.getString("coyoteConnector.protocolHandlerInstantiationFailed"), e);} finally {this.protocolHandler = p;}
}public void setProtocol(String protocol) {// AprLifecycleListener.getUseAprConnector() 默认是falseboolean aprConnector = AprLifecycleListener.isAprAvailable() &&AprLifecycleListener.getUseAprConnector();if ("HTTP/1.1".equals(protocol) || protocol == null) {if (aprConnector) {setProtocolHandlerClassName("org.apache.coyote.http11.Http11AprProtocol");} else {setProtocolHandlerClassName("org.apache.coyote.http11.Http11NioProtocol");}} else if ("AJP/1.3".equals(protocol)) {if (aprConnector) {setProtocolHandlerClassName("org.apache.coyote.ajp.AjpAprProtocol");} else {setProtocolHandlerClassName("org.apache.coyote.ajp.AjpNioProtocol");}} else {setProtocolHandlerClassName(protocol);}
}

所以看到这里 protocolHandler 是谁, 就是org.apache.coyote.http11.Http11NioProtocol 也就是 http1.1协议嘛, 这个连接器就是处理http协议的嘛,

protocolHandler.init();
回到我们的连接器初始化方法, 到现在我们还没有看到我们熟悉的8080端口, 别问我这里为什么是28080, 我自己改的= _ =, 别急协议处理者很快就会开始监听端口了

//   org.apache.coyote.http11.AbstractHttp11Protocol
//  为什么是 AbstractHttp11Protocol 而不是 Http11NioProtocol  它是 Http11NioProtocol  的父类
public void init() throws Exception {// 这个跳过for (UpgradeProtocol upgradeProtocol : upgradeProtocols) {configureUpgradeProtocol(upgradeProtocol);}super.init();
}// org.apache.coyote.AbstractProtocol
@Override
public void init() throws Exception {//... 省略代码String endpointName = getName();endpoint.setName(endpointName.substring(1, endpointName.length()-1));endpoint.setDomain(domain);endpoint.init();
}

endpoint 是什么? 实例化 org.apache.coyote.http11.Http11NioProtocol 的时候创建的, 这里就不带大家看了, 可以自己去看构造器, 这个地方的 endpoint 是 org.apache.tomcat.util.net.NioEndpoint

endpoint.init();

// 父类org.apache.tomcat.util.net.AbstractEndpoint
public void init() throws Exception {if (bindOnInit) {// 绑定端口bind();bindState = BindState.BOUND_ON_INIT;}// ... 代码省略
}// 回到子类 org.apache.tomcat.util.net.NioEndpoint
@Override
public void bind() throws Exception {if (!getUseInheritedChannel()) {serverSock = ServerSocketChannel.open();socketProperties.setProperties(serverSock.socket());InetSocketAddress addr = (getAddress()!=null?new InetSocketAddress(getAddress(),getPort()):new InetSocketAddress(getPort()));// 看这里看这里, 创建socket 并且绑定端口serverSock.socket().bind(addr,getAcceptCount());} else {// Retrieve the channel provided by the OSChannel ic = System.inheritedChannel();if (ic instanceof ServerSocketChannel) {serverSock = (ServerSocketChannel) ic;}if (serverSock == null) {throw new IllegalArgumentException(sm.getString("endpoint.init.bind.inherited"));}}// 配置阻塞serverSock.configureBlocking(true); //mimic APR behavior// Initialize thread count defaults for acceptor, pollerif (acceptorThreadCount == 0) {// FIXME: Doesn't seem to work that well with multiple accept threadsacceptorThreadCount = 1;}if (pollerThreadCount <= 0) {//minimum one poller threadpollerThreadCount = 1;}setStopLatch(new CountDownLatch(pollerThreadCount));// Initialize SSL if neededinitialiseSsl();selectorPool.open();
}

到这里, 我们的组件实例化也就基本完成了, 剩下的就是一步步出栈了, 端口已经绑定了 serverSock.socket().bind(addr,getAcceptCount());

下一章我将和大家分享 组件的启动

(5.2) Tomcat 8 源码, 初始化组件相关推荐

  1. (5.0) Tomcat 8 源码, 初始化 bootstrap

    下载源码 https://www.cnblogs.com/grasp/p/10061577.html 一般启动tomcat 都是使用 startup.bat set "EXECUTABLE= ...

  2. (5.1) Tomcat 8 源码, 实例化组件

    上一章:  初始化 bootstrap.init() https://blog.csdn.net/weixin_42209307/article/details/108580214 我们回到, org ...

  3. Vue源码学习 - 组件化(三) 合并配置

    Vue源码学习 - 组件化(三) 合并配置 合并配置 外部调用场景 组件场景 总结 学习内容和文章内容来自 黄轶老师 黄轶老师的慕课网视频教程地址:<Vue.js2.0 源码揭秘>. 黄轶 ...

  4. Vue源码学习 - 组件化一 createComponent

    Vue源码学习 - 组件化一 createComponent 组件化 createComponent 构造子类构造函数 安装组件钩子函数 实例化 VNode 总结 学习内容和文章内容来自 黄轶老师 黄 ...

  5. 小程序直播带货app源码直播组件接入指引

    小程序直播带货app源码直播组件接入指引 一.简介 小程序直播带货系统,是微信提供给小程序开发者的直播组件.通过调用该组件,商家可以在直播带货app源码中实现直播功能. 按下面的使用说明接入,在你的直 ...

  6. springboot-嵌入式Servlet容器(Tomcat)源码分析以及容器切换

    目录 一.springboot的嵌入式Servlet容器(tomcat) 1.原理 2.源码 (1)ServletWebServerApplicationContext的createWebServer ...

  7. Spring MVC 源码-初始化阶段

    我们首先找到DispatcherServlet 这个类,必然是寻找init()方法.然后,我们发现其init方法其实在父类HttpServletBean 中,其源码如下: @Override publ ...

  8. vue 初始化方法_前端发展方向指南—Vue源码初始化

    Vue 的源码结构比较绕,同时使用了大量的面向对象的高级技巧.重写方法,扩展方法,多态等应用.从 Vue 实例的加载过程就可以看出来,这一节重点看看 Vue 的源码加载流程是什么. 前言 vue已是目 ...

  9. vue v2.5.0源码-初始化流程

    vue的生命周期 代码 运行结果 源码分析 1 function Vue (options) { 2 this._init(options) 3 } 1 Vue.prototype._init = f ...

最新文章

  1. flutter创建可移动的stack小部件
  2. Python习题week1
  3. 计算机应用的时间地点意义,计算机应用在教学中的作用
  4. 程序员们记得还是八五年PC登陆我国时候的事?
  5. 【系统架构设计师】软考高级职称,一次通过,2017年下半年系统架构设计师考试论文真题(论软件架构风格)
  6. SQL数据库不用SQL语句能显示全表的内容_详解mysql数据库sql优化技巧总结
  7. pytest.5.参数化的Fixture
  8. linux是发展历史,linux发展历史.doc.doc
  9. 数据中心201812-4
  10. 微信小程序添加外部字体方法
  11. delphi实现延时的方法,很多人首先就想到用timer控件,这里我们不用timer控delphi直接用settimer函数实现延时的方法...
  12. 易宝支付回调不成功问题解决
  13. python网络编程原理_图解Python网络编程
  14. html id 命名,html类,id规范命名
  15. L - 芜湖塔台请求起飞
  16. 神经网络中矩阵求导术的应用
  17. docker安装kafka镜像
  18. MassGrid分布式计算网络
  19. 团体无线心理测评系统:心理健康评估、抑郁筛查、危机预警、问卷调查、物联网测评
  20. 数据收集-数据收集软件-数据收集工具免费

热门文章

  1. ubuntu 安装phpstorm
  2. subprocess installed post-installation script returned error exit status 1
  3. 调用另一个python文件中的代码
  4. sqldbx oracle mysql
  5. jni invalid jobject
  6. 计算机音乐作曲排名2019,2019金曲排行榜_2019《全球华人歌曲排行榜》年度五强名单公布...
  7. linux socket文件数限制,Linux下高并发socket最大连接数所受的限制问题
  8. xdebug模块输出文件名的配置说明
  9. wpf c 登录注册 mysql代码代码_Wpf+数据库代码封装+策略模式封装
  10. android.mk 比较字变量,Android.mk的用法和基础