In SpringBoot Tomcat is how to start

image

Foreword

We know SpringBoot brought us a new development experience, we can reach directly to the web application jar package, direct start, which benefited from SpringBoot built container that can be started directly, it will Tomcat as an example, look at look SpringBoot is how to start Tomcat, but will also expand the Tomcat source code under study, understand Tomcat design.

Speaking from the Main method

Used SpringBoot people know, first of all to write a main method to start

@SpringBootApplication
public class TomcatdebugApplication {

    public static void main(String[] args) {
        SpringApplication.run(TomcatdebugApplication.class, args);
    }

}
复制代码

We click on the source of the run method, tracking down and issued a final runmethod is to call the ConfigurableApplicationContextmethod, source code is as follows:

public ConfigurableApplicationContext run(String... args) {
		StopWatch stopWatch = new StopWatch();
		stopWatch.start();
		ConfigurableApplicationContext context = null;
		Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
		//设置系统属性『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;
	}
复制代码

In fact, this way we can simply summarize steps

  1. Configuration Properties
  2. Get the listener, started publishing application event
  3. Initialization input parameters
  4. Configure the environment, the output banner
  5. Creating a Context
  6. Pretreatment context
  7. Refresh context
  8. Then refresh context
  9. Event publishing application has been launched
  10. Posted complete application launch event

In fact, the above code, if as long as the content analysis tomcat, then only need to focus on two elements can be, how to create a context, the context is how to refresh, the method is respectively createApplicationContext()and refreshContext(context), then we look at these two way to do something.

protected ConfigurableApplicationContext createApplicationContext() {
		Class<?> contextClass = this.applicationContextClass;
		if (contextClass == null) {
			try {
				switch (this.webApplicationType) {
				case SERVLET:
					contextClass = Class.forName(DEFAULT_SERVLET_WEB_CONTEXT_CLASS);
					break;
				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);
	}
复制代码

This is according to our webApplicationTypejudge what type of Servlet creation code correspond to the type of Web (SERVLET), responsive Web type (REACTIVE), non-Web type (default), we have established a Web type, so certainly examples of DEFAULT_SERVLET_WEB_CONTEXT_CLASSthe specified class, that is, AnnotationConfigServletWebServerApplicationContextclass, let's use this diagram to illustrate the relationship of class

Through this class diagram we can see, this class is inherited ServletWebServerApplicationContext, that's our real hero, and this class ultimately inherits AbstractApplicationContext, after you create a context to understand the situation, let us look to refresh the context, the relevant code is as follows:

//类:SpringApplication.java

private void refreshContext(ConfigurableApplicationContext context) {
    //直接调用刷新方法
		refresh(context);
		if (this.registerShutdownHook) {
			try {
				context.registerShutdownHook();
			}
			catch (AccessControlException ex) {
				// Not allowed in some environments.
			}
		}
	}
//类:SpringApplication.java

protected void refresh(ApplicationContext applicationContext) {
		Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext);
		((AbstractApplicationContext) applicationContext).refresh();
	}
复制代码

Here is directly transmitted call of this class refresh(context)methods, and finally converted to the parent strong AbstractApplicationContextcall its refresh()methods, the code is as follows:

// 类:AbstractApplicationContext	
public void refresh() throws BeansException, IllegalStateException {
		synchronized (this.startupShutdownMonitor) {
			// Prepare this context for refreshing.
			prepareRefresh();

			// Tell the subclass to refresh the internal bean factory.
			ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

			// Prepare the bean factory for use in this context.
			prepareBeanFactory(beanFactory);

			try {
				// Allows post-processing of the bean factory in context subclasses.
				postProcessBeanFactory(beanFactory);

				// Invoke factory processors registered as beans in the context.
				invokeBeanFactoryPostProcessors(beanFactory);

				// Register bean processors that intercept bean creation.
				registerBeanPostProcessors(beanFactory);

				// Initialize message source for this context.
				initMessageSource();

				// Initialize event multicaster for this context.
				initApplicationEventMulticaster();

				// Initialize other special beans in specific context subclasses.这里的意思就是调用各个子类的onRefresh()
				onRefresh();

				// Check for listener beans and register them.
				registerListeners();

				// Instantiate all remaining (non-lazy-init) singletons.
				finishBeanFactoryInitialization(beanFactory);

				// Last step: publish corresponding event.
				finishRefresh();
			}

			catch (BeansException ex) {
				if (logger.isWarnEnabled()) {
					logger.warn("Exception encountered during context initialization - " +
							"cancelling refresh attempt: " + ex);
				}

				// Destroy already created singletons to avoid dangling resources.
				destroyBeans();

				// Reset 'active' flag.
				cancelRefresh(ex);

				// Propagate exception to caller.
				throw ex;
			}

			finally {
				// Reset common introspection caches in Spring's core, since we
				// might not ever need metadata for singleton beans anymore...
				resetCommonCaches();
			}
		}
	}

复制代码

Here we see the onRefresh()method is invoked to achieve its subclasses, according to our analysis above, we here subclass ServletWebServerApplicationContext.

//类:ServletWebServerApplicationContext
protected void onRefresh() {
		super.onRefresh();
		try {
			createWebServer();
		}
		catch (Throwable ex) {
			throw new ApplicationContextException("Unable to start web server", ex);
		}
	}
	
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();
	}

复制代码

Here, in fact, the truth has come out, createWebServer()it is to start a web service, but not really start Tomcat, since it webServeris through ServletWebServerFactoryto get, we take a look at the true face of this factory.

