博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Tomcat中的服务器组件和 服务组件
阅读量:4974 次
发布时间:2019-06-12

本文共 28791 字,大约阅读时间需要 95 分钟。

  开始学习Tocmat时,都是学习如何通过实例化一个连接器 和 容器 来获得一个Servlet容器,并将连接器  和 servlet容器相互关联,但是之前学习的都只有一个连接器可以使用,该连接器服务8080端口上的HTTP请求,无法添加另一个连接器来服务 诸如 HTTPS之类的其他请求,而且前面所有学的示例,都缺少一种启动或者 关闭servlet容器的机制,那么下面学习一下提供这两种机制的特性的组件,分别是服务器组件 和 服务组件。

  服务器组件.

  org.apahce.catalina.Server接口的实例 表示Catalina的整个Servlet引擎,囊括了所有的组件,服务器组件是非常有用的,因为它使用了一种方法很容易的就启动/关闭整个系统,不需要对连接器 和 servlet容器 分别操作。

下面来说明一下启动/关闭机制的具体工作原理,当启动服务器组件是,它会启动它囊括的所有组件,然后它就无期限的等待关闭命令,如果想要关闭系统,可以向指定的端口发送一条关闭命令,服务器组件接收到关闭命令之后,就会关闭其中所有的组件。

  服务器组件使用了另一个组件(即服务组件)来包含其他组件,如一个容器组件 和 一个或者多个连接器组件,

下面先来看一下服务器组件的接口定义

1 package org.apache.catalina;  2   3 import org.apache.catalina.deploy.NamingResources;  4   5 /**  6  *   7  * 

8 * Title:Server.java 9 *

10 *

11 * Copyright:ChenDong 2018 12 *

13 *

14 * Company:仅学习时使用 15 *

16 *

17 * 类功能描述:Server元素表示整个Catalina 18 * servlet容器。它的属性代表了servlet容器的整体特性。服务器可以包含一个或多个服务以及顶级命名资源集。 19 * 20 * 21 * 22 * 通常,该接口的实现还将实现生命周期,这样当调用start()和stop()方法时,所有定义的服务也会启动或停止。 23 * 24 * 25 * 26 * 在这两者之间,实现必须打开端口属性指定的端口号上的服务器套接字。当接受连接时,读取第一行并与指定的关闭命令进行比较。如果命令匹配,则启动服务器关闭。 27 *

28 * 29 * @author 30 * @date 2018年12月17日 下午8:04:51 31 * @version 1.0 32 */ 33 34 public interface Server { 35 36 // ------------------------------------------------------------- Properties 37 38 /** 39 * 40 * 41 *

42 * Title: getInfo 43 *

44 * 45 * @date 2018年12月17日 下午8:05:15 46 * 47 *

48 * 功能描述:返回该类的实现信息 49 *

50 * 51 * @return 52 */ 53 public String getInfo(); 54 55 /** 56 * 返回全局命名资源。 57 */ 58 public NamingResources getGlobalNamingResources(); 59 60 /** 61 * 62 * 设置全局命名资源 63 * 64 * @param namingResources 65 * 新的全局命名资源 66 */ 67 public void setGlobalNamingResources(NamingResources globalNamingResources); 68 69 /** 70 * 71 * 72 *

73 * Title: getPort 74 *

75 * 76 * @date 2018年12月17日 下午8:06:42 77 * 78 *

79 * 功能描述:返回我们用来监听关闭命令的端口号 80 *

81 * 82 * @return 83 */ 84 public int getPort(); 85 86 /** 87 * 88 * 89 *

90 * Title: setPort 91 *

92 * 93 * @date 2018年12月17日 下午8:07:27 94 * 95 *

96 * 功能描述:设置我们用来监听关闭命令的端口号 97 *

98 * 99 * @param port100 * 新的监听端口号101 */102 public void setPort(int port);103 104 /**105 * 返回我们正在等待的关闭命令字符串。106 * 107 * 108 */109 public String getShutdown();110 111 /**112 * 设置我们正在等待的关闭命令字符串113 *114 * @param shutdown115 * 新的关闭命令字符串116 */117 public void setShutdown(String shutdown);118 119 // --------------------------------------------------------- Public Methods120 121 /**122 * 向定义的服务集添加新服务。123 *124 * @param service125 * 要被添加的新服务126 */127 public void addService(Service service);128 129 /**130 * 等待直到收到正确的关闭命令,然后返回。131 */132 public void await();133 134 /**135 * 136 * 137 *

138 * Title: findService139 *

140 * 141 * @date 2018年12月17日 下午8:10:42142 * 143 *

144 * 功能描述:返回指定的服务(如果存在);否则返回null145 *

146 * 147 * @param name148 * @return149 */150 public Service findService(String name);151 152 /**153 * 154 * 155 *

156 * Title: findServices157 *

158 * 159 * @date 2018年12月17日 下午8:11:18160 * 161 *

162 * 功能描述:返回在此服务器中定义的服务集。163 *

164 * 165 * @return166 */167 public Service[] findServices();168 169 /**170 * 171 * 172 *

173 * Title: removeService174 *

175 * 176 * @date 2018年12月17日 下午8:12:02177 * 178 *

179 * 功能描述:从该服务器 定义的服务集中删除指定的服务180 *

181 * 182 * @param service183 */184 public void removeService(Service service);185 186 /**187 * 188 * 189 *

190 * Title: initialize191 *

192 * 193 * @date 2018年12月17日 下午8:12:58194 * 195 *

196 * 功能描述:调用启动前的初始化。这用于允许连接器绑定到Unix操作环境中的受限端口。当然只要是在系统执行前 你爱写啥写啥197 *

198 * 199 * @throws LifecycleException200 */201 public void initialize() throws LifecycleException;202 }

  shutdown属性保存了必须发送给Server实例用来关闭整个系统的关闭命令,port属性定义了服务器组件会从哪一个端口获取关闭命令,可以调用其addService方法为服务器组件添加服务组件,或者通过removeService方法来删除某个服务组件,findService方法返回添加到此服务器组件中的服务集合,initialize方法包含了在系统启动之前要执行的一些代码

  在Catalina中 server同样有其标准实现类,StandardServer

