tomcat(6)生命周期

【0】README
0.1)本文部分文字描述转自“深入剖析tomcat”,旨在学习 “tomcat生命周期” 的基础知识;
0.2)for source code, please visit  https://github.com/pacosonTang/HowTomcatWorks/tree/master/chapter6 
0.3)温馨建议:建议阅读本文之前,已阅读过 tomcat(1~5)的系列文章,因为它们是环环相扣的;

1)生命周期LifeCycle接口引入的背景:Catalina包含很多组件,当Catalina启动或关闭时,这些组件也会启动或关闭。而通过实现 org.apache.catalina.Lifecyle接口,可以达到统一启动或关闭这些组件的目的;
2)实现了Lifecycle接口的组件可以触发一个或多个事件:BEFORE_START_ENVET, START_EVENT, AFTER_START_EVENT, BEFORE_STOP_EVENT, STOP_EVENT, AFTER)STOP_EVENT(共6个事件);
3)当组件启动时,会触发前3个事件;而当组件关闭时,会触发后3个事件;
4)事件监听器:如果Catalina组件可以触发事件,那么需要编写相应的事件监听器对这些事件进行响应。事件监听器是 org.apache.catalina.LifecycleListener 接口的实例;
5)本文会介绍3个相关类:分别是 Lifecycle, LifecycleEvent, LifecycleListener;还外加一个工具类 LifecycleSupport,该类提供了一个简单的方法来触发某个组件的生命周期事件,并对事件监听器进行处理;