Into the internal Tomcat

According to the graph we find that the factory is a class interface, each specific service that each subclass to achieve, so we went to look at TomcatServletWebServerFactory.getWebServer()implementation.

	@Override
	public WebServer getWebServer(ServletContextInitializer... initializers) {
		Tomcat tomcat = new Tomcat();
		File baseDir = (this.baseDirectory != null) ? this.baseDirectory : createTempDir("tomcat");
		tomcat.setBaseDir(baseDir.getAbsolutePath());
		Connector connector = new Connector(this.protocol);
		tomcat.getService().addConnector(connector);
		customizeConnector(connector);
		tomcat.setConnector(connector);
		tomcat.getHost().setAutoDeploy(false);
		configureEngine(tomcat.getEngine());
		for (Connector additionalConnector : this.additionalTomcatConnectors) {
			tomcat.getService().addConnector(additionalConnector);
		}
		prepareContext(tomcat.getHost(), initializers);
		return getTomcatWebServer(tomcat);
	}
复制代码

According to the above code, we find that mainly do two things, the first thing is to add Connnctor (we call connectors) object to the Tomcat, the second thing is configureEngine, this connector we can barely understand ( do not understand the back will tell), then this Engineis it? We view the tomcat.getEngine()source code:

    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;
    }
复制代码

According to the above source, we discovered that the Engine is a container, we continue to track the source, find Containerthe interface

The figure above, we see the four sub-interfaces, respectively, Engine, Host, Context, Wrapper. We know from the inheritance they are containers, they in the end so what difference does it make? I look at their notes is how to say.

 /**
 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
 */
public interface Engine extends Container {
    //省略代码
}
/**
 * <p>
 * The parent Container attached to a Host is generally an Engine, but may
 * be some other implementation, or may be omitted if it is not necessary.
 * <p>
 * The child containers attached to a Host are generally implementations
 * of Context (representing an individual servlet context).
 *
 * @author Craig R. McClanahan
 */
public interface Host extends Container {
//省略代码
    
}
/*** <p>
 * The parent Container attached to a Context is generally a Host, but may
 * be some other implementation, or may be omitted if it is not necessary.
 * <p>
 * The child containers attached to a Context are generally implementations
 * of Wrapper (representing individual servlet definitions).
 * <p>
 *
 * @author Craig R. McClanahan
 */
public interface Context extends Container, ContextBind {
    //省略代码
}
/**<p>
 * The parent Container attached to a Wrapper will generally be an
 * implementation of Context, representing the servlet context (and
 * therefore the web application) within which this servlet executes.
 * <p>
 * Child Containers are not allowed on Wrapper implementations, so the
 * <code>addChild()</code> method should throw an
 * <code>IllegalArgumentException</code>.
 *
 * @author Craig R. McClanahan
 */
public interface Wrapper extends Container {

    //省略代码
}

复制代码

The above comments translates, Engineit is the highest-level container, the container is its child Host, Hostthe child container is Context, Wrapperis a Contextchild container, so the relationship between these four vessels is the parent-child relationship, that is, Engine> Host> Context> Wrapper. Let us look at Tomcatthe source code like:

//部分源码,其余部分省略。
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;
    }
    
}
复制代码

Reading Tomcatof getServer()us can know that Tomcatthe topmost is Server, Server is Tomcatan instance of a Tomcata Server; by getEngine()that we can understand Server Service is below, and is more of a Service on behalf of a deployment of our application, but we also know that Enginethe container one servicehas only one; according to the parent-child relationship, we look at setHost()the source code can know that hostcontainer has more; Similarly, we find that addContext()at the source, Contextbut also more; addServlet()indicates that Wrapperthe container is more, and this code also suggests that, in fact, Wrapperand Servletis a layer of meaning. The addition, we setConnectorcan know the source, a connector ( Connector) is disposed serviceunder, and is provided a plurality of connectors ( Connector).

According to the above analysis, we can Nodules: the Tomcat core contains two major components, a connector (Connector) and the container (Container), Fig follows:

A Tomcatis a Server, a Servercase where a plurality service, i.e. we deploy a plurality of applications, a plurality of connectors (next application Connector) and a container ( Container), the plurality of sub-container vessel, represented by the relationship is as follows:

EngineThe plurality of Hostsub-containers, Hostwhen a plurality of Contextsub-containers, Contextwhen a plurality of Wrappersub-containers.

to sum up

SpringBoot started using new SpringApplication()examples to start, start the process mainly to do several things as follows:

  1. Configuration Properties
  2. Get the listener, started publishing application event
  3. Initialization input parameters
  4. Configure the environment, the output banner
  5. Creating a Context
  6. Pretreatment context
  7. Refresh context
  8. Then refresh context
  9. Event publishing application has been launched
  10. Posted complete application launch event

Tomcat is initiated at step 7, "Refresh context"; Tomcat startup initialization mainly two core components, the connector (Connector) and the container (Container), a Tomcat instance is a Server, comprising a plurality of-Service Server, that is a plurality of applications, comprising a plurality of connectors each Service (Connetor) and a container (container), there are a plurality of sub-container and the lower container, according to parent-child relationships are: Engine, Host, Context, Wrapper, wherein in addition Engine, the rest of the plurality of containers are possible.

Looking to the next issue

Articles in this issue by starting SpringBoot to spy on the internal structure of Tomcat, the next issue, we analyze the connectors under this article ( Connetorrole) and the container (Container), so stay tuned.

Guess you like

Origin juejin.im/post/5d3f95ebf265da039e12959e