前两篇我们分别粗线条和细粒度的讲解了tomcat的服务是如何启动以及连接器Connector和容器Container又分别是如何被启动的.

  本篇我们主要侧重tomcat中server、service以及connector和container之间是如何相互关联起来的。在此之前,我们分别看下这个类中的一些主要方法,尤其是用于相互关联绑定的方法。

  Server:(Server代表了整个Catalina容器,一个server可以包含一个或多个Services)

 1   getInfo                    //获取server的版本
 2     getGlobalNamingResources
 3     setGlobalNamingResources
 4     getPort                    //返回监听关闭server的端口
 5     setPort
 6     getShutdown                //返回关闭server的命令字符串比如"SHUTDOWN"
 7     setShutdown
 8     addService                //在该server上添加一个service
 9     await                    //一直监听,直到出现shutdown指令
10     findService                //返回指定名称的service
11     findServices            //返回所有在这个server上的services集合
12     removeService
13     initialize

  Service:(Service是一组包含了一个Container和一个或多个Connector的集合)

 1   getContainer            //返回容器,该容器用于处理Service上的Connectors发送过来请求
 2     setContainer
 3     getInfo                    //返回Service的版本信息
 4     getName                    //返回该Service的名字
 5     setName
 6     getServer                //返回与此Service关联的Server,这与Server中的addService遥相呼应
 7     setServer                //绑定一个Server
 8     addConnector            //添加一个Connector
 9     findConnectors            //返回该Service上的所有Connector
10     removeConnector            //删除指定的Connector,同时也以为该Connector与Container也解除联系
11     initialize
12     addExecutor                //添加一个执行器
13     findExecutors
14     getExecutor
15     removeExecutor

  Connector:(前面已经说过,一个Service中可以包含多个Container,但是只会有一个Connector,而Container有多层实现关系,并且有自己的实现规范,所以定义成了接口,而这里的Connector就是一个类而非接口)

 1 Connector
 2     Connector                    //构造函数,其中有设置Connector要用到的协议
 3     getProperty                    //根据属性名,返回属性值
 4     setProperty
 5     getAttribute                //也是根据属性名,返回属性值,但是getProperty返回的是String类型,这里是Object对象
 6     setAttribute
 7     removeProperty
 8     getService                    //返回与之绑定的Service
 9     setService                    //绑定Service
10     getAllowTrace
11     setAllowTrace                //设置allowTrace,用于跟踪http的信息
12     isAvailable                    //判断是否可用于处理request,里面判断的标记是started,这意味着只有Connector启动了才能用于处理request
13     getBufferSize
14     setBufferSize
15     getContainer                //返回当前Connector移交request的接收Container对象
16     setContainer
17     getEmptySessionPath
18     setEmptySessionPath
19     getEnableLookups
20     setEnableLookups
21     getInfo                        //返回Connector的版本信息
22     getMapper
23     getMaxHeaderCount            //返回Container允许的最大headers个数
24     setMaxHeaderCount
25     getMaxParameterCount        //返回GET和POST方法的最大个数
26     setMaxParameterCount
27     ...
28     getPort                        //返回监听request的端口
29     setPort
30     getProtocol                    //返回使用到的protocol handler,有Http/1.1和AJP/1.3
31     setProtocol
32     getProtocolHandlerClassName
33     setProtocolHandlerClassName
34     getProtocolHandler
35     getProxyName                //设置代理的名字
36     setProxyName
37     getProxyPort
38     setProxyPort
39     getRedirectPort                //重定向端口,如果
40     setRedirectPort
41     getScheme
42     setScheme
43     getSecure
44     setSecure
45     getURIEncoding                //返回URI编码
46     setURIEncoding
47     getUseBodyEncodingForURI
48     setUseBodyEncodingForURI
49     getXpoweredBy
50     setXpoweredBy
51     setUseIPVHosts
52     getUseIPVHosts
53     getExecutorName
54     createRequest                //创建或指派并返回Request对象,这里的Request有和Container关联
55     createResponse
56     addLifecycleListener
57     findLifecycleListeners
58     removeLifecycleListener
59     createObjectName
60     initialize                    //初始化Connector对象
61     pause
62     resume
63     start
64     stop
65     ...
66     init
67     destroy
68     toString

  Container:(Container可以执行来自客户端的Request请求,并返回相应的Response)