【1】Lifecycle接口(生命周期接口)
1)intro to Lifecycle:Catalina在设计上允许一个组件包含其他组件,以使得所有的组件都置于其父组件的监护之下;这样一来,Catalina的启动类只需要启动一个组件就可以将全部应用的组件都启动起来。这种单一启动/关闭机制是通过 Lifecycle 接口来实现的;(干货——Lifecycle接口的引入目的是:Catalina的启动类只需要启动一个组件就可以将全部应用的组件都启动起来)(干货——单一启动/关闭机制
public interface Lifecycle {public static final String START_EVENT = "start";public static final String BEFORE_START_EVENT = "before_start";public static final String AFTER_START_EVENT = "after_start";public static final String STOP_EVENT = "stop";public static final String BEFORE_STOP_EVENT = "before_stop"; public static final String AFTER_STOP_EVENT = "after_stop"; public void addLifecycleListener(LifecycleListener listener); public LifecycleListener[] findLifecycleListeners(); public void removeLifecycleListener(LifecycleListener listener); public void start() throws LifecycleException; public void stop() throws LifecycleException; 
}
【2】LifecycleEvent类(生命周期事件类)
public final class LifecycleEvent  extends EventObject { public LifecycleEvent(Lifecycle lifecycle, String type) {this(lifecycle, type, null);}public LifecycleEvent(Lifecycle lifecycle, String type, Object data) {super(lifecycle);this.lifecycle = lifecycle;this.type = type;this.data = data;}private Object data = null;private Lifecycle lifecycle = null;private String type = null;public Object getData() {return (this.data);}public Lifecycle getLifecycle() {return (this.lifecycle);}public String getType() {return (this.type);}
}
【3】LifecycleListener接口(生命周期事件监听器接口)
1)该接口只有一个方法:即 lifecycleEvent 方法,当某个事件监听器监听到相关事件发生时,会调用该方法;
public interface LifecycleListener {public void lifecycleEvent(LifecycleEvent event);}
【4】LifecycleSupport类
1)LifecycleSupport类:实现了 Lifecycle接口, 并且对某个事件注册了监听器的组件必须提供 Lifecycle接口中3个与监听器相关的方法(分别是 addLifecycleListener(), findLifecycleListeners(), removeLifecycleListener())的实现。
2)然后,该组件需要将所有注册的事件监听器存储到一个数组,ArrayList 或其他类似的对象中。
3)Catalina提供了一个工具类——org.apache.catalina.util.LifecycleSupport: 来帮助组件管理监听器,并触发相应的生命周期事件;
4)LifecycleSupport类的定义如下:
public final class LifecycleSupport {public LifecycleSupport(Lifecycle lifecycle) {super();this.lifecycle = lifecycle;}private Lifecycle lifecycle = null;private LifecycleListener listeners[] = new LifecycleListener[0];public void addLifecycleListener(LifecycleListener listener) { //添加生命周期事件监听器synchronized (listeners) {LifecycleListener results[] =new LifecycleListener[listeners.length + 1];for (int i = 0; i < listeners.length; i++)results[i] = listeners[i];results[listeners.length] = listener;listeners = results;}}public LifecycleListener[] findLifecycleListeners() {return listeners;}public void fireLifecycleEvent(String type, Object data) { // 触发生命周期事件监听器LifecycleEvent event = new LifecycleEvent(lifecycle, type, data);LifecycleListener interested[] = null;synchronized (listeners) {interested = (LifecycleListener[]) listeners.clone();}for (int i = 0; i < interested.length; i++)interested[i].lifecycleEvent(event);}public void removeLifecycleListener(LifecycleListener listener) { // 移除生命周期事件监听器synchronized (listeners) {int n = -1;for (int i = 0; i < listeners.length; i++) {if (listeners[i] == listener) {n = i;break;}}if (n < 0)return;LifecycleListener results[] =new LifecycleListener[listeners.length - 1];int j = 0;for (int i = 0; i < listeners.length; i++) {if (i != n)results[j++] = listeners[i];}listeners = results;}}
}
5)添加和删除事件监听器的方法(干货——添加和删除事件监听器的方法,其处理技巧非常重要,代码如上)
5.1)添加事件监听器:当调用addLifecycleListener()方法添加一个事件监听器时,会创建一个新数组,大小为原数组的元素个数加1;然后将原数组中的所有元素copy到 新数组中,并将新的事件监听器添加到新数组中;
5.2)删除事件监听器:当调用 removeLifecycleListener() 方法删除一个事件监听器时,也会新建一个数组,大小为原数组的元素个数减1;然后将除了指定监听器外的其他所有监听器都copy到 新数组中;
6)触发生命周期事件(fireLifecycleEvent()方法会触发一个生命周期事件。首先,它会copy 事件监听器数组,然后调用数组中每个成员的lifecycleEvent() 方法,并传入要触发的事件;
public void fireLifecycleEvent(String type, Object data) {LifecycleEvent event = new LifecycleEvent(lifecycle, type, data); LifecycleListener interested[] = null;synchronized (listeners) {interested = (LifecycleListener[]) listeners.clone(); // step1}for (int i = 0; i < interested.length; i++)interested[i].lifecycleEvent(event); // step2}
7)当要添加一个事件监听器时,SimpleContext实例 会调用LifecycleSupport类的 addLifecycleListener() 方法:
 // implementation of the Lifecycle interface's methodspublic void addLifecycleListener(LifecycleListener listener) {lifecycle.addLifecycleListener(listener);}
【5】应用程序
【5.1】 SimpleContext类
1)SimpleContext类使用变量lifecycle 引用了一个 LifecycleSupport 实例:
2)SimpleContext的start方法和stop方法
public synchronized void start() throws LifecycleException { // SimpleContext.start()if (started)throw new LifecycleException("SimpleContext has already started");// Notify our interested LifecycleListenerslifecycle.fireLifecycleEvent(BEFORE_START_EVENT, null);started = true;try {// 启动它的组件和子容器,当前程序中,共有两个组件实现了Lifecycle接口,分别是// SimpleLoader类和 SimplePipeline类.// Start our subordinate components, if anyif ((loader != null) && (loader instanceof Lifecycle))((Lifecycle) loader).start(); // highlight line.// Start our child containers, if anyContainer children[] = findChildren();for (int i = 0; i < children.length; i++) {if (children[i] instanceof Lifecycle)((Lifecycle) children[i]).start(); // highlight line.}// Start the Valves in our pipeline (including the basic),// if anyif (pipeline instanceof Lifecycle)((Lifecycle) pipeline).start(); // highlight line.// Notify our interested LifecycleListeners// 组件和子容器都启动完毕后,会触发两个事件:START_EVENT AND AFTER_START_EVENT.lifecycle.fireLifecycleEvent(START_EVENT, null);}catch (Exception e) {e.printStackTrace();}// Notify our interested LifecycleListenerslifecycle.fireLifecycleEvent(AFTER_START_EVENT, null);}// stop方法 类似于上述的 start方法.public void stop() throws LifecycleException { // SimpleContext.stop() 方法if (!started)throw new LifecycleException("SimpleContext has not been started");// Notify our interested LifecycleListenerslifecycle.fireLifecycleEvent(BEFORE_STOP_EVENT, null);lifecycle.fireLifecycleEvent(STOP_EVENT, null);started = false;try {// Stop the Valves in our pipeline (including the basic), if anyif (pipeline instanceof Lifecycle) {((Lifecycle) pipeline).stop();}// Stop our child containers, if anyContainer children[] = findChildren();for (int i = 0; i < children.length; i++) {if (children[i] instanceof Lifecycle)((Lifecycle) children[i]).stop();}if ((loader != null) && (loader instanceof Lifecycle)) {((Lifecycle) loader).stop();}}catch (Exception e) {e.printStackTrace();}// Notify our interested LifecycleListenerslifecycle.fireLifecycleEvent(AFTER_STOP_EVENT, null);}
3)problem+solution:
3.1)problem:
problem1)start() 方法是如何将所有子容器,以及与之相关的组件,包括载入器,管道和映射器等,启动起来的?
problem2)又是如何关闭这些容器和组件的?
3.2)solution:就是使用前面提到的单一启动/关闭机制;使用这种机制,只需要启动最高层级的组件即可,其余组件会由各自的父组件去启动。同样,关闭这些组件时,也只需要关闭最高层级的组件即可;(干货——单一启动/关闭机制
4)start() 方法的调用过程
step1)首先检查组件是否已经启动
step2)触发BEFORE_START_EVENT事件
step3)将started 设置为true,表明该组件已经启动了
step4)启动start方法所在类的组件和子容器。当前应用程序中,共有两个组件实现了Lifecycle接口,分别是 SimpleLoader and SimplePipeline类。
step5)触发两个事件:START_EVENT and AFTER_START_EVENT;
step6)触发 BEFORE_STOP_EVENT事件 和 STOP_EVENT事件,重置started
5)stop() 方法调用过程(与start方法类似)
step1)关闭与它关联的所有组件和 SimpleContext 实例的子容器;
step2)触发 AFTER_STOP_EVENT 事件

