Tomcat组件梳理—Service组件
1.组件定义
Tomcat中只有一个Server,一个Server可以用多个Service,一个Service可以有多个Connector和一个Container。
Server掌握着整个Tomcat的生死大权。
Service是对外提供服务的。一个Server可以有多个Service,只不过Cataina中只添加了一个,这一个就代表了Tomcat的所有服务。
Connector用于接收请求并将请求封装成Request和Response来具体处理
Container用于封装和管理Servlet,以及具体处理reqeust请求
如上图,一个Service包含多个Connector和一个Engine,两者的关联关系使用Mapper来做映射,还有一个可选的线程池Executor。
2.属性
先把Service的属性代码摆出来:
/** * service的名称 */ private String name = null; /** * Service所属的Server */ private Server server = null; /** * 组件对属性改变的支持 */ protected final PropertyChangeSupport support = new PropertyChangeSupport(this); /** * 跟这个Service相关联的Connector集合 */ protected Connector connectors[] = new Connector[0]; /** * Connector的锁 */ private final Object connectorsLock = new Object(); /** * 线程池 */ protected final ArrayListexecutors = new ArrayList<>(); /** * Servlet的引擎 */ private Engine engine = null; /** * 类加载器 */ private ClassLoader parentClassLoader = null; /** * Mapper. */ protected final Mapper mapper = new Mapper(); /** * Mapper 监听器 */ protected final MapperListener mapperListener = new MapperListener(this);
解释一下这里面关键的几个点:
- Connector connectors[]:多个连接器,一个Servlet服务接受两个不同的协议连接,只不过不同的协议通过对应的Connector都被处理成了一个Request对象,这样对于Engine来说,都是一样的请求。
- Engine engine:Servlet引擎,就是专门用来处理请求的,其他的都不管。
Mapper mapper:mapper保存了一个映射关系,不同请求路径对应哪一个Servlet的API。
PropertyChangeSupport support:JDK自带的观察者模式,主要是观察Java Bean对象的属性更改的,等会拿出来单独说。
在Service属性中,主要就是这四个东西,主要的架构关系,在上面的图中有解析,就不再多介绍。
3.动作
Service的方法比较简单,因为只是包装,自己没有太多的一个功能,所有主要功能有:1.监听Service属性变化,2.启动,3.关闭。没了,就这三个,其他的都是对属性的setter和getter的具体实现,就不管了。
3.1.启动
在Server组件的分析中,我们已经知道了Server会调用Service的init()方法和start()方法来完成启动操作,那我们分别来看一下Service组件的init()和start()。
首先是init()方法:
protected void initInternal() throws LifecycleException { //1.父类执行init super.initInternal(); //2.执行servlet的引擎engine的init if (engine != null) { engine.init(); } // 3.执行Executors的init for (Executor executor : findExecutors()) { if (executor instanceof JmxEnabled) { ((JmxEnabled) executor).setDomain(getDomain()); } executor.init(); } // 4.mapper监听器的init mapperListener.init(); // 5.connect的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); } } } }}
好像没有太多可说的,自己没有做啥事情,就是调用自己的子组件的init()方法。主要包括Engine,Executor,Mapper,Connector这四个。
再看一个start()方法:
protected void startInternal() throws LifecycleException { //1.设置生命周期的状态 setState(LifecycleState.STARTING); //2.执行engine的start if (engine != null) { synchronized (engine) { engine.start(); } } //3.执行executor的start synchronized (executors) { for (Executor executor: executors) { executor.start(); } } //4.执行mapper监听器的start mapperListener.start(); // 5.执行connect的start synchronized (connectorsLock) { for (Connector connector: connectors) { try { // If it has already failed, don't try and start it if (connector.getState() != LifecycleState.FAILED) { connector.start(); } } catch (Exception e) { log.error(sm.getString( "standardService.connector.startFailed", connector), e); } } }}
同init()方法一样,没啥好说的。
3.2.关闭
Service的关闭操作主要调用两个方法,分别是stop(),destroy()。这两个代码里的业务逻辑跟启动里面的是一样的,就是去调用子组件里对应的方法。没啥好说的,不过还是把代码放一下看看。
首先是stop()方法
protected void stopInternal() throws LifecycleException { //先暂停或者关闭Connector synchronized (connectorsLock) { for (Connector connector: connectors) { try { //暂停 connector.pause(); } catch (Exception e) { log.error(sm.getString( "standardService.connector.pauseFailed", connector), e); } //如果有绑定socket,就关闭掉 connector.getProtocolHandler().closeServerSocketGraceful(); } } setState(LifecycleState.STOPPING); // 调用engine的stop()方法 if (engine != null) { synchronized (engine) { engine.stop(); } } // 调用Connector的stop()方法 synchronized (connectorsLock) { for (Connector connector: connectors) { if (!LifecycleState.STARTED.equals( connector.getState())) { // Connectors only need stopping if they are currently // started. They may have failed to start or may have been // stopped (e.g. via a JMX call) continue; } try { connector.stop(); } catch (Exception e) { log.error(sm.getString( "standardService.connector.stopFailed", connector), e); } } } // 调用mapperListener的stop()方法 if (mapperListener.getState() != LifecycleState.INITIALIZED) { mapperListener.stop(); } //调用executor的stop()方法 synchronized (executors) { for (Executor executor: executors) { executor.stop(); } }}
然后是destroy()方法
protected void destroyInternal() throws LifecycleException { //1.调用mapper的destroy()方法 mapperListener.destroy(); //2.调用每个Connector的destroy()方法 synchronized (connectorsLock) { for (Connector connector : connectors) { try { connector.destroy(); } catch (Exception e) { log.error(sm.getString( "standardService.connector.destroyFailed", connector), e); } } } //3.调用Executor的destroy()的方法 for (Executor executor : findExecutors()) { executor.destroy(); } //4.调用engine的destroy()方法 if (engine != null) { engine.destroy(); } super.destroyInternal();}
如上,真的没啥好说的,就不说了。
3.3.监听Service属性变化
这里是一个比较有意思的操作,就是你怎么去监听一个java对象的属性被改变了?
我想大部分都有思路,使用监听器设计模式,是的。但是JDK已经提供好了这种使用方法,并在Tomcat里有了比较好的应用。这里只看Tomcat中是怎么用的。介绍会比较简单,详细的可以查看下面的 "参考文章"。
Tomcat中使用JDK中的PropertyChangeSupport
类来实现监听的需求,使用该类需要按照如下要求:
1.在Service中构造一个PropertyChangeSupport类,并将这个Java Bean传入。
//org.apache.catalina.core.StandardService/** * 属性改变的监听管理 */protected final PropertyChangeSupport support = new PropertyChangeSupport(this);
2.需要在Service中添加对应的添加监听器方法和删除监听器方法,对应如下:
/** * Add a property change listener to this component. * @param listener The listener to add */public void addPropertyChangeListener(PropertyChangeListener listener) { support.addPropertyChangeListener(listener);}/** * Remove a property change listener from this component. * @param listener The listener to remove */public void removePropertyChangeListener(PropertyChangeListener listener) { support.removePropertyChangeListener(listener);}
3.最后,如果需要实现自己的监听器,只需要实现void propertyChange(PropertyChangeEvent evt);
方法即可。
public interface PropertyChangeListener extends java.util.EventListener { /** * This method gets called when a bound property is changed. * @param evt A PropertyChangeEvent object describing the event source * and the property that has changed. */ void propertyChange(PropertyChangeEvent evt);}
这样,就可以来完成对Service属性的监听了。非常好的方法,之前从来不知道。
5.总结
Service的动作不多,主要是对Connector和Engine的包装成一个组件,方便统一管理和映射。但是Service里面有一个监听Java Bean属性变化的使用还是挺有意思的。具体可以参考下面的文章
参考文章:
- 《Java架构师必读源码之Tomcat8》https://www.jianshu.com/p/2ca506449b90
- 《PropertyChangeSupport类》https://blog.csdn.net/todebug/article/details/1776324