精华内容
下载资源
问答
  • tomcat运行原理
    2017-06-20 14:05:42
    前言:Tomcat作为Sun公司官方推荐的jsp和Servlet容器越来越多的受到原件公司和开发人员的喜爱。今天就Tomcat的组成部分和运行原理进行一些讨论。
    一、Tomcat的各个组件是在/conf/server.xml中配置的。
    <server>顶层类元素,包含多个service
    <service>顶层类元素,包含一个Engin和多个Connector
    <connector port="8080" protocol="HTTP/1.1">链接类容器,代表通信接口
    <connector port="8009" protocol="AJP/1.3">
    <Engine>容器元素,为Service处理客户请求,含多个Host
    <Host>容器元素,为Host处理客户请求,含多个Context
    <Context>为web应用处理客户请求
    </service>
    </server>
    一个java web应用在tomcat中与一个Context对应,是一一对应关系
    二、Tomcat Server处理一个http请求的过程

    假设来自客户的请求为: http://localhost:8080/wsota/wsota_index.jsp
    1)请求被发送到本机端口8080 ,被在那里监听的coyote HTTP/1.1 Connector 获得
    2)Connector把该请求交给他所在的service的的Engine来处理,并等待来自Engine的回应。
    3)Engine获得请求localhost/wsota/wsota_index.jsp,匹配他所拥有的所有虚拟主机Host
    4) Engine匹配到名为localhost的Host(即使匹配不到也把请求交给该Host处理,因为该Host被定义为该Engine的默认主机)
    5) localhost Host获得请求/wsota/wsota_index.jsp,匹配它所拥有的所有Context
    6) Host匹配到路径为/wsota的Context(如果匹配不到就把该请求交给路径名为""的Context去处理)
    7) path="/wsota"的Context获得请求/wsota_index.jsp,在它的mapping table中寻找对应的servlet
    8) Context匹配到URL PATTERN为*.jsp的servlet,对应于JspServlet类
    9) 构造HttpServletRequest对象和HttpServletResponse对象,作为参数调用JspServlet的doGet或doPost方法
    10)Context把执行完了之后的HttpServletResponse对象返回给Host
    11)Host把HttpServletResponse对象返回给Engine
    12)Engine把HttpServletResponse对象返回给Connector
    13)Connector把HttpServletResponse对象返回给客户browser



    更多相关内容
  • Tomcat运行原理

    千次阅读 2019-07-15 22:52:20
    Tomcat运行在JVM中的一个进程。通过处理scoket通信 (Socket) 来运行。 Web项目的本质,是一大堆的资源文件和方法。Web项目没有入口方法(main方法),意味着Web项目中的方法不会自动运行起来。 Web项目部署进...

    Tomcat服务器本质

    1. Tomcat是运行在JVM中的一个进程。通过处理scoket通信 (Socket) 来运行。
    2. Web项目的本质,是一大堆的资源文件和方法。Web项目没有入口方法(main方法),意味着Web项目中的方法不会自动运行起来。
    3. Web项目部署进Tomcat的webapp中,那就是希望Tomcat去调用写好的方法去为客户端返回需要的资源和数据。
    4. Tomcat一定有一个main方法。Tomcat可以运行起来,并调用写好的方法。
    5. 对于Tomcat而言,它并不知道我们会有什么样的方法,这些都只是在项目被部署进webapp下后才确定的,由此分析,必然用到了Java的反射来实现类的动态加载、实例化、获取方法、调用方法。但是我们部署到Tomcat的中的Web项目必须是按照规定好的接口来进行编写,以便进行调用。

    Tomcat体系架构

    在这里插入图片描述

    1. Service组件
      Service主要用于关联一个引擎和与此引擎相关的连接器,每个连接器通过一个特定的端口和协议接收入站请求交将其转发至关联的引擎进行处理。各Server的定义不能使用同一个端口,这意味着如果在同一个物理机上启动了多个Server实例,必须配置它们使用不同的端口。
    2. Connector组件
      Tomcat应该考虑工作情形并为相应情形下的请求分别定义好需要的连接器才能正确接收来自于客户端的请求。一个引擎可以有一个或多个连接器,以适应多种请求方式。
    3. Engine组件
      Engine是Servlet处理器的一个实例,即servlet引擎
    4. Host组件
      位于Engine容器中,用于接收请求并进行相应处理的主机或虚拟主机
    5. Context组件
      Context在某些意义上类似于apache中的路径别名,一个Context定义用于标识tomcat实例中的一个Web应用程序

    Tomcat 处理一个HTTP请求的过程

    在这里插入图片描述

    1. 用户在浏览器中输入网址localhost:8080/test/index.jsp,请求被发送到本机端口8080,被在那里监听的Coyote HTTP/1.1 Connector获得;
    2. Connector把该请求交给它所在的Service的Engine(Container)来处理,并等待Engine的回应;
    3. Engine获得请求localhost/test/index.jsp,匹配所有的虚拟主机Host;
    4. Engine匹配到名为localhost的Host(即使匹配不到也把请求交给该Host处理,因为该Host被定义为该Engine的默认主机),名为localhost的Host获得请求/test/index.jsp,匹配它所拥有的所有Context。Host匹配到路径为/test的Context(如果匹配不到就把该请求交给路径名为“ ”的Context去处理);
    5. path=“/test”的Context获得请求/index.jsp,在它的mapping table中寻找出对应的Servlet。Context匹配到URL Pattern为*.jsp的Servlet,对应于JspServlet类;
    6. 构造HttpServletRequest对象和HttpServletResponse对象,作为参数调用JspServlet的doGet()或doPost(),执行业务逻辑、数据存储等;
    7. Context把执行完之后的HttpServletResponse对象返回给Host;
    8. Host把HttpServletResponse对象返回给Engine;
    9. Engine把HttpServletResponse对象返回Connector;
    10. Connector把HttpServletResponse对象返回给客户Browser

    参考(https://www.cnblogs.com/small-boy/p/8042860.html)

    展开全文
  • tomcat深入剖析 tomcat原理,由浅入深;由老美所写,的翻译版;看完之后有种顿悟的感觉;tomcat深入剖析.pdf+源码(tomcat运行原理)
  • ​ SpringBoot的创造简化了我们创建项目和运行项目的工作。我们不必再像SpringMVC时代那样,将项目先打包成jar包,然后放入Tomcat中再启动。我们可以忽略打包的操作和tomat的配置问题,编写完代码后直接启动项目即可...

    SpringBoot中是如何初始化Tomcat 的

    前言

    ​ SpringBoot的创造简化了我们创建项目和运行项目的工作。我们不必再像SpringMVC时代那样,将项目先打包成jar包,然后放入Tomcat中再启动。我们可以忽略打包的操作和tomat的配置问题,编写完代码后直接启动项目即可。这一切都得益于SpringBoot的内置容器的实现。那么,SpringBoot具体是如何为我们完成了初始化Tomcat 的这一系列的操作的呢?下面我将以第一人称的学习源码视角带领大家来一起快乐分析SpringBoot的源码来学习下…

    源码入口

    main方法

    SpringBoot的项目启动都是从启动Application中的run方法开始,那么Tomcat 的初始化也必然在其中。

    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
        System.out.println("Demo Application is Running!");
    }
    

    我们点击run方法的源码进行查看:

    public ConfigurableApplicationContext run(String... args) {
    		StopWatch stopWatch = new StopWatch();
    		stopWatch.start();
    		ConfigurableApplicationContext context = null;
    		Collection<springbootexceptionreporter> exceptionReporters = new ArrayList&lt;&gt;();
    		//设置系统属性『java.awt.headless』,为true则启用headless模式支持
    		configureHeadlessProperty();
    		//通过*SpringFactoriesLoader*检索*META-INF/spring.factories*,
           //找到声明的所有SpringApplicationRunListener的实现类并将其实例化,
           //之后逐个调用其started()方法,广播SpringBoot要开始执行了
    		SpringApplicationRunListeners listeners = getRunListeners(args);
    		//发布应用开始启动事件
    		listeners.starting();
    		try {
    		//初始化参数
    			ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
    			//创建并配置当前SpringBoot应用将要使用的Environment(包括配置要使用的PropertySource以及Profile),
            //并遍历调用所有的SpringApplicationRunListener的environmentPrepared()方法,广播Environment准备完毕。
    			ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
    			configureIgnoreBeanInfo(environment);
    			//打印banner
    			Banner printedBanner = printBanner(environment);
    			//创建应用上下文
    			context = createApplicationContext();
    			//通过*SpringFactoriesLoader*检索*META-INF/spring.factories*,获取并实例化异常分析器
    			exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
    					new Class[] { ConfigurableApplicationContext.class }, context);
    			//为ApplicationContext加载environment,之后逐个执行ApplicationContextInitializer的initialize()方法来进一步封装ApplicationContext,
            //并调用所有的SpringApplicationRunListener的contextPrepared()方法,【EventPublishingRunListener只提供了一个空的contextPrepared()方法】,
            //之后初始化IoC容器,并调用SpringApplicationRunListener的contextLoaded()方法,广播ApplicationContext的IoC加载完成,
            //这里就包括通过**@EnableAutoConfiguration**导入的各种自动配置类。
    			prepareContext(context, environment, listeners, applicationArguments, printedBanner);
    			//刷新上下文
    			refreshContext(context);
    			//再一次刷新上下文,其实是空方法,可能是为了后续扩展。
    			afterRefresh(context, applicationArguments);
    			stopWatch.stop();
    			if (this.logStartupInfo) {
    				new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
    			}
    			//发布应用已经启动的事件
    			listeners.started(context);
    			//遍历所有注册的ApplicationRunner和CommandLineRunner,并执行其run()方法。
            //我们可以实现自己的ApplicationRunner或者CommandLineRunner,来对SpringBoot的启动过程进行扩展。
    			callRunners(context, applicationArguments);
    		}
    		catch (Throwable ex) {
    			handleRunFailure(context, ex, exceptionReporters, listeners);
    			throw new IllegalStateException(ex);
    		}
     
    		try {
    		//应用已经启动完成的监听事件
    			listeners.running(context);
    		}
    		catch (Throwable ex) {
    			handleRunFailure(context, ex, exceptionReporters, null);
    			throw new IllegalStateException(ex);
    		}
    		return context;
    	}
    

    这里,run方法的运行逻辑我们可以总结为几下几点:

    1、配置系统属性

    2、获取spring应用的监听器,发布应用的启动事件

    3、初始化应用启动参数

    4、配置应用启动的环境,输出banner

    5、创建上下文(包含tomcat的初始化)

    6、预处理上下文

    7、刷新上下文(包含tomcat的初始化)

    8、再刷新上下文,为了后续的扩展预留操作,也可以称之为空刷新。

    9、发布应用已经启动的监听事件

    10、发布应用启动完成的监听事件

    createApplicationContext方法

    ​ 其中,第5步和第7步为我们在研究tomcat如何初始化的重要的操作步骤。下面,我们分别来看下这两个方法的源码,到底做了什么事情:

    /**
     * Strategy method used to create the {@link ApplicationContext}. By default this
     * method will respect any explicitly set application context or application context
     * class before falling back to a suitable default.
     * @return the application context (not yet refreshed)
     * @see #setApplicationContextClass(Class)
     */
    protected ConfigurableApplicationContext createApplicationContext() {
       Class<?> contextClass = this.applicationContextClass;
       if (contextClass == null) {
          try {
          	// 确定项目是应该创建那种类型的servlet
             switch (this.webApplicationType) {
             // web类型
             case SERVLET:
                contextClass = Class.forName(DEFAULT_SERVLET_WEB_CONTEXT_CLASS);
                break;
             // 非web类型
             case REACTIVE:
                contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);
                break;
             // 默认类型
             default:
                contextClass = Class.forName(DEFAULT_CONTEXT_CLASS);
             }
          }
          catch (ClassNotFoundException ex) {
             throw new IllegalStateException(
                   "Unable create a default ApplicationContext, please specify an ApplicationContextClass", ex);
          }
       }
       return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
    }
    

    ​ 在createApplicationContext这个方法中,我们可以大概的知道,这里主要是做对于应用的配置类型的界定。我们是创建web类型(SERVLET)?非web类型(REACTIVE)?默认类型?

    ​ 既然我们是研究tomcat,那么必然我们是启动web类型的应用。所以,这里我们通过反射创建的必然是DEFAULT_SERVLET_WEB_CONTEXT_CLASS这个类。

    AnnotationConfigServletWebServerApplicationContext类

    ​ 上面,DEFAULT_SERVLET_WEB_CONTEXT_CLASS这个常量对应的类是:

    /**
    	 * The class name of application context that will be used by default for web
    	 * environments.
    	 */
    	public static final String DEFAULT_SERVLET_WEB_CONTEXT_CLASS = "org.springframework.boot."
    			+ "web.servlet.context.AnnotationConfigServletWebServerApplicationContext";
    

    那么,我们下来看下这个类:

    public class AnnotationConfigServletWebServerApplicationContext extends ServletWebServerApplicationContext
          implements AnnotationConfigRegistry {...}
    

    在这个类中,别的我不想去管,主要是我看到了其继承的父类ServletWebServerApplicationContext这个类。

    ServletWebServerApplicationContext类

    从名字上,我们可以看到这个类的作用 – web类型的服务应用的上下文类。那么,下面我们一起看下这个类:

    /*
     * Copyright 2012-2020 the original author or authors.
     *
     * Licensed under the Apache License, Version 2.0 (the "License");
     * you may not use this file except in compliance with the License.
     * You may obtain a copy of the License at
     *
     *      https://www.apache.org/licenses/LICENSE-2.0
     *
     * Unless required by applicable law or agreed to in writing, software
     * distributed under the License is distributed on an "AS IS" BASIS,
     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     * See the License for the specific language governing permissions and
     * limitations under the License.
     */
    
    package org.springframework.boot.web.servlet.context;
    
    import java.util.Collection;
    import java.util.Collections;
    import java.util.EventListener;
    import java.util.HashMap;
    import java.util.LinkedHashSet;
    import java.util.Map;
    import java.util.Set;
    
    import javax.servlet.Filter;
    import javax.servlet.Servlet;
    import javax.servlet.ServletConfig;
    import javax.servlet.ServletContext;
    import javax.servlet.ServletException;
    
    import org.apache.commons.logging.Log;
    import org.apache.commons.logging.LogFactory;
    
    import org.springframework.beans.BeansException;
    import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
    import org.springframework.beans.factory.config.Scope;
    import org.springframework.beans.factory.support.DefaultListableBeanFactory;
    import org.springframework.boot.web.context.ConfigurableWebServerApplicationContext;
    import org.springframework.boot.web.server.WebServer;
    import org.springframework.boot.web.servlet.FilterRegistrationBean;
    import org.springframework.boot.web.servlet.ServletContextInitializer;
    import org.springframework.boot.web.servlet.ServletContextInitializerBeans;
    import org.springframework.boot.web.servlet.ServletRegistrationBean;
    import org.springframework.boot.web.servlet.server.ServletWebServerFactory;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.ApplicationContextException;
    import org.springframework.core.io.Resource;
    import org.springframework.util.StringUtils;
    import org.springframework.web.context.ContextLoaderListener;
    import org.springframework.web.context.ServletContextAware;
    import org.springframework.web.context.WebApplicationContext;
    import org.springframework.web.context.support.GenericWebApplicationContext;
    import org.springframework.web.context.support.ServletContextAwareProcessor;
    import org.springframework.web.context.support.ServletContextResource;
    import org.springframework.web.context.support.ServletContextScope;
    import org.springframework.web.context.support.WebApplicationContextUtils;
    
    /**
     * A {@link WebApplicationContext} that can be used to bootstrap itself from a contained
     * {@link ServletWebServerFactory} bean.
     * <p>
     * This context will create, initialize and run an {@link WebServer} by searching for a
     * single {@link ServletWebServerFactory} bean within the {@link ApplicationContext}
     * itself. The {@link ServletWebServerFactory} is free to use standard Spring concepts
     * (such as dependency injection, lifecycle callbacks and property placeholder variables).
     * <p>
     * In addition, any {@link Servlet} or {@link Filter} beans defined in the context will be
     * automatically registered with the web server. In the case of a single Servlet bean, the
     * '/' mapping will be used. If multiple Servlet beans are found then the lowercase bean
     * name will be used as a mapping prefix. Any Servlet named 'dispatcherServlet' will
     * always be mapped to '/'. Filter beans will be mapped to all URLs ('/*').
     * <p>
     * For more advanced configuration, the context can instead define beans that implement
     * the {@link ServletContextInitializer} interface (most often
     * {@link ServletRegistrationBean}s and/or {@link FilterRegistrationBean}s). To prevent
     * double registration, the use of {@link ServletContextInitializer} beans will disable
     * automatic Servlet and Filter bean registration.
     * <p>
     * Although this context can be used directly, most developers should consider using the
     * {@link AnnotationConfigServletWebServerApplicationContext} or
     * {@link XmlServletWebServerApplicationContext} variants.
     *
     * @author Phillip Webb
     * @author Dave Syer
     * @author Scott Frederick
     * @since 2.0.0
     * @see AnnotationConfigServletWebServerApplicationContext
     * @see XmlServletWebServerApplicationContext
     * @see ServletWebServerFactory
     */
    public class ServletWebServerApplicationContext extends GenericWebApplicationContext
          implements ConfigurableWebServerApplicationContext {
    
       private static final Log logger = LogFactory.getLog(ServletWebServerApplicationContext.class);
    
       /**
        * Constant value for the DispatcherServlet bean name. A Servlet bean with this name
        * is deemed to be the "main" servlet and is automatically given a mapping of "/" by
        * default. To change the default behavior you can use a
        * {@link ServletRegistrationBean} or a different bean name.
        */
       public static final String DISPATCHER_SERVLET_NAME = "dispatcherServlet";
    
       private volatile WebServer webServer;
    
       private ServletConfig servletConfig;
    
       private String serverNamespace;
    
       /**
        * Create a new {@link ServletWebServerApplicationContext}.
        */
       public ServletWebServerApplicationContext() {
       }
    
       /**
        * Create a new {@link ServletWebServerApplicationContext} with the given
        * {@code DefaultListableBeanFactory}.
        * @param beanFactory the DefaultListableBeanFactory instance to use for this context
        */
       public ServletWebServerApplicationContext(DefaultListableBeanFactory beanFactory) {
          super(beanFactory);
       }
    
       /**
        * Register ServletContextAwareProcessor.
        * @see ServletContextAwareProcessor
        */
       @Override
       protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
          beanFactory.addBeanPostProcessor(new WebApplicationContextServletContextAwareProcessor(this));
          beanFactory.ignoreDependencyInterface(ServletContextAware.class);
          registerWebApplicationScopes();
       }
    
       @Override
       public final void refresh() throws BeansException, IllegalStateException {
          try {
             super.refresh();
          }
          catch (RuntimeException ex) {
             stopAndReleaseWebServer();
             throw ex;
          }
       }
    
       @Override
       protected void onRefresh() {
          super.onRefresh();
          try {
             createWebServer();
          }
          catch (Throwable ex) {
             throw new ApplicationContextException("Unable to start web server", ex);
          }
       }
    
       @Override
       protected void finishRefresh() {
          super.finishRefresh();
          WebServer webServer = startWebServer();
          if (webServer != null) {
             publishEvent(new ServletWebServerInitializedEvent(webServer, this));
          }
       }
    
       @Override
       protected void onClose() {
          super.onClose();
          stopAndReleaseWebServer();
       }
    
       private void createWebServer() {
          WebServer webServer = this.webServer;
          ServletContext servletContext = getServletContext();
          if (webServer == null && servletContext == null) {
             ServletWebServerFactory factory = getWebServerFactory();
             this.webServer = factory.getWebServer(getSelfInitializer());
          }
          else if (servletContext != null) {
             try {
                getSelfInitializer().onStartup(servletContext);
             }
             catch (ServletException ex) {
                throw new ApplicationContextException("Cannot initialize servlet context", ex);
             }
          }
          initPropertySources();
       }
    
       /**
        * Returns the {@link ServletWebServerFactory} that should be used to create the
        * embedded {@link WebServer}. By default this method searches for a suitable bean in
        * the context itself.
        * @return a {@link ServletWebServerFactory} (never {@code null})
        */
       protected ServletWebServerFactory getWebServerFactory() {
          // Use bean names so that we don't consider the hierarchy
          String[] beanNames = getBeanFactory().getBeanNamesForType(ServletWebServerFactory.class);
          if (beanNames.length == 0) {
             throw new ApplicationContextException("Unable to start ServletWebServerApplicationContext due to missing "
                   + "ServletWebServerFactory bean.");
          }
          if (beanNames.length > 1) {
             throw new ApplicationContextException("Unable to start ServletWebServerApplicationContext due to multiple "
                   + "ServletWebServerFactory beans : " + StringUtils.arrayToCommaDelimitedString(beanNames));
          }
          return getBeanFactory().getBean(beanNames[0], ServletWebServerFactory.class);
       }
    
       /**
        * Returns the {@link ServletContextInitializer} that will be used to complete the
        * setup of this {@link WebApplicationContext}.
        * @return the self initializer
        * @see #prepareWebApplicationContext(ServletContext)
        */
       private org.springframework.boot.web.servlet.ServletContextInitializer getSelfInitializer() {
          return this::selfInitialize;
       }
    
       private void selfInitialize(ServletContext servletContext) throws ServletException {
          prepareWebApplicationContext(servletContext);
          registerApplicationScope(servletContext);
          WebApplicationContextUtils.registerEnvironmentBeans(getBeanFactory(), servletContext);
          for (ServletContextInitializer beans : getServletContextInitializerBeans()) {
             beans.onStartup(servletContext);
          }
       }
    
       private void registerApplicationScope(ServletContext servletContext) {
          ServletContextScope appScope = new ServletContextScope(servletContext);
          getBeanFactory().registerScope(WebApplicationContext.SCOPE_APPLICATION, appScope);
          // Register as ServletContext attribute, for ContextCleanupListener to detect it.
          servletContext.setAttribute(ServletContextScope.class.getName(), appScope);
       }
    
       private void registerWebApplicationScopes() {
          ExistingWebApplicationScopes existingScopes = new ExistingWebApplicationScopes(getBeanFactory());
          WebApplicationContextUtils.registerWebApplicationScopes(getBeanFactory());
          existingScopes.restore();
       }
    
       /**
        * Returns {@link ServletContextInitializer}s that should be used with the embedded
        * web server. By default this method will first attempt to find
        * {@link ServletContextInitializer}, {@link Servlet}, {@link Filter} and certain
        * {@link EventListener} beans.
        * @return the servlet initializer beans
        */
       protected Collection<ServletContextInitializer> getServletContextInitializerBeans() {
          return new ServletContextInitializerBeans(getBeanFactory());
       }
    
       /**
        * Prepare the {@link WebApplicationContext} with the given fully loaded
        * {@link ServletContext}. This method is usually called from
        * {@link ServletContextInitializer#onStartup(ServletContext)} and is similar to the
        * functionality usually provided by a {@link ContextLoaderListener}.
        * @param servletContext the operational servlet context
        */
       protected void prepareWebApplicationContext(ServletContext servletContext) {
          Object rootContext = servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);
          if (rootContext != null) {
             if (rootContext == this) {
                throw new IllegalStateException(
                      "Cannot initialize context because there is already a root application context present - "
                            + "check whether you have multiple ServletContextInitializers!");
             }
             return;
          }
          servletContext.log("Initializing Spring embedded WebApplicationContext");
          try {
             servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this);
             if (logger.isDebugEnabled()) {
                logger.debug("Published root WebApplicationContext as ServletContext attribute with name ["
                      + WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE + "]");
             }
             setServletContext(servletContext);
             if (logger.isInfoEnabled()) {
                long elapsedTime = System.currentTimeMillis() - getStartupDate();
                logger.info("Root WebApplicationContext: initialization completed in " + elapsedTime + " ms");
             }
          }
          catch (RuntimeException | Error ex) {
             logger.error("Context initialization failed", ex);
             servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, ex);
             throw ex;
          }
       }
    
       private WebServer startWebServer() {
          WebServer webServer = this.webServer;
          if (webServer != null) {
             webServer.start();
          }
          return webServer;
       }
    
       private void stopAndReleaseWebServer() {
          WebServer webServer = this.webServer;
          if (webServer != null) {
             try {
                webServer.stop();
                this.webServer = null;
             }
             catch (Exception ex) {
                throw new IllegalStateException(ex);
             }
          }
       }
    
       @Override
       protected Resource getResourceByPath(String path) {
          if (getServletContext() == null) {
             return new ClassPathContextResource(path, getClassLoader());
          }
          return new ServletContextResource(getServletContext(), path);
       }
    
       @Override
       public String getServerNamespace() {
          return this.serverNamespace;
       }
    
       @Override
       public void setServerNamespace(String serverNamespace) {
          this.serverNamespace = serverNamespace;
       }
    
       @Override
       public void setServletConfig(ServletConfig servletConfig) {
          this.servletConfig = servletConfig;
       }
    
       @Override
       public ServletConfig getServletConfig() {
          return this.servletConfig;
       }
    
       /**
        * Returns the {@link WebServer} that was created by the context or {@code null} if
        * the server has not yet been created.
        * @return the embedded web server
        */
       @Override
       public WebServer getWebServer() {
          return this.webServer;
       }
    
       /**
        * Utility class to store and restore any user defined scopes. This allow scopes to be
        * registered in an ApplicationContextInitializer in the same way as they would in a
        * classic non-embedded web application context.
        */
       public static class ExistingWebApplicationScopes {
    
          private static final Set<String> SCOPES;
    
          static {
             Set<String> scopes = new LinkedHashSet<>();
             scopes.add(WebApplicationContext.SCOPE_REQUEST);
             scopes.add(WebApplicationContext.SCOPE_SESSION);
             SCOPES = Collections.unmodifiableSet(scopes);
          }
    
          private final ConfigurableListableBeanFactory beanFactory;
    
          private final Map<String, Scope> scopes = new HashMap<>();
    
          public ExistingWebApplicationScopes(ConfigurableListableBeanFactory beanFactory) {
             this.beanFactory = beanFactory;
             for (String scopeName : SCOPES) {
                Scope scope = beanFactory.getRegisteredScope(scopeName);
                if (scope != null) {
                   this.scopes.put(scopeName, scope);
                }
             }
          }
    
          public void restore() {
             this.scopes.forEach((key, value) -> {
                if (logger.isInfoEnabled()) {
                   logger.info("Restoring user defined scope " + key);
                }
                this.beanFactory.registerScope(key, value);
             });
          }
    
       }
    
    }
    

    ​ 在这个类中,无非就是对于整个web应用的生命周期操作的一些方法。但是,有一个方法显得很突出自我:那就是createWebServer()方法。

    createWebServer方法

    从名字上看,这个方法的作用就是创建一个web类型的应用。那么,真相到底是什么呢?我们一起来看下:

    private void createWebServer() {
       WebServer webServer = this.webServer;
       ServletContext servletContext = getServletContext();
       if (webServer == null && servletContext == null) {
         	// 通过工厂模式,创建webServer的实例
          ServletWebServerFactory factory = getWebServerFactory();
          this.webServer = factory.getWebServer(getSelfInitializer());
       }
       else if (servletContext != null) {
          try {
             getSelfInitializer().onStartup(servletContext);
          }
          catch (ServletException ex) {
             throw new ApplicationContextException("Cannot initialize servlet context", ex);
          }
       }
       initPropertySources();
    }
    

    ​ 纵观整个方法,我们找到了创建web应用实例的地方。但是,还是没有找到在那里启动tomcat的,坑呀!!!但是,我们知道了其通过ServletWebServerFactory来创建了一个webServer的实例对象。再回首ServletWebServerApplicationContext这个类,既然我们有了应用的实例对象,那么下来是不是就该启动这个实例对象了?我们查找下ServletWebServerApplicationContext中是否有启动方法。

    startWebServer方法

    不出所料,还真有一个启动webServer的方法。我们看下这个方法在搞什么事情:

    private WebServer startWebServer() {
       WebServer webServer = this.webServer;
       if (webServer != null) {
          webServer.start();
       }
       return webServer;
    }
    

    ​ 尴尬了,这里直接获取了上面创建的实例对象后,判断不为空后就直接调用了WebServer中的start()方法启动了。我们在这里打个断点调试下代码,看看运行到这里后是什么样子:

    Connected to the target VM, address: '127.0.0.1:59767', transport: 'socket'
    
      .   ____          _            __ _ _
     /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
    ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
     \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
      '  |____| .__|_| |_|_| |_\__, | / / / /
     =========|_|==============|___/=/_/_/_/
     :: Spring Boot ::        (v2.2.8.RELEASE)
    
    2020-06-28 17:03:07.041  INFO 4687 --- [           main] com.example.demo.DemoApplication         : Starting DemoApplication on fanyuanhangdeMacBook-Pro.local with PID 4687 (/Users/fanyuanhang/Downloads/springboot/target/classes started by fanyuanhang in /Users/fanyuanhang/Downloads/springboot)
    2020-06-28 17:03:07.044  INFO 4687 --- [           main] com.example.demo.DemoApplication         : No active profile set, falling back to default profiles: default
    2020-06-28 17:03:08.040  INFO 4687 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat initialized with port(s): 8080 (http)
    2020-06-28 17:03:08.049  INFO 4687 --- [           main] o.apache.catalina.core.StandardService   : Starting service [Tomcat]
    2020-06-28 17:03:08.049  INFO 4687 --- [           main] org.apache.catalina.core.StandardEngine  : Starting Servlet engine: [Apache Tomcat/9.0.36]
    2020-06-28 17:03:08.120  INFO 4687 --- [           main] o.a.c.c.C.[Tomcat].[localhost].[/]       : Initializing Spring embedded WebApplicationContext
    2020-06-28 17:03:08.120  INFO 4687 --- [           main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 1025 ms
    2020-06-28 17:03:08.302  INFO 4687 --- [           main] o.s.s.concurrent.ThreadPoolTaskExecutor  : Initializing ExecutorService 'applicationTaskExecutor'
    2020-06-28 17:03:08.484  INFO 4687 --- [           main] o.s.b.w.embedded.tomcat.TomcatWebServer  : Tomcat started on port(s): 8080 (http) with context path ''
    2020-06-28 17:03:28.064  INFO 4687 --- [           main] com.example.demo.DemoApplication         : Started DemoApplication in 21.385 seconds (JVM running for 22.101)
    

    ​ 我去!!!刚好运行完这个方法后tomcat启动了,还配置了默认端口8080…无语,看来配置tomcat这件事在启动webServer前就完成了…

    ​ 那么,到底tomcat在那里初始化的?难道还有createTomcatxxx()这样的方法存在于ServletWebServerApplicationContext中吗?我们搜搜看:

    在这里插入图片描述
    在这里插入图片描述

    ​ GG!!!木有啊!!!看来初始化tomcat这件事情SpringBoot制作组藏得够深的啊…那么,我们冷静下来分析下:

    1、启动前已经初始化好了tomcat

    2、没有在ServletWebServerApplicationContext存在初始化方法

    ​ 顺着这个思路我们想想自己是制作人会怎么做:按我写代码的习惯,创建一个实例的时候,我自己会将这个实例所需要的所有配置在创建实例的时候也一同配置好。难道?跟我想到一起去了?那么我们再次回顾下createWebServer这个方法。

    再回首createWebServer方法
    private void createWebServer() {
       WebServer webServer = this.webServer;
       ServletContext servletContext = getServletContext();
       if (webServer == null && servletContext == null) {
          ServletWebServerFactory factory = getWebServerFactory();
          this.webServer = factory.getWebServer(getSelfInitializer());
       }
       else if (servletContext != null) {
          try {
             getSelfInitializer().onStartup(servletContext);
          }
          catch (ServletException ex) {
             throw new ApplicationContextException("Cannot initialize servlet context", ex);
          }
       }
       initPropertySources();
    }
    

    ​ 我们再次细细品味下createWebServer方法。通过ServletWebServerFactory这个工厂接口的提供的getWebServer()方法获取了webServer实例对象…em,看来有必要看下这个接口的实现类具体是怎么实现的了。冥冥之中,我感觉我距离真相进了一步,哈哈。

    ServletWebServerFactory类图结构

    调出这个接口的类图结构来:

    在这里插入图片描述

    ​ em,你品,你细品。ServletWebServerFactory接口的最终实现子类有三个,其中,分别对应了最开始createApplicationContext方法中根据类型来获取对应的应用类型的配置。

    ​ 这里想都不用想,目标直指TomcatServletWebServerFactory这个类,我们来看下这里是怎么实现的getWebServer方法的。

    TomcatServletWebServerFactory类
    @Override
    public WebServer getWebServer(ServletContextInitializer... initializers) {
       if (this.disableMBeanRegistry) {
          Registry.disableRegistry();
       }
       // 创建tomcat实例
       Tomcat tomcat = new Tomcat();
       File baseDir = (this.baseDirectory != null) ? this.baseDirectory : createTempDir("tomcat");
       tomcat.setBaseDir(baseDir.getAbsolutePath());
       Connector connector = new Connector(this.protocol);
       connector.setThrowOnFailure(true);
       tomcat.getService().addConnector(connector);
       customizeConnector(connector);
       // 将创建好的tomcat设置连接器connector
       tomcat.setConnector(connector);
       tomcat.getHost().setAutoDeploy(false);
       // 配置引擎
       configureEngine(tomcat.getEngine());
       for (Connector additionalConnector : this.additionalTomcatConnectors) {
          tomcat.getService().addConnector(additionalConnector);
       }
       prepareContext(tomcat.getHost(), initializers);
       // 启动tomcat
       return getTomcatWebServer(tomcat);
    }
    

    ​ 呦西,看看我们看到什么?哈哈,果然,在TomcatServletWebServerFactory实现的getWebServer方法中创建并初始化了tomcat。

    ​ 到这里,我们终于找到了tomcat在那里创建的了!!!小兴奋一下。在Tomcat对象中,指定了一些默认属性的配置:

    public class Tomcat {
    
        private static final StringManager sm = StringManager.getManager(Tomcat.class);
    
        // Some logging implementations use weak references for loggers so there is
        // the possibility that logging configuration could be lost if GC runs just
        // after Loggers are configured but before they are used. The purpose of
        // this Map is to retain strong references to explicitly configured loggers
        // so that configuration is not lost.
        private final Map<String, Logger> pinnedLoggers = new HashMap<>();
    
        protected Server server;
    
        protected int port = 8080;
        protected String hostname = "localhost";
        protected String basedir;
    

    ​ 好的,我们找到了Tomcat创建的地方。那么,创建了Tomcat,又是在那里启动的tomcat的呢?我们细品下TomcatServletWebServerFactory.getWebServer()方法。

    ​ 我们继续在getWebServer()方法中加入断点,看看到底在哪个方法启动了tomcat:

    /**
     * Factory method called to create the {@link TomcatWebServer}. Subclasses can
     * override this method to return a different {@link TomcatWebServer} or apply
     * additional processing to the Tomcat server.
     * @param tomcat the Tomcat server.
     * @return a new {@link TomcatWebServer} instance
     */
    protected TomcatWebServer getTomcatWebServer(Tomcat tomcat) {
       return new TomcatWebServer(tomcat, getPort() >= 0);
    }
    

    根据调试结果显示,在执行 getTomcatWebServer(Tomcat tomcat)的时候启动了tomcat应用。而 getTomcatWebServer(Tomcat tomcat)又返回了一个TomcatWebServer的实例。那么在TomcatWebServer的构造函数中必然启动了tomcat,我们来看下。

    TomcatWebServer中的初始化以及启动
    /**
     * Create a new {@link TomcatWebServer} instance.
     * @param tomcat the underlying Tomcat server
     * @param autoStart if the server should be started
     */
    public TomcatWebServer(Tomcat tomcat, boolean autoStart) {
       Assert.notNull(tomcat, "Tomcat Server must not be null");
       this.tomcat = tomcat;
       this.autoStart = autoStart;
       initialize();
    }
    

    看下initialize方法:

    TomcatWebServer.initialize方法
    private void initialize() throws WebServerException {
       logger.info("Tomcat initialized with port(s): " + getPortsDescription(false));
       synchronized (this.monitor) {
          try {
             addInstanceIdToEngineName();
    
             Context context = findContext();
             context.addLifecycleListener((event) -> {
                if (context.equals(event.getSource()) && Lifecycle.START_EVENT.equals(event.getType())) {
                   // Remove service connectors so that protocol binding doesn't
                   // happen when the service is started.
                   removeServiceConnectors();
                }
             });
    
             // Start the server to trigger initialization listeners
             this.tomcat.start();
    
             // We can re-throw failure exception directly in the main thread
             rethrowDeferredStartupExceptions();
    
             try {
                ContextBindings.bindClassLoader(context, context.getNamingToken(), getClass().getClassLoader());
             }
             catch (NamingException ex) {
                // Naming is not enabled. Continue
             }
    
             // Unlike Jetty, all Tomcat threads are daemon threads. We create a
             // blocking non-daemon to stop immediate shutdown
             startDaemonAwaitThread();
          }
          catch (Exception ex) {
             stopSilently();
             destroySilently();
             throw new WebServerException("Unable to start embedded Tomcat", ex);
          }
       }
    }
    

    ​ 至此,我们已经找到了关于SpringBoot是如何初始化并启动tomcat的所有新路历程,我也以我看源码的第一视角带着代价了解了源码。

    思考与总结

    ​ 如果只是想了解SpringBoot中tomcat的流程的那么,到这里就可以结束了。下面,我们再思考一个问题,我们在实际生产中也会将多service到一个tomcat下去同时启动。那么,tomcat是如何做到的呢?这就关乎到tomcat的运行原理了。所以,有兴趣的同学继续和我一起看下去。

    Tomcat运行原理

    导言

    ​ 在上面, 我们在看TomcatServletWebServerFactory中的getWebServer时,不知道大家是否还记得这些代码:

    @Override
    public WebServer getWebServer(ServletContextInitializer... initializers) {
       if (this.disableMBeanRegistry) {
          Registry.disableRegistry();
       }
       // 1、创建tomcat实例
       Tomcat tomcat = new Tomcat();
       File baseDir = (this.baseDirectory != null) ? this.baseDirectory : createTempDir("tomcat");
       tomcat.setBaseDir(baseDir.getAbsolutePath());
       Connector connector = new Connector(this.protocol);
       connector.setThrowOnFailure(true);
       tomcat.getService().addConnector(connector);
       customizeConnector(connector);
       // 2、将创建好的tomcat设置连接器connector
       tomcat.setConnector(connector);
       tomcat.getHost().setAutoDeploy(false);
       // 3、配置引擎
       configureEngine(tomcat.getEngine());
       for (Connector additionalConnector : this.additionalTomcatConnectors) {
          tomcat.getService().addConnector(additionalConnector);
       }
       prepareContext(tomcat.getHost(), initializers);
       // 4、启动tomcat
       return getTomcatWebServer(tomcat);
    }
    

    Connector连接器是什么鬼?为什么要放到Tomcat实例中?configureEngine()方法中,从Tomcat实例中获取到的Engine又是什么呢?

    Engine

    我们看到,在创建Tomcat实例的方法getWebServer中,在第4步时执行了configureEngine(tomcat.getEngine())方法获取了Engine,我们一起看下这个方法的源码了解下什么是Engine:

    /**
     * Access to the engine, for further customization.
     * @return The engine
     */
    public Engine getEngine() {
        // 1、获取服务中的第一个service
        Service service = getServer().findServices()[0];
      	// 2、如果获取到的service中已经在Engine容器中,则返回当Engine容器
        if (service.getContainer() != null) {
            return service.getContainer();
        }
      	// 3、不存在,则创建一个容器并命名为tomcat后返回
        Engine engine = new StandardEngine();
        engine.setName( "Tomcat" );
        engine.setDefaultHost(hostname);
        engine.setRealm(createDefaultRealm());
        service.setContainer(engine);
        return engine;
    }
    

    根据源码的定义,看来Engine是一个服务类的容器了。那么,Engine这个容器的本质原理是什么呢?我们还是需要一起看下Engine的源码:

    public interface Engine extends Container {
    
        /**
         * @return the default host name for this Engine.
         */
        public String getDefaultHost();
    
    
        /**
         * Set the default hostname for this Engine.
         *
         * @param defaultHost The new default host
         */
        public void setDefaultHost(String defaultHost);
    
    
        /**
         * @return the JvmRouteId for this engine.
         */
        public String getJvmRoute();
    
    
        /**
         * Set the JvmRouteId for this engine.
         *
         * @param jvmRouteId the (new) JVM Route ID. Each Engine within a cluster
         *        must have a unique JVM Route ID.
         */
        public void setJvmRoute(String jvmRouteId);
    
    
        /**
         * @return the <code>Service</code> with which we are associated (if any).
         */
        public Service getService();
    
    
        /**
         * Set the <code>Service</code> with which we are associated (if any).
         *
         * @param service The service that owns this Engine
         */
        public void setService(Service service);
    }
    

    我们可以看到,Engine接口中主要提供的方法基本上都是对于整个应用以及运行时JVM配置的一些方法。那么,我们再一起看下这个接口的描述信息:

    /**
     * An <b>Engine</b> is a Container that represents the entire Catalina servlet
     * engine.  It is useful in the following types of scenarios:
     * <ul>
     * <li>You wish to use Interceptors that see every single request processed
     *     by the entire engine.
     * <li>You wish to run Catalina in with a standalone HTTP connector, but still
     *     want support for multiple virtual hosts.
     * </ul>
     * In general, you would not use an Engine when deploying Catalina connected
     * to a web server (such as Apache), because the Connector will have
     * utilized the web server's facilities to determine which Context (or
     * perhaps even which Wrapper) should be utilized to process this request.
     * <p>
     * The child containers attached to an Engine are generally implementations
     * of Host (representing a virtual host) or Context (representing individual
     * an individual servlet context), depending upon the Engine implementation.
     * <p>
     * If used, an Engine is always the top level Container in a Catalina
     * hierarchy. Therefore, the implementation's <code>setParent()</code> method
     * should throw <code>IllegalArgumentException</code>.
     *
     * @author Craig R. McClanahan
     */
    

    翻译一下,总结为以下几点:

    1、Engine代表整个Catalina Servlet的容器引擎

    2、运行Catalina至web应用时,一般不使用容器,当然也可以使用

    3、如果使用容器引擎,那么,Engine必然是顶层容器

    Container

    那么, 既然Engine作为顶层容器,那么其父类Container为上级容器。我们再一起看下Container这个接口。

    在这里插入图片描述

    ​ Container是容器的父接口,该容器的设计用的是典型的责任链的设计模式,它由四个自容器组件构成,分别是Engine、Host、Context、Wrapper。这四个组件是负责关系,存在包含关系。通常一个Servlet class对应一个Wrapper,如果有多个Servlet定义多个Wrapper,如果有多个Wrapper就要定义一个更高的Container,如Context。

    ​ Context 还可以定义在父容器 Host 中,Host 不是必须的,但是要运行 war 程序,就必须要 Host,因为 war 中必有 web.xml 文件,这个文件的解析就需要 Host 了,如果要有多个 Host 就要定义一个 top 容器 Engine 了。而 Engine 没有父容器了,一个 Engine 代表一个完整的 Servlet 引擎。

    ​ Tomcat 还有其它重要的组件,如安全组件 security、logger 日志组件、session、mbeans、naming 等其它组件。这些组件共同为 Connector 和 Container 提供必要的服务。

    ​ 我们从Container的类图上可以看到,Container作为Engine的上级接口,和Engine平级的子接口还有Context、Host、Wapper。那么,四个接口的关系是什么呢?

    Host、Context、Wapper、Engine的关系
        //部分源码,其余部分省略。
    public class Tomcat {
    //设置连接器
         public void setConnector(Connector connector) {
            Service service = getService();
            boolean found = false;
            for (Connector serviceConnector : service.findConnectors()) {
                if (connector == serviceConnector) {
                    found = true;
                }
            }
            if (!found) {
                service.addConnector(connector);
            }
        }
        //获取service
           public Service getService() {
            return getServer().findServices()[0];
        }
        //设置Host容器
         public void setHost(Host host) {
            Engine engine = getEngine();
            boolean found = false;
            for (Container engineHost : engine.findChildren()) {
                if (engineHost == host) {
                    found = true;
                }
            }
            if (!found) {
                engine.addChild(host);
            }
        }
        //获取Engine容器
         public Engine getEngine() {
            Service service = getServer().findServices()[0];
            if (service.getContainer() != null) {
                return service.getContainer();
            }
            Engine engine = new StandardEngine();
            engine.setName( "Tomcat" );
            engine.setDefaultHost(hostname);
            engine.setRealm(createDefaultRealm());
            service.setContainer(engine);
            return engine;
        }
        //获取server
           public Server getServer() {
        if (server != null) {
            return server;
        }
     
        System.setProperty("catalina.useNaming", "false");
     
        server = new StandardServer();
     
        initBaseDir();
     
        // Set configuration source
        ConfigFileLoader.setSource(new CatalinaBaseConfigurationSource(new File(basedir), null));
     
        server.setPort( -1 );
     
        Service service = new StandardService();
        service.setName("Tomcat");
        server.addService(service);
        return server;
    }
    
    //添加Context容器
      public Context addContext(Host host, String contextPath, String contextName,
            String dir) {
        silence(host, contextName);
        Context ctx = createContext(host, contextPath);
        ctx.setName(contextName);
        ctx.setPath(contextPath);
        ctx.setDocBase(dir);
        ctx.addLifecycleListener(new FixContextListener());
     
        if (host == null) {
            getHost().addChild(ctx);
        } else {
            host.addChild(ctx);
        }
        
    //添加Wrapper容器
         public static Wrapper addServlet(Context ctx,
                                      String servletName,
                                      Servlet servlet) {
        // will do class for name and set init params
        Wrapper sw = new ExistingStandardWrapper(servlet);
        sw.setName(servletName);
        ctx.addChild(sw);
     
        return sw;
     }
    }
    

    通过Tomcat类的方法为突破口,我们可以知道如下的关系:

    1、通过getServer()方法:一个Tomcat对应一个Server

    2、通过getEngine()方法:一个Server下对应多个Service;一个Service代表我们的一个应用;一个Engine内只有一个Service

    3、通过setHost()方法:一个Host内可以有多个Engine

    4、通过addContext()方法:Context可以在Tomcat内拥有多个

    5、通过addServlet()方法:Wrapper容器可以有多个

    6、通过setConncetor()方法:connector是设置在service下的,一个service可以有多个connector

    ​ 其中,Engine为特定的Service组件处理所有客户的请求,Host组件为特定的虚拟主机处理所有的客户请求。Context组件为特定的Web应用处理所有的客户请求。

    ​ 连接器类元素代表了介于客户与服务之间的通信接口,负责将客户的请求发送给服务器,并将服务器的响应传递给客户。

    运行原理图

    在这里插入图片描述

    1、用户点击网页内容,请求被发送到本机端口8080,被在那里监听的Coyote HTTP/1.1 Connector获得。

    2、tomcat将此请求作为任务加入一个队列中,线程池中若干工作者线程从这个队列中获取任务,并把该请求交给它所在的Service的Engine来处理,并等待Engine的回应。

    3、Engine获得请求localhost/test/index.jsp,匹配所有的虚拟主机Host。

    4、Engine匹配到名为localhost的Host(即使匹配不到也把请求交给该Host处理,因为该Host被定义为该Engine的默认主机),名为localhost的Host获得请求/test/index.jsp,匹配它所拥有的所有的Context。Host匹配到路径为/test的Context(如果匹配不到就把该请求交给路径名为“ ”的Context去处理)。

    5、path=“/test”的Context获得请求/index.jsp,在它的mapping table中寻找出对应的Servlet。Context匹配到URL PATTERN为*.jsp的Servlet,对应于JspServlet类。

    6、构造HttpServletRequest对象和HttpServletResponse对象,作为参数调用JspServlet的doGet()或doPost().执行业务逻辑、数据存储等程序。

    7、Context把执行完之后的HttpServletResponse对象返回给Host。

    8、Host把HttpServletResponse对象返回给Engine。

    9、Engine把HttpServletResponse对象返回Connector。

    10、Connector把HttpServletResponse对象返回给客户Browser。

    展开全文
  • Tomcat服务器创建一个ServletRequest对象,在ServletRequest对象中包含了客户请求信息及其他关于客户的信息,如请求头,请求正文,以及客户机的IP地址等
  • Tomcat工作原理运行机制

    万次阅读 多人点赞 2018-03-25 14:26:48
    一、Tomcat运行原理分析1.Tomcat是运行在JVM中的一个进程。它定义为【中间件】,顾名思义,是一个在Java项目与JVM之间的中间容器。2.Web项目的本质,是一大堆的资源文件和方法。Web项目没有入口方法(main方法),,...

    一、Tomcat运行原理分析
    1.Tomcat是运行在JVM中的一个进程。它定义为【中间件】,顾名思义,是一个在Java项目与JVM之间的中间容器。

    2.Web项目的本质,是一大堆的资源文件和方法。Web项目没有入口方法(main方法),意味着Web项目中的方法不会自动运行起来。

    3.Web项目部署进Tomcatwebapp中的目的是很明确的,那就是希望Tomcat去调用
    写好的方法去为客户端返回需要的资源和数据。
    4. Tomcat
    可以运行起来,并调用写好的方法。那么,Tomcat一定有一个main方法。
    5.
    对于Tomcat而言,它并不知道我们会有什么样的方法,这些都只是在项目被部署进webapp下后才确定的,由此分析,必然用到了Java的反射来实现类的动态加载、实例化、获取方法、调用方法。但是我们部署到Tomcat的中的Web项目必须是按照规定好的接口来进行编写,以便进行调用

    6.Tomcat如何确定调用什么方法呢。这取却于客户端的请求,http://127.0.0.1:8080/JayKing.Tomcat.Study/index.java?show这样的一个请求,通过http协议,在浏览器发往本机的8080端口,携带的参数show方法,包含此方法的路径为JayKing.Tomcat.Study,文件名为:index.java

    二、模拟Tomcat运行

    1.客户端类

    package JayKing.Tomcat.Study;
    import java.io.IOException;
    import java.io.OutputStreamWriter;
    import java.io.Writer;
    import java.net.Socket;
    import java.net.UnknownHostException;
    import java.util.Scanner;
    public class Client {
    	private static int port = 5228;
    	private static String host = "127.0.0.1";
        //http://127.0.0.1:8080/JayKing.Tomcat.Study/index.java?show
    	public static void main(String[] args) {
    		try {
    			Socket con=new Socket(host,port);
    			System.out.println("请输入URL地址:");
    			Scanner scanner=new Scanner(System.in);
    			String info=scanner.nextLine().trim();
    			Writer writer = new OutputStreamWriter(con.getOutputStream());
    			writer.write(info);
    			writer.flush();
    			writer.close();
    		} catch (UnknownHostException e) {
    			// TODO Auto-generated catch block
    			e.printStackTrace();
    		} catch (IOException e) {
    			// TODO Auto-generated catch block
    			e.printStackTrace();
    		}
    	}
    }
    
    
    

    2.服务器类

    package JayKing.Tomcat.Study;
    
    import java.io.BufferedReader;
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.InputStreamReader;
    import java.lang.reflect.InvocationTargetException;
    import java.lang.reflect.Method;
    import java.net.ServerSocket;
    import java.net.Socket;
    
    public class TomcatTest {
    	private static int post = 5228;
     
    	private static UrlUtil urlutil = new UrlUtil();
    
    	public static void main(String[] args) {
    		System.out.println(" My Tomcat is Running");
    		try {
    			ServerSocket server = new ServerSocket(post);
    			while (true) {
    				Socket socket = server.accept();// 服务器每接受一次请求,就创建一个socket对象
    				InputStream in = socket.getInputStream();
    				BufferedReader br = new BufferedReader(
    						new InputStreamReader(in));
    				String info = null;
    				String infoline = br.readLine();
    				while (infoline != null) {
    					info =info+infoline;
    					infoline = br.readLine();
    				}
    				UrlBean url = urlutil.readString(info);
    				if (url != null) {
    					String path=url.getPath();
    					String className = url.getFileName();
    					String methodName = url.getParameter().trim();
    					ClassLoader classloader=ClassLoader.getSystemClassLoader();	
    					try {
    						classloader.loadClass(path+"."+className);
    						Class<?> getclass=Class.forName(path+"."+className);
    						Method method=getclass.getMethod(methodName, null);
    						method.invoke(getclass.newInstance(), null);
    						
    					} catch (ClassNotFoundException e) {
    						// TODO Auto-generated catch block
    						e.printStackTrace();
    					} catch (NoSuchMethodException e) {
    						// TODO Auto-generated catch block
    						e.printStackTrace();
    					} catch (SecurityException e) {
    						// TODO Auto-generated catch block
    						e.printStackTrace();
    					} catch (IllegalAccessException e) {
    						// TODO Auto-generated catch block
    						e.printStackTrace();
    					} catch (IllegalArgumentException e) {
    						// TODO Auto-generated catch block
    						e.printStackTrace();
    					} catch (InvocationTargetException e) {
    						// TODO Auto-generated catch block
    						e.printStackTrace();
    					} catch (InstantiationException e) {
    						// TODO Auto-generated catch block
    						e.printStackTrace();
    					}		
    				} else {
    
    				}
    			}
    		} catch (IOException e) {
    			// TODO Auto-generated catch block
    			e.printStackTrace();
    		}
    	}
    }
    

    3.工具类

    package JayKing.Tomcat.Study;
    
    //格式:协议://主机号:端口号/目录路径/文件名
    //例如: http://127.0.0.1:8080/Test/Manage/index.jsp
    public class UrlUtil {
    	public UrlBean readString(String info) {
    		UrlBean url = null;
    		int tag1 = info.indexOf(":");
    		int tag2 = info.lastIndexOf(":");
    		int tag3 = info.indexOf("/", tag2);
    		int tag4 = info.lastIndexOf("/");
    		int tag5 = info.indexOf("?");
    		int tag6=info.lastIndexOf(".");
    		String Protocol = info.substring(0, tag1);
    		String Host = info.substring(tag1 + 3, tag2);
    		String Port = info.substring(tag2 + 1, tag3);
    		String Path = info.substring(tag3 + 1, tag4);
    		String FileName = info.substring(tag4 + 1, tag6);
    		String Parameter = info.substring(tag5 + 1, info.trim().length());
    		if (Host != null && Path != null && FileName != null) {
    			if (Protocol == null) {
    				Protocol = "http";
    			}
    			if (Port == null) {
    				Port = "8080";
    			}
    			url = new UrlBean(Protocol, Host, Port, Path, FileName, Parameter);
    			return url;
    		}
    
    		return url;
    
    	}
    }
    
    4.Model类
    package JayKing.Tomcat.Study;
    
    //格式:协议://主机号:端口号/目录路径/文件名
    //例如: http://127.0.0.1:8080/Test/Manage/index.jsp?a=1&b=2
    public class UrlBean {
    	private String Protocol;
    	private String Host;
    	private String Port;
    	private String Path;
    	private String FileName;
    	private String Parameter;
    
    	public UrlBean(String protocol, String host, String port, String path,
    			String fileName, String parameter) {
    		super();
    		Protocol = protocol;
    		Host = host;
    		Port = port;
    		Path = path;
    		FileName = fileName;
    		Parameter = parameter;
    	}
    
    	public UrlBean() {
    	}
    
    	public String getProtocol() {
    		return Protocol;
    	}
    
    	public void setProtocol(String protocol) {
    		Protocol = protocol;
    	}
    
    	public String getHost() {
    		return Host;
    	}
    
    	public void setHost(String host) {
    		Host = host;
    	}
    
    	public String getPort() {
    		return Port;
    	}
    
    	public void setPort(String port) {
    		Port = port;
    	}
    
    	public String getPath() {
    		return Path;
    	}
    
    	public void setPath(String path) {
    		Path = path;
    	}
    
    	public String getFileName() {
    		return FileName;
    	}
    
    	public void setFileName(String fileName) {
    		FileName = fileName;
    	}
    
    	public String getParameter() {
    		return Parameter;
    	}
    
    	public void setParameter(String parameter) {
    		Parameter = parameter;
    	}
    }
    
    5.测试方法类
    package JayKing.Tomcat.Study;
    public class index {
    	public void show() {
    		System.out.println("方法已经被执行!");
    	}
    }
    

    6.运行结果

     

     

    三、Tomcat原理总结

    1. Tomcat需要main方法启动。
    2. Tomcat需要监听本机上的某个端口。
    3. Tomcat需要抓取此端口上来自客户端的链接并获得请求调用的方法与参数。

    4. Tomcat需要根据请求调用的方法,动态地加载方法所在的类,完成累的实例化并通过该实例获得需要的方法最终将请求传入方法执行。

    5. 将结果返回给客户端(jsp/html页面、json/xml字符串)


     



    展开全文
  • idea部署tomcat服务器2. 创建动态web工程,选择application server3. 修改web工程的tomcat服务器配置信息4. 新建web工程的目录解析5. 通过新建library管理项目jar包 1. idea部署tomcat服务器 2. 创建动态web工程,...
  • tomcat的工作原理

    千次阅读 2021-05-20 21:05:51
    本文源自转载:你还记得 Tomcat 的工作原理么 一、Tomcat 整体架构 Tomcat 是一个免费的、开源的、轻量级的 Web 应用服务器。适合在并发量不是很高的中小企业项目中使用。 二、文件目录结构 以下是 Tomcat 8 ...
  • IDEA启动tomcat 工作原理

    千次阅读 2018-11-20 10:17:00
    1.IDEA启动tomcat工作原理 idea启动tomcat 会打印: 我们需要关注的是CATALINA_BASE与CATALINA_HOME的区别,这里虽然表示的路径是一样的,配置tomcat多实例的时候就会不一样了 CATALINA_BASE 是tomcat的工作目录 ...
  • idea 启动tomcat 原理 首先我们需要知道两个变量对应的含义 CATALINA_HOME是Tomcat的安装目录 CATALINA_BASE是Tomcat的工作目录 我们来观察 tomcat 的 startup.bat启动和 idea 启动的区别 发现了吗 ,只有CATALINA_...
  • Tomcat&Servlet 运行原理讲解

    千次阅读 多人点赞 2022-03-20 11:01:37
    文章目录一、客户端(浏览器)与服务器之间的交互二、Tomcat启动过程三、Tomcat处理请求过程四...事实就是,main 方法在Tomcat 中,Servlet 程序是配合Tomcat运行的,Tomcat 就有了main 方法,就会拖着 Servlet 运行
  • Tomcat工作原理

    2020-06-09 23:01:40
    Tomcat是什么 Tomcat 服务器是一个免费的开放源代码的Web 应用服务器,属于轻量级应用服务器,在中小型系统和并发访问用户不是很多的场合下被普遍使用,是开发和调试JSP 程序的首选。 web服务器按功能分类可分为:...
  • Tomcat运行原理总结

    2022-08-11 15:56:47
    tomcat原理
  • 这个对于要了解web工程启动过程,及tomcat处理请求及发送请求的过程原理 可以了解了解...
  • 我们开发的web应用都是跑在web容器中的。Tomcat就是一个常用的Web容器。...进入tomcat目录,能看到如下目录:其中bin目录是里存放了tomcat的启动脚本,conf目录保存的是tomcat的配置文件。在命令行中进...
  • tomcat服务器工作原理

    2018-12-28 11:38:12
    更深入的了解tomcat服务器的运行机制 更好的了解底层技术实现。
  • TOMCAT原理

    千次阅读 2022-03-09 21:50:57
    一、TOMCAT的基本架构 二、Coyote连接器 三、Catalina容器 四、启动流程介绍 五、请求处理流程介绍 六、Jasper引擎 七、服务器配置 1、Server、Service 2、Executor 3、Connector 4、Engine、Host 5、...
  • Tomcat 服务器是一个免费的开放源代码的Web 应用服务器,属于轻量级应用...实际上Tomcat 部分是Apache 服务器的扩展,但它是独立运行的,所以当你运行tomcat 时,它实际上作为一个与Apache 独立的进程单独运行的。
  • eclipse中Tomcat基本运行原理

    千次阅读 2018-09-25 13:41:41
    eclipse中tomcat程序运行原理: 我们现在来分析一下上面的程序运行原理。 当在服务器上运行后,会生成与工程文件并列的一个文件夹:Servers。如下:(如果删掉了Servers文件夹,当重新运行时,文件夹又会自动...
  • Tomcat底层原理

    千次阅读 2019-11-06 11:02:21
    一、Tomcat启动时到底对我们的应用程序做了什么? 当我们把一个应用程序的war包放到Tomcat的webapps目录后,启动Tomcat,然后就可以通过浏览器发送Http请求访问该war包内的Servlet了。 这个过程包括: 1、启动...
  • 3.将这个war包直接放入到tomcat运行。 4.启动startup.bat,你可以发现控制台,并没有打印springboot启动的信息。 从上面的tomcat启动信息,可以看到springboot并没有启动,即spring的环境和springmvc的...
  • Servlet运行原理Tomcat为例)

    千次阅读 2018-07-22 20:52:48
    一、首先servlet是一个java应用程序,运行在服务器端,用来处理客户端请求(http请求)并作出响应的程序。 二、http定义了与服务器交互的几种方式最基本常用的有:GET、POST、PUT、DELETE。使用频率最高的就是GET、...
  • IDEA中部署Tomcat原理

    2020-04-12 16:42:17
    IDEA中部署Tomcat 1.新建项目 1.1 创建一个JavaWeb工程 File->New->Project->Java Enterprise选择Web Application 1.2 选择路径 2.JavaWeb目录介绍 Day50:项目名称 src:存放Java代码 web:存放web...
  • Tomcat的实现原理

    2022-04-26 16:16:38
    Tomcat 是一个Web容器。作为 Web 应用的容器承载着 Web 请求处理和响应的工作 最开始用户通过浏览器查看诸如新闻之类的静态资源,此时就需要通过 HTTP 服务器向浏览器返回静态 HTML 资源,浏览器将解析的 HTML 呈现...
  • Tomcat底层原理及实现

    2020-01-31 22:17:51
    Tomcat 原理:(java编写 socket 监听 包含死循环 main方法–>主线程){接受请求 , 返回请求 请求 分发} 计算机通过网卡与外界交互 ; 一台电脑逻辑上端口:2^16个端口 (物理上是一个地方) 不会出现不够用这种情况 ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 94,288
精华内容 37,715
关键字:

tomcat运行原理