SpringMvc源码分析(一):启动tomcat服务器,加载DispatcherServlet并将DispatcherServlet纳入tomcat管理

news2025/1/13 13:27:38

      SpringMvc是主流的MVC框架,它是基于Spring提供的web应用框架,该框架遵循servlet规范。该框架的作用是接收Servlet容器(如Tomcat)传递过来的请求并返回响应。SpringMvc的核心就是servlet实例,而这个servlet在spring中就是DispatcherServlet实例。

    dispatcher英文翻译过来就是调度的意思。它的主要功能如下:

   1、文件上传解析,假设请求类型是multipart将通过MultipartResolver进行文件上传解析;

   2、通过HandlerMapping,将请求映射到处理器(返回一个HandlerExecutionChain,它包含一个处理器、多个HandlerInterceptor拦截器;

   3、 通过HandlerAdapter支持多种类型的处理器(HandlerExecutionChain中的处理器);

   4、通过ViewResolver解析逻辑视图名到详细视图实现;

   5、本地化解析;

   6、渲染详细的视图等;

   7、假设运行过程中遇到异常将交给HandlerExceptionResolver来解析。 从以上我们能够看出DispatcherServlet主要负责流程的控制(并且在流程中的每一个关键点都是非常easy扩展的)

     Springboot项目在启动过程中涉及到  配置系统属性、获取监听器,发布应用开始启动事件、初始化参数、配置环境、创建应用上下文、预处理上下文、刷新上下文、再次刷新上下文、发布应用已经启动事件、发布应用启动完成事件。而启动 Tomcat 是刷新上下文 这一步。

    下文中,我们将从代码的角度了解DispatcherServlet是如何在tomcat服务器启动的过程中加载并对外提供服务。

步骤一:DispatcherServlet bean对象的生成

1.在自动配置类DispatcherServletAutoConfiguration类中,内部类DispatcherServletConfiguration通过

@Bean(name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)

的方式向spring容器中注入了类型为DispatcherServlet,beanName为dispatcherServlet的bean实例。

内部类DispatcherServletRegistrationConfiguration通过

@Bean(name = DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME)

注入了类型为DispatcherServletRegistrationBean,beanName为dispatcherServletRegistration的bean实例。

public static final String DEFAULT_DISPATCHER_SERVLET_BEAN_NAME = "dispatcherServlet";

	/*
	 * The bean name for a ServletRegistrationBean for the DispatcherServlet "/"
	 */
	public static final String DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME = "dispatcherServletRegistration";

	@Configuration
	@Conditional(DefaultDispatcherServletCondition.class)
	@ConditionalOnClass(ServletRegistration.class)
	@EnableConfigurationProperties(WebMvcProperties.class)
	protected static class DispatcherServletConfiguration {

		private final WebMvcProperties webMvcProperties;

		public DispatcherServletConfiguration(WebMvcProperties webMvcProperties) {
			this.webMvcProperties = webMvcProperties;
		}

        //注意此处,该注解向spring容器中注入了beanName为dispatcherServlet的bean
		@Bean(name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
		public DispatcherServlet dispatcherServlet() {
			DispatcherServlet dispatcherServlet = new DispatcherServlet();
			dispatcherServlet.setDispatchOptionsRequest(
					this.webMvcProperties.isDispatchOptionsRequest());
			dispatcherServlet.setDispatchTraceRequest(
					this.webMvcProperties.isDispatchTraceRequest());
			dispatcherServlet.setThrowExceptionIfNoHandlerFound(
					this.webMvcProperties.isThrowExceptionIfNoHandlerFound());
			return dispatcherServlet;
		}

		@Bean
		@ConditionalOnBean(MultipartResolver.class)
		@ConditionalOnMissingBean(name = DispatcherServlet.MULTIPART_RESOLVER_BEAN_NAME)
		public MultipartResolver multipartResolver(MultipartResolver resolver) {
			// Detect if the user has created a MultipartResolver but named it incorrectly
			return resolver;
		}

	}

	@Configuration
	@Conditional(DispatcherServletRegistrationCondition.class)
	@ConditionalOnClass(ServletRegistration.class)
	@EnableConfigurationProperties(WebMvcProperties.class)
	@Import(DispatcherServletConfiguration.class)
	protected static class DispatcherServletRegistrationConfiguration {

		private final ServerProperties serverProperties;

		private final WebMvcProperties webMvcProperties;

		private final MultipartConfigElement multipartConfig;

		public DispatcherServletRegistrationConfiguration(
				ServerProperties serverProperties, WebMvcProperties webMvcProperties,
				ObjectProvider<MultipartConfigElement> multipartConfigProvider) {
			this.serverProperties = serverProperties;
			this.webMvcProperties = webMvcProperties;
			this.multipartConfig = multipartConfigProvider.getIfAvailable();
		}
    
        //注意此处,该注解向spring容器注入了beanName为dispatcherServletRegistration的bean
		@Bean(name = DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME)
		@ConditionalOnBean(value = DispatcherServlet.class, name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
		public DispatcherServletRegistrationBean dispatcherServletRegistration(
				DispatcherServlet dispatcherServlet) {
            //注意此处,将dispatcherServlet赋值到DispatcherServletRegistrationBean父类
            //对象ServletRegistrationBean的属性servlet中。
			DispatcherServletRegistrationBean registration = new DispatcherServletRegistrationBean(
					dispatcherServlet, this.serverProperties.getServlet().getPath());
			registration.setName(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME);
			registration.setLoadOnStartup(
					this.webMvcProperties.getServlet().getLoadOnStartup());
			if (this.multipartConfig != null) {
				registration.setMultipartConfig(this.multipartConfig);
			}
			return registration;
		}

	}

2.在自动装配类ServletWebServerFactoryAutoConfiguration中该类通过

@Import({ ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class,
      ServletWebServerFactoryConfiguration.EmbeddedTomcat.class,
      ServletWebServerFactoryConfiguration.EmbeddedJetty.class,
      ServletWebServerFactoryConfiguration.EmbeddedUndertow.class })

引入了ServletWebServerFactoryConfiguration类。

在该类中可以看到

@ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)

 根据springboot默认引入的是tomcat服务器,所以最终tomcat、jetty、undertow三种注入bean的判断中最终引入的是tomcat。此时根据条件spring容器注入的是TomcatServletWebServerFactory类型的bean。

class ServletWebServerFactoryConfiguration {

	@Configuration
	@ConditionalOnClass({ Servlet.class, Tomcat.class, UpgradeProtocol.class })
	@ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)
	public static class EmbeddedTomcat {
        //注意此处,往spring容器注入的是TomcatServletWebServerFactory类型的bean
		@Bean
		public TomcatServletWebServerFactory tomcatServletWebServerFactory() {
			return new TomcatServletWebServerFactory();
		}

	}

	/**
	 * Nested configuration if Jetty is being used.
	 */
	@Configuration
	@ConditionalOnClass({ Servlet.class, Server.class, Loader.class,
			WebAppContext.class })
	@ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)
	public static class EmbeddedJetty {

		@Bean
		public JettyServletWebServerFactory JettyServletWebServerFactory() {
			return new JettyServletWebServerFactory();
		}

	}

	/**
	 * Nested configuration if Undertow is being used.
	 */
	@Configuration
	@ConditionalOnClass({ Servlet.class, Undertow.class, SslClientAuthMode.class })
	@ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)
	public static class EmbeddedUndertow {

		@Bean
		public UndertowServletWebServerFactory undertowServletWebServerFactory() {
			return new UndertowServletWebServerFactory();
		}

	}

}