【5.2】 SimpleContextLifecycleListener类(事件监听器的实现类)
1)其中的 lifecycleEvent() 方法:它仅仅输出已触发事件类型;

【5.3】SimpleLoader类(仅仅是返回类加载器,与以往的Loader不同的是,它实现了 Lifecycle接口)
1)该类的Lifecycle接口中各个方法的实现只是向console输出字符串。重要的是, 通过实现Lifecycle接口, 启动SimpleLoader实例的任务就可以由与其相关联的servlet容器来完成了;

【5.4】SimplePipeline类(管道,管道包括多个阀,每个阀就是一个任务,遍历管道中的阀,就是挨个执行任务,基础阀最后执行)

【5.5】SimpleWrapper类(利用SimpleLoader返回的类加载器,加载并返回相应的servlet)
1)该类实现了 Lifecycle接口,就可以由其父容器来启动该实例。
2)参见其start方法(与SimpleContext类的start方法类似):
public synchronized void start() throws LifecycleException {  // SimpleWrapper.start()System.out.println("Starting Wrapper " + name);if (started)throw new LifecycleException("Wrapper already started");// Notify our interested LifecycleListenerslifecycle.fireLifecycleEvent(BEFORE_START_EVENT, null);started = true;// Start our subordinate components, if anyif ((loader != null) && (loader instanceof Lifecycle))((Lifecycle) loader).start();// Start the Valves in our pipeline (including the basic), if anyif (pipeline instanceof Lifecycle)((Lifecycle) pipeline).start();// Notify our interested LifecycleListenerslifecycle.fireLifecycleEvent(START_EVENT, null);// Notify our interested LifecycleListenerslifecycle.fireLifecycleEvent(AFTER_START_EVENT, null);}  
3)参见其stop方法(比较有趣)
3.1)stop方法的调用steps as follows:
step1)除了输出一个简单的字符串外,它还要调用servlet实例的destroy方法
step2)检查Wrapper实例是否启动
step3)触发BEFORE_STOP_EVENT 和 STOP_EVENT事件,并重置started
step4)关闭与其相关联的载入器和管道组件
step5)最后, 触发 AFTER_STOP_EVENT事件
  public void stop() throws LifecycleException { // SimpleWrapper.stop() 方法System.out.println("Stopping wrapper " + name);// Shut down our servlet instance (if it has been initialized)try {instance.destroy();}catch (Throwable t) {}instance = null;if (!started)throw new LifecycleException("Wrapper " + name + " not started");// Notify our interested LifecycleListenerslifecycle.fireLifecycleEvent(BEFORE_STOP_EVENT, null);// Notify our interested LifecycleListenerslifecycle.fireLifecycleEvent(STOP_EVENT, null);started = false;// Stop the Valves in our pipeline (including the basic), if anyif (pipeline instanceof Lifecycle) {((Lifecycle) pipeline).stop();}// Stop our subordinate components, if anyif ((loader != null) && (loader instanceof Lifecycle)) {((Lifecycle) loader).stop();}// Notify our interested LifecycleListenerslifecycle.fireLifecycleEvent(AFTER_STOP_EVENT, null);}


【6】运行应用程序
0)app startup
public final class Bootstrap {public static void main(String[] args) {// 连接器,创建服务器套接字,维护HttpProcessor 对象池(stack)Connector connector = new HttpConnector(); // servlet最低级容器 Wrapper,用于封装servlet,并提供类加载器,加载相应的servletWrapper wrapper1 = new SimpleWrapper();wrapper1.setName("Primitive");wrapper1.setServletClass("servlet.PrimitiveServlet");Wrapper wrapper2 = new SimpleWrapper();wrapper2.setName("Modern");wrapper2.setServletClass("servlet.ModernServlet");// 将Wrapper容器添加到其父容器ContextContext context = new SimpleContext(); context.addChild(wrapper1);context.addChild(wrapper2);// 映射器Mapper,其作用是通过 请求路径,如 http://localhost:8080/Modern;// 通过HttpProcessor.process() 方法解析reqeust,获取 访问绝对路径 /Modern// 通过在映射器(的map方法)查找该路径对应的容器资源名称(Modern)// 然后还是在其map方法中继续通过容器名称(Modern)映射到servlet名称(servlet.ModernServlet)// 这样才通过 /Modern 映射到对应的servlet访问路径(加载路径,以便类加载器加载)Mapper mapper = new SimpleContextMapper();mapper.setProtocol("http");// 添加生命周期监听器LifecycleListener listener = new SimpleContextLifecycleListener();((Lifecycle) context).addLifecycleListener(listener);context.addMapper(mapper);// 添加类加载器Loader loader = new SimpleLoader();context.setLoader(loader);// 添加servlet访问路径(资源路径) 和 资源名称的映射关系// context.addServletMapping(pattern, name);context.addServletMapping("/Primitive", "Primitive");context.addServletMapping("/Modern", "Modern");connector.setContainer(context);try {connector.initialize(); // 初始化,主要返回服务器套接字// 触发生命周期事件(可以参考下面的测试用例调用过程示例图)((Lifecycle) connector).start(); // highlight line.((Lifecycle) context).start(); // highlight line.// make the application wait until we press a key.System.in.read();((Lifecycle) context).stop(); // highlight line.}catch (Exception e) {e.printStackTrace();}/* try { // these are some code in tomcat(5)-servlet container.connector.initialize();connector.start();// make the application wait until we press a key.System.in.read();}*/}
}
Attention)习惯上,我还是总结了测试用例(生命周期,事件+监听器)的调用过程示例图


