我的编程空间,编程开发者的网络收藏夹
学习永远不晚

怎么在tomcat中调用Servlet实现初始化

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

怎么在tomcat中调用Servlet实现初始化

怎么在tomcat中调用Servlet实现初始化?相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。

一、代码启动tomcat

平常我们不论是Windows还是linux,我们都是通过脚本来启动tomcat,这对于我们分析源码不是很友好,所以我们 需要通过代码启动,启动代码如下:

Tomcat tomcat = new Tomcat();        tomcat.setPort(8080);        //new 出各层容器,并且维护各层容器的关系        tomcat.addWebapp("/","/");        tomcat.start();        //阻塞监听端口        tomcat.getServer().await();

启动代码还是非常非常简单,从代码中我们就可以看出,我们本篇博客主要分析的就是 addWebapp()方法和start()方法,通过这两个方法我们就可以找到servlet容器是在什么时候被初始化的。

二、tomcat框架

在我们进行分析上面两个方法之前,我们先总结一下tomcat的基础框架,其实从我们非常熟悉的 server.xml配置文件中就可以知道,tomcat就是一系列父子容器组成:

Server ---> Service --> Connector Engine addChild---> context(servlet容器) ,这就是我们从配置文件中分析出来的几个容器,tomcat启动时候就是逐层启动容器。

三、创建容器(addWebapp())

3.1 方法 调用流程图

怎么在tomcat中调用Servlet实现初始化

上面的流程图就是,从源码中逐步分析出来的几个重要的方法,这对于我们分析源码非常有帮助。

3.2 源码分析

1)通过反射获得configContext监听器

方法路径:package org.apache.catalina.startup.Tomcat.addWebapp(Host host, String contextPath, String docBase);

     public Context  addWebapp(Host host, String contextPath, String docBase) {        //通过反射获得一个监听器  ContextConfig,        //通过反射得到的一定是LifecycleListener的一个实现类,进入getConfigClass得到实现类(org.apache.catalina.startup.ContextConfig)        LifecycleListener listener = null;        try {            Class<?> clazz = Class.forName(getHost().getConfigClass());            listener = (LifecycleListener) clazz.getConstructor().newInstance();        } catch (ReflectiveOperationException e) {            // Wrap in IAE since we can't easily change the method signature to            // to throw the specific checked exceptions            throw new IllegalArgumentException(e);        }         return addWebapp(host, contextPath, docBase, listener);    }

2) 获得一个context容器(StandardContext)

在下面代码中,createContext()方法通过反射加载StandardContext容器,并且将设置监听ContextConfig, ctx.addLifecycleListener(config);

public Context addWebapp(Host host, String contextPath, String docBase,            LifecycleListener config) {         silence(host, contextPath);         //获得一个context容器(StandardContext)        Context ctx = createContext(host, contextPath);        ctx.setPath(contextPath);        ctx.setDocBase(docBase);         if (addDefaultWebXmlToWebapp) {            ctx.addLifecycleListener(getDefaultWebXmlListener());        }         ctx.setConfigFile(getWebappConfigFile(docBase, contextPath));        //把监听器添加到context中去        ctx.addLifecycleListener(config);         if (addDefaultWebXmlToWebapp && (config instanceof ContextConfig)) {            // prevent it from looking ( if it finds one - it'll have dup error )            ((ContextConfig) config).setDefaultWebXml(noDefaultWebXmlPath());        }         if (host == null) {            //getHost会逐层创建容器,并维护容器父子关系            getHost().addChild(ctx);        } else {            host.addChild(ctx);        }         return ctx;    }

3)维护各层容器

