开场话
-
2023又到了第一场激动人心的节假日了。中秋+国庆===脱了缰的野马。这个中秋国庆你打算怎么过呢?去哪玩?
-
在此佳节我想就中秋谈谈国民的节日文化内涵完虐国外节日。中秋节不仅是阖家团圆的日子,更是举国欢庆的时刻,月圆时刻,寄托了人们对故乡、亲人的思念,同时也表达了人们对美好生活的热爱和向往。 并且通过中秋节这个传统节日,也增进了国家、人民之间的文化、民族认同,具有深远意义。
-
圆就是中秋的核心思想,圆也可以理解成完整的意思,我们根深蒂固的思想落叶归根同样也是应承着这个主题,今天我想从技术的角度谈谈技术层面上哪些具有完整圆的设计思想。
-
Spring 是我们Java 程序员的钟爱对象,因为他介于对象完整和不完整之间游走,完美的实现了对象的生命周期。通过三级缓存解决了对象相互依赖的问题,也解决了对象拦截的问题。而Spring中存在这么个引领全局的角色---上下文。
-
想想学生时代语文试卷中的阅读理解往往需要我们结合上下文进行分析某一两句话的深远意义,在Spring 中同样一个代码片段你可能不知道有什么作用,但是结合spring 的上下文你就能够将它运行起来并且发挥着作用。而在Spring 中我发现了一个很诡异的问题。
问题描述
- 在 springboot 中我通过
ContextLoader.getCurrentWebApplicationContext ()
获取 WebApplicationContext 但是居然是 Null. 然后搜索了一番也找到解决办法就是通过实现ApplicationContextAware
接口,方便 spirng 将上下文填充进来。
java
@Component
@Lazy(false)
public class ApplicationContextRegister implements ApplicationContextAware {
private static final Logger LOGGER = LoggerFactory.getLogger(ApplicationContextRegister.class);
private static ApplicationContext APPLICATION_CONTEXT;
/**
* 设置spring上下文 * * @param applicationContext spring上下文 * @throws BeansException
*/
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
LOGGER.debug("ApplicationContext registed-->{}", applicationContext);
APPLICATION_CONTEXT = applicationContext;
}
public static ApplicationContext getApplicationContext() {
return APPLICATION_CONTEXT;
}
}
定位跟踪
- 虽然问题得倒解决但是我还是非常想知道为什么会是 null 。于是乎我就开始的 debug 断点跟踪之路。
- 我们在 Servlet 中想获取到 WebApplicationContext 从而能够讲 Servlet 交给 Spring 扫描。但是我们最终发现这里获取的 WebApplicationContext 为 null。主要是因为 Springboot 的启动方式并不会触发 WebApplicationContext 的构建。那么接下来我们看看他的构建周期
- 我们能够看到内部他实际上通过 ThreadLocal 进行变量传递的。那么我们只需要追踪该 ThreadLocal 是在哪里 put 即可。
- 通过定位我们发现在 ContextLoader.initWebApplicationContext 中存在设置该 ThreadLocal 的场景。
- 继续向上追踪能够发现始作俑者是
ContextLoaderListener.contextInitialized
的方法中。
- 我们讲断点打到这个地方然后启动程序发现这里的 listener 并不是上面提到的
ContextLoaderListener
这也就说明上面为什么我们获取的是 null 。那么问题就回到了为什么 listener 不是ContextLoaderListener
, listener 是通过 instances 中获取的,那么我们来看看这个 instances 是怎么获取的。
- 继续跟踪发现这个就是内部维护的一个变量。
- 我们在通过 idea 提供的断点功能打在这个字段上,然后在重启服务看看这个字段是什么时候赋值的。
- 这个断点作用是
TomcatEmbeddedContext
创建的时候生成的。
- 我们能够发现 applicationLifecycleListenersObjects 在三个地方会被赋值,分别是
setApplicationLifecycleListenersObjects
、addApplicationLifecycleListener
、resetContext
.我们分别在这三个地方打上断点。
- 不出意外果真是
setApplicationLifecycleListeners
这个地方设置了。我们能够看到是WsContextListener
这个类。
- 接力棒来到了
lifecycleListeners
了。
lifecycleListeners
又将接力棒传给了 results ,这个变量我喜欢因为他简单纯粹。
- 点进
findApplicationListeners
能够发现也是个变量。
- 最终我们能够发现在
StandContext
中添加了监听器
- 继续追踪我们已经看到了 xml 了
- 但是通过断点跟踪发现实际上是
TomcatWebSocketServletWebServerCustomizer
追踪BUG意义
- 上面我通过idea 断点跟踪技术,一点一点的追踪着Spring的深远内涵。最终发现是因为Spring内置的tomcat并没有完全实现Tomcat 的功能,导致在WebApplicationContext赋值的地方压根就没有触发。在这点上Spring 的Tomcat 实现有点月有阴晴圆缺的感觉了。本文就当是中秋前的提前赏月了,希望届时我们能够观赏到有史以来最大最圆的月亮了。
回扣中秋主题
- 圆即是完整的意思,无论是生活还是Spring,处处都能体会到完整的意思,于生活我们要对家人负责,于工作我们要对任务负责。有始有终才是我们尽责尽力的表现。