步骤二:spring容器启动并将DispatcherServlet与tomcat服务器关联起来

1.在springboot项目中,应用是通过Application里的main方法启动,跟踪main方法

+Application#main

 +SpringApplication#run(Class<?> primarySource, String... args)

  +SpringApplication#run(Class<?>[] primarySources, String[] args)

   +SpringApplication#run(String... args)

进入到SpringApplication的run方法中。

public ConfigurableApplicationContext run(String... args) {
		StopWatch stopWatch = new StopWatch();
		stopWatch.start();
		ConfigurableApplicationContext context = null;
		Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
		configureHeadlessProperty();
		SpringApplicationRunListeners listeners = getRunListeners(args);
		listeners.starting();
		try {
			ApplicationArguments applicationArguments = new DefaultApplicationArguments(
					args);
			ConfigurableEnvironment environment = prepareEnvironment(listeners,
					applicationArguments);
			configureIgnoreBeanInfo(environment);
			Banner printedBanner = printBanner(environment);
            //关注此处,此处生成了应用上下文
			context = createApplicationContext();
			exceptionReporters = getSpringFactoriesInstances(
					SpringBootExceptionReporter.class,
					new Class[] { ConfigurableApplicationContext.class }, context);
			prepareContext(context, environment, listeners, applicationArguments,
					printedBanner);
            //关注此处,此处完成了tomcat服务器和servlet的关联
			refreshContext(context);
			afterRefresh(context, applicationArguments);
			stopWatch.stop();
			if (this.logStartupInfo) {
				new StartupInfoLogger(this.mainApplicationClass)
						.logStarted(getApplicationLog(), stopWatch);
			}
			listeners.started(context);
			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)createApplicationContext方法

(2)refreshContext方法

进入到createApplicationContext方法,该方法会返回AnnotationConfigServletWebServerApplicationContext类型的上下文.

protected ConfigurableApplicationContext createApplicationContext() {
		Class<?> contextClass = this.applicationContextClass;
		if (contextClass == null) {
			try {
                //webApplicationType是通过SpringApplication构造方法里的deduceWebApplicationType方法赋值的,赋值的类型是SERVLET
				switch (this.webApplicationType) {
                /** 由于webApplicationType的类型是SERVLET,返回DEFAULT_WEB_CONTEXT_CLASS类型的上下文,
而DEFAULT_WEB_CONTEXT_CLASS的定义是public static final String DEFAULT_WEB_CONTEXT_CLASS 
= "org.springframework.boot."
			+ "web.servlet.context.AnnotationConfigServletWebServerApplicationContext";
所以会返回的上下文是AnnotationConfigServletWebServerApplicationContext
**/
				case SERVLET:
					contextClass = Class.forName(DEFAULT_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);
	}

继续跟踪refreshContext方法,该方法的入参是createApplicationContext返回的AnnotationConfigServletWebServerApplicationContext类型的上下文。点击源码深入到AbstractApplicationContext#refresh方法中,该方法是调用的AnnotationConfigServletWebServerApplicationContext对象的refresh方法。

+SpringApplication#run(String... args)

 +SpringApplication#refreshContext(ConfigurableApplicationContext context)

  +SpringApplication#refresh(ApplicationContext applicationContext)

   +AbstractApplicationContext#refresh()

可以看到该方法中有一个onRefresh方法,该方法是一个空方法。

@Override
	public void refresh() throws BeansException, IllegalStateException {
		synchronized (this.startupShutdownMonitor) {
           //。。。。省略

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

				//。。。。省略

				// Initialize other special beans in specific context subclasses.
				onRefresh();

				//。。。。省略
			}

			catch (BeansException ex) {
				//。。。。省略
			}

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

根据继承树我们发现AnnotationConfigServletWebServerApplicationContext继承了ServletWebServerApplicationContext,最终继承了AbstractApplicationContext类。

 

 根据java的动态绑定机制,AbstractApplicationContext的onRefresh方法调用时,实际调用的是ServletWebServerApplicationContext中的重写的onRefresh方法。

	@Override
	protected void onRefresh() {
		super.onRefresh();
		try {
            //关注此方法,该方法建立了spring容器与tomcat之间的关系
			createWebServer();
		}
		catch (Throwable ex) {
			throw new ApplicationContextException("Unable to start web server", ex);
		}
	}

关注并进入createWebServer方法

private void createWebServer() {
		WebServer webServer = this.webServer;
		ServletContext servletContext = getServletContext();
		if (webServer == null && servletContext == null) {
            //获取工厂对象,返回的是TomcatServletWebServerFactory类型的实例
			ServletWebServerFactory factory = getWebServerFactory();
            //获取web服务器对象
			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();
	}

 点击进入到getWebServerFactory方法中,该方法通过getBeanFactory().getBean(beanNames[0], ServletWebServerFactory.class)返回一个ServletWebServerFactory类型的工厂bean。

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);
	}

之前在步骤一中,spring通过自动装配返回的是 TomcatServletWebServerFactory类型的工厂bean。查看TomcatServletWebServerFactory的继承和实现树,发现该类实现了ServletWebServerFactory,所以在aplicationContext中的获取的是TomcatServletWebServerFactory的实例。

 在 ServletWebServerApplicationContext#createWebServer方法中调用factory.getWebServer(getSelfInitializer())方法,因为factory是TomcatServletWebServerFactory类型的对象,所以调用的是TomcatServletWebServerFactory里的getWebServer方法。该方法的入参是getSelfInitializer()返回的对象,跟踪getSelfInitializer()方法最后定位到ServletWebServerApplicationContext#selfInitialize(ServletContext servletContext)方法。

private void selfInitialize(ServletContext servletContext) throws ServletException {
		prepareWebApplicationContext(servletContext);
		ConfigurableListableBeanFactory beanFactory = getBeanFactory();
		ExistingWebApplicationScopes existingScopes = new ExistingWebApplicationScopes(
				beanFactory);
		WebApplicationContextUtils.registerWebApplicationScopes(beanFactory,
				getServletContext());
		existingScopes.restore();
		WebApplicationContextUtils.registerEnvironmentBeans(beanFactory,
				getServletContext());
        //关注getServletContextInitializerBeans方法
		for (ServletContextInitializer beans : getServletContextInitializerBeans()) {
			beans.onStartup(servletContext);
		}
	}

进入到ServletWebServerApplicationContext#getServletContextInitializerBeans方法。发现该方法返回一个ServletContextInitializerBeans类型的对象。

protected Collection<ServletContextInitializer> getServletContextInitializerBeans() {
		return new ServletContextInitializerBeans(getBeanFactory());
	}

进入ServletContextInitializerBeans的构造方法,关注addServletContextInitializerBeans方法。

public ServletContextInitializerBeans(ListableBeanFactory beanFactory) {
		this.initializers = new LinkedMultiValueMap<>();
        //关注此方法,该方法往initializers这个Map对象里添加ServletContextInitializer类型的数据
		addServletContextInitializerBeans(beanFactory);
		addAdaptableBeans(beanFactory);
		List<ServletContextInitializer> sortedInitializers = this.initializers.values()
				.stream()
				.flatMap((value) -> value.stream()
						.sorted(AnnotationAwareOrderComparator.INSTANCE))
				.collect(Collectors.toList());
		this.sortedList = Collections.unmodifiableList(sortedInitializers);
	}

一路跟踪进去

  +ServletContextInitializerBeans#addServletContextInitializerBeans(ListableBeanFactory beanFactory)方法中找到getOrderedBeansOfType(beanFactory, ServletContextInitializer.class)这个方法,getOrderedBeansOfType是从spring容器中获取ServletContextInitializer类型的bean。在步骤一的自动装配阶段,代码向容器中注入了类型为DispatcherServletRegistrationBean的bean对象。

private void addServletContextInitializerBeans(ListableBeanFactory beanFactory) {
		for (Entry<String, ServletContextInitializer> initializerBean : getOrderedBeansOfType(
				beanFactory, ServletContextInitializer.class)) {
            //向容器注入servlet
			addServletContextInitializerBean(initializerBean.getKey(),
					initializerBean.getValue(), beanFactory);
		}
	}

获取DispatcherServletRegistrationBean类的继承和实现树,发现DispatcherServletRegistrationBean实现了ServletContextInitializer接口,getOrderedBeansOfType(beanFactory, ServletContextInitializer.class)实际上返回的是DispatcherServletRegistrationBean类型的对象。

继续跟踪下去,发现ServletContextInitializerBeans#addServletContextInitializerBeans(ListableBeanFactory beanFactory)方法里调用了addServletContextInitializerBean(String beanName, ServletContextInitializer initializer, ListableBeanFactory beanFactory)方法。源码如下图所示

    因DispatcherServletRegistrationBean继承了ServletRegistrationBean所以会进入第一个if判断里的

private void addServletContextInitializerBean(String beanName,
			ServletContextInitializer initializer, ListableBeanFactory beanFactory) {
		if (initializer instanceof ServletRegistrationBean) {
			Servlet source = ((ServletRegistrationBean<?>) initializer).getServlet();
			addServletContextInitializerBean(Servlet.class, beanName, initializer,
					beanFactory, source);
		}
		else if (initializer instanceof FilterRegistrationBean) {
			Filter source = ((FilterRegistrationBean<?>) initializer).getFilter();
			addServletContextInitializerBean(Filter.class, beanName, initializer,
					beanFactory, source);
		}
		else if (initializer instanceof DelegatingFilterProxyRegistrationBean) {
			String source = ((DelegatingFilterProxyRegistrationBean) initializer)
					.getTargetBeanName();
			addServletContextInitializerBean(Filter.class, beanName, initializer,
					beanFactory, source);
		}
		else if (initializer instanceof ServletListenerRegistrationBean) {
			EventListener source = ((ServletListenerRegistrationBean<?>) initializer)
					.getListener();
			addServletContextInitializerBean(EventListener.class, beanName, initializer,
					beanFactory, source);
		}
		else {
			addServletContextInitializerBean(ServletContextInitializer.class, beanName,
					initializer, beanFactory, initializer);
		}
	}

 在该逻辑中调用了addServletContextInitializerBean方法,最终向initializers对象里添加了DispatcherServletRegistrationBean类型的bean。

private void addServletContextInitializerBean(Class<?> type, String beanName,
			ServletContextInitializer initializer, ListableBeanFactory beanFactory,
			Object source) {
        //向initializers对象中添加DispatcherServletRegistrationBean类型的bean
		this.initializers.add(type, initializer);
		if (source != null) {
			// Mark the underlying source as seen in case it wraps an existing bean
			this.seen.add(source);
		}
		if (ServletContextInitializerBeans.logger.isDebugEnabled()) {
			String resourceDescription = getResourceDescription(beanName, beanFactory);
			int order = getOrder(initializer);
			ServletContextInitializerBeans.logger.debug("Added existing "
					+ type.getSimpleName() + " initializer bean '" + beanName
					+ "'; order=" + order + ", resource=" + resourceDescription);
		}
	}

了解完ServletWebServerApplicationContext类中getSelfInitializer()方法的逻辑后,回到createWebServer方法里调用的factory.getWebServer(getSelfInitializer())方法。因为factory对象是TomcatServletWebServerFactory类型的实例,所以getWebServer方法调用的是TomcatServletWebServerFactory里的getWebServer方法。

观察getWebServer方法

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 实现了StandardEngine放入StandardHost的逻辑
		tomcat.getHost().setAutoDeploy(false);
		configureEngine(tomcat.getEngine());
		for (Connector additionalConnector : this.additionalTomcatConnectors) {
			tomcat.getService().addConnector(additionalConnector);
		}
        //关注此处
		prepareContext(tomcat.getHost(), initializers);
        //关注此处
		return getTomcatWebServer(tomcat);
	}

点开其中的prepareContext方法,

protected void prepareContext(Host host, ServletContextInitializer[] initializers) {
		File documentRoot = getValidDocumentRoot();
        //继承了StandardContext类
		TomcatEmbeddedContext context = new TomcatEmbeddedContext();
		if (documentRoot != null) {
			context.setResources(new LoaderHidingResourceRoot(context));
		}
		context.setName(getContextPath());
		context.setDisplayName(getDisplayName());
		context.setPath(getContextPath());
		File docBase = (documentRoot != null) ? documentRoot
				: createTempDir("tomcat-docbase");
		context.setDocBase(docBase.getAbsolutePath());
          // 注册一个FixContextListener监听,这个监听用于设置context的配置状态以及是否加入登录验证的逻辑
		context.addLifecycleListener(new FixContextListener());
		context.setParentClassLoader(
				(this.resourceLoader != null) ? this.resourceLoader.getClassLoader()
						: ClassUtils.getDefaultClassLoader());
		resetDefaultLocaleMapping(context);
		addLocaleMappings(context);
		context.setUseRelativeRedirects(false);
		configureTldSkipPatterns(context);
		WebappLoader loader = new WebappLoader(context.getParentClassLoader());
		loader.setLoaderClass(TomcatEmbeddedWebappClassLoader.class.getName());
		loader.setDelegate(true);
		context.setLoader(loader);
		if (isRegisterDefaultServlet()) {
			addDefaultServlet(context);
		}
		if (shouldRegisterJspServlet()) {
			addJspServlet(context);
			addJasperInitializer(context);
		}
		context.addLifecycleListener(new StaticResourceConfigurer(context));
		ServletContextInitializer[] initializersToUse = mergeInitializers(initializers);
		host.addChild(context);
        //关注此处
		configureContext(context, initializersToUse);
		postProcessContext(context);
	}
protected void configureContext(Context context,
			ServletContextInitializer[] initializers) {
		TomcatStarter starter = new TomcatStarter(initializers);
		if (context instanceof TomcatEmbeddedContext) {
			// Should be true
			((TomcatEmbeddedContext) context).setStarter(starter);
		}
        //将tomcatStarter这个ServletContainerInitializer放入上下文中
		context.addServletContainerInitializer(starter, NO_CLASSES);
		for (LifecycleListener lifecycleListener : this.contextLifecycleListeners) {
			context.addLifecycleListener(lifecycleListener);
		}
		for (Valve valve : this.contextValves) {
			context.getPipeline().addValve(valve);
		}
		for (ErrorPage errorPage : getErrorPages()) {
			new TomcatErrorPage(errorPage).addToContext(context);
		}
		for (MimeMappings.Mapping mapping : getMimeMappings()) {
			context.addMimeMapping(mapping.getExtension(), mapping.getMimeType());
		}
		configureSession(context);
		for (TomcatContextCustomizer customizer : this.tomcatContextCustomizers) {
			customizer.customize(context);
		}
	}

 继续回到TomcatServletWebServerFactory#getWebServer方法,在该方法的最后面返回的是getTomcatWebServer方法返回的对象。进入到该方法发现new 了一个TomcatWebServer对象。

protected TomcatWebServer getTomcatWebServer(Tomcat tomcat) {
		return new TomcatWebServer(tomcat, getPort() >= 0);
	}

进入该对象的构造方法

public TomcatWebServer(Tomcat tomcat, boolean autoStart) {
		Assert.notNull(tomcat, "Tomcat Server must not be null");
		this.tomcat = tomcat;
		this.autoStart = autoStart;
        //关注此方法
		initialize();
	}

 进入initialize方法,代码如下

private void initialize() throws WebServerException {
		TomcatWebServer.logger
				.info("Tomcat initialized with port(s): " + getPortsDescription(false));
		synchronized (this.monitor) {
			try {
                //生成StandardService,StandardEngine,并赋值到StandardEngine对象到
                //StandardService的engine属性中
				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();
					}
				});
                //关注此方法,启动tomcat服务器
				// 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();
				throw new WebServerException("Unable to start embedded Tomcat", ex);
			}
		}
	}

 关注this.tomcat.start();方法,该方法启动了tomcat服务器,进入start方法。