getInfogetLoader                    //返回与此Container相关的Loader对象,如果没有Loader,则返回与其父Container关联的Loader
    setLoadergetLogger                    //返回Logger对象,用于打印log,同理如果当前没有Logger对象,则寻找父级LoggergetManager                    //返回Manager对象
    setManagergetMappingObject            //返回JMX对象名字
    getObjectNamegetPipeline                    //返回与此Container相关的用于管理Valves的Pipeline
    getClustersetClustergetBackgroundProcessorDelaysetBackgroundProcessorDelaygetNamesetNamegetParent                    //返回父级Container
    setParentgetParentClassLoader        //返回父级类加载器
    setParentClassLoadergetRealmsetRealmgetResourcessetResourcesbackgroundProcessaddChild                    //添加一个子容器,在添加之前,需要在子容器中先调用setParent方法addContainerListener        //添加事件监听器addPropertyChangeListener    //添加属性值变化监听器
    findChildfindChildrenfindContainerListenersinvoke                        //执行具体的Request,并得到具体的Response对象
    removeChildremoveContainerListenerremovePropertyChangeListenerlogAccess

1.连接原理举例

  首先我们在Catalina类的load方法中调用了方法createStartDigester,该方法在之前几篇有介绍过,主要是对于加载的server.xml文件中定义各个组件之间的关系。

  比如方法中的片段:

digester.addObjectCreate("Server","org.apache.catalina.core.StandardServer","className");
digester.addSetProperties("Server");
digester.addSetNext("Server","setServer","org.apache.catalina.Server");

  • addObjectCreate就是添加一个模式,当解析server.xml遇到Server的时候,就根据Server的className实例化一个Server对象,而默认实例化的类就是org.apache.catalina.core.StandardServer;
  • addSetProperties用于设置Server的一些属性,具体属性在server.xml中有定义;
  • addSetNext用于调用Server类的setServer方法,把当前Server添加进去。

  再比如这几行代码:

