WebApplicationInitializer启动分析
从Servlet 3.0 开始Tomcat已经支持注解式的配置。了解下,在注解的配置方式下,Web是怎样启动起来的。
通过注解配置一个Web应用
下面是一个通过注解实现一个简单的Web应用
public class SpringWebInitializer extend AbstractAnnotationConfigDispatcherServletInitializer {
//这里可以配置servlet,filter,listener,context param,attribute等
}
类图如下:
上面四个类,各有各的职责,相对于些.xml
文件,你只需要实现这些抽象类的抽象方法即可。详细解释这里省略。
我们现在关注最顶层的那个接口 WebApplicaitonInitializer
。经过查看其源码。
public interface WebApplicationInitializer { /**
* Configure the given {@link ServletContext} with any servlets, filters, listeners
* context-params and attributes necessary for initializing this web application. See
* examples {@linkplain WebApplicationInitializer above}.
* @param servletContext the {@code ServletContext} to initialize
* @throws ServletException if any call against the given {@code ServletContext}
* throws a {@code ServletException}
*/
void onStartup(ServletContext servletContext) throws ServletException; }
SpringServletContainerInitializer
起作用的。源码如下:@HandlesTypes(WebApplicationInitializer.class)
public class SpringServletContainerInitializer implements ServletContainerInitializer { @Override
public void onStartup(Set<Class<?>> webAppInitializerClasses, ServletContext servletContext)
throws ServletException { .......去除无用代码....... for (WebApplicationInitializer initializer : initializers) { // 通知启动WebApplicationInitializer 的实现 initializer.onStartup(servletContext);
}
} }
Tomcat:
在Tomcat应用服务器启动以后。它会通过反射,利用ClassLoader来加载Web-App文件夹下面的Web应用的jar包。
我们知道,每个Web应用都有一个自己唯一的一个ServletContext
,在Tomcat里面,SerlvetContext
的实现类是ApplicationContext
。看名字也可以知道这代表着一个Application应用的上下文环境。我们现在只关心这个类就可以了。这个类里面有个类型为StandardContext,这是Context
标准实现。
说到这个类,然后我们就需要谈起一个接口ServletContextListener
,这个接口用来提供一个观察者模式,简单的讲就是用来监控ServletContext的启动,销毁生命周期的。
看一下Tomcat所有容器的抽象类里面的ContainerMBean
类里面的addChild
方法,这个StandardContext
x其实就是一个ServletContext
,即为一个应用容器。
//为容器添加子容器
public void addChild(String type, String name) throws MBeanException{ Container contained = (Container) newInstance(type);
contained.setName(name); if(contained instanceof StandardHost){
HostConfig config = new HostConfig();
contained.addLifecycleListener(config);
} else if(contained instanceof StandardContext){
//添加的容器是一个Tomcat子容器的话,就分配其一个ContextCofig
ContextConfig config = new ContextConfig();
//将ContextConfig添加到Container的监听者行列中
contained.addLifecycleListener(config);
} boolean oldValue= true; ContainerBase container = doGetManagedResource();
try {
oldValue = container.getStartChildren();
container.setStartChildren(false);
container.addChild(contained);
/*
开始一个初始化,会通知所有正在监听Container的观察者
对于ContextConfig来说,现在应该做的是加载配置等
*/
contained.init();
} catch (LifecycleException e){
throw new MBeanException(e);
} finally {
if(container != null) {
container.setStartChildren(oldValue);
}
}
}
Tomcat 在为Host容器添加Context子容器时,会为其分配一个CntextConfig
类。当你看到这个类名应该就会想到,这应该是和Web配置加载有关的一个类。
@Override
public void lifecycleEvent(LifecycleEvent event) { ......省略无用代码......
// Process the event that has occurred
if (event.getType().equals(Lifecycle.CONFIGURE_START_EVENT)) {
//当监听的容器发送,CONFIGURE_START_EVENT时,配置开始
configureStart();
}
.....省略无用代码.......
}
再来看看configureStart()
方法
protected synchronized void configureStart() { ....省略无用代码····· webConfig(); if (!context.getIgnoreAnnotations()) {
applicationAnnotationsConfig();
}
·····省略无用代码·····
}
代码中可以看到,Tomcat先是从调用WebConfig()
函数.这个函数的主要的动作就是读取web.xml
配置文件,
- 函数webConfig()的执行动作的流程
- 读取web-fragment.xml和各个jar模块
- 排序所有读取到的fragments
- 查找所有的ServletContainerInitializer(SCIs)
- 处理WEB-INF/Classes文件夹下面的
- 处理所有的注解配置类和,并缓存
- 将所有的web-fragment.xml合并
- 转换所有的JSP代码成Java代码
- 将Web.xml配置转变成代码式的配置
- 查找静态资源默认文件夹
WEB-INF/classes/META-INF/resources
- 将所有的实现ServletContainerInitializers的类添加到StandardContext的initializers集合中
我们在这里重点关注 第三步和第十三步,webConfig()中会调用processServletContainerInitializers()
这个方法即为加载所有的经过 @HandlesTypes注解的类。
protected void webConfig() { .... // Step 3. 查找ServletContainerInitializers
if (ok) {
processServletContainerInitializers();
} ...... // Step 11. ServletContainerInitializer 交给StandardContext去处理!在这里即为我们的应用所在的容器
if (ok) {
for (Map.Entry<ServletContainerInitializer,
Set<Class<?>>> entry :
initializerClassMap.entrySet()) {
if (entry.getValue().isEmpty()) {
context.addServletContainerInitializer(
entry.getKey(), null);
} else {
context.addServletContainerInitializer(
entry.getKey(), entry.getValue());
}
}
}
}
我们去StandardContext里面去看一下它是怎么处理这个集合属性的。在StandardContext
类里面的startInternel()
方法,就是启动这个
@Override
protected synchronized void startInternal() throws LifecycleException {
....
// Call ServletContainerInitializers,启动这些
for (Map.Entry<ServletContainerInitializer, Set<Class<?>>> entry :
initializers.entrySet()) {
try {
//启动所有的ServletContainerInitializer
entry.getKey().onStartup(entry.getValue(),
getServletContext());
} catch (ServletException e) {
log.error(sm.getString("standardContext.sciFail"), e);
ok = false;
break;
}
}
...
}
即: 通过内部启动时,它会通知所有正在监听的 ServletContainerInitializers,这样,即完成了Web应用的加载和初始化的配置!
总结
- Tomcat的Host容器在添加子容器时,会通过解析.xml并通过classloader加载 @HandlesTypes注解的类
- 读取@HandlesTypes注解value值。并放入ServletContainerInitializers 对应的Set集合中
- 在ApplicationContext 内部启动时会通知 ServletContainerInitializers 的onStart方法()。这个onStart方法的第一个参数就是@HandlesTypes注解的value 值指定的Class集合
- 在Spring 应用中,对ServletContainerInitializers的实现就是SpringServletContainerInitializer,注解指定的类就是WebApplicationInitializer.
@HandlesTypes(WebApplicationInitializer.class)
public class SpringServletContainerInitializer implements ServletContainerInitializer {
@Override
public void onStartup(Set<Class<?>> webAppInitializerClasses, ServletContext servletContext)
throws ServletException {
.......篇幅有限,去除无用代码.......
for (WebApplicationInitializer initializer : initializers) { // 通知启动WebApplicationInitializer 的实现
initializer.onStartup(servletContext);
}
}
}
参考:https://docs.spring.io/spring/docs/current/spring-framework-reference/web.html#mvc
WebApplicationInitializer启动分析的更多相关文章
- SpringBoot源码解析:tomcat启动分析
>> spring与tomcat的启动分析:war包形式 tomcat:xml加载规范 1.contex-param: 初始化参数 2.listener-class: contextloa ...
- Nginx学习笔记(八) Nginx进程启动分析
Nginx进程启动分析 worker子进程的执行循环的函数是ngx_worker_process_cycle (src/os/unix/ngx_process_cycle.c). 其中,捕获事件.分发 ...
- mkimage工具 加载地址和入口地址 内核启动分析
第三章第二节 mkimage工具制作Linux内核的压缩镜像文件,需要使用到mkimage工具.mkimage这个工具位于u-boot-2013. 04中的tools目录下,它可以用来制作不压缩或者压 ...
- Android Binder------ServiceManager启动分析
ServiceManager启动分析 简述: ServiceManager是一个全局的manager.调用了Jni函数,实现addServicew getService checkService ...
- Android系统--输入系统(七)Reader_Dispatcher线程启动分析
Android系统--输入系统(七)Reader_Dispatcher线程启动分析 1. Reader/Dispatcher的引入 对于输入系统来说,将会创建两个线程: Reader线程(读取事件) ...
- 第3阶段——内核启动分析之start_kernel初始化函数(5)
内核启动分析之start_kernel初始化函数(init/main.c) stext函数启动内核后,就开始进入start_kernel初始化各个函数, 下面只是浅尝辄止的描述一下函数的功能,很多函数 ...
- 一起读源码之zookeeper(1) -- 启动分析
从本文开始,不定期分析一个开源项目源代码,起篇从大名鼎鼎的zookeeper开始. 为什么是zk,因为用到zk的场景实在太多了,大部分耳熟能详的分布式系统都有zookeeper的影子,比如hbase, ...
- Tomcat启动分析(转自:http://docs.huihoo.com/apache/tomcat/heavyz/01-startup.html)
Tomcat启动分析 1 - Tomcat Server的组成部分 1.1 - Server A Server element represents the entire Catalina servl ...
- FPGA低温不能启动分析(转)
FPGA低温不能启动分析 现象描述:在给medium板光端机做低温试验时,分别给发送版.接收板断电重新启动,发现有的板子在-40°可以启动,而有些板子在-20°都不能启动,需要升高温度到0°以上才能启 ...
随机推荐
- SpringCloud的入门学习之Eureka(Eureka的单节点)
SpringCloud--->Spring生态体系的微服务架构:https://spring.io/ 官网贴图,如下所示,介绍了微服务的架构体系(话说,英语好读官网,可能很爽吧,没体验过). 1 ...
- VS2017安装使用Easyx时出现的问题及解决方法
EasyX 是针对 Visual C++ 的绘图库,在初学 C 语言实现图形和游戏编程.图形学.分形学等需要绘图实践的领域有一定应用. EasyX 库在 Visual C++ 中模拟了 Turbo C ...
- Python GUI开发,效率提升10倍的方法!
1 框架简介 这个框架的名字叫 PySimpleGUI,它完全基于Python语言,能非常方便地开发GUI界面,代码量相比现有框架减少50%到90%.并且,它提供了极为友好的Python风格的接口,大 ...
- Java的23种设计模式,详细讲解(一)
本人免费整理了Java高级资料,涵盖了Java.Redis.MongoDB.MySQL.Zookeeper.Spring Cloud.Dubbo高并发分布式等教程,一共30G,需要自己领取.传送门:h ...
- spring web mvc环境搭建
这两天在学习spring,哎,没办法呀,成都基本都是java,不喜欢学这个也没有用.多学点多条路.还得生活不是. 好了,言归正传,我这里记录下我搭建spring mvc的过程,其实过程不算太难,主要是 ...
- DevExpress的分页Tab控件XtraTabControl控件的使用
场景 Winform控件-DevExpress18下载安装注册以及在VS中使用: https://blog.csdn.net/BADAO_LIUMANG_QIZHI/article/details/1 ...
- DDCTF 2019 部分WP
WEB 滴~ http://117.51.150.246/index.php?jpg=TmpZMlF6WXhOamN5UlRaQk56QTJOdz09 观察链接可发现jpg的值是文件名转hex再bas ...
- ORACLE ORA-1652的解决方法
原创 Oracle 作者:wzq609 时间:2015-02-04 22:11:07 17183 0 前言:在检查数据库的alert日志,发现数据库报了ORA-1652: unable to exte ...
- Django框架(二十二)-- Django rest_framework-解析器
一.解析器的作用 根据请求头 content-type 选择对应的解析器对请求体内容进行处理,将传过来的数据解析成字典 二.使用解析器 1.局部使用 在视图类中重定义parser_classes即可, ...
- JavaScript显式类型转换与隐式类型转换
隐式类型转换 四则运算 判断语句 toString 在 JavaScript 中声明变量不需指定类型,对变量赋值也没有类型检查,同时还允许隐式类型转换. 这些特征说明 JavaScript 属于弱类型 ...