前言
上篇文章中分析了 Server 类的 init 和 start 方法,其中最核心的内容就是调用了 StandardServer 类的的 Service 类型的成员的 init 和 start 方法。Service 是 Tomcat 的核心组件之一。Service 的实现类是 StandardService。StandardService 和 StandardServer 一样也是继承自 LifecycleMBeanBase。
1 StandardService#initInternal 方法
首先看 initInternal 方法
/**
* Invoke a pre-startup initialization. This is used to allow connectors
* to bind to restricted ports under Unix operating environments.
*/
@Override
protected void initInternal() throws LifecycleException {
super.initInternal();
if (engine != null) {
engine.init();
}
// Initialize any Executors
for (Executor executor : findExecutors()) {
if (executor instanceof JmxEnabled) {
((JmxEnabled) executor).setDomain(getDomain());
}
executor.init();
}
// Initialize mapper listener
mapperListener.init();
// Initialize our defined Connectors
synchronized (connectorsLock) {
for (Connector connector : connectors) {
connector.init();
}
}
}
/**
* Retrieves all executors
* @return Executor[]
*/
@Override
public Executor[] findExecutors() {
synchronized (executors) {
Executor[] arr = new Executor[executors.size()];
executors.toArray(arr);
return arr;
}
}
initInternal 代码很短,思路也很清晰,就是依次调用了这个成员变量的 init 方法
engine.init()
executor.init
mapperListener.init()
connector.init()
先看看这几个成员变量的定义
private Engine engine = null;
/**
* The list of executors held by the service.
*/
protected final ArrayList<Executor> executors = new ArrayList<>();
/**
* Mapper listener.
*/
protected final MapperListener mapperListener = new MapperListener(this);
/**
* The set of Connectors associated with this Service.
*/
protected Connector connectors[] = new Connector[0];
上面四个成员变量,除了 mapperListener,都是在 Catalina 解析 server.xml 文件是根据配置文件的配置初始化的。
其中 engine 和 connector 是也是 Tomcat 核心组件之二,会分别单独解析,这里就先略过。
executor 的实现类是,StandardThreadExecutor,也是继承自 LifecycleMBeanBase,它的作用跟线程池类似。
mapperListener 的作用是在 start 的时候将容器类对象注册到 Mapper 对象中。
/**
* Create mapper listener.
*
* @param service The service this listener is associated with
*/
public MapperListener(Service service) {
this.service = service;
this.mapper = service.getMapper();
}
service.getMapper() 返回的是 StandardService 对象的 mapper 成员变量。
/**
* Mapper.
*/
protected final Mapper mapper = new Mapper();
Mapper是 Tomcat 处理 Http 请求时非常重要的组件。Tomcat 使用 Mapper 来处理一个 Request 到 Host、Context 的映射关系,从而决定使用哪个 Service 来处理请求。
MapperListener 也是继承自 LifecycleMBeanBase,不过没有重载 initInternal 方法。
2 StandardService#startInternal 方法
/**
* Start nested components ({@link Executor}s, {@link Connector}s and
* {@link Container}s) and implement the requirements of
* {@link org.apache.catalina.util.LifecycleBase#startInternal()}.
*
* @exception LifecycleException if this component detects a fatal error
* that prevents this component from being used
*/
@Override
protected void startInternal() throws LifecycleException {
if(log.isInfoEnabled())
log.info(sm.getString("standardService.start.name", this.name));
setState(LifecycleState.STARTING);
// Start our defined Container first
if (engine != null) {
synchronized (engine) {
engine.start();
}
}
synchronized (executors) {
for (Executor executor: executors) {
executor.start();
}
}
mapperListener.start();
// Start our defined Connectors second
synchronized (connectorsLock) {
for (Connector connector: connectors) {
// If it has already failed, don't try and start it
if (connector.getState() != LifecycleState.FAILED) {
connector.start();
}
}
}
}
startInternal 跟 initInternal 方法一样,也是依次调用
engine.start();
executor.start();
mapperListener.start();
connector.start();
同样地,engine 和 connector 的 start 方法会分别单独讲解,这里先略过。
executor 的 start 方法初始化了内部的线程池。
2.1 MapperListener#startInternal 方法
MapperListener 的 startInternal 方法
@Override
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())) {
// Registering the host will register the context and wrappers
registerHost(host);
}
}
}
2.1.1 findDefaultHost() 方法
首先看 findDefaultHost() 方法
private void findDefaultHost() {
Engine engine = service.getContainer();
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));
}
}
findDefaultHost() 是主要是找出 defaultHost ,并调用
mapper.setDefaultHostName(defaultHost);
这个 defaultHost 是 server.xml 的 <Engine> 标签的属性,一般都是 "localHost"。
从上面代码 for 代码块里可以看出,Host 是 Engine 的子 Container。for 语句就是找出一个名字跟 defaultHost 指定的名字相同的 Host 对象。
2.1.2 addListeners(engine) 方法
/**
* Add this mapper to the container and all child containers
*
* @param container
*/
private void addListeners(Container container) {
container.addContainerListener(this);
container.addLifecycleListener(this);
for (Container child : container.findChildren()) {
addListeners(child);
}
}
这个方法的作用是,将 MapperListener 这个监听器添加到 Engine 及其子容器中
2.1.3 for循环代码块
for循环代码块的作用就是,调用 registerHost方法来注册 Engine 的字容器 Host。
/**
* Register host.
*/
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);
}
}
// Default host may have changed
findDefaultHost();
if(log.isDebugEnabled()) {
log.debug(sm.getString("mapperListener.registerHost",
host.getName(), domain, service));
}
}
registerHost 方法先调用 mapper.addHost,然后调用 registerContext 方法注册 Host 的子容器 Context。
mapper.addHost 方法是将 Host 加入的 Mapper 类的的成员变量
MappedHost[] hosts 中。
接着看 registerContext 方法
/**
* Register context.
*/
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<WrapperMappingInfo> 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));
}
}
registerContext 里先获取一些对象,比如 WebResourceRoot 对象、WrapperMappingInfo 对象,然后调用 mapper.addContextVersion。
Mapper#addContextVersion 方法比较琐细,就不细讲了。
其主要逻辑是将 Context 对象,以及 Context 的子容器 Wrapper 对象,每一个都分别构建一个对应的 MappedContext 和 MappedWrapper 对象,
然后把 MappedContext 和 MappedWrapper 塞进 ContextVersion 对象中,
最后把 Context 和 ContextVersion 的对应关系放在 Mapper 对象的一个 Map 里。
这里的 MappedContext 和 MappedWrapper 在 Tomcat 处理 Http 请求的时候是比较关键的。
registerHost 最后再更新了一下可能发生改变里的的 defaultHost
。
3 engine、host、context、wrapper 和 connector
上面的分析中提到了 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 对象逐层地处理请求的。关于 Container 是如何处理请求的,这里就不细讲了,留待后面的文章分析。
Connector 也继承了 LifecycleMBeanBase。Connector 是 Tomcat 的核心组件。他的主要作用是处理 Http 连接,并转发请求给 Container 处理的。
小结
本篇文章介绍了 StandardService 的 init 和 start 方法,可以看出,StandardService 的 init、start 方法中调用 Engine、Connector、MapperListener 的 init、start 方法,通过这些对象的对应方法来完成 Service 的启动。
在本篇文章中,简单介绍了一下 Engine、Host、Context、Wrapper 和 Connector,这些都是 Tomcat 的核心组件。后面的文章将分别进入这些组件。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。