getHost()方法中得到各层容器,并且维护父亲容器关系,其中包括,server容器、Engine容器。并且将StandardContext容器通过getHost().addChild(ctx); 调用containerBase中的addChild()方法维护在 children 这个map中。

  public Host getHost() {        //将每一层的容器都new 出来        Engine engine = getEngine();        if (engine.findChildren().length > 0) {            return (Host) engine.findChildren()[0];        }         Host host = new StandardHost();        host.setName(hostname);        //维护tomcat中的父子容器        getEngine().addChild(host);        return host;    }

getEngine().addChild(host); 方法选择调用父类containerBase中的addChild方法

怎么在tomcat中调用Servlet实现初始化

  @Override    public void addChild(Container child) {        if (Globals.IS_SECURITY_ENABLED) {            PrivilegedAction<Void> dp =                new PrivilegedAddChild(child);            AccessController.doPrivileged(dp);        } else {            //这里的child 参数是 context 容器            addChildInternal(child);        }    }

addChildInternal()方法的 核心代码

 private void addChildInternal(Container child) {         if( log.isDebugEnabled() )            log.debug("Add child " + child + " " + this);        synchronized(children) {            if (children.get(child.getName()) != null)                throw new IllegalArgumentException("addChild:  Child name '" +                                                   child.getName() +                                                   "' is not unique");            child.setParent(this);  // May throw IAE            children.put(child.getName(), child);    }

四、启动容器(tomcat.start())

4.1、方法调用流程图

怎么在tomcat中调用Servlet实现初始化

4.2、源码分析

说明:StandardServer 、StandardService、StandardEngine等容器都是继承LifecycleBase

所以这里是模板模式的经典应用

1)逐层启动容器

此时的server对应的是我们前面创建的StandardServer

  public void start() throws LifecycleException {        //防止server容器没有创建        getServer();        //获得connector容器,并且将得到的connector容器设置到service容器中        getConnector();        //这里的start的实现是在 LifecycleBase类中实现        //LifecycleBase方法是一个模板方法,在tomcat启动流程中非常关键        server.start();    }

2) 进入start方法

怎么在tomcat中调用Servlet实现初始化

进入LifecycelBase中的start方法,其中核心方法是startInternal。

怎么在tomcat中调用Servlet实现初始化

从上面我们知道现在我们调用的是StandardServer容器的startInternal()方法,所以我们这里选择的是StandardServer

怎么在tomcat中调用Servlet实现初始化

方法路径:org.apache.catalina.core.StandardServer.startInternal()

protected void startInternal() throws LifecycleException {         fireLifecycleEvent(CONFIGURE_START_EVENT, null);        setState(LifecycleState.STARTING);         globalNamingResources.start();         // Start our defined Services        synchronized (servicesLock) {            //启动 service容器,一个tomcat中可以配置多个service容器,每个service容器都对应这我们的一个服务应用            for (Service service : services) {                //对应 StandardService.startInternal()                service.start();            }        }    }

从上面代码中我们可以看出,启动server容器的时候需要启动子容器 service容器,从这里开始就是容器 逐层向向内引爆,所以接下来就是开始依次调用各层容器的star方法。在这里就不在赘述。

2)ContainerBase中的startInternal()方法 核心代码,从这开始启动StandardContext容器

 // Start our child containers, if any        //在addWwbapp的流程中 addChild方法中加入的,所以这里需要找出来        //这里找出来的就是 context 容器        Container children[] = findChildren();        List<Future<Void>> results = new ArrayList<>();        for (Container child : children) {            //通过线程池 异步的方式启动线程池 开始启动 context容器,进入new StartChild            results.add(startStopExecutor.submit(new StartChild(child)));        }

new StartChild(child)) 方法开始启动StandardContext容器

private static class StartChild implements Callable<Void> {         private Container child;         public StartChild(Container child) {            this.child = child;        }         @Override        public Void call() throws LifecycleException {            //开始启动context,实际调用 StandardContext.startInternal()            child.start();            return null;        }    }

StandardContext.startInternal() 方法中的核心代码:

怎么在tomcat中调用Servlet实现初始化

   protected void fireLifecycleEvent(String type, Object data) {        LifecycleEvent event = new LifecycleEvent(this, type, data);        //lifecycleListeners 在addwebapp方法的第一步中,设置的监听的 contextConfig对象        for (LifecycleListener listener : lifecycleListeners) {            //这里调用的是 contextConfig的lifecycleEvent()方法            listener.lifecycleEvent(event);        }    }

进入到 contextConfig中的lifecycleEvent()方法

怎么在tomcat中调用Servlet实现初始化

public void lifecycleEvent(LifecycleEvent event) {         // Identify the context we are associated with        try {            context = (Context) event.getLifecycle();        } catch (ClassCastException e) {            log.error(sm.getString("contextConfig.cce", event.getLifecycle()), e);            return;        }         // Process the event that has occurred        if (event.getType().equals(Lifecycle.CONFIGURE_START_EVENT)) {            //完成web.xml的内容解析            configureStart();        } else if (event.getType().equals(Lifecycle.BEFORE_START_EVENT)) {            beforeStart();        } else if (event.getType().equals(Lifecycle.AFTER_START_EVENT)) {            // Restore docBase for management tools            if (originalDocBase != null) {                context.setDocBase(originalDocBase);            }        } else if (event.getType().equals(Lifecycle.CONFIGURE_STOP_EVENT)) {            configureStop();        } else if (event.getType().equals(Lifecycle.AFTER_INIT_EVENT)) {            init();        } else if (event.getType().equals(Lifecycle.AFTER_DESTROY_EVENT)) {            destroy();        }     }

在上面方法中,完成对web.xml的加载和解析,同时加载xml中配置的servlet并且封装成wrapper对象。

3)、启动servlet容器,StandardContext.startInternal() 中的 loadOnStartup(findChildren())方法

public boolean loadOnStartup(Container children[]) {         // Collect "load on startup" servlets that need to be initialized        TreeMap<Integer, ArrayList<Wrapper>> map = new TreeMap<>();        for (Container child : children) {            //这里的 Wrapper就是 我们前面封装的 servlet            Wrapper wrapper = (Wrapper) child;            int loadOnStartup = wrapper.getLoadOnStartup();            if (loadOnStartup < 0) {                continue;            }            Integer key = Integer.valueOf(loadOnStartup);            ArrayList<Wrapper> list = map.get(key);            if (list == null) {                list = new ArrayList<>();                map.put(key, list);            }            list.add(wrapper);        }         // Load the collected "load on startup" servlets        for (ArrayList<Wrapper> list : map.values()) {            for (Wrapper wrapper : list) {                try {                    //通过 load 方法  最终会调用 servlet的init方法                    wrapper.load();                } catch (ServletException e) {                    getLogger().error(sm.getString("standardContext.loadOnStartup.loadException",                          getName(), wrapper.getName()), StandardWrapper.getRootCause(e));                    // NOTE: load errors (including a servlet that throws                    // UnavailableException from the init() method) are NOT                    // fatal to application startup                    // unless failCtxIfServletStartFails="true" is specified                    if(getComputedFailCtxIfServletStartFails()) {                        return false;                    }                }            }        }        return true;     }

看完上述内容,你们掌握怎么在tomcat中调用Servlet实现初始化的方法了吗?如果还想学到更多技能或想了解更多相关内容,欢迎关注编程网行业资讯频道,感谢各位的阅读!

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

怎么在tomcat中调用Servlet实现初始化

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

怎么在tomcat中调用Servlet实现初始化

怎么在tomcat中调用Servlet实现初始化?相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。一、代码启动tomcat平常我们不论是Windows还是linux,我们都是通过脚
2023-06-14

Java中怎么实现初始化

Java中怎么实现初始化,很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人可以来学习下,希望你能有所收获。1、静态块优先程序首先会执行静态块的内容,这也就有了不写main方法就跑hello wor
2023-06-17

Vue3初始化怎么调用函数

这篇文章主要介绍“Vue3初始化怎么调用函数”,在日常操作中,相信很多人在Vue3初始化怎么调用函数问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”Vue3初始化怎么调用函数”的疑惑有所帮助!接下来,请跟着小编
2023-07-02

C#中怎么实现对象初始化

本篇文章给大家分享的是有关C#中怎么实现对象初始化,小编觉得挺实用的,因此分享给大家学习,希望大家阅读完这篇文章后可以有所收获,话不多说,跟着小编一起来看看吧。C#对象初始化器 (Object Initializers) :.NET2.0框
2023-06-17

怎么在java中初始化数组

这篇文章给大家介绍怎么在java中初始化数组,内容非常详细,感兴趣的小伙伴们可以参考借鉴,希望对大家能有所帮助。Java可以用来干什么Java主要应用于:1. web开发;2. Android开发;3. 客户端开发;4. 网页开发;5. 企
2023-06-14

怎么在CentOS中初始化服务器

今天就跟大家聊聊有关怎么在CentOS中初始化服务器,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。一、挂载硬盘1、磁盘分区fdisk -l #查看设备,一般可以看到设备名为/dev/
2023-06-07

C++11类内初始化怎么实现

本篇内容主要讲解“C++11类内初始化怎么实现”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“C++11类内初始化怎么实现”吧!重复的初始化代码考察下面的代码:enum LineStyle{
2023-06-19

go map初始化赋值怎么实现

在Go语言中,可以使用字面量的方式对map进行初始化赋值。以下是几种常见的map初始化赋值方法:使用make函数创建一个空的map,然后逐个添加键值对:m := make(map[string]int)m["a"] = 1m["b"]
2023-10-23

怎么在python中初始化进程池Pool

本篇文章为大家展示了怎么在python中初始化进程池Pool,内容简明扼要并且容易理解,绝对能使你眼前一亮,通过这篇文章的详细介绍希望你能有所收获。python的数据类型有哪些?python的数据类型:1. 数字类型,包括int(整型)、l
2023-06-14

怎么在python中对defaultdict进行初始化

怎么在python中对defaultdict进行初始化?相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。python是什么意思Python是一种跨平台的、具有解释性、编译性、互动性
2023-06-14

怎么在Java中动态初始化数组

怎么在Java中动态初始化数组?针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。Java可以用来干什么Java主要应用于:1. web开发;2. Android开发;3. 客户
2023-06-14

怎么在Java中初始化二维数组

今天就跟大家聊聊有关怎么在Java中初始化二维数组,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。Java可以用来干什么Java主要应用于:1. web开发;2. Android开发;
2023-06-14

java初始化数组为空怎么实现

在Java中,可以使用null来表示一个空数组。以下是一种初始化数组为空的方法:声明一个数组变量,但不分配内存空间。使用null将数组变量初始化为空数组。示例代码如下:// 声明一个整数数组变量int[] array;// 将数组变量
2023-10-27

Java数组怎么实现动态初始化

这篇文章主要讲解了“Java数组怎么实现动态初始化”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Java数组怎么实现动态初始化”吧!概念1、数组动态初始化只给定数组长度,系统默认初始化值。2
2023-06-30

怎么在Python中利用for循环初始化数组

这篇文章给大家介绍怎么在Python中利用for循环初始化数组,内容非常详细,感兴趣的小伙伴们可以参考借鉴,希望对大家能有所帮助。python是什么意思Python是一种跨平台的、具有解释性、编译性、互动性和面向对象的脚本语言,其最初的设计
2023-06-14

怎么在Pytorch 中对TORCH.NN.INIT 参数进行初始化

怎么在Pytorch 中对TORCH.NN.INIT 参数进行初始化?很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人可以来学习下,希望你能有所收获。初始化函数:torch.nn.init# -*
2023-06-06

Tomcat中怎么实现session持久化

在Tomcat中,可以通过配置一个Store元素来实现Session持久化。Store元素可以指定一个用于存储Session数据的存储介质,比如数据库、文件等。具体步骤如下:打开Tomcat的conf目录下的server.xml文件。在其中
Tomcat中怎么实现session持久化
2024-04-03

C# 3.0对象和集合初始化怎么实现

这篇文章主要讲解了“C# 3.0对象和集合初始化怎么实现”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“C# 3.0对象和集合初始化怎么实现”吧!Point类public class Poin
2023-06-17

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录