Tomcat源码分析(4)—Service组件


在上一篇文章中笔者通过Bootstrap类分析了tomcat的启动流程,包括Server的初始化、启动等,而Server类中的主要方法是根据解析server.xml配置文件初始化tomcat运行所需要的组件,例如Server实例、Service、Engine容器、Connector连接器等等,接着调用组件的init()及start()方法完成tomcat服务器的启动。

什么是Server

每一个Server实例表示一个tomcat容器,而一个tomcat容器也只能有一个server实例。在类继承关系中,Server是一个接口,它的实现类是StandardServer,同时StandarServer继承了LifecycleMBeanBase抽象类,间接地实现了Lifecycle接口定义的生命周期方法,包括init()、start()、stop()等,这相当于把StandardServer的生命周期保持与其他组件启动和关闭的一致性。

server_class

在Catalina实例执行load()方法时,通过解析器Digester解析server.xml并构建生成server对象实例。

 1/**
 2* Start a new server instance.
 3*/
 4public void load() {
 5	 //省略部分代码
 6  Digester digester = createStartDigester();
 7  digester.parse(inputSource);
 8  //省略部分代码
 9  initStreams();
10  getServer().init();
11}

如果是内嵌的方式可以直接通过new StandardServer()的方式直接实例化,可参考下面代码:

 1public Server getServer() {
 2  Server server = new StandardServer();
 3  initBaseDir();
 4  server.setPort( -1 );
 5  
 6  Service service = new StandardService();
 7  service.setName("Tomcat");
 8  server.addService(service);
 9  return server;
10}

初始化

在解析完server.xml配置文件后,调用server实例的init()方法启动自身及嵌套的组件,同时更新其生命周期状态为初始化中,当初始化完成后更新为初始化完成状态。

StandardServer类通过模板设计方式继承LifecycleMBeanBase抽象类并实现了Server接口,在初始化时调用LifecycleBase的方法更改生命周期状态、触发生命周期事件等。

server_init

在tomcat组件中,每个组件会通过JMX方式注册到MBeanServer中,因此在讲解StandardServer初始化逻辑之前,需先知道Java JMX是什么。

JMX的全称为Java Management Extensions. 顾名思义,是管理Java的一种扩展。这种机制可以方便的管理正在运行中的Java程序。常用于管理线程,内存,日志Level,服务重启,系统环境等,可参考:https://www.cnblogs.com/trust-freedom/p/6842332.html。

在JMX涉及到几个概念:

  • MBean:MBean可理解为是一种资源对象。
  • MBeanServer:MBean Server是一个集中的管理MBean对象实例的地方。通过MBeanServer,你可以查询、管理、操作Agent应用发布的特定的MBean。
  • ObjectName:MBean Server通过object name来对mbean进行区分,同一个MBean类可能有多个实例对象,但是每个实例对象都有一个唯一的objectname。

在StandardServer初始化时,StandardServer实例也不例外,它会作为MBean对象注册到MBeanServer中,在StandardServer实例的initInternal()方法体中,主要执行以下逻辑:

  1. 初始化MBeanServer,并将standardServer实例注册到MBeanServer管理。
  2. 注册字符串缓存实例到MBeanServer管理。
  3. 实例化MBean工厂并注册到MBeanServer管理。
  4. 初始化命名、JNDI上下文定义的命名资源。
  5. 校验给定的jar包文件是否包含MANIFEST,若包含则添加到系统资源中。
  6. 初始化service组件。
 1@Override
 2protected void initInternal() throws LifecycleException {
 3  
 4	//初始化MBeanServer,并将当前组件注册到MBean Server
 5  super.initInternal();
 6  
 7  //注册实现字节、字符缓冲的字符串缓存实例到MBean Server
 8  onameStringCache = register(new StringCache(), "type=StringCache");
 9
10  // 实例化MBean工厂,并注册到MBean Server
11  MBeanFactory factory = new MBeanFactory();
12  factory.setContainer(this);
13  onameMBeanFactory = register(factory, "type=MBeanFactory");
14
15  // 初始化命名、JNDI上下文定义的命名资源
16  globalNamingResources.init();
17
18  //校验给定的jar包文件是否包含MANIFEST,若包含则添加到系统资源中
19  if (getCatalina() != null) {
20    ClassLoader cl = getCatalina().getParentClassLoader();
21    while (cl != null && cl != ClassLoader.getSystemClassLoader()) {
22      if (cl instanceof URLClassLoader) {
23        URL[] urls = ((URLClassLoader) cl).getURLs();
24        for (URL url : urls) {
25          if (url.getProtocol().equals("file")) {
26            try {
27              File f = new File (url.toURI());
28              if (f.isFile() &&
29                  f.getName().endsWith(".jar")) {
30                ExtensionValidator.addSystemResource(f);
31              }
32            } catch (URISyntaxException e) {
33              // Ignore
34            } catch (IOException e) {
35              // Ignore
36            }
37          }
38        }
39      }
40      cl = cl.getParent();
41    }
42  }
43  //初始化service组件
44  for (Service service : services) {
45    service.init();
46  }
47}

全局命名资源即globalNamingResources也继承了LifecycleMBeanBase抽象类,因此在初始化命名、JNDI上下文资源时,也会将自身作为MBean注册到MBeanServer管理,同时将web应用的资源、应用环境类目、web应用的资源链接ResourceLink统一注册到MBeanServer管理。

除了StandardServer之外,在其他组件初始化时也会调用LifecycleMBeanBase实例的initInternal()方法构建MBean并将自身注册到MBeanServer管理,最后可通过MBeanServer查询、管理、操作发布的MBean。

启动

当StandardServer初始化操作准备完成时,下一步就是启动server及其嵌套的组件,同样在StandardServer启动前变更生命周期状态为启动中,完成后更新为启动成功或失败,下面是StandardServer启动的时序流程图:

server_start

从流程图中看出,Catalina调用start()方法实际上是委托给LifecycleBase抽象类,而LifecycleBase再委托给StandardServer自身,通过模板方法设计模式把具体的实现交由给子类,在子类中完成生命周期状态更新、命名资源组件的启动等。

 1    @Override
 2    protected void startInternal() throws LifecycleException {
 3
 4        fireLifecycleEvent(CONFIGURE_START_EVENT, null);
 5        setState(LifecycleState.STARTING);
 6
 7        globalNamingResources.start();
 8
 9        // Start our defined Services
10        synchronized (servicesLock) {
11            for (Service service : services) {
12                service.start();
13            }
14        }
15    }

观察上面的代码,得知在调用NamingResource组件的start()方法时,完成了命名、JNDI上下文状态的变更、监听器触发等逻辑,接着遍历、启动其关联的Service组件(Service组件将在下一篇文章中阐述)。

最后总结

本文讲解了Server组件的含义及它在tomcat中扮演的角色、作用,每一个Server实例即是一个tomcat容器,而一个tomcat容器也只能有一个server实例,读者通过阅读本文能掌握tomcat在启动时server实例初始化、启动的流程与细节,更好学习tomcat的其他组件。