public void start() throws LifecycleException {
        getServer();
        getConnector();
        server.start();
    }

点击server.start方法 发现start方法是一个待实现的方法。

public void start() throws LifecycleException;

 start方法是server对象调用的,server对象是tomcat对象的一个属性,回到TomcatServletWebServerFactory#getWebServer方法中tomcat对象是通过

Tomcat tomcat = new Tomcat();

 来实现的。tomcat的server属性是在getWebServer方法的tomcat.getService().addConnector(connector);这段代码中getService方法赋值的。点开该方法

public Service getService() {
        return getServer().findServices()[0];
    }

 继续点击Tomcat类里的getServer方法

public Server getServer() {

        if (server != null) {
            return server;
        }

        System.setProperty("catalina.useNaming", "false");
        //对tomcat里的server属性赋值
        server = new StandardServer();

        initBaseDir();

        server.setPort( -1 );

        Service service = new StandardService();
        service.setName("Tomcat");
        //对standardServer里的services属性赋值
        server.addService(service);
        return server;
    }

观看上面的代码发现server是 StandardServer类型的对象。获取该类型的继承实现树,发现继承了

LifecycleBase类。

所以server.start方法这个待实现的方法实际调用的是LifecycleBase里的start方法

public final synchronized void start() throws LifecycleException {

        if (LifecycleState.STARTING_PREP.equals(state) || LifecycleState.STARTING.equals(state) ||
                LifecycleState.STARTED.equals(state)) {

            if (log.isDebugEnabled()) {
                Exception e = new LifecycleException();
                log.debug(sm.getString("lifecycleBase.alreadyStarted", toString()), e);
            } else if (log.isInfoEnabled()) {
                log.info(sm.getString("lifecycleBase.alreadyStarted", toString()));
            }

            return;
        }

        if (state.equals(LifecycleState.NEW)) {
            init();
        } else if (state.equals(LifecycleState.FAILED)) {
            stop();
        } else if (!state.equals(LifecycleState.INITIALIZED) &&
                !state.equals(LifecycleState.STOPPED)) {
            invalidTransition(Lifecycle.BEFORE_START_EVENT);
        }

        try {
            setStateInternal(LifecycleState.STARTING_PREP, null, false);
            //关注该方法
            startInternal();
            if (state.equals(LifecycleState.FAILED)) {
                // This is a 'controlled' failure. The component put itself into the
                // FAILED state so call stop() to complete the clean-up.
                stop();
            } else if (!state.equals(LifecycleState.STARTING)) {
                // Shouldn't be necessary but acts as a check that sub-classes are
                // doing what they are supposed to.
                invalidTransition(Lifecycle.AFTER_START_EVENT);
            } else {
                setStateInternal(LifecycleState.STARTED, null, false);
            }
        } catch (Throwable t) {
            // This is an 'uncontrolled' failure so put the component into the
            // FAILED state and throw an exception.
            ExceptionUtils.handleThrowable(t);
            setStateInternal(LifecycleState.FAILED, null, false);
            throw new LifecycleException(sm.getString("lifecycleBase.startFail", toString()), t);
        }
    }