StandardServer类

  org.apahce.catalina.core.StandardServer类是Server接口的标准实现,为什么要说下这个类,主要是对其中的关闭机制 也就上文提到的一个关闭所有组件的机制感兴趣,而这也是这个类最重要的特性,该类中的许多方法都与新server.xml文件中的服务器配置的存储相关,但这些并不是下面要说的重点,

  一个服务器组件可以有0个或者多个服务组件,StandardServer类提供了addService方法、removeService方法、和findService方法的实现。

  StandardServer类有四个与生命周期相关的方法,分别是initialize方法、start方法、stop方法,就像其他组件一样,可以初始化并且启动服务器组件。也可以调用 await方法 和 stop方法,调用await方法会一直阻塞住,直到它从定义 的 8005;端口(也可以自己配置其他端口)上接收到关闭命令,当await方法返回的时候,会运行stop方法来关闭其下的所有服务组件。

下面会分别讨论上面的 四个与生命周期相关的方法。

initialize方法

  方法主要是用于初始化添加到StandardServer的服务组件,

1 /** 2      * 调用启动前的初始化。 3      */ 4     public void initialize() throws LifecycleException { 5         if (initialized) 6             throw new LifecycleException(sm.getString("standardServer.initialize.initialized")); 7         initialized = true; 8  9         // 初始化我们定义的服务集中的每一个服务10         for (int i = 0; i < services.length; i++) {11             services[i].initialize();12         }13     }

start方法

  start方法用于启动服务器组件,在StandardServer类的start方法的实现中,它会启动器所有的服务组件,逐个启动所有的组件,如连接器组件 和 Servlet容器,

1 /** 2      * 该服务器组件的启动方法,它会启动器所有的服务组件,逐个启动所有的组件, 3      */ 4  5     public void start() throws LifecycleException { 6  7         // 验证和更新当前组件状态 如果已经启动直接报错 8         if (started) 9             throw new LifecycleException(sm.getString("standardServer.start.started"));10         // 向监听器发送 BEFORE_START_EVENT事件 与 START_EVENT事件11         lifecycle.fireLifecycleEvent(BEFORE_START_EVENT, null);12 13         lifecycle.fireLifecycleEvent(START_EVENT, null);14         started = true;15 16         // 逐个启动当前定义的服务集合中的每一个服务17         synchronized (services) {18             for (int i = 0; i < services.length; i++) {19                 if (services[i] instanceof Lifecycle)20                     ((Lifecycle) services[i]).start();21             }22         }23 24         // 向监听器发送 AFTER_START_EVENT事件25         lifecycle.fireLifecycleEvent(AFTER_START_EVENT, null);26 27     }

StandardServer为了防止服务器组件重复启动,在start中设置为true 在stop方法中 重置为false。

stop方法

  stop方法用于关闭服务器组件,在方法中会关闭服务器中的额所有组件

1     /** 2      * 优雅地终止该组件的公共方法的主动使用。此方法应该是调用该组件的给定实例的最后一个方法。 3      * 它还应该向任何注册的监听器发送STOP_EVENT类型的停止事 4      */ 5     public void stop() throws LifecycleException { 6  7         // 如果还没有启动则 直接抛出错误 8         if (!started) 9             throw new LifecycleException(sm.getString("standardServer.stop.notStarted"));10 11         // 向监听器发送监听事件12         lifecycle.fireLifecycleEvent(BEFORE_STOP_EVENT, null);13 14         lifecycle.fireLifecycleEvent(STOP_EVENT, null);15         // 将started标志 置为false 这样才可以再次启动16         started = false;17 18         // 逐个停止我们定义服务集中的每一个服务19         for (int i = 0; i < services.length; i++) {20             if (services[i] instanceof Lifecycle)21                 ((Lifecycle) services[i]).stop();22         }23 24         // 发送监听事件25         lifecycle.fireLifecycleEvent(AFTER_STOP_EVENT, null);26 27     }

调用stop方法hi关闭所有的服务组件并重置布尔变量started,这样才可以再次启动服务器组件。

await方法

  await方法负责等待关闭整个Tomcat部署的命令。

 

1     /** 2      * 等待直到收到正确的关闭命令,然后返回。 3      */ 4     public void await() { 5  6         // 设置要等待的服务器套接字 7         ServerSocket serverSocket = null; 8         try { 9             // 利用咱们自己定义的port(默认 8005 )端口号来 初始化服务器套接字10             serverSocket = new ServerSocket(port, 1, InetAddress.getByName("127.0.0.1"));11         } catch (IOException e) {12             System.err.println("StandardServer.await: create[" + port + "]: " + e);13             e.printStackTrace();14             System.exit(1);15         }16 17         // 循环等待连接和有效命令18         while (true) {19 20             // 等待下一个连接21             Socket socket = null;22             InputStream stream = null;23             try {24                 socket = serverSocket.accept();//25                 socket.setSoTimeout(10 * 1000); // 接收到Socket之后 read方法仅在十秒钟之内有效26                                                 // 超时 将会抛出27                                                 // java.net.SocketTimeoutException28                 stream = socket.getInputStream();29             } catch (AccessControlException ace) {30                 System.err.println("StandardServer.accept security exception: " + ace.getMessage());31                 continue;32             } catch (IOException e) {33                 System.err.println("StandardServer.await: accept: " + e);34                 e.printStackTrace();35                 System.exit(1);36             }37 38             // 从套接字中读取一组字符39             StringBuffer command = new StringBuffer();40             int expected = 1024; // 切断以避免DoS攻击41             while (expected < shutdown.length()) {42                 if (random == null)43                     random = new Random(System.currentTimeMillis());44                 expected += (random.nextInt() % 1024);45             }46             while (expected > 0) {47                 int ch = -1;48                 try {49                     ch = stream.read();50                 } catch (IOException e) {51                     System.err.println("StandardServer.await: read: " + e);52                     e.printStackTrace();53                     ch = -1;54                 }55                 if (ch < 32) // 控制字符或EOF终止循环56                     break;57                 command.append((char) ch);58                 expected--;59             }60 61             // 既然我们用完了,就把socket关上。62             try {63                 socket.close();64             } catch (IOException e) {65                 ;66             }67 68             // 匹配命令字符串69             boolean match = command.toString().equals(shutdown);70             if (match) {71                 //匹配到就 跳出循环72                 break;73             } else74                 System.err.println("StandardServer.await: Invalid command '" + command.toString() + "' received");75 76         }77 78         //关闭服务器套接字并返回79         try {80             serverSocket.close();81         } catch (IOException e) {82             ;83         }84 85     }

   await方法创建一个ServerSocket对象,监听8005端口,并在while循环中调用它的accpect方法挡在指定端口上接收到消息时,才会从accept方法中返回一个socket,然后将接收到的消息与关闭命令字符串做比较,相同的话就跳出循环,关闭ServerSocke,否则再次循环。接续等待消息。

Service

  服务组件是org.apche.catalina.Service接口的实例,一个服务组件可以有一个servlet容器和 一个或者多个连接器实例,可以自由的把连接器实例添加到服务组件中,所有的连接器都会与这个servlet容器相关联,

package org.apache.catalina;/** *  * 

* Title:Service.java *

*

* Copyright:ChenDong 2018 *

*

* Company:仅学习时使用 *

* * *

* 服务组件是org.apache.catalina.Service接口的实例,一个服务组件可以有一个servlet容器和 * 多个连接器实例,可以自由的把连接器实例添加到服务组件中的,所有的 连接器都会与这个servlet容器相关联 *

* * @author * @date 2018年12月17日 下午8:02:49 * @version 1.0 */public interface Service { // ------------------------------------------------------------- Properties /** * * *

* Title: getContainer *

* * @date 2018年12月19日 下午7:47:23 * *

* 功能描述: 返回处理与此服务关联的所有Connectors 请求的servlet容器。 *

* * @return */ public Container getContainer(); /** * * *

* Title: setContainer *

* * @date 2018年12月19日 下午7:48:10 * *

* 功能描述:设置处理此服务中所有连接器的servlet容器 *

* * @param container * 被设置的servlet容器 */ public void setContainer(Container container); /** * * *

* Title: getInfo *

* * @date 2018年12月19日 下午7:49:07 * *

* 功能描述:返回该类的实现信息 *

* * @return */ public String getInfo(); /** * 返回此服务的名称. */ public String getName(); /** * 设置此服务的名称 * * @param name * 新的服务名 */ public void setName(String name); /** * Return the Server with which we are associated (if any). */ /** * * *

* Title: getServer *

* * @date 2018年12月19日 下午7:51:23 * *

* 功能描述:返回与该服务关联的服务器组件 *

* * @return */ public Server getServer(); /** * * *

* Title: setServer *

* * @date 2018年12月19日 下午7:52:01 * *

* 功能描述: 设置拥有该服务的服务器组件 *

* * @param server * 与该服务关联的服务器组件 */ public void setServer(Server server); // --------------------------------------------------------- Public Methods /** * * *

* Title: addConnector *

* * @date 2018年12月19日 下午7:53:50 * *

* 功能描述:向当前定义的 连接器集合 中添加新的 连接器,并将其与该服务中唯一的一个Servlet容器相关联 *

* * @param connector * 添加的连接器 */ public void addConnector(Connector connector); /** * * *

* Title: findConnectors *

* * @date 2018年12月19日 下午7:57:20 * *

* 功能描述:查找并返回该服务中定义的连连接器集合 *

* * @return */ public Connector[] findConnectors(); /** * Remove the specified Connector from the set associated from this Service. * The removed Connector will also be disassociated from our Container. * * @param connector * The Connector to be removed */ /** * * *

* Title: removeConnector *

* * @date 2018年12月19日 下午8:00:09 * *

* 功能描述:从此服务中的连接器集合中移除指定的连接器,并将其与服务中唯一的一个servlet容器分离 *

* * @param connector */ public void removeConnector(Connector connector); /** * * *

* Title: initialize *

* * @date 2018年12月19日 下午8:01:45 * *

* 功能描述:初始化方法 *

* * @throws LifecycleException */ public void initialize() throws LifecycleException;}

在Catalina中,StandardService类是其标准实现

StandardSetvice

  org.apahce.catalina.core.StandardService类是 服务组件Service接口的标准实现,StandardService类的initialize方法用于初始化添加到其中所有的连接器,此外,StandardService类还实现了Service以及org.apache.catalina.Lifecycle声明周期接口,然后,它的start方法也可以启动连接器和所有的servlet容器。

  connector 和 container

StandardService类有两种组件,分别是连接器和 servlet容器,其中servlet容器只能有一个,而连接器可以有多个,多个连接器使Tomcat可以为多种不同的请求协议提供服务,例如,一个连接器处理HTTP请求,而另外一个连接器处理HTTPS请求。

StandardService类使用变量Container 来指向一个Container接口的实例,使用数组connectors来保存所有连接器的引用。

1 /**2      * 保存服务中所有连接器引用的数组3      */4     private Connector connectors[] = new Connector[0];5 6     /**7      * 保存服务中唯一的一个Servlet容器8      */9     private Container container = null;

  需要调用setContainer方法将servlet容器与服务组件相关联,

1 /** 2      *  3      * 设置处理此服务中所有连接器的servlet容器 4      *  5      * @param container 6      *            新的servlet容器 7      */ 8     public void setContainer(Container container) { 9         // 取出当前服务中唯一的servlet容器10         Container oldContainer = this.container;11         // 如果取出的servlet容器不为空 且为Engine级别的servlet容器12         if ((oldContainer != null) && (oldContainer instanceof Engine))13             // 将调用其setSevice方法 断掉与服务的关联14             ((Engine) oldContainer).setService(null);15         // 设置新的servlet容器16         this.container = container;17         // 如果新的servlet容器 不为空 且 是Engine级别的容器18         if ((this.container != null) && (this.container instanceof Engine))19             // 将该服务实例传入容器 与之关联20             ((Engine) this.container).setService(this);21         // 如果该服务已经启动 且 服务中的容器实例不为空 且 这个容器实例是一个生命周期的实现类22         if (started && (this.container != null) && (this.container instanceof Lifecycle)) {23             try {24                 // 调用其start方法25                 ((Lifecycle) this.container).start();26             } catch (LifecycleException e) {27                 ;28             }29         }30         // 逐个调用连接器集合中的每一个连接器的setContainer方法 与服务中 的容器相挂关联31         synchronized (connectors) {32             for (int i = 0; i < connectors.length; i++)33                 connectors[i].setContainer(this.container);34         }35         // 如果服务已经启动且旧的容器不为空 且是一个生命周期实现 则 调用旧容器的stop方法36         if (started && (oldContainer != null) && (oldContainer instanceof Lifecycle)) {37             try {38                 ((Lifecycle) oldContainer).stop();39             } catch (LifecycleException e) {40                 ;41             }42         }43 44         // 通知属性变更监听器45         support.firePropertyChange("container", oldContainer, this.container);46 47     }

  与服务组件相关联的servlet容器的实例将被传给每个连接器对象的setContainer方法中,这样在服务组件中就可以形成每个连接器和servlet容器之间的关联关系。

  可以调用addConnector方法将连接器添加到服组件中,调用removeConnectoe方法将某个连接器移除,

1     /** 2      *  3      *  4      *  5      * 添加一个新的连接器到连接器集合中 并且将之与服务中的servlet容器相关联 6      *  7      * @param connector 8      *            要被添加连接器 9      */10 11     public void addConnector(Connector connector) {12 13         synchronized (connectors) {14             // 添加15             connector.setContainer(this.container);16             connector.setService(this);17             Connector results[] = new Connector[connectors.length + 1];18             System.arraycopy(connectors, 0, results, 0, connectors.length);19             results[connectors.length] = connector;20             connectors = results;21 22             // 若该服务已经初始化过了23             if (initialized) {24                 try {25                     // 则初始化这个新加的连接器26                     connector.initialize();27                 } catch (LifecycleException e) {28                     e.printStackTrace(System.err);29                 }30             }31 32             // 若服务已经被启动则将新添加的连接器启动33             if (started && (connector instanceof Lifecycle)) {34                 try {35                     ((Lifecycle) connector).start();36                 } catch (LifecycleException e) {37                     ;38                 }39             }40 41             // Report this property change to interested listeners42             support.firePropertyChange("connector", null, connector);43         }44 45     }

removeConnector

1 /** 2      * 从该服务组件的连接器集合中移除指定的连接器, 3      *  4      * @param connector 5      *            被指定移除的连接器 6      */ 7     public void removeConnector(Connector connector) { 8  9         synchronized (connectors) {10             // 被移除的连接器在集合中的索引位置11             int j = -1;12             for (int i = 0; i < connectors.length; i++) {13                 // 找到被移除的连接器在数组中的索引之后 赋值给j 并跳出循环14                 if (connector == connectors[i]) {15                     j = i;16                     break;17                 }18             }19             // 如果j小于0 说明 被指定要移除的连接器 并不存在于连接器数组中20             if (j < 0)21                 return;22             // 如果服务已经被启动过了,且被删除的连接器 又是一个声明周期接口的实现23             if (started && (connectors[j] instanceof Lifecycle)) {24                 try {25                     // 调用被删除连接器的stop方法26                     ((Lifecycle) connectors[j]).stop();27                 } catch (LifecycleException e) {28                     ;29                 }30             }31             // 并向被被删除的连接器setContainer方法中传入null,清除与该服务中唯一的servlet容器的关联关系32             connectors[j].setContainer(null);33             // 去掉与该服务的关联关系34             connector.setService(null);35             int k = 0;36             // 重新组合新的数组 去掉 被删除的连接器37             Connector results[] = new Connector[connectors.length - 1];38             for (int i = 0; i < connectors.length; i++) {39                 if (i != j)40                     results[k++] = connectors[i];41             }42             connectors = results;43 44             // 并触发属性改变监听器45             support.firePropertyChange("connector", connector, null);46         }47 48     }

 与生命周期有关的方法

  与生命周期有关的方法包括从Lifecycle接口中实现的start和stop方法,在加上initialize方法,其中initialize方法会调用该服务组件中所有连接器的initialize方法,

1 /** 2      * 该服务的初始化方法 将会调用该服务组件中连接器集合中的每一个连接器的初始化方法 3      */ 4     public void initialize() throws LifecycleException { 5         if (initialized) 6             throw new LifecycleException(sm.getString("standardService.initialize.initialized")); 7         initialized = true; 8  9         //逐个调用连接器集合中的每个连接器的初始化方法10         synchronized (connectors) {11             for (int i = 0; i < connectors.length; i++) {12                 connectors[i].initialize();13             }14         }15     }

start方法负责启动被添加到该服务组件中的连接器和 servlet容器,

1 /** 2      * 该服务组件的声明周期接口 开始方法的实现,其内部会将调用该 服务中的所有连接器和 servlet容器的start方法 并通知相关监听事件 3      */ 4     public void start() throws LifecycleException { 5  6         // 这个组件是否已经被启动过了 7         if (started) { 8             throw new LifecycleException(sm.getString("standardService.start.started")); 9         }10 11         // 向声明周期监听器发送 BEFORE_START_EVENT事件12         lifecycle.fireLifecycleEvent(BEFORE_START_EVENT, null);13 14         System.out.println(sm.getString("standardService.start.name", this.name));15         // 向生命周期监听器 发送START_EVENT事件16         lifecycle.fireLifecycleEvent(START_EVENT, null);17         // 将启动标志置为 true 表示已经启动18         started = true;19 20         // 首先启动服务组件中定义的唯一的一个servlet容器21         if (container != null) {22             synchronized (container) {23                 if (container instanceof Lifecycle) {24                     ((Lifecycle) container).start();25                 }26             }27         }28 29         // 第二步 启动我们定义的连接器集合中的每一个连接器30         synchronized (connectors) {31             for (int i = 0; i < connectors.length; i++) {32                 if (connectors[i] instanceof Lifecycle)33                     ((Lifecycle) connectors[i]).start();34             }35         }36 37         // 向生命周期监听器发送AFTER_START_EVENT事件38         lifecycle.fireLifecycleEvent(AFTER_START_EVENT, null);39 40     }

stop方法 用于关闭与该服务组件相关联的servlet容器和所有的连接器,

/**     * 生命周期接口的停止方法的实现,会将该服务中唯一一个 servlet容器 和 被添加到该服务组件中的连接器 关闭     */    public void stop() throws LifecycleException {        // 验证状态        if (!started) {            throw new LifecycleException(sm.getString("standardService.stop.notStarted"));        }        // 向生命周期监听器发送 BEFORE_STOP_EVENT事件        lifecycle.fireLifecycleEvent(BEFORE_STOP_EVENT, null);        // 向生命周期监听器STOP_EVENT 事件        lifecycle.fireLifecycleEvent(STOP_EVENT, null);        System.out.println(sm.getString("standardService.stop.name", this.name));        // 将启动标志置为 false 这样才可以继续启动        started = false;        // 第一步 停止服务组件中的所有连接器        synchronized (connectors) {            for (int i = 0; i < connectors.length; i++) {                if (connectors[i] instanceof Lifecycle)                    ((Lifecycle) connectors[i]).stop();            }        }        // 第二步 停止服务组件中唯一一个servlet容器        if (container != null) {            synchronized (container) {                if (container instanceof Lifecycle) {                    ((Lifecycle) container).stop();                }            }        }        // 向声明周期监听器 发送 AFTER_STOP_EVENT事件        lifecycle.fireLifecycleEvent(AFTER_STOP_EVENT, null);    }

那上面就大概的将 Tomcat中的服务和 服务器组件都大致的展示了一下,下面我们来写一个简单的应用程序来实际操作一下。

下面的 应用程序重在说明如何使用服务器组件 和 服务组件,特别是在StandardServer中如何利用启动 和 关闭机制。

1 package myex14.pyrmont.startup;  2   3 import org.apache.catalina.Connector;  4 import org.apache.catalina.Context;  5 import org.apache.catalina.Engine;  6 import org.apache.catalina.Host;  7 import org.apache.catalina.Lifecycle;  8 import org.apache.catalina.LifecycleListener;  9 import org.apache.catalina.Loader; 10 import org.apache.catalina.Server; 11 import org.apache.catalina.Service; 12 import org.apache.catalina.Wrapper; 13 import org.apache.catalina.connector.http.HttpConnector; 14 import org.apache.catalina.core.StandardContext; 15 import org.apache.catalina.core.StandardEngine; 16 import org.apache.catalina.core.StandardHost; 17 import org.apache.catalina.core.StandardServer; 18 import org.apache.catalina.core.StandardService; 19 import org.apache.catalina.core.StandardWrapper; 20 import org.apache.catalina.loader.WebappLoader; 21  22 import ex14.pyrmont.core.SimpleContextConfig; 23  24 /** 25  * 

26 * Title:Bootstrap.java 27 *

28 *

29 * Copyright:ChenDong 2018 30 *

31 *

32 * Company:仅学习时使用 33 *

34 *

35 * 类功能描述:应用程序启动类,应用程序重在说明如何使用 服务器组件 和 服务组件,重点是 StandardServer中的开启和关闭机制 36 *

37 * 38 * @author 陈东 39 * @date 2018年12月19日 下午9:33:03 40 * @version 1.0 41 */ 42 public class Bootstrap { 43 44 /** 45 * 46 *

47 * Title: main 48 *

49 * 50 * @date 2018年12月19日 下午9:33:03 51 * 52 *

53 * 功能描述: 54 *

55 * 56 * @param args 57 * 58 */ 59 public static void main(String[] args) { 60 System.setProperty("catalina.base", System.getProperty("user.dir")); 61 // 初始化一个连接器 62 Connector connector = new HttpConnector(); 63 64 // 初始化 对应servlet的Wrapper容器 65 Wrapper wrapper1 = new StandardWrapper(); 66 wrapper1.setName("Primitive"); 67 wrapper1.setServletClass("PrimitiveServlet"); 68 69 Wrapper wrapper2 = new StandardWrapper(); 70 wrapper2.setName("Modern"); 71 wrapper2.setServletClass("ModernServlet"); 72 73 // 初始化一个Context容器 74 Context context = new StandardContext(); 75 // 设置根路径 76 context.setPath("/app1"); 77 // 设置根文件夹 78 context.setDocBase("app1"); 79 // 添加子容器 80 context.addChild(wrapper1); 81 context.addChild(wrapper2); 82 83 // 给StandardContext 创建一个 配置监听器 84 LifecycleListener listener = new SimpleContextConfig(); 85 86 ((Lifecycle) context).addLifecycleListener(listener); 87 88 // 初始化一个Host级别的容器 89 Host host = new StandardHost(); 90 host.addChild(context); 91 host.setName("localhost"); 92 host.setAppBase("webapps"); 93 94 // 初始化一个加载器 95 Loader loader = new WebappLoader(); 96 context.setLoader(loader); 97 98 // 添加servletMapping映射 99 context.addServletMapping("/Primitive", "Primitive");100 context.addServletMapping("/Modern", "Modern");101 102 // 初始化一个Engine级别的容器103 Engine engine = new StandardEngine();104 // 添加孩子host105 engine.addChild(host);106 // 根据 host的name属性设置默认的Host107 engine.setDefaultHost("localhost");108 109 // -----------------------重点来了110 // 初始化一个Service111 Service servie = new StandardService();112 servie.setName("stand-alone Service");113 // 初始化一个Server114 Server server = new StandardServer();115 server.addService(servie);116 servie.addConnector(connector);117 servie.setContainer(engine);118 119 // 启动服务器组件120 if (server instanceof Lifecycle) {121 try {122 server.initialize();123 ((Lifecycle) server).start();124 //启动监听关闭命令的端口开始监听关闭指令,进入循环等待125 //此时连接器已经处于运行状态126 //在await方法没有接到关闭命令之前 是不回复返回的 一旦接到关闭命令则 会执行下面的stop反方127 server.await();128 } catch (Exception e) {129 e.printStackTrace();130 }131 }132 133 //关闭服务器组件134 if(server instanceof Lifecycle){135 try {136 ((Lifecycle)server).stop();137 138 } catch (Exception e) {139 // TODO: handle exception140 }141 142 }143 144 }145 146 }

Stopper类

  Stopper类提供了一种 优雅的方式来关闭Catalina服务器,它保证了所有生命周期组件的stop方法都能够调用

1 package myex14.pyrmont.startup; 2  3 import java.io.IOException; 4 import java.io.OutputStream; 5 import java.net.Socket; 6  7 /** 8  * 

9 * Title:Stopper.java10 *

11 *

12 * Copyright:ChenDong 201813 *

14 *

15 * Company:仅学习时使用16 *

17 *

18 * 类功能描述:目的是向Server负责监听关闭命令的端口发送 关闭命令19 *

20 * 21 * @author 陈东22 * @date 2018年12月19日 下午10:02:3723 * @version 1.024 */25 public class Stopper {26 27 /**28 * 29 *

30 * Title: main31 *

32 * 33 * @date 2018年12月19日 下午10:02:3734 * 35 *

36 * 功能描述:37 *

38 * 39 * @param args40 * 41 */42 public static void main(String[] args) {43 int port = 8005;44 String shutdown = "SHUTDOWN";45 try {46 Socket socket = new Socket("127.0.0.1", port);47 48 OutputStream stream = socket.getOutputStream();49 50 for(int i=0;i

 当然了 这个Stopper类 只是一个简单的例子 真正部署的时候 不会像现在这样的简单 ,只要是想关闭服务器 其实就是自己 创建了一个 连接到 指定ip指定端口的 套接字实例,并且使用该套接字,直接发送 关闭命令

转载于:https://www.cnblogs.com/ChenD/p/Tomcat-Server-StandardServer.html

你可能感兴趣的文章
数组的初始化
查看>>
bzoj3156 防御准备
查看>>
Git----查看提交日志
查看>>
XE7/X10.2 Datasnap使用 dbExpress 连接MySQL数据库
查看>>
Eclipse修改编码格式
查看>>
生成器和协程 —— 你想知道的都在这里了
查看>>
初级算法-6.两个数组的交集 II
查看>>
欧拉函数 / 蒙哥马利快速幂 / 容斥
查看>>
Makefile
查看>>
软件开发文档以及项目开发流程理解
查看>>
2019微软Power BI 每月功能更新系列——Power BI 4月版本功能完整解读
查看>>
truncate 、delete、drop的区别
查看>>
DynamoDB 中的限制
查看>>
mysql做主从配置
查看>>
Docker练习例子:基于 VNCServer + noVNC 构建 Docker 桌面系统
查看>>
《码出高效 Java开发手册》第六章 数据结构与集合
查看>>
Python获取本机外网IP
查看>>
sleep和wait的区别
查看>>
[导入]弯管机3D DEMO
查看>>
关于51单片机使用printf串口调试
查看>>