对以上调用过程的分析(SimpleContext.start() 方法为起点的 Analysis)
A1)容器:本应用程序(Bootstrap.java)有两种容器Wrapper 和 Context,实现类分别是 SimpleWrapper 和 SimpleContext;每种容器分别有 管道类(SimplePipeline),而管道类通过阀数组类封装非基础阀,和一个阀的实例来封装基础阀(基础阀是可以手动设置的);即两种容器Wrapper 和 Context 的管道是不同的,非基础阀是不同的,当然基础阀也是不同的;(干货——理解到容器的概念非常重要,之后就是管道中的阀,非基础阀和基础阀,还有管道中阀的遍历,最后遍历基础阀,这些都是晒干了很久的干货);
A2)SimpleContext.start()方法(第一张图):通过生命周期实例(lifycycle,这在main方法中已经设定了)调用fireLifecycleEvent()方法去触发一个生命周期事件,这里触发的是START_EVENT事件:
step1)fireLifecycleEvent方法:创建生命周期事件,得到监听器数组的拷贝(多个监听器封装在数组里面),依据监听器数组里面监听器list,挨个排的调用单个监听器的lifecycleEvent()
step2)单个监听器实例: 是由SimpleContextLifecycleListener实例提供的,SimpleContextLifecycleListener.lifecycleEvent() 方法就打印一些对应于 事件的info;
step3)调用fireLifecycleEvent方法后,继续调用findChildren()方法:首先要知道children 是一个封装Wrapper类型的HashMap集合,在main方法中就已经填充了children(键和值(键值对)分别是 Wrapper实例的名称 和 Wrapper实例,而Wrapper负责加载servlet,并返回servlet);第二,findChildren会返回children对应的Wrapper数组;第三,在for循环中,遍历该数组,并调用单个Wrapper实例的start方法,转向SimpleWrapper.start()方法;
step4)SimpleWrapper.start()方法: 该方法和SimpleContext.start()方法有点类似,这里就省略了,因为SimpleWrapper  和 SimpleContext都是容器,都实现了 Lifecycle 接口,所以它们的start方法类似,调用过程也是类似的,参见A2中对 SimpleContext.start()方法的描述; (下图所示)