在start方法中找到startInternal()方法,发现该方法也是一个未实现的方法,根据动态绑定机制实际调用的是StandardServer类里的startInternal()方法。

protected void startInternal() throws LifecycleException {

        fireLifecycleEvent(CONFIGURE_START_EVENT, null);
        setState(LifecycleState.STARTING);

        globalNamingResources.start();

        // Start our defined Services
        synchronized (servicesLock) {
            for (int i = 0; i < services.length; i++) {
                //关注此处
                services[i].start();
            }
        }
    }

 在上面的代码段services[i].start()中,servics是standardServer的一个属性,该属性是在Tomcat类里的getServer方法里的server.addService(service)实现赋值的。赋值的类型是StandardService。所以services[i].start()执行的是StandardService里的start方法。

而StandardService的继承实现关系如下图所示。

 所以start方法实际调用的是LifecycleBase里的start方法,重复上面的的逻辑,在start方法中执行startInternal方法,在上面的内容中提到startInternal方法是一个待实现的方法。它调用的是StandardService类里的startInternal方法。

if(log.isInfoEnabled())
            log.info(sm.getString("standardService.start.name", this.name));
        setState(LifecycleState.STARTING);

        // Start our defined Container first
        if (engine != null) {
            synchronized (engine) {
                //关注此处
                engine.start();
            }
        }

        synchronized (executors) {
            for (Executor executor: executors) {
                executor.start();
            }
        }

        mapperListener.start();

        // Start our defined Connectors second
        synchronized (connectorsLock) {
            for (Connector connector: connectors) {
                try {
                    // If it has already failed, don't try and start it
                    if (connector.getState() != LifecycleState.FAILED) {
                        connector.start();
                    }
                } catch (Exception e) {
                    log.error(sm.getString(
                            "standardService.connector.startFailed",
                            connector), e);
                }
            }
        }
    }

 在该方法中可以看到engine.start()方法,engine是在此文上面TomcatWebServer类构造时的initialize方法中赋值的,赋值的对象类型是StandardEngine。

