参考资料:
《Tomcat源码解析系列(三)Server》
《Tomcat - Server的设计和实现: StandardServer》
《Tomcat源码解析系列(四)Service》
《Tomcat - Service的设计和实现: StandardService》
前文:
《Tomcat源码:启动类Bootstrap与Catalina的加载》
《Tomcat源码:容器的生命周期管理与事件监听》
写在开头:本文为个人学习笔记,内容比较随意,夹杂个人理解,如有错误,欢迎指正。
在前文中我们介绍了tomcat启动类的加载,在Catalina初始化时加载了server.xml,并调用了getServer().init()方法加载server接口的实现类standserver。另外还介绍了以standserver为代表的容器组件共同继承的用于管理生命周期的抽象类LifecycleBase。
本文我们接着前文的内容继续介绍standserver与standservice。
目录
前言
一、StandardServer
1、init
1.1、LifecycleBase#init
1.2、LifecycleMBeanBase#initInternal
1.3、StandServer#initInternal
2、Start
2.1、LifecycleBase#start
2.2、StandardServer#start
二、StandService
1、init
1.1、StandardService#initInternal
2、Start
2.1、StandardService#startInternal
2.2、 MapperListener#startInternal
2.3、findDefaultHost
2.3、registerHost
Catalina类的主要作用就是根据server.xml的配置来初始化Tomcat运行所需要的组件,比如 Server,Service 等等,然后调用成员变量Server类对象的init和start方法,来启动 tomcat。
一个 Server 类的实例就代表了一个 Tomcat 的容器,一个Tomcat 进程只会有一个 Server 实例。Server 是一个接口,它的实现类是 StandardServer
StandardServer的init方法由父类LifecycleBase实现,可以看到LifecycleBase使用模板模式将初始化的具体操作留给了每个容器自己实现。
@Override
public final synchronized void init() throws LifecycleException {
// 非NEW状态,不允许调用init()方法
if (!state.equals(LifecycleState.NEW)) {
invalidTransition(Lifecycle.BEFORE_INIT_EVENT);
}
try {
// 初始化逻辑之前,先将状态变更为`INITIALIZING`
setStateInternal(LifecycleState.INITIALIZING, null, false);
// 初始化,该方法为一个abstract方法,需要组件自行实现
initInternal();
// 初始化完成之后,状态变更为`INITIALIZED`
setStateInternal(LifecycleState.INITIALIZED, null, false);
} catch (Throwable t) {
// 初始化的过程中,可能会有异常抛出,这时需要捕获异常,并将状态变更为`FAILED`
ExceptionUtils.handleThrowable(t);
setStateInternal(LifecycleState.FAILED, null, false);
throw new LifecycleException(
sm.getString("lifecycleBase.initFail",toString()), t);
}
}
先看第一步,调用父类中的initInternal,这个方法的实现我们可以在父类LifecycleMBeanBase中找到
// StandardServer.java
protected void initInternal() throws LifecycleException {
// 调用父类LifecycleMBeanBase中的实现
super.initInternal();
// 其余代码
}
该方法其实就是将当前容器注册到MBeanServer中,相关的知识可以看我之前的文章《Java8之JMX与MBean》,不了解的可以将它当作Spring中的IOC,可以管控容器中的方法调用与生命周期,这里就是将StandServer进行注册。
// LifecycleMBeanBase.java
private ObjectName oname = null;
protected MBeanServer mserver = null;
protected void initInternal() throws LifecycleException {
if (oname == null) {
mserver = Registry.getRegistry(null, null).getMBeanServer();
oname = register(this, getObjectNameKeyProperties());
}
}
protected final ObjectName register(Object obj,
String objectNameKeyProperties) {
StringBuilder name = new StringBuilder(getDomain());
name.append(':');
name.append(objectNameKeyProperties);
ObjectName on = null;
try {
on = new ObjectName(name.toString());
Registry.getRegistry(null, null).registerComponent(obj, on, null);
} catch (Exception e) {
log.warn(sm.getString("lifecycleMBeanBase.registerFail", obj, name), e);
}
return on;
}
// StandardServer.java
protected String getObjectNameKeyProperties() {
return "type=Server";
}
再回到StandServer#initInternal中,调用完父类的initInternal方法后初始化 onameMBeanFactory和onameStringCache这属性,内容其实就是注册StringCache对象和 MBeanFactory对象到MBeanServer,再然后就是调用 globalNamingResources 对象的 init 方法。
protected void initInternal() throws LifecycleException {
// 调用父类LifecycleMBeanBase中的实现
super.initInternal();
// 注册StringCache对象和 MBeanFactory对象到MBeanServer
onameStringCache = register(new StringCache(), "type=StringCache");
MBeanFactory factory = new MBeanFactory();
factory.setContainer(this);
onameMBeanFactory = register(factory, "type=MBeanFactory");
globalNamingResources.init();
// 其余代码
}
globalNamingResources为全局命名资源,对应server.xml中的
private NamingResourcesImpl globalNamingResources = null;
public StandardServer() {
super();
globalNamingResources = new NamingResourcesImpl();
globalNamingResources.setContainer(this);
if (isUseNaming()) {
namingContextListener = new NamingContextListener();
addLifecycleListener(namingContextListener);
} else {
namingContextListener = null;
}
}
再往下这段是将catalina的parentClassLoader(这个属性在Bootstrap#init方法里通过反射调用Catalina的setParentClassLoader将sharedClassLoader传进去,也就是说这个 parentClassLoader就是sharedClassLoader指向的对象,也就是一个URLClassLoader对象)里能加载的jar文件的目录,都添加到ExtensionValidator 这个集合中。
protected void initInternal() throws LifecycleException {
// 其余代码
// 添加可以加载jar包的路径
if (getCatalina() != null) {
ClassLoader cl = getCatalina().getParentClassLoader();
while (cl != null && cl != ClassLoader.getSystemClassLoader()) {
if (cl instanceof URLClassLoader) {
URL[] urls = ((URLClassLoader) cl).getURLs();
for (URL url : urls) {
if (url.getProtocol().equals("file")) {
try {
File f = new File(url.toURI());
if (f.isFile() && f.getName().endsWith(".jar")) {
ExtensionValidator.addSystemResource(f);
}
} catch (URISyntaxException | IOException e) {
}
}
}
}
cl = cl.getParent();
}
}
// 其余代码
}
最后就是循环创建所有的service对象。Server里的service是在server.xml里定义的,在 Catalina解析server.xml的时候初始化,并注入到Server对象里。
显然 StandardServer#initInternal() 方法最重要的就是这段,调用 Service#init 方法来创建Service这个Tomcat的核心组件之一。
protected void initInternal() throws LifecycleException {
// 其余代码
// 初始化service
for (Service service : services) {
service.init();
}
}
介绍完了server组件的初始化方法init,接着来看启动方法,该流程是在catlina中调用getServer().start()触发的。
和init方法一样,server的start方法也是由父类LifecycleBase中实现,也同样使用模板模式将具体实现留给了子类Standserver。
public final synchronized void start() throws LifecycleException {
// `STARTING_PREP`、`STARTING`和`STARTED时,将忽略start()逻辑
if (LifecycleState.STARTING_PREP.equals(state) || LifecycleState.STARTING.equals(state) ||
LifecycleState.STARTED.equals(state)) {
return;
}
// `NEW`状态时,执行init()方法
if (state.equals(LifecycleState.NEW)) {
init();
}
// `FAILED`状态时,执行stop()方法
else if (state.equals(LifecycleState.FAILED)) {
stop();
}
// 不是`INITIALIZED`和`STOPPED`时,则说明是非法的操作
else if (!state.equals(LifecycleState.INITIALIZED) &&
!state.equals(LifecycleState.STOPPED)) {
invalidTransition(Lifecycle.BEFORE_START_EVENT);
}
try {
// start前的状态设置
setStateInternal(LifecycleState.STARTING_PREP, null, false);
// start逻辑,抽象方法,由组件自行实现
startInternal();
// start过程中,可能因为某些原因失败,这时需要stop操作
if (state.equals(LifecycleState.FAILED)) {
stop();
} else if (!state.equals(LifecycleState.STARTING)) {
invalidTransition(Lifecycle.AFTER_START_EVENT);
} else {
// 设置状态为STARTED
setStateInternal(LifecycleState.STARTED, null, false);
}
} catch (Throwable t) {
ExceptionUtils.handleThrowable(t);
setStateInternal(LifecycleState.FAILED, null, false);
throw new LifecycleException(sm.getString("lifecycleBase.startFail", toString()), t);
}
}
方法的第一行代码先触发 CONFIGURE_START_EVENT 事件,以便执行 StandardServer 的 LifecycleListener 监听器,然后调用 setState 方法设置成 LifecycleBase 的 state 属性为 LifecycleState.STARTING。
接着就 globalNamingResources.start(),跟 initInternal 方法其实是类似的。
protected void startInternal() throws LifecycleException {
fireLifecycleEvent(CONFIGURE_START_EVENT, null);
setState(LifecycleState.STARTING);
globalNamingResources.start();
// 启动service
synchronized (servicesLock) {
for (Service service : services) {
service.start();
}
}
}
本节介绍了Server组件,Server组件是tomcat的核心组件之一,它是通过调用init和start方法来启动tomcat的,而Server的init方法和start方法则是调用Service的init和start方法来启动 Service(tomcat的另一个核心组件)。
可以看出,一个 Tomcat 进程只有一个 Server 实例,一个 Server 实例可以包含多个 Service 对象。
Service 是Tomcat的核心组件之一,大概可以分为4个部分,service属性、executor属性、connector属性、engine属性。
上文中分析了 Server 类的 init 和 start 方法,其中最核心的内容就是调用了 StandardServer 类的的 Service 类型的成员的 init 和 start 方法。Service 的实现类是StandardService。StandardService和StandardServer一样也是继承自 LifecycleMBeanBase。
开头和StandServer一样调用了父类的initInternal方法,然后分别调用了四个成员变量engine、executor、mapperListener、connector的init方法。
protected void initInternal() throws LifecycleException {
super.initInternal();
if (engine != null) {
engine.init();
}
for (Executor executor : findExecutors()) {
if (executor instanceof JmxEnabled) {
((JmxEnabled) executor).setDomain(getDomain());
}
executor.init();
}
mapperListener.init();
synchronized (connectorsLock) {
for (Connector connector : connectors) {
try {
connector.init();
} catch (Exception e) {
String message = sm.getString("standardService.connector.initFailed", connector);
log.error(message, e);
if (Boolean.getBoolean("org.apache.catalina.startup.EXIT_ON_INIT_FAILURE")) {
throw new LifecycleException(message);
}
}
}
}
}
先看看这几个成员变量的定义
private Engine engine = null;
protected final ArrayList executors = new ArrayList<>();
protected final MapperListener mapperListener = new MapperListener(this);
protected Connector connectors[] = new Connector[0];
除了mapperListener,都是解析server.xml文件时根据配置文件的配置初始化的。其中engine 和connector也是Tomcat核心组件之二,会分别单独解析,这里就先略过。
executor的实现类是,StandardThreadExecutor,同样继承自 LifecycleMBeanBase,它的作用跟线程池类似,这部分内容我们会在介绍connect时一并介绍。
public Executor[] findExecutors() {
synchronized (executors) {
return executors.toArray(new Executor[0]);
}
}
mapperListener的作用是在start的时候将容器类对象注册到Mapper对象中,这里我们先来看下他的构造方法。
// StandardService.java
protected final MapperListener mapperListener = new MapperListener(this);
// MapperListener.java
private final Mapper mapper;
private final Service service;
public MapperListener(Service service) {
this.service = service;
this.mapper = service.getMapper();
}
service.getMapper() 返回的是StandardService对象的mapper成员变量,该变量用来处理 Http 请求。Tomcat使用Mapper来处理一个Request到Host、Context 的映射关系,从而决定使用哪个 Service 来处理请求。
MapperListener不过没有重载 initInternal 方法,因此init只是单纯的注册了下MBean,没有别的操作。
protected void startInternal() throws LifecycleException {
setState(LifecycleState.STARTING);
if (engine != null) {
synchronized (engine) {
engine.start();
}
}
synchronized (executors) {
for (Executor executor : executors) {
executor.start();
}
}
mapperListener.start();
synchronized (connectorsLock) {
for (Connector connector : connectors) {
try {
if (connector.getState() != LifecycleState.FAILED) {
connector.start();
}
} catch (Exception e) {
log.error(sm.getString("standardService.connector.startFailed", connector), e);
}
}
}
}
startInternal 跟 initInternal 方法一样,也是依次调用。executor 的 start 方法初始化了内部的线程池,具体内容我们后续介绍,这里先看下mapperListener的启动。
engine.start();
executor.start();
mapperListener.start();
connector.start();
public void startInternal() throws LifecycleException {
setState(LifecycleState.STARTING);
Engine engine = service.getContainer();
if (engine == null) {
return;
}
findDefaultHost();
addListeners(engine);
Container[] conHosts = engine.findChildren();
for (Container conHost : conHosts) {
Host host = (Host) conHost;
if (!LifecycleState.NEW.equals(host.getState())) {
registerHost(host);
}
}
}
findDefaultHost()其实就是找出 defaultHost ,并存储到mapper中。这个 defaultHost 是 server.xml 的
for循环中的代码块总结下就是取出Engine中的子Container(即host)然后找出一个名字跟 defaultHost 指定的名字相同的 Host 对象。
private void findDefaultHost() {
Engine engine = service.getContainer();
// 找出defaultHost
String defaultHost = engine.getDefaultHost();
boolean found = false;
if (defaultHost != null && defaultHost.length() > 0) {
Container[] containers = engine.findChildren();
for (Container container : containers) {
Host host = (Host) container;
if (defaultHost.equalsIgnoreCase(host.getName())) {
found = true;
break;
}
String[] aliases = host.findAliases();
for (String alias : aliases) {
if (defaultHost.equalsIgnoreCase(alias)) {
found = true;
break;
}
}
}
}
if (found) {
mapper.setDefaultHostName(defaultHost);
} else {
log.error(sm.getString("mapperListener.unknownDefaultHost", defaultHost, service));
}
}
addListeners就是将MapperListener这个监听器添加到 Engine 及其子容器中
private void addListeners(Container container) {
container.addContainerListener(this);
container.addLifecycleListener(this);
for (Container child : container.findChildren()) {
addListeners(child);
}
}
最后是for循环中的代码,这段的作用就是,调用 registerHost方法来注册 Engine 的字容器 Host。
Container[] conHosts = engine.findChildren();
for (Container conHost : conHosts) {
Host host = (Host) conHost;
if (!LifecycleState.NEW.equals(host.getState())) {
// Registering the host will register the context and wrappers
registerHost(host);
}
}
registerHost方法先调用mapper.addHost将Host加入的Mapper类的的成员变量,然后调用 registerContext方法注册Host的子容器Context。
private void registerHost(Host host) {
String[] aliases = host.findAliases();
mapper.addHost(host.getName(), aliases, host);
for (Container container : host.findChildren()) {
if (container.getState().isAvailable()) {
registerContext((Context) container);
}
}
findDefaultHost();
if (log.isDebugEnabled()) {
log.debug(sm.getString("mapperListener.registerHost", host.getName(), domain, service));
}
}
registerContext又继续将context下面的每个wrapper与context都添加到mapper
private void registerContext(Context context) {
String contextPath = context.getPath();
if ("/".equals(contextPath)) {
contextPath = "";
}
Host host = (Host) context.getParent();
WebResourceRoot resources = context.getResources();
String[] welcomeFiles = context.findWelcomeFiles();
List wrappers = new ArrayList<>();
for (Container container : context.findChildren()) {
prepareWrapperMappingInfo(context, (Wrapper) container, wrappers);
if (log.isDebugEnabled()) {
log.debug(sm.getString("mapperListener.registerWrapper", container.getName(), contextPath, service));
}
}
mapper.addContextVersion(host.getName(), host, contextPath, context.getWebappVersion(), context, welcomeFiles,
resources, wrappers);
if (log.isDebugEnabled()) {
log.debug(sm.getString("mapperListener.registerContext", contextPath, service));
}
}
prepareWrapperMappingInfo用于准备注册到mapper下的wrapper,这儿mapper对于wrapper的支持是wrapper的包装对象WrapperMappingInfo。而一个context可能有多个wrapper,所以WrapperMappingInfo是一个list。
简单来说就是将映射url、wrapper名字和资源只读标记等信息组合成对象添加到wrappers中。
private void prepareWrapperMappingInfo(Context context, Wrapper wrapper, List wrappers) {
String wrapperName = wrapper.getName();
boolean resourceOnly = context.isResourceOnlyServlet(wrapperName);
String[] mappings = wrapper.findMappings();
for (String mapping : mappings) {
boolean jspWildCard = (wrapperName.equals("jsp") && mapping.endsWith("/*"));
wrappers.add(new WrapperMappingInfo(mapping, wrapper, jspWildCard, resourceOnly));
}
}
上面的分析中提到了 Engine、Host、Context、Wrapper 和 Connector。除了 Connector,其余的都是 Container 接口的实现类。它们的父类是 ContainerBase,ContainerBase 继承自 LifecycleMBeanBase,因此 Container 也有生命周期方法。
每一个 Container 都可能有子Container,其中,Engine的子Container是Host,Host的子 Container是Context,Context 的子Container是Wrapper,这里说的父子关系,不是指类继承关系,而是说一个 Container 内部有一个 Map,这个 Map 保存了其他类型的 Container。
Container是能够执行客户端请求并且给出响应的对象,Tomcat便是用这些 Container 对象逐层地处理请求的。