1)运行参数
E:\bench-cluster\cloud-data-preprocess\HowTomcatWorks\src>java -cp .;lib/servlet.jar;lib/catalina_4_1_24.jar;E:\bench-cluster\cloud-data-preprocess\HowTomcatWorks\webroot com.tomcat.chapter6.startup.B ootstrap 
HttpConnector Opening server socket on all host IP addresses
HttpConnector[8080] Starting background thread
SimpleContextLifecycleListener's event before_start
Starting SimpleLoader
Starting Wrapper Primitive
Starting Wrapper Modern
SimpleContextLifecycleListener's event start
Starting context.
SimpleContextLifecycleListener's event after_start
ModernServlet -- init
init
from service
SimpleContextLifecycleListener's event before_stop
SimpleContextLifecycleListener's event stop
Stopping context.
Stopping wrapper Primitive
destroy
2)运行结果



本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/331234.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

移位操作提高代码的可读性_本地记录或类,以提高流操作的可读性

移位操作提高代码的可读性Java 14带有Records的预览语言功能—一种特殊的轻量级类&#xff0c;可以与其他语言中的类似构造进行比较&#xff0c;例如C&#xff03;中的record类&#xff0c;Kotlin中的data类和Scala中的case类。 A&#xff09;已经有许多博客文章解释Java 14记…

漫画算法:无序数组排序后的最大相邻差值