engine赋值过程具体的调用链路如下:

+TomcatWebServer#TomcatWebServer(Tomcat tomcat, boolean autoStart)

 +TomcatWebServer#initialize()

  +TomcatWebServer#addInstanceIdToEngineName()

   +Tomcat#getEngine()

    +StandardServer#setContainer(Engine engine)

     +StandardService#setContainer(Engine engine)

在addInstanceIdToEngineName方法中通过this.tomcat.getEngine()找到Tomcat类里的getEngine方法,该方法中通过Engine engine = new StandardEngine();进行初始化,并通过service.setContainer(engine)【注:service是通过getServer().findServices()[0]返回的StandardService】赋值,service.setContainer(engine)实际调用的是StandardService里的setContainer方法并通过this.engine = engine对engine属性赋值。以下的代码取自StandardService类里的setContainer方法,可以看到赋值过程。

@Override
    public void setContainer(Engine engine) {
        Engine oldEngine = this.engine;
        if (oldEngine != null) {
            oldEngine.setService(null);
        }
        //对StandardService里的engine属性赋值
        this.engine = engine;
        if (this.engine != null) {
            this.engine.setService(this);
        }
        if (getState().isAvailable()) {
            if (this.engine != null) {
                try {
                    this.engine.start();
                } catch (LifecycleException e) {
                    log.warn(sm.getString("standardService.engine.startFailed"), e);
                }
            }
            // Restart MapperListener to pick up new engine.
            try {
                mapperListener.stop();
            } catch (LifecycleException e) {
                log.warn(sm.getString("standardService.mapperListener.stopFailed"), e);
            }
            try {
                mapperListener.start();
            } catch (LifecycleException e) {
                log.warn(sm.getString("standardService.mapperListener.startFailed"), e);
            }
            if (oldEngine != null) {
                try {
                    oldEngine.stop();
                } catch (LifecycleException e) {
                    log.warn(sm.getString("standardService.engine.stopFailed"), e);
                }
            }
        }

        // Report this property change to interested listeners
        support.firePropertyChange("container", oldEngine, this.engine);
    }

 继续回到StandardService里的startInternal()方法里的engine.start()方法,该方法调用的是StandardEngine里的start方法,根据该类的继承实现树,实际调用的是LifecycleBase里的start方法。

 该方法上文有分析,继续根据start方法里调用的startInternal方法,这个时候实际调用的是StandardEngine里的startInternal方法。以下是StandardEngine里的startInternal方法源码。

 在该源码中可以看到super.startInternal方法,实际调用的是ContainerBase类里的startInternal方法。

 protected synchronized void startInternal() throws LifecycleException {

        // Log our server identification information
        if(log.isInfoEnabled())
            log.info( "Starting Servlet Engine: " + ServerInfo.getServerInfo());
        //关注此处代码
        // Standard container startup
        super.startInternal();
    }

 进入ContainerBase类里的startInternal方法,关注

protected synchronized void startInternal() throws LifecycleException {

        // Start our subordinate components, if any
        logger = null;
        getLogger();
        Cluster cluster = getClusterInternal();
        if (cluster instanceof Lifecycle) {
            ((Lifecycle) cluster).start();
        }
        Realm realm = getRealmInternal();
        if (realm instanceof Lifecycle) {
            ((Lifecycle) realm).start();
        }
        // 关注此处
        // Start our child containers, if any
        Container children[] = findChildren();
        List<Future<Void>> results = new ArrayList<>();
        for (int i = 0; i < children.length; i++) {
            results.add(startStopExecutor.submit(new StartChild(children[i])));
        }

        boolean fail = false;
        for (Future<Void> result : results) {
            try {
                result.get();
            } catch (Exception e) {
                log.error(sm.getString("containerBase.threadedStartFailed"), e);
                fail = true;
            }

        }
        if (fail) {
            throw new LifecycleException(
                    sm.getString("containerBase.threadedStartFailed"));
        }

        // Start the Valves in our pipeline (including the basic), if any
        if (pipeline instanceof Lifecycle)
            ((Lifecycle) pipeline).start();


        setState(LifecycleState.STARTING);

        // Start our thread
        threadStart();

    }

在Container children[] = findChildren();这段代码中获取到获取到容器children里的值

public Container[] findChildren() {
        synchronized (children) {
            Container results[] = new Container[children.size()];
            return children.values().toArray(results);
        }
    }

 而children里的值是在

+TomcatServletWebServerFactory#getWebServer(ServletContextInitializer... initializers)

 +Tomcat#getHost()

  +StandardEngine#addChild(Container child)

   +ContainerBase#addChild(Container child)

    +ContainerBase#addChildInternal(Container child)

中赋值的

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属性赋值,赋值类型为StandardHost
            children.put(child.getName(), child);
        }

 在赋值过程中实际的代码调用过程为 

1.TomcatServletWebServerFactory类里的getWebServer方法中的tomcat.getHost().setAutoDeploy(false)

2.Tomcat类中的getHost()方法通过Host host = new StandardHost()初始化了一个StandardHost类型的对象并调用了getEngine().addChild(host)方法

3.getEngine返回的是StandardEngine类型的对象。此时addChild调用的是StandardEngine里的addChild方法。

4.StandardEngine的addChild方法里调用了super.addChild(child),StandardEngine的父类是ContainerBase类,实际调用的是ContainerBase里的addChild方法。

5.ContainerBase类中的addChild方法中调用了addChildInternal(Container child)方法,该方法对

ContainerBase中的children属性进行了赋值。

回到本文ContainerBase类里的startInternal方法,在该方法中通过线程池的方式调用了StandardHost中的start方法。即通过内部类StartChild中的call方法调用逻辑child.start()实现了

对StandardHost中start方法的调用。

Callable和Future的关系
可以用Future.get来获取Callable接口返回的执行结果,还可以通过Future.isDone()来判断任务是否已经执行完了
在call()未执行完毕之前,调用get()的线程(假定此时是主线程)会被阻塞,直到call方法返回了结果后,此时future.get才会得到结果,然后主线程才会切换到runnable状态
Future是一个存储器,存储了call()这个任务的结果,而这个任务的执行时间是无法提前确定的

        List<Future<Void>> results = new ArrayList<>();
        for (int i = 0; i < children.length; i++) {
            results.add(startStopExecutor.submit(new StartChild(children[i])));
        }

        boolean fail = false;
        for (Future<Void> result : results) {
            try {
                //调用了StartChild里的call方法
                result.get();
            } catch (Exception e) {
                log.error(sm.getString("containerBase.threadedStartFailed"), e);
                fail = true;
            }

        }

而StandardHost中没有start方法,参考 StandardHost的继承实现树,实际调用的是LifecycleBase里的start方法,在该start方法中调用了startInternal方法。startInternal方法是一个待实现的方法,实际调用的是StandardHost中的startInternal方法

 在该类的startInternal方法中

