关于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这个注解的呢。

最新文章

  1. ecshop二次开发常用代码
  2. CSS书写规范及顺序
  3. 多线线程async与await关键字
  4. IIS限制ip访问
  5. DOS 循环读取txt每一行内容
  6. sql Truncate 与 delete的区别
  7. git学习之创建版本库
  8. 理解性能的奥秘——应用程序中慢,SSMS中快(4)——收集解决参数嗅探问题的信息
  9. python之lambda函数
  10. [cf1025D][区间dp]
  11. Redis主从哨兵和集群搭建
  12. miniprogrampatch 提供 watch 和 computed 特性
  13. C# DataTable.Compute()用法
  14. [C#][Quartz]帮助类
  15. 5种分布式共享session的方法
  16. TZOJ 2415 Arctic Network(最小生成树第k小边)
  17. npm 报错: npm ERR! Please try running this command again as root/Administrator.
  18. 深度学习笔记之【随机梯度下降(SGD)】
  19. 【spring boot】使用注解@ConfigurationProperties读取配置文件时候 报错 org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name &#39;rocketmqAutoConfiguration&#39;: Unsatisfied dependenc
  20. shfileoperation 删除文件 FileDelete(CString strName)

热门文章

  1. 基于通用jar、动态配置、组件编排的会员任务中心系统设计
  2. u盘重装ubuntu16.04过程遇到的问题
  3. 将SpringBoot部署在外部tomcat中
  4. Spring入门(十四):Spring MVC控制器的2种测试方法
  5. .ssh/config 文件的解释算法及配置原则
  6. python,json解析字符串时ValueError: Expecting property name enclosed in double quotes: line 1 column 2 (char 1)
  7. 【django】分页
  8. 小记redis持久化的机制
  9. Spring 梳理-启用MVC
  10. Hadoop点滴-初识MapReduce(1)