digester.addObjectCreate("Server/Listener",null, // MUST be specified in the element"className");
digester.addSetProperties("Server/Listener");
digester.addSetNext("Server/Listener","addLifecycleListener","org.apache.catalina.LifecycleListener");

  对应在server.xml中就是这几行

1 <!--APR library loader. Documentation at /docs/apr.html -->
2   <Listener className="org.apache.catalina.core.AprLifecycleListener" SSLEngine="on" />
3   <!--Initialize Jasper prior to webapps are loaded. Documentation at /docs/jasper-howto.html -->
4   <Listener className="org.apache.catalina.core.JasperListener" />
5   <!-- Prevent memory leaks due to use of particular java/javax APIs-->
6   <Listener className="org.apache.catalina.core.JreMemoryLeakPreventionListener" />
7   <!-- JMX Support for the Tomcat server. Documentation at /docs/non-existent.html -->
8   <Listener className="org.apache.catalina.mbeans.ServerLifecycleListener" />
9   <Listener className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener" />

  • 同理addObjectCreate说的是在Server下的Listeners,根据className创建listener对象;
  • addSetProperties用于设置Server的一些属性,具体属性在server.xml中有定义;
  • addSetNext用于调用Server类的addLifecycleLisntener方法,把server.xml中定义的5个监听器都实例化并添加到server上。

  还有关于Server和Service之间关系的代码

digester.addObjectCreate("Server/Service","org.apache.catalina.core.StandardService","className");
digester.addSetProperties("Server/Service");
digester.addSetNext("Server/Service","addService","org.apache.catalina.Service");

  通过这些代码我们很容易理解Server和Service之间的关联关系(后面会详细介绍)

  除了Server和Service之间的从属关系,我们还可以看到Service和Connector之间的关系

digester.addRule("Server/Service/Connector",new ConnectorCreateRule());
digester.addRule("Server/Service/Connector", new SetAllPropertiesRule(new String[]{"executor"}));
digester.addSetNext("Server/Service/Connector","addConnector","org.apache.catalina.connector.Connector");

  同理这里也是在Service下调用addConnector添加Connector(后面会详细介绍)

2.Connector和Container以及Connector和Service何时连接?

  我们从Catalina的load方法开始,当执行到load中的digester.parse(inputSource)时,即跳转到Digester类的parse方法中,之后开始解析server.xml中依次遇到的各个元素。

  当遇到server元素的时候,在代码中方法的执行顺序为Catalina.load->Digester.parse->Digester.startElement

  startElement方法如下:

 1 public void startElement(String namespaceURI, String localName,
 2                          String qName, Attributes list)
 3         throws SAXException {
 4     boolean debug = log.isDebugEnabled();
 5
 6     if (saxLog.isDebugEnabled()) {
 7         saxLog.debug("startElement(" + namespaceURI + "," + localName + "," +
 8                 qName + ")");
 9     }
10
11     // Parse system properties
12     list = updateAttributes(list);
13
14     // Save the body text accumulated for our surrounding element
15     bodyTexts.push(bodyText);
16     if (debug) {
17         log.debug("  Pushing body text '" + bodyText.toString() + "'");
18     }
19     bodyText = new StringBuffer();
20
21     // the actual element name is either in localName or qName, depending
22     // on whether the parser is namespace aware
23     String name = localName;
24     if ((name == null) || (name.length() < 1)) {
25         name = qName;
26     }
27
28     // Compute the current matching rule
29     StringBuffer sb = new StringBuffer(match);
30     if (match.length() > 0) {
31         sb.append('/');
32     }
33     sb.append(name);
34     match = sb.toString();
35     if (debug) {
36         log.debug("  New match='" + match + "'");
37     }
38
39     // Fire "begin" events for all relevant rules
40     List rules = getRules().match(namespaceURI, match);
41     matches.push(rules);
42     if ((rules != null) && (rules.size() > 0)) {
43         for (int i = 0; i < rules.size(); i++) {
44             try {
45                 Rule rule = (Rule) rules.get(i);
46                 if (debug) {
47                     log.debug("  Fire begin() for " + rule);
48                 }
49                 rule.begin(namespaceURI, name, list);
50             } catch (Exception e) {
51                 log.error("Begin event threw exception", e);
52                 throw createSAXException(e);
53             } catch (Error e) {
54                 log.error("Begin event threw error", e);
55                 throw e;
56             }
57         }
58     } else {
59         if (debug) {
60             log.debug("  No rules found matching '" + match + "'.");
61         }
62     }
63
64 }

Digester.startElement

当执行到rule.begin(namespaceURI, name, list)这行的时候,通过调试信息可以看到该rule的className为org.apache.catalina.core.StandardServer,所以最终会进入StandardServer的构造函数中。

  另外,当解析server.xml到5个listener的时候,就会调用StandardServer的addLifecycleListener分别将这5个监听器实例化并添加到server上。

  继续解析,当解析到

igester.addSetNext("Server/Service/Connector","addConnector","org.apache.catalina.connector.Connector");

  的时候就会跳转到StandardService的addConnector方法中

 1 public void addConnector(Connector connector) {
 2
 3     synchronized (connectors) {
 4         connector.setContainer(this.container);
 5         connector.setService(this);
 6         Connector results[] = new Connector[connectors.length + 1];
 7         System.arraycopy(connectors, 0, results, 0, connectors.length);
 8         results[connectors.length] = connector;
 9         connectors = results;
10
11         if (initialized) {
12             try {
13                 connector.initialize();
14             } catch (LifecycleException e) {
15                 log.error(sm.getString(
16                         "standardService.connector.initFailed",
17                         connector), e);
18             }
19         }
20
21         if (started && (connector instanceof Lifecycle)) {
22             try {
23                 ((Lifecycle) connector).start();
24             } catch (LifecycleException e) {
25                 log.error(sm.getString(
26                         "standardService.connector.startFailed",
27                         connector), e);
28             }
29         }
30
31         // Report this property change to interested listeners
32         support.firePropertyChange("connector", null, connector);
33     }
34
35 }

  首先解析到的是server.xml中的这个connector

<Connector port="8080" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" />

  下面的两行代码交代了一个Connector是如何关联上Container和Service的:

  • connector.setContainer(this.container):说明了connector和container是如何关联的,调用connector对象的setContainer方法,而传进的值为this.Container,也就是当前StandardService的Container对象,这样就完成了Connector和Container之间的连接
  • connector.setService(this):对外这里绑定了当前的StandardService作为其从属的service。

3.Service和Container是何时连接的?

  继续解析直到Catalina.createStartDigester定义的

digester.addRuleSet(new HostRuleSet("Server/Service/Engine/"));

  这时候会调用StandardService的setContainer方法:

 1 public void setContainer(Container container) {
 2
 3     Container oldContainer = this.container;
 4     if ((oldContainer != null) && (oldContainer instanceof Engine))
 5         ((Engine) oldContainer).setService(null);
 6     this.container = container;
 7     if ((this.container != null) && (this.container instanceof Engine))
 8         ((Engine) this.container).setService(this);
 9     if (started && (this.container != null) &&
10         (this.container instanceof Lifecycle)) {
11         try {
12             ((Lifecycle) this.container).start();
13         } catch (LifecycleException e) {
14             ;
15         }
16     }
17     synchronized (connectors) {
18         for (int i = 0; i < connectors.length; i++)
19             connectors[i].setContainer(this.container);
20     }
21     if (started && (oldContainer != null) &&
22         (oldContainer instanceof Lifecycle)) {
23         try {
24             ((Lifecycle) oldContainer).stop();
25         } catch (LifecycleException e) {
26             ;
27         }
28     }
29
30     // Report this property change to interested listeners
31     support.firePropertyChange("container", oldContainer, this.container);
32
33 }

  当执行到((Engine) this.container).setService(this);这里会跳转到StandardEngine的setService交代了container是如何绑定StandardService的。

  并且在代码

1 synchronized (connectors) {
2     for (int i = 0; i < connectors.length; i++)
3         connectors[i].setContainer(this.container);
4 }

  我们可以看到通过遍历所有的connector,将其与container绑定。

4.Server和Service又是何时连接的?

  继续解析直到Catalina.createStartDigerster中的

digester.addSetNext("Server/Service","addService","org.apache.catalina.Service");

  会调用StandardServer的addService方法

 1 public void addService(Service service) {
 2
 3     service.setServer(this);
 4
 5     synchronized (services) {
 6         Service results[] = new Service[services.length + 1];
 7         System.arraycopy(services, 0, results, 0, services.length);
 8         results[services.length] = service;
 9         services = results;
10
11         if (initialized) {
12             try {
13                 service.initialize();
14             } catch (LifecycleException e) {
15                 log.error(e);
16             }
17         }
18
19         if (started && (service instanceof Lifecycle)) {
20             try {
21                 ((Lifecycle) service).start();
22             } catch (LifecycleException e) {
23                 ;
24             }
25         }
26
27         // Report this property change to interested listeners
28         support.firePropertyChange("service", null, service);
29     }
30
31 }

  service.setServer(this):该行会调用StandardService中的setServer为service绑定当前的StandardServer对象

5.小结

  当server.xml中的rule解析完毕后,我们起码明白了:

  • Server和Service是如何关联的;
  • Service和Connector是如何关联的;
  • Service和Container是如何关联的;
  • Connector和Container是如何关联的

如果您觉得阅读本文对您有帮助,请点一下“推荐”按钮,您的“推荐”将是我最大的写作动力!如果您想持续关注我的文章,请扫描二维码,关注JackieZheng的微信公众号,我会将我的文章推送给您,并和您一起分享我日常阅读过的优质文章。



友情赞助

如果你觉得博主的文章对你那么一点小帮助,恰巧你又有想打赏博主的小冲动,那么事不宜迟,赶紧扫一扫,小额地赞助下,攒个奶粉钱,也是让博主有动力继续努力,写出更好的文章^^。

    1. 支付宝                          2. 微信

                      

转载于:https://www.cnblogs.com/bigdataZJ/p/TomcatSourceZJ6.html

探秘Tomcat——连接篇相关推荐

  1. Tomcat 原理篇

    TOMCAT 原理篇 一.Tomcat 组成(Tomcat 由以下组件组成) 1.server a) Server是一个Catalina Servlet容器: b) Server 可以包含一个或多个s ...

  2. TOMCAT 连接池数据库密码加密方法

    分享一下我老师大神的人工智能教程!零基础,通俗易懂!http://blog.csdn.net/jiangjunshow 也欢迎大家转载本篇文章.分享知识,造福人民,实现我们中华民族伟大复兴! 原文来自 ...

  3. tomcat连接池的配置与使用

    今天做接jsp的作业,在页面跳转的时候一直遇到个问题,"org.apache.commons.dbcp.SQLNestedException: Cannot create JDBC driv ...

  4. 数据库连接池和Tomcat连接池的配置问题

    在做系统优化的时候师哥给我们提了一个连接池的概念,问我们有没有配置,我对这个概念一无所知,于是进行了一些研究,连接池有很多,比如WCF.EF还有数据库.CAS也可以配连接池,这些连接池用通俗的语言来说 ...

  5. Tomcat连接池配置

    1.  Tomcat c3p0连接池配置 (1)  修改conf/server.xml,在<GlobalNamingResources>标签内添加如下内容 <Resource nam ...

  6. 探秘Tomcat(一)——Myeclipse中导入Tomcat源码

    前言:有的时候自己不知道自己是井底之蛙,这并没有什么可怕的,因为你只要蜷缩在方寸之间的井里,无数次的生活轨迹无非最终归结还是一个圆形:但是可怕的是有一天你不得不从井里跳出来生活,需要重新审视井以外的生 ...

  7. tomcat连接mysql数据库_tomcat连接常用数据库的用法

    一.用于数据库连接的术语: JDBC:(Java database connectivity)是基于java数据访问技术的一个API通过客户端访问服务器的数据库,是一个面向关系型数据库并提供一种方法查 ...

  8. 蓝牙耳机与电脑连接篇

    蓝牙耳机怎样与电脑连接,这是很多蓝牙耳机用户比较关心的问题,今天编者kevin为大家带来新手上路之蓝牙耳机与电脑连接篇,让用户们充分感受到蓝牙耳机给我们生活带来的乐趣.      一般连接蓝牙耳机与电 ...

  9. java tomcat连接池,tomcat容器的内置连接池怎么使用(按时结帐)

    tomcat容器的内置连接池怎么使用(按时结帐) tomcat容器的内置连接池怎么使用(按时结帐) 日期:2014-05-18 浏览次数:20325 次 tomcat容器的内置连接池如何使用(按时结帐 ...

最新文章

  1. JavaScript时间日期格式化
  2. outdated: 29.Blitter Function, RAW Texture Loading
  3. java中商业数据计算时用到的类BigDecimal和DecimalFormat
  4. 93号涨0.86元售6.2元/升 20日油价正式上调
  5. 阿里云Kubernetes服务上从零搭建GitLab+Jenkins+GitOps应用发布模型的实践全纪录
  6. centos 7 mysql 中文,解决centOS7 下mysql插入中文字符报错相关问题
  7. 想成为一个怎样的人?
  8. Java二十三设计模式之-----桥接模式
  9. mybatis 打印自定义完整日志
  10. 俄罗斯方块java代码_java的俄罗斯方块代码
  11. kis商贸系列加密服务器,金蝶KIS商贸系列V3.0发版说明教程.doc
  12. 恶意代码分析实战学习——静态分析
  13. 如何批量抠图换背景?这两个方法可以做到
  14. charles安装教程--以及针对安装证书后https<unknown>爬坑
  15. Oracle数据库管理每周一例-第十七期 ADG
  16. 指数分布c语言,C语言下泊松分布以及指数分布随机数生成器实现
  17. win10,win11后在cmd命令行输入python自动调用微软应用商店
  18. 本地Windows MIMIC III数据入库
  19. 麒麟v10服务器安装vnc
  20. Mysql的基本操作和用户权限----2021(ZSD版)

热门文章

  1. (0094)iOS开发之本地文件预览的三种方法(2)
  2. python3+selenium3登录封装与调用(把登陆做成公共模块)
  3. java 一次性输入与输出_Java入门——(7)IO(输入与输出)
  4. python数组文件_python数组
  5. uniapp 安卓和IOS退出App的方法
  6. Django框架restful序列化组件get/post请求接口设计
  7. 关于防范csrf攻击基于token鉴权
  8. Json字符串解析原理、超大json对象的解析
  9. winform 界面设计
  10. 利用scrapy和MongoDB来开发一个爬虫