protected synchronized void startInternal() throws LifecycleException {

        // Set error report valve
        String errorValve = getErrorReportValveClass();
        if ((errorValve != null) && (!errorValve.equals(""))) {
            try {
                boolean found = false;
                Valve[] valves = getPipeline().getValves();
                for (Valve valve : valves) {
                    if (errorValve.equals(valve.getClass().getName())) {
                        found = true;
                        break;
                    }
                }
                if(!found) {
                    Valve valve =
                        (Valve) Class.forName(errorValve).getConstructor().newInstance();
                    getPipeline().addValve(valve);
                }
            } catch (Throwable t) {
                ExceptionUtils.handleThrowable(t);
                log.error(sm.getString(
                        "standardHost.invalidErrorReportValveClass",
                        errorValve), t);
            }
        }
        //关注此处,调用了父类的startInternal方法
        super.startInternal();
    }

调用了父类的startInternal方法即ContainerBase的startInternal方法。 重复之前的流程分析,

在ContainerBase 类中startInternal方法中Container children[] = findChildren();这段代码的children属性是通过

+TomcatServletWebServerFactory#getWebServer(ServletContextInitializer... initializers)

 +TomcatServletWebServerFactory#prepareContext(Host host, ServletContextInitializer[] initializers)

  +StandardHost#addChild(Container child)

   +ContainerBase#addChild(Container child)

    +ContainerBase#addChildInternal(Container child)

上面的调用链路赋值的。

1.TomcatServletWebServerFactory类中的getWebServer方法调用了该类的prepareContext方法。

2.在prepareContext方法中TomcatEmbeddedContext context = new TomcatEmbeddedContext()

处理化了一个TomcatEmbeddedContext即TomcatEmbedded上下文对象。

3.prepareContext方法中通过host.addChild(context)方法,将TomcatEmbeddedContext赋值到host对象即StandardHost类型的对象中。

4.StandardHost类中的addChild(Container child)方法里调用了super.addChild(child)方法。

5.ContainerBase(基础容器)是StandardHost的父类,addChild实际调用的是ContainerBase里的addChild方法。

6.ContainerBase中的addChild调用了该类中的addChildInternal方法,该方法通过入参child对children属性进行了赋值

7.addChildInternal的入参child实际上是步骤3中初始化的TomcatEmbeddedContext类型的context。所以

StandardHost里的children属性实际上是TomcatEmbeddedContext类型的context。

注:addChildInternal源码可以参考本文上面的源码,此处不再贴现。

回到StandardHost调用的startInternal方法的分析,在本文上面我们明确了实际调用的是ContainerBase类里的startInternal方法。类似StandardEngine调用ContainerBase类中的startInternal方法,通过多线程池的方法调用了

results.add(startStopExecutor.submit(new StartChild(children[i])))

StartChild类中的call方法,该方法中调用了child.start方法,而child是StartChild构造方法的入参。

而传入的children[i] 实际上是TomcatEmbeddedContext类型的对象。而child.start实际上是TomcatEmbeddedContext调用了start方法。而TomcatEmbeddedContext中没有start方法,根据动态绑定机制,真正执行的是LifecycleBase类里的start方法。

 在LifecycleBase中的start方法中调用了startInternal方法。因start方法的调用者是TomcatEmbeddedContext。按照动态绑定机制回调用TomcatEmbeddedContext中的startInternal方法,因TomcatEmbeddedContext中没有该方法,所以实际上调用的是其父类StandardContext中的startInternal方法。

由于StandardContext中的startInternal方法很长,我们只截取关键部分

protected synchronized void startInternal() throws LifecycleException {

        //。。。。省略
        try {
            //。。。。省略


            // Call ServletContainerInitializers
            for (Map.Entry<ServletContainerInitializer, Set<Class<?>>> entry :
                initializers.entrySet()) {
                try {
                    //关注此处
                    entry.getKey().onStartup(entry.getValue(),
                            getServletContext());
                } catch (ServletException e) {
                    log.error(sm.getString("standardContext.sciFail"), e);
                    ok = false;
                    break;
                }
            }

            //。。。。省略
            // Start ContainerBackgroundProcessor thread
            super.threadStart();
        } finally {
            // Unbinding thread
            unbindThread(oldCCL);
        }

        //。。。。省略
    }

 而initializers属性是通过下面的调用链路

+TomcatServletWebServerFactory#getWebServer(ServletContextInitializer... initializers)

 +TomcatServletWebServerFactory#prepareContext(Host host, ServletContextInitializer[] initializers)

  +TomcatServletWebServerFactory#configureContext(Context context, ServletContextInitializer[] initializers)

   +StandardContext.addServletContainerInitializer( ServletContainerInitializer sci, Set<Class<?>> classes)

在TomcatServletWebServerFactory的configureContext方法中调用了

context.addServletContainerInitializer(starter, NO_CLASSES)这一段代码

在这段代码中context是在prepareContext中通过

TomcatEmbeddedContext context = new TomcatEmbeddedContext()

生成的。入参starter是在configureContext中通过

TomcatStarter starter = new TomcatStarter(initializers)

方法生成的。因为TomcatEmbeddedContext中没有addServletContainerInitializer方法实际上是调用的其父类StandardContext里的addServletContainerInitializer的方法。设置的StandardContext里的initializers属性实际上是一个TomcatStarter类型的对象。

在StandardContext中的startInternal方法中,entry.getKey()返回的是TomcatStarter,

for (Map.Entry<ServletContainerInitializer, Set<Class<?>>> entry :
                initializers.entrySet()) {
                try {
                    //关注此处,getServletContext返回的是ApplicationContext
                    entry.getKey().onStartup(entry.getValue(),
                            getServletContext());
                } catch (ServletException e) {
                    log.error(sm.getString("standardContext.sciFail"), e);
                    ok = false;
                    break;
                }
            }

 onStartup方法调用的是TomcatStarter中的onStartup方法。该方法中的initializer.onStartup中的调用者initializer属性是

ServletWebServerApplicationContext中createWebServer方法里调用的getSelfInitializer生成的对象来赋值的,按照本文上面的分析getSelfInitializer返回的是DispatcherServletRegistrationBean类型的对象。

        

public void onStartup(Set<Class<?>> classes, ServletContext servletContext)
			throws ServletException {
		try {
			for (ServletContextInitializer initializer : this.initializers) {
				initializer.onStartup(servletContext);
			}
		}
		catch (Exception ex) {
			this.startUpException = ex;
			// Prevent Tomcat from logging and re-throwing when we know we can
			// deal with it in the main thread, but log for information here.
			if (logger.isErrorEnabled()) {
				logger.error("Error starting Tomcat context. Exception: "
						+ ex.getClass().getName() + ". Message: " + ex.getMessage());
			}
		}
	}

 所以根据DispatcherServletRegistrationBean的继承树,调用的实际上是RegistrationBean里的onStartup方法。

 在该方法中调用了register(String description, ServletContext servletContext)方法

实际上调用的是父类DynamicRegistrationBean里的register方法。

protected final void register(String description, ServletContext servletContext) {
        //关注此处
		D registration = addRegistration(description, servletContext);
		if (registration == null) {
			logger.info(StringUtils.capitalize(description) + " was not registered "
					+ "(possibly already registered?)");
			return;
		}
		configure(registration);
	}

 继续分析addRegistration方法,该方法是一个未实现的方法,实际调用的是DispatcherServletRegistrationBean父类ServletRegistrationBean里的addRegistration方法。

