Servlet在启动时加载的tomcat源码(原创)
tomcat 8.0.36
知识点:
- 通过配置loadOnStartup可以设置Servlet是否在Tomcat启动时加载,以及按值大小进行有序加载,其最小有效值为0,最大有效值为Integer.MAX_VALUE。
- Jsp Servlet的类是org.apache.jasper.servlet.JspServlet。
- Jsp Servlet是强制性启动时加载,其loadOnStartup的默认值,或其值是失效值时,将使用最大有效值。
- 通过配置Context或Host的failCtxIfServletStartFails属性值,优先使用Context的,设置tomcat启动时加载servlet时,是否忽略抛出的ServletException异常,如果不忽略,则tomcat启动失败,默认不忽略。
- servlet有多线程模式,和单线程模式,默认是多线程模式,单线程模式需要实现SingleThreadModel接口。单线程模式下,开启一个实例池,同一时间,一个实例只分配给一个线程占用。
解说源码:
Tomcat在启动时,就会调用loadOnStartup,传入的参数是一个Container[]的数组,这个数组代表的是从我们的web.xml,web-fragment.xml,注解,以及动态添加等方式配置的关于Servlet等信息的数组。
ok设置为false,表示Tomcat启动失败,将无法运作。即loadOnStartup的返回值将影响Tomcat是否运作。
if (!loadOnStartup(findChildren())) {
ok = false;
}
进入loadOnStartup方法后,这里首先定义了一个TreeMap,使用TreeMap的主要目的就是可以排序。
然后遍历Servlet信息集,将遍历中的Servlet信息转化为实际类型Wrapper。
通过getLoadOnStartup方法得到Servlet的启动序级,过滤掉启动序级小于0的Servlet。
接下来,就把大于或等于0的启动序级加入到TreeMap,映射的值是一个Wrapper表,即把Servlet分箱装入到所属启动序级的箱子里面。
TreeMap<Integer, ArrayList<Wrapper>> map = new TreeMap<>();
for (int i = 0; i < children.length; i++) {
Wrapper wrapper = (Wrapper) children[i];
int loadOnStartup = wrapper.getLoadOnStartup();
if (loadOnStartup < 0)
continue;
Integer key = Integer.valueOf(loadOnStartup);
ArrayList<Wrapper> list = map.get(key);
if (list == null) {
list = new ArrayList<>();
map.put(key, list);
}
list.add(wrapper);
}
实际上Wrapper只是一个接口,真正实现Wrapper方法的是StandardWrapper,而StandardWrapper还实现了servlet规范中的ServletConfig接口。
public class StandardWrapper extends ContainerBase
implements ServletConfig, Wrapper
但是,最后传递给应用使用的ServletConfig并非StandardWrapper,而是StandardWrapperFacade,当然也是实现ServletConfig接口。
public final class StandardWrapperFacade
implements ServletConfig
正如名字上的意思,使用外观模式再一次包装StandardWrapper,然后调用其的同名方法。
protected final StandardWrapperFacade facade = new StandardWrapperFacade(this);
所以servlet的所有配置信息都存放在StandardWrapper,如成员变量loadOnStartup就是配置servlet中的一项,其默认值为-1。
protected int loadOnStartup = -1;
getLoadOnStartup方法主要获取成员变量loadOnStartup值,但有特殊情况,成员变量isJspServlet是记录配置的Servlet类是不是org.apache.jasper.servlet.JspServlet,即一个Servlet是不是Jsp Servlet就看它的类是不是这个类。
简单的说,普通Servlet的启动序级的默认值为-1,最小有效值为0,最大有效值为Integer.MAX_VALUE。Jsp Servlet的启动序级的默认值为最大值,有效值与普通Servlet一致,并且其值失效时将使用最大值。
public int getLoadOnStartup() {
if (isJspServlet && loadOnStartup < 0) {
return Integer.MAX_VALUE;
} else {
return (this.loadOnStartup);
}
}
回到原来的getLoadOnStartup方法里,map已经把需要启动时加载的Servlet分装到按启动序级排列好的箱子里面,接下来就是先按启动序级顺序,再按加入顺序遍历servlet,并且执行其load方法。
load方法如果抛出ServletException异常,将通过getComputedFailCtxIfServletStartFails获取的值来决定是否让Tomcat启动失败。
而获取的值是Context配置的failCtxIfServletStartFails属性,如果该属性未配置,则使用Host配置failCtxIfServletStartFails的属性,其默认值为false。
for (ArrayList<Wrapper> list : map.values()) {
for (Wrapper wrapper : list) {
try {
wrapper.load();
} catch (ServletException e) {
if (getComputedFailCtxIfServletStartFails()) {
return false;
}
}
}
}
return true;
进入load方法后,load方法是一个线程安全的方法,为什么需要线程安全?因为,servlet大多数情况下是不进行预加载,什么时候用到,就什么时候加载。换句话说,为了节省从来没用到的servlet资源空间,所以等到有请求访问到servlet时才加载。
所以这种后加载的方式,就有可能出现两个请求,在同一时间访问到同一个servlet时,这时候就需要线程安全了。那什么时候需要预加载,什么时候不需要预加载,这个问题可以思考到,为什么定义Jsp Servlet时,tomcat是强制性要预加载,这里不再往深叙述了。
成员变量instance就是配置的servlet类的实例,实例是需要加载与初始化,才能对请求做处理。
public synchronized void load() throws ServletException {
instance = loadServlet(); if (!instanceInitialized) {
initServlet(instance);
}
}
来到loadServlet方法,在加载实例之前,这里要验证servlet实例是一个单例还是多例。如果是单例,并且实例已经存在,那就返回该实例。如果是多例,不管实例存与不存在,都将再创建实例。
判断是使用单例还是多例,由成员变量singleThreadModel的值来判断,其含意就是如果同一个servlet实例,只允许在同一时间让一个请求(线程)访问,所以叫单线程模式。反之,同一个servlet实例,允许同一时间,让多个请求(线程)访问,就叫多线程模式。
显然,单线程与多线程的区别在于,servlet的成员变量共享与不共享,设计的重点就得往这里思考。那变量singleThreadModel默认值为false,即servlet模认是多线程模式,其如何改变为单线程模式在后面。
if (!singleThreadModel && (instance != null))
return instance;
接下来获取实例管理器,然后通过servlet类加载并创建servlet,这个过实例化的过程,内容也是挺丰富多彩的,其中包括JNDI注入,POST构造方法调用等,这里就不往里面叙述了。
InstanceManager instanceManager = ((StandardContext)getParent()).getInstanceManager();
Servlet servlet = (Servlet) instanceManager.newInstance(servletClass);
判断servlet是否单线程模式,如果serlvet实现了SingleThreadModel接口,那么成员变量singleThreadModel标记为true,同时创建出实例池。
if (servlet instanceof SingleThreadModel) {
if (instancePool == null) {
instancePool = new Stack<>();
}
singleThreadModel = true;
}
如果servlet需要得到Wrapper对象实例,那么servlet实现ContainerServlet接口,并且在Context的配置里设置属性privileged为true,默认为false。
if ((servlet instanceof ContainerServlet)
&&(Context) getParent()).getPrivileged()) {
((ContainerServlet) servlet).setWrapper(this);
}
来到initServlet方法,如果servlet是多线程模式,则实例只执行一次初始化。反之,如果servlet是单线程模式,每次创建的实例,都会执行一次初始化。
private synchronized void initServlet(Servlet servlet) throws ServletException {
if (instanceInitialized && !singleThreadModel)
return;
servlet.init(facade);
instanceInitialized = true;
}
Servlet在启动时加载的tomcat源码(原创)的更多相关文章
- web.xml中配置启动时加载的servlet,load-on-starup
web.xml中配置启动时加载的servlet,load-on-starup 使用servlet来初始化配置文件数据: 在servlet的配置当中,<load-on-startup>1&l ...
- ElasticSearch 启动时加载 Analyzer 源码分析
ElasticSearch 启动时加载 Analyzer 源码分析 本文介绍 ElasticSearch启动时如何创建.加载Analyzer,主要的参考资料是Lucene中关于Analyzer官方文档 ...
- 【 js 模块加载 】【源码学习】深入学习模块化加载(node.js 模块源码)
文章提纲: 第一部分:介绍模块规范及之间区别 第二部分:以 node.js 实现模块化规范 源码,深入学习. 一.模块规范 说到模块化加载,就不得先说一说模块规范.模块规范是用来约束每个模块,让其必须 ...
- Mybatis 懒加载使用及源码分析
Mybatis 懒加载的使用 什么是懒加载?懒加载的意思就是在使用的时候才去加载,不使用不去加载,相反的就叫饥饿加载或者立即加载.懒加载在Mybatis中一般是存在与联合查询的情况,比如查询一个对象的 ...
- 2款不同样式的CSS3 Loading加载动画 附源码
原文:2款不同样式的CSS3 Loading加载动画 附源码 我们经常看到的Loading加载很多都是转圈圈的那种,今天我们来换一种有创意的CSS3 Loading加载动画,一种是声波形状的动画,另一 ...
- 设置程序启动时加载的storyboard
这个设置表明:程序启动时会加载Main.storyboard
- 别翻了,这篇文章绝对让你深刻理解java类的加载以及ClassLoader源码分析【JVM篇二】
目录 1.什么是类的加载(类初始化) 2.类的生命周期 3.接口的加载过程 4.解开开篇的面试题 5.理解首次主动使用 6.类加载器 7.关于命名空间 8.JVM类加载机制 9.双亲委派模型 10.C ...
- Tomcat启动时加载数据到缓存---web.xml中listener加载顺序(优先初始化Spring IOC容器)
JavaWebSpringTomcatCache 最近用到在Tomcat服务器启动时自动加载数据到缓存,这就需要创建一个自定义的缓存监听器并实现ServletContextListener接口,并且 ...
- Tomcat启动时加载数据到缓存---web.xml中listener加载顺序(例如顺序:1、初始化spring容器,2、初始化线程池,3、加载业务代码,将数据库中数据加载到内存中)
最近公司要做功能迁移,原来的后台使用的Netty,现在要迁移到在uap上,也就是说所有后台的代码不能通过netty写的加载顺序加载了. 问题就来了,怎样让迁移到tomcat的代码按照原来的加载顺序进行 ...
随机推荐
- 用Eclipse搭建ssh框架
问:ssh是哪三大框架,以及他们的作用是什么? 答:分别是struts,spring,hibernate. struts的作用是:是web层,其核心是mvc模式,他可以自动获取参数,自动类型转换,自动 ...
- xss其他标签下的js用法总结大全
前段时间我遇到一个问题,就是说普通的平台获取cookie的语句为↓ Default <script src=js地址></script> 1 <scr ...
- 快速定位性能瓶颈,检查出所有资源(CPU、内存、磁盘IO等)的利用率(utilization)、饱和度(saturation)和错误(error)度量,即USE方法
命令:uptime说明:查看机器分别在1分钟.5分钟.15分钟的平均负载情况,显示的数字表示等待cpu资源的进程和阻塞在不可中断io进程的数量,如果1分钟的平均负载很高,而15分钟的平均负载很低,说明 ...
- 20145337 GDB调试汇编堆栈过程分析
20145337 GDB调试汇编堆栈过程分析 测试代码 #include<stdio.h> short addend1 = 1; static int addend2 = 2; const ...
- Ubuntu添加开机自动启动程序方法
1. 开机启动时自动运行程序 Linux加载后, 它将初始化硬件和设备驱动, 然后运行第一个进程init.init根据配置 文件继续引导过程,启动其它进程.通常情况下,修改放置在 / ...
- Android动画
[浅谈Android动画] 总共四种:Tween Animation变换动画.Frame Animation帧动画 Layout Animation布局动画.Property Animation 属性 ...
- golang reflect
golang reflect go语言中reflect反射机制.详细原文:地址 接口值到反射对象 package main import ( "fmt" "reflect ...
- Go项目结构和模块导入
Go项目结构和模块导入 golang项目结构与其他语言类似,但是仍然有一些需要注意的地方. 项目结构 环境配置 go 命令依赖一个重要的环境变量:$GOPATH,它表示GO项目的路径,如下设置 exp ...
- 《Entity Framework 6 Recipes》中文翻译系列 (17) -----第三章 查询之分页、过滤和使用DateTime中的日期部分分组
翻译的初衷以及为什么选择<Entity Framework 6 Recipes>来学习,请看本系列开篇 3-12 分页和过滤 问题 你想使用分页和过滤来创建查询. 解决方案 假设你有如图3 ...
- 2013 duilib入门简明教程 -- 界面设计器 DuiDesigner (10)
上一个教程讲解了怎么布局最大化.最小化.关闭按钮,但是如果手动去计算这三个按钮的位置和大小的话,非常的不直观,也很不方便. 所以这一章准备介绍duilib的UI设计器,由于这个设计器很 ...