转载自 玻璃猫 程序员小灰 小灰一边回忆一边讲述起当时面试的情景...... 题目&#xff1a;有一个无序整型数组&#xff0c;如何求出这个数组排序后的任意两个相邻元素的最大差值&#xff1f;要求时间和空间复杂度尽可能低。&#xff08;例如&#xff1a;无序数组 2,3,1,4,6&…

serv-u 数据备份_如何使用用户数据脚本在EC2实例上安装Apache Web Server

serv-u 数据备份你好朋友&#xff0c; 在本教程中&#xff0c;我们将看到如何使用用户数据脚本在EC2实例上安装Apache Web Server。 在我以前的教程之一中&#xff0c;我已经解释了如何使用AWS控制台启动EC2实例。如果您还没有完成该操作&#xff0c;我建议您先进行一下操作。…

哈儿小波分解和重构(降维和升维)实现算法

【0】README0.1&#xff09;本文旨在讲解 哈儿小波变换&#xff08;分解和重构&#xff09;进行数据的降维和升维&#xff1b;【timestamp: 1703281610】时隔几个月再来review 哈儿小波变换算法的具体思路&#xff1a;1&#xff09;分解降维&#xff1a;首先对所有item进行分解…

漫画算法:判断2的乘方

转载自 玻璃猫 程序员小灰 小灰陷入回忆当中。。。。 题目&#xff1a;实现一个方法&#xff0c;判断一个正整数是否是2的乘方&#xff08;比如16是2的4次方&#xff0c;返回True&#xff1b;18不是2的乘方&#xff0c;返回False&#xff09;。要求性能尽可能高。 解法一&…

k66 pit计时功能配置_PIT,JUnit 5和Gradle –仅需额外的一行配置

k66 pit计时功能配置在Gradle&#xff08;带有gradle-pitest-plugin 1.4.7&#xff09;中发现简单&#xff0c;改进的PIT和JUnit 5配置。 不可否认&#xff0c;如今JUnit 5越来越受欢迎。 虽然为JUnit 5提供了一个专用于PIT的插件&#xff0c;并且gradle-pitest-plugin支持了很…

tomcat(7)日志记录器

【0】README0.1&#xff09;本文部分文字描述转自&#xff1a;“深入剖析tomcat”&#xff0c;旨在学习 “tomcat的日志记录器” 的基础知识&#xff1b;0.2&#xff09;intro to 日志记录器&#xff1a;日志记录器是用来记录消息的组件&#xff1b;0.3&#xff09;for complet…

漫画算法:最小栈的实现

转载自 玻璃猫 程序员小灰 小灰回忆起当时的情景…… 题目&#xff1a;实现一个栈&#xff0c;带有出栈&#xff08;pop&#xff09;&#xff0c;入栈&#xff08;push&#xff09;&#xff0c;取最小元素&#xff08;getMin&#xff09;三个方法。要保证这三个方法的时间复杂度…

oidc auth2.0_将Auth0 OIDC(OAUTH 2)与授权(组和角色)集成

oidc auth2.0如果您正在使用Auth0对多个现有应用程序中的用户进行身份验证和授权&#xff0c;则可能需要将下一个Web应用程序与Auth0集成。 有多种方法可以执行此操作&#xff0c;例如&#xff0c;如果要将Jenkins与Auth0集成&#xff0c;则可以使用SAML v2&#xff1b;否则&a…

follow 开源项目关于NoClassDefFoundError错误的解决方法

【0】README0.1&#xff09;本文旨在po出当我们follow open projects 的时候&#xff0c;遇到一些在open API中不存在&#xff08;NoClassDefFoundError&#xff09;的类时的解决方法&#xff1b;【1】problemsolution&#xff1a;1.1&#xff09;problem&#xff1a;一般case下…

camel apache_如何使用Apache Camel,Quarkus和GraalVM快速运行100个骆驼