到此我们终于分析到DipatcherServlet被Tomcat管理的最核心的代码了。

protected ServletRegistration.Dynamic addRegistration(String description,
			ServletContext servletContext) {
		String name = getServletName();
		logger.info("Servlet " + name + " mapped to " + this.urlMappings);
        //关注此处
		return servletContext.addServlet(name, this.servlet);
	}

 在ServletRegistrationBean中的servlet属性是通过DispatcherServletRegistrationBean自动装配时的

DispatcherServletRegistrationBean registration = new DispatcherServletRegistrationBean( dispatcherServlet, this.serverProperties.getServlet().getPath());

这段代码进行赋值的。赋值的类型是DispatcherServlet。而servletContext对象是在StandardContext类里的startInternal方法中调用getServletContext()方法

生成的,生成的源码如下

public ServletContext getServletContext() {

        if (context == null) {
            context = new ApplicationContext(this);
            if (altDDName != null)
                context.setAttribute(Globals.ALT_DD_ATTR,altDDName);
        }
        return (context.getFacade());

    }

其中context.getFacade调用了getFacade方法,该方法返回的是ApplicationContext里的facade属性,该属性是通过

private final ServletContext facade = new ApplicationContextFacade(this)

赋值。传入的this就是ApplicationContext类型的对象。分析其构造方法,其对ApplicationContextFacade里的context属性赋值,赋值类型是ApplicationContext。

public ApplicationContextFacade(ApplicationContext context) {
        super();
        this.context = context;

        classCache = new HashMap<>();
        objectCache = new ConcurrentHashMap<>();
        initClassCache();
    }

 所以context.getFacade返回的是ApplicationContextFacade类型的值。

DynamicRegistrationBean类中的addRegistration方法中调用的servletContext.addServlet(name, this.servlet)实际上是ApplicationContextFacade调用addServlet方法

public ServletRegistration.Dynamic addServlet(String servletName,
            Servlet servlet) {
        if (SecurityUtil.isPackageProtectionEnabled()) {
            return (ServletRegistration.Dynamic) doPrivileged("addServlet",
                    new Class[]{String.class, Servlet.class},
                    new Object[]{servletName, servlet});
        } else {
            return context.addServlet(servletName, servlet);
        }
    }

 在该方法中调用了context.addServlet(servletName, servlet),context是ApplicationContextFacade属性按本文上面分析是ApplicationContext,所以调用的是ApplicationContext里的addServlet方法

private ServletRegistration.Dynamic addServlet(String servletName, String servletClass,
            Servlet servlet, Map<String,String> initParams) throws IllegalStateException {

        if (servletName == null || servletName.equals("")) {
            throw new IllegalArgumentException(sm.getString(
                    "applicationContext.invalidServletName", servletName));
        }

        if (!context.getState().equals(LifecycleState.STARTING_PREP)) {
            //TODO Spec breaking enhancement to ignore this restriction
            throw new IllegalStateException(
                    sm.getString("applicationContext.addServlet.ise",
                            getContextPath()));
        }

        Wrapper wrapper = (Wrapper) context.findChild(servletName);

        // Assume a 'complete' ServletRegistration is one that has a class and
        // a name
        if (wrapper == null) {
            wrapper = context.createWrapper();
            wrapper.setName(servletName);
            context.addChild(wrapper);
        } else {
            if (wrapper.getName() != null &&
                    wrapper.getServletClass() != null) {
                if (wrapper.isOverridable()) {
                    wrapper.setOverridable(false);
                } else {
                    return null;
                }
            }
        }

        ServletSecurity annotation = null;
        if (servlet == null) {
            wrapper.setServletClass(servletClass);
            Class<?> clazz = Introspection.loadClass(context, servletClass);
            if (clazz != null) {
                annotation = clazz.getAnnotation(ServletSecurity.class);
            }
        } else {
         
            wrapper.setServletClass(servlet.getClass().getName());
            //关注此处,此处将servlet即DispatcherServlet设置到StandardWrapper里的instance属性中
            wrapper.setServlet(servlet);
            if (context.wasCreatedDynamicServlet(servlet)) {
                annotation = servlet.getClass().getAnnotation(ServletSecurity.class);
            }
        }

        if (initParams != null) {
            for (Map.Entry<String, String> initParam: initParams.entrySet()) {
                wrapper.addInitParameter(initParam.getKey(), initParam.getValue());
            }
        }

        ServletRegistration.Dynamic registration =
                new ApplicationServletRegistration(wrapper, context);
        if (annotation != null) {
            registration.setServletSecurity(new ServletSecurityElement(annotation));
        }
        return registration;
    }

组件名称

说明

Server

表示整个Servlet 容器,因此 Tomcat 运行环境中只有唯一一个 Server 实例

Service

Service 表示一个或者多个 Connector 的集合,这些 Connector 共享同一个 Container 来处理其请求。在同一个 Tomcat 实例内可以包含任意多个 Service 实例,他们彼此独立。

Connector

Tomcat 连接器,用于监听和转化 Socket 请求,同时将读取的 Socket 请求交由 Container 处理,支持不同协议以及不同的 I/O 方式。

Container

Container 表示能够执行客户端请求并返回响应的一类对象,在 Tomcat 中存在不同级别的容器:Engine、Host、Context、Wrapper

Engine

Engine 表示整个 Servlet 引擎。在 Tomcat 中,Engine 为最高层级的容器对象,虽然 Engine 不是直接处理请求的容器,确是获取目标容器的入口

Host

Host 作为一类容器,表示 Servlet 引擎(即Engine)中的虚拟机,与一个服务器的网络名有关,如域名等。客户端可以使用这个网络名连接服务器,这个名称必须要在 DNS 服务器上注册

Context

Context 作为一类容器,用于表示 ServletContext,在 Servlet 规范中,一个 ServletContext 即表示一个独立的 web 应用

Wrapper

Wrapper 作为一类容器,用于表示 Web 应用中定义的 Servlet

Executor

表示 Tomcat 组件间可以共享的线程池

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/156411.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

IB地理科SL和HL课程的区别

今期我们会谈到IB地理科这一科目的标准级别&#xff08;StandardLevel&#xff0c;SL&#xff09;课程和高级级别&#xff08;HigherLevel&#xff0c;HL&#xff09;。 两课程的最大区别:试卷数目和题目数量的不同&#xff0c;但两者的教材内容和科目指引&#xff08;SubjectG…

VTK-不同类型的数据集

前言&#xff1a;本博文主要讲解vtk中不同类型的数据集以及它们之间的关系&#xff0c;如何进行转换等。 目录 vtkImageData vtkRectilinearGrid vtkStructuredGrid vtkUnstructuredPoints vtkPolyData vtkUnstructuredGrid vtkPolyData->vtkImageData vtkPolyData…

Go反射学习

文章目录反射介绍&#xff1a;反射应用点变量-空接口-reflect.Value&#xff08;Type)类型值方法结构体&#xff1a;反射修改变量值反射操作结构体MethodCall反射介绍&#xff1a; 反射是在运行时&#xff0c;动态的获取变量的各种信息&#xff0c;如变量的类型&#xff0c;类…

