Java SPI、servlet3.0与@HandlesTypes源码分析
关于Java SPI与servlet3.0的应用,这里说的很精炼,链接地址如下。
https://blog.csdn.net/pingnanlee/article/details/80940993
以Tomcat8.5.31对Servlet的实现为例,简单提一点,Tomcat获取ServletContainerInitializer的实现类是在org.apache.catalina.startup.ContextConfig.webConfig() 中,Step 3调用processServletContainerInitializers(),
使用了自己的WebappServiceLoader,解释为A variation of Java's JAR ServiceLoader。
顺带一提tomcat启动时webConfig() 的调用链:
Tomcat.start()->各种代理的start()->org.apache.catalina.core.StandardContext.startInternal->LifecycleBase.fireLifecycleEvent->org.apache.catalina.startup.ContextConfig.lifecycleEvent->configureStart->webConfig
另:感叹一下tomcat的源码中,每一个方法都好长,和spring源码的深层次相比简直各有千秋,以及那个叫做ok的boolean 变量,可能相比用异常来表示更为直观吧。
@HandlesTypes的实现原理:
首先这个注解最开始令我非常困惑,他的作用是将注解指定的Class对象作为参数传递到onStartup(ServletContainerInitializer)方法中。
然而这个注解是要留给用户扩展的,他指定的Class对象并没有要继承ServletContainerInitializer,更没有写入META-INF/services/的文件(也不可能写入)中,那么Tomcat是怎么扫描到指定的类的呢。
答案是Byte Code Engineering Library (BCEL),这是Apache Software Foundation 的Jakarta 项目的一部分,作用同ASM类似,是字节码操纵框架。
webConfig() 在调用processServletContainerInitializers()时记录下注解的类名,然后在Step 4和Step 5中都来到processAnnotationsStream这个方法,使用BCEL的ClassParser在字节码层面读取了/WEB-INF/classes和某些jar(应该可以在叫做fragments的概念中指定)中class文件的超类名和实现的接口名,判断是否与记录的注解类名相同,若相同再通过org.apache.catalina.util.Introspection类load为Class对象,最后保存起来,于Step 11中交给org.apache.catalina.core.StandardContext,也就是tomcat实际调用
ServletContainerInitializer.onStartup()的地方。
至此,谜团终于解开。
不过还有一个小疑问,StandardContext存放@HandlesTypes的对象叫做Map<ServletContainerInitializer,Set<Class<?>>> initializers,他的addServletContainerInitializer方法除了webConfig()以外,还被TomcatEmbeddedServletContainerFactory.addJasperInitializer和TomcatEmbeddedServletContainerFactory.configureContext调用,不知道运行起来是否有多余的class混入其中。也难怪spring要在SpringServletContainerInitializer.onstart的处理中这样注释的原因了吧:D
// Be defensive: Some servlet containers provide us with invalid classes,
// no matter what @HandlesTypes says...
不知道其他Servlet,比如Jetty引擎,是怎么实现@HandlesTypes这个注解的呢。
最新文章
- ecshop二次开发常用代码
- CSS书写规范及顺序
- 多线线程async与await关键字
- IIS限制ip访问
- DOS 循环读取txt每一行内容
- sql Truncate 与 delete的区别
- git学习之创建版本库
- 理解性能的奥秘——应用程序中慢,SSMS中快(4)——收集解决参数嗅探问题的信息
- python之lambda函数
- [cf1025D][区间dp]
- Redis主从哨兵和集群搭建
- miniprogrampatch 提供 watch 和 computed 特性
- C# DataTable.Compute()用法
- [C#][Quartz]帮助类
- 5种分布式共享session的方法
- TZOJ 2415 Arctic Network(最小生成树第k小边)
- npm 报错: npm ERR! Please try running this command again as root/Administrator.
- 深度学习笔记之【随机梯度下降(SGD)】
- 【spring boot】使用注解@ConfigurationProperties读取配置文件时候 报错 org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name &#39;rocketmqAutoConfiguration&#39;: Unsatisfied dependenc
- shfileoperation 删除文件 FileDelete(CString strName)
热门文章
- 基于通用jar、动态配置、组件编排的会员任务中心系统设计
- u盘重装ubuntu16.04过程遇到的问题
- 将SpringBoot部署在外部tomcat中
- Spring入门(十四):Spring MVC控制器的2种测试方法
- .ssh/config 文件的解释算法及配置原则
- python,json解析字符串时ValueError: Expecting property name enclosed in double quotes: line 1 column 2 (char 1)
- 【django】分页
- 小记redis持久化的机制
- Spring 梳理-启用MVC
- Hadoop点滴-初识MapReduce(1)