camel apache今天&#xff0c;我继续在youtube上练习&#xff0c;并录制了10分钟的视频&#xff0c;介绍了如何创建一个新的Camel and Quarkus项目&#xff0c;该项目包括Rest和HTTP服务以及开箱即用的健康检查和指标。 然后比较在JVM模式下运行示例与使用GraalVM编译的本机的…

漫画算法:找出缺失的整数

转载自 玻璃猫 程序员小灰 小灰一边回忆一边讲述起当时面试的情景...... 题目&#xff1a;一个无序数组里有99个不重复正整数&#xff0c;范围从1到100&#xff0c;唯独缺少一个整数。如何找出这个缺失的整数&#xff1f; 解法一&#xff1a; 创建一个HashMap&#xff0c;以1到…

tomcat(8)载入器

【0】README0.0&#xff09;本文部分描述转自“深入剖析tomcat”&#xff0c;旨在学习 tomcat(8)载入器 的基础知识&#xff1b;0.1&#xff09;一个标准web 应用程序中的载入器&#xff1a;简单来说就是 tomcat中的载入器&#xff1b;0.2&#xff09;servlet容器需要实现一个自…

micrometer_具有InlfuxDB的Spring Boot和Micrometer第3部分:Servlet和JDBC

micrometer在上一个博客中&#xff0c;我们使用由InfluxDB支持的Micrometer设置了React式应用程序。 在本教程中&#xff0c;我们将使用传统的带有JDBC的基于Servlet的阻塞Spring堆栈。 我选择的数据库是postgresql。 我将使用与先前博客文章相同的脚本。 因此&#xff0c;我…

漫画算法:辗转相除法是什么鬼

转载自 玻璃猫 程序员小灰 大四毕业前夕&#xff0c;计算机学院的小灰又一次顶着炎炎烈日&#xff0c; 去某IT公司面试研发工程师岗位…… 半小时后&#xff0c;公司会议室&#xff0c;面试开始…… 小灰奋笔疾书&#xff0c;五分钟后…… 小灰的思路十分简单。他使用暴力…

tomcat(9)Session管理

【0】README0.0&#xff09;本文部分描述转自“深入剖析tomcat”&#xff0c;旨在学习“tomcat-Session管理” 的基础知识&#xff1b;0.1&#xff09;Catalina通过一个称为Session 管理器的组件来管理建立的Session对象&#xff0c;该组件由org.apache.catalina.Manager接口来…

micrometer_具有InlfuxDB的Spring Boot和Micrometer第2部分:添加InfluxDB

micrometer自从我们添加了基本应用程序以来&#xff0c;是时候启动InfluxDB实例了。 我们将按照之前的教程进行操作&#xff0c;并添加一个docker实例。 docker run –rm -p 8086&#xff1a;8086 –name influxdb-本地influxdb 是时候在我们的pom上添加微米InfluxDB依赖项了…

漫画:什么是volatile关键字?(整合版)

转载自 永远爱大家的 程序员小灰 ————— 第二天 ————— ———————————— Java内存模型简称JMM&#xff08;Java Memory Model&#xff09;&#xff0c;是Java虚拟机所定义的一种抽象规范&#xff0c;用来屏蔽不同硬件和操作系统的内存访问差异&#xff0c;让j…

tomcat(supplement)HttpConnector.initialize() 和 start() 方法 以及 StandardContext.start()方法的分析

【0】README 0.0&#xff09;本文中源代码的背景&#xff0c;参见 tomcat(9)session管理 0.1&#xff09;本文主要以图片的形式分析他们大致的调用过程&#xff1b; 0.2&#xff09;HttpConnector org.apache.catalina.connector.http.HttpConnector; 而StandardContext o…

restful rest_HATEOAS的RESTful服务。 超媒体:REST的秘密要素

restful rest在这篇文章中&#xff0c;我们将介绍有关HATEOAS的RESTful服务的综合文章。 超媒体是REST的秘密成分。 1.简介 在本教程的前一部分中&#xff0c;我们花了一些时间来刷新有关REST体系结构样式的基本原理的知识。 业界对REST状态的批判性眼光揭示了一个令人失望的…