Springboot中如何优雅的写好Service层代码

前言《Springboot中如何优雅的写好controller层代码》一不小心进入了全站综合热榜&#xff0c;收到了大家热情的支持&#xff0c;非常感谢大家&#xff0c;同时说明大家都有同样一个诉求&#xff0c;想好好写代码&#xff0c;不想给别人挖坑&#xff0c;争取可以早点下班。今天…

【Spring源码】CommonAnnotationBeanPostProcessor.postProcessMergedBeanDefinition()详解

CommonAnnotationBeanPostProcessor的postProcessMergedBeanDefinition()中一共包含3个方法&#xff0c;上篇文章我们介绍了的第一个方法&#xff0c;它一个父类调用&#xff08;如下图&#xff09;&#xff0c;其实就是处理PostConstruct和PreDestroy这两个注解的这篇我们继续…

一起聊聊数据治理

统一赵秦车轨&#xff0c;推行秦篆&#xff0c;统一七国文字&#xff0c;兵器统一标准&#xff0c;统一度量衡… 我们优秀的数据治理专家-秦始皇&#xff01; 数据治理这个名字起得好&#xff0c;一般人听不懂&#xff0c;实际上并不是IT人员的专属&#xff0c;广义上来说我们日…

纳米软件分享:为什么要进行电池充放电测试?电池充放电系统测试步骤

在日常生活中电能一直是我们接触过的最为方便的能源&#xff0c;而我们也通过各种方法对电能进行储存从而让我们能随时随地的使用这种能源&#xff0c;比如手机中的锂电池、电动车中的充电电池等。 充电锂电池经过多年的技术革新&#xff0c;综合性能不断提升&#xff0c;已经应…

基于node.js和Vue的食堂窗口美食评价系统/美食网站

摘要本论文主要论述了如何使用Node.js语言开发一个食堂窗口美食评价系统&#xff0c;本系统将严格按照软件开发流程进行各个阶段的工作&#xff0c;采用B/S架构&#xff0c;面向对象编程思想进行项目开发。在引言中&#xff0c;作者将论述食堂窗口美食评价系统的当前背景以及系…

【高级人工智能】国科大《高级人工智能》联结主义 笔记 + 考试回忆

国科大《高级人工智能》吴老师部分——联结主义笔记 吴老师上课dddd&#xff0c;上课东西太多太杂&#xff0c;听不太懂比较煎熬&#xff0c;但是课后花点时间理解理解&#xff0c;还是挺有帮助的考试按照重点复习即可&#xff0c;虽然答疑时提到的传教士野人没考&#x1f605;…

神经网络、激活函数

目录1.双层神经网络计算神经网络层数的时候不包括输入层。2.逻辑回归的神经网络如何实现隐藏单元如何计算&#xff1f;&#xff0c;3x1矩阵&#xff0c;3x1矩阵,上标[1]表示第一层向量化(单个训练样本):隐藏层&#xff1a;&#xff0c;&#xff0c;为4x3矩阵&#xff0c;x为3x1…

spring cloud gateway 整合sentinel使用过程使用遇到的问题

最近在进行spring cloud gateway 整合 sentinel 在此过程中遇到的问题进行汇总 1. spring gateway 整合sentinel gateway的路由会自动加一个前缀 效果如下 问题原因 代码在 org.springframework.cloud.gateway.discovery.DiscoveryClientRouteDefinitionLocator#DiscoveryClie…

【迅为iMX6Q】开发板 Linux 5.15.71 RTL8211E 以太网驱动适配

相关参考 【迅为iMX6Q】开发板 u-boot 2022.04 SD卡 启动 【迅为iMX6Q】开发板 u-boot 2020.04 RTL8211E 以太网驱动适配 【迅为iMX6Q】开发板 Linux 5.15.71 SD卡 启动 开发环境 win10 64位 VMware Workstation Pro 16 ubuntu 22.04 -【迅为imx6q】开发板&#xff0c;…

【学习笔记】【Pytorch】三、常用的Transforms

【学习笔记】【Pytorch】三、常用的Transforms学习地址主要内容一、Transforms模块介绍二、transforms.ToTensor类的使用1.使用说明2.代码实现三、transforms.Normalize类的使用1.使用说明2.代码实现四、transforms.Resize类的使用1.使用说明2.代码实现五、transforms.Compose类…

IIC协议初识及OLED写入指令、数据和显示的代码实现

目录 一、IIC协议概述 二、IIC协议起始和终止信号 三、应答信号 四、数据发送的时序 五、OLED写入指令和数据 六、OLED显示一个点的思路 七、OLED显示一个点的代码 八、OLED列地址 九、OLED显示图片 一、IIC协议概述 1、IIC概述 IIC&#xff08;集成电路总线&#…

UI设计师与UE设计师有什么区别?

设计在我们的生活中扮演着重要的角色&#xff0c;但是面对越来越多的专业术语和相近的岗位职责&#xff0c;人们总是困惑&#xff1a;明明是差不多的岗位&#xff0c;为什么要整那么多的词&#xff1f;其实&#xff0c;在它们神似的外表下&#xff0c;潜藏着巨大的差异&#xf…

虹科分享 | 实时操作系统INtime RTOS第7版功能更新介绍

INtime简介 INtime是专为基于PC的嵌入式解决方案而设计的可扩展实时操作系统&#xff0c;功能包括动态控制多个主机上多个节点&#xff08;核心&#xff09;上的进程&#xff0c;以及系统所有节点之间的丰富进程间通信&#xff0c;可应用于多核x86兼容处理器的非对称多处理(AM…

【阶段三】Python机器学习17篇:机器学习项目实战:随机森林回归模型

本篇的思维导图: 项目实战(随机森林回归模型) 项目背景 作为国家的支柱产业,房地产的稳定发展关乎国际民生。近几年,房地产价格飞速上涨,连创新高。在这种情况下研究房价的影响因素,具有重要的理论和现实意义。弄清影响房价的主要经济因素,探究我国房地产投资是…

calc()方法的使用

calc实现css中动态计算 环境 根据元素高度的变化,动态控制padding值 <marquee:name"up index"behavior"scroll"scrolldelay"30"direction"up"v-else-if"item.scrollPocition 3 ? true : false":style"{--positi…

核心乐理---五线谱基础

音符 音符的长度 音符的长度都是相对的定义 一般来说讲全音符设为一拍&#xff0c;但如果将二分音符设为一拍的话&#xff0c;全音符就是两拍 附点音符 可以在音符后跟上一个小点来改变音符的长度&#xff0c;如果一个音符后面跟了n个原点&#xff0c;它的长度就会变为原来的…

【学习记录】阿里云服务器+宝塔

环境&#xff1a;阿里云服务器&#xff0c;linux Centos系统 一、安装宝塔 参考链接&#xff1a;阿里云 ECS 服务器入门使用宝塔安装使用 1、进入云服务器控制台&#xff0c;打开远程连接 2、初次使用&#xff0c;可重置登录密码和连接密码 这是远程连接时需要输入的密码 这…