zoukankan      html  css  js  c++  java
  • SpringBoot源码:启动内置Tomcat和加载DispatcherServlet原理

    本文基于springboot 2.2.x分支源码。

    相关的自动配置类

    关于Servlet、web内容在org.springframework.boot.autoconfigure.web.servlet包下面:

    image-20211215184240007

    该包下面主要有四个自动配置类:DispatcherServletAutoConfigurationHttpEncodingAutoConfigurationServletWebServerFactoryAutoConfigurationWebMvcAutoConfiguration

    • DispatcherServletAutoConfiguration:该类主要做DispatcherServlet相关的自动配置
    • HttpEncodingAutoConfiguration:该类主要做http字符编码相关的配置
    • ServletWebServerFactoryAutoConfiguration:该类做的是Servlet web容器工厂相关配置
    • WebMvcAutoConfiguration:该类做的是springvc相关的配置,例如配置一些消息转换器、视图解析器、包括HandlerAdapter、HandlerMapping等相关配置

    因为我们要了解springboot启动内置tomcat以及加载DispatcherServlet的原理,就需要深入DispatcherServletAutoConfigurationServletWebServerFactoryAutoConfiguration类了。

    ServletWebServerFactoryAutoConfiguration

    先看该类的注解:

    @Configuration(proxyBeanMethods = false) //配置类
    @AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)//自动配置顺序
    @ConditionalOnClass(ServletRequest.class)//类路径有ServletRequest类
    @ConditionalOnWebApplication(type = Type.SERVLET)//SERVLET的web环境
    @EnableConfigurationProperties(ServerProperties.class)//使ServerProperties生效
    //导入四个组件
    @Import({ ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class,
    		ServletWebServerFactoryConfiguration.EmbeddedTomcat.class,
    		ServletWebServerFactoryConfiguration.EmbeddedJetty.class,
    		ServletWebServerFactoryConfiguration.EmbeddedUndertow.class })
    public class ServletWebServerFactoryAutoConfiguration {
        
            //导入ServletWebServerFactoryCustomizer组件,主要对ServletWebServerFactory做定制化配置
            @Bean
    	public ServletWebServerFactoryCustomizer servletWebServerFactoryCustomizer(ServerProperties serverProperties) {
    		return new ServletWebServerFactoryCustomizer(serverProperties);
    	}
    
            //导入TomcatServletWebServerFactoryCustomizer组件,主要对TomcatServletWebServerFactory做定制化配置
    	@Bean
    	@ConditionalOnClass(name = "org.apache.catalina.startup.Tomcat")
    	public TomcatServletWebServerFactoryCustomizer tomcatServletWebServerFactoryCustomizer(
    			ServerProperties serverProperties) {
    		return new TomcatServletWebServerFactoryCustomizer(serverProperties);
    	}
     	//其他方法省略   
    }
    

    先看ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar

    该类实现了ImportBeanDefinitionRegistrar,所以在ConfigurationClassPostProcessor解析配置类的时候,自动调用该接口的registerBeanDefinitions方法,

    这里的BeanPostProcessorsRegistrar在该方法中注册了两个bean的定义信息:一个是ErrorPageRegistrarBeanPostProcessor,这个暂且不用管,主要是给ErrorPageRegistry中注册错误页的。

    	public static class BeanPostProcessorsRegistrar implements ImportBeanDefinitionRegistrar, BeanFactoryAware {
                    //留下主要方法,其他方法省略
    		@Override
    		public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,
    				BeanDefinitionRegistry registry) {
    			if (this.beanFactory == null) {
    				return;
    			}
    			registerSyntheticBeanIfMissing(registry, "webServerFactoryCustomizerBeanPostProcessor",
    					WebServerFactoryCustomizerBeanPostProcessor.class);
    			registerSyntheticBeanIfMissing(registry, "errorPageRegistrarBeanPostProcessor",
    					ErrorPageRegistrarBeanPostProcessor.class);
    		}
    		private void registerSyntheticBeanIfMissing(BeanDefinitionRegistry registry, String name, Class<?> beanClass) {
    			if (ObjectUtils.isEmpty(this.beanFactory.getBeanNamesForType(beanClass, true, false))) {
    				RootBeanDefinition beanDefinition = new RootBeanDefinition(beanClass);
    				beanDefinition.setSynthetic(true);
    				registry.registerBeanDefinition(name, beanDefinition);
    			}
    		}
    	}
    

    另一个是注册的bean定义信息是WebServerFactoryCustomizerBeanPostProcessor,这个类就比较有用了,看我在代码中的注释。

    //实现了BeanPostProcessor,会拦截指定bean的创建
    public class WebServerFactoryCustomizerBeanPostProcessor implements BeanPostProcessor, BeanFactoryAware {
    	private ListableBeanFactory beanFactory;
    	private List<WebServerFactoryCustomizer<?>> customizers;
    	@Override
    	public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
            //当创建的bean是WebServerFactory时,会调用postProcessBeforeInitialization(WebServerFactory) bean)
    		if (bean instanceof WebServerFactory) {
    			postProcessBeforeInitialization((WebServerFactory) bean);
    		}
    		return bean;
    	}
    	@SuppressWarnings("unchecked")
    	private void postProcessBeforeInitialization(WebServerFactory webServerFactory) {
            //getCustomizers():拿到容器中所有注册的WebServerFactoryCustomizer组件
    		LambdaSafe.callbacks(WebServerFactoryCustomizer.class, getCustomizers(), webServerFactory)
    				.withLogger(WebServerFactoryCustomizerBeanPostProcessor.class)
                	//依次执行WebServerFactoryCustomize的customize方法,该方法主要是对WebServerFactory工厂做一些定制化的工作(配置)
    				.invoke((customizer) -> customizer.customize(webServerFactory));
    	}
    	//其他方法省略。。。
    }
    

    再回来看ServletWebServerFactoryAutoConfiguration,还导入了其他三个组件。

    • ServletWebServerFactoryConfiguration.EmbeddedTomcat

    • ServletWebServerFactoryConfiguration.EmbeddedJetty

    • ServletWebServerFactoryConfiguration.EmbeddedUndertow

    默认情况,只有EmbeddedTomcat会生效,看它的代码:导入了一个TomcatServletWebServerFactory类型的组件,传入三个ObjectProvider类型参数,都是对Connector、Context、ProtocolHandler做定制化操作的,默认情况下这三个参数都没有配置,如果想要配置,只需要自己在容器中导入TomcatConnectorCustomizer、TomcatContextCustomizer、TomcatProtocolHandlerCustomizer组件就行了。

    记得这里导入了TomcatServletWebServerFactory,后面tomcat的创建就靠它。

    	@Configuration(proxyBeanMethods = false)
    	@ConditionalOnClass({ Servlet.class, Tomcat.class, UpgradeProtocol.class })
    	@ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)
    	static class EmbeddedTomcat {
    
    		@Bean
    		TomcatServletWebServerFactory tomcatServletWebServerFactory(
    				ObjectProvider<TomcatConnectorCustomizer> connectorCustomizers,
    				ObjectProvider<TomcatContextCustomizer> contextCustomizers,
    				ObjectProvider<TomcatProtocolHandlerCustomizer<?>> protocolHandlerCustomizers) {
    			TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory();
    			factory.getTomcatConnectorCustomizers()
    					.addAll(connectorCustomizers.orderedStream().collect(Collectors.toList()));
    			factory.getTomcatContextCustomizers()
    					.addAll(contextCustomizers.orderedStream().collect(Collectors.toList()));
    			factory.getTomcatProtocolHandlerCustomizers()
    					.addAll(protocolHandlerCustomizers.orderedStream().collect(Collectors.toList()));
    			return factory;
    		}
    
    	}
    

    DispatcherServletAutoConfiguration

    再看这个配置类,这里面导入了两个比较重要的组件:

    1.导入DispatcherServlet这个组件,注意这个类仅仅只是保存到IOC容器中,并没有在tomcat中初始化

    @AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)//自动配置顺序
    @Configuration(proxyBeanMethods = false)
    @ConditionalOnWebApplication(type = Type.SERVLET)
    @ConditionalOnClass(DispatcherServlet.class)
    @AutoConfigureAfter(ServletWebServerFactoryAutoConfiguration.class)//在ServletWebServerFactoryAutoConfiguration自动配置之后再自动配置
    public class DispatcherServletAutoConfiguration {
    
    	/**
    	 * The bean name for a DispatcherServlet that will be mapped to the root URL "/".
    	 */
    	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(proxyBeanMethods = false)
    	@Conditional(DefaultDispatcherServletCondition.class)
    	@ConditionalOnClass(ServletRegistration.class)
    	@EnableConfigurationProperties({ HttpProperties.class, WebMvcProperties.class })
    	protected static class DispatcherServletConfiguration {
    
    		@Bean(name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
    		public DispatcherServlet dispatcherServlet(HttpProperties httpProperties, WebMvcProperties webMvcProperties) {
    			DispatcherServlet dispatcherServlet = new DispatcherServlet();
    			dispatcherServlet.setDispatchOptionsRequest(webMvcProperties.isDispatchOptionsRequest());
    			dispatcherServlet.setDispatchTraceRequest(webMvcProperties.isDispatchTraceRequest());
    			dispatcherServlet.setThrowExceptionIfNoHandlerFound(webMvcProperties.isThrowExceptionIfNoHandlerFound());
    			dispatcherServlet.setPublishEvents(webMvcProperties.isPublishRequestHandledEvents());
    			dispatcherServlet.setEnableLoggingRequestDetails(httpProperties.isLogRequestDetails());
    			return dispatcherServlet;
    		}
    		//
    	}
    
    	@Configuration(proxyBeanMethods = false)
    	@Conditional(DispatcherServletRegistrationCondition.class)
    	@ConditionalOnClass(ServletRegistration.class)
    	@EnableConfigurationProperties(WebMvcProperties.class)
    	@Import(DispatcherServletConfiguration.class)
    	protected static class DispatcherServletRegistrationConfiguration {
    
    		@Bean(name = DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME)
    		@ConditionalOnBean(value = DispatcherServlet.class, name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
    		public DispatcherServletRegistrationBean dispatcherServletRegistration(DispatcherServlet dispatcherServlet,
    				WebMvcProperties webMvcProperties, ObjectProvider<MultipartConfigElement> multipartConfig) {
    			DispatcherServletRegistrationBean registration = new DispatcherServletRegistrationBean(dispatcherServlet,
    					webMvcProperties.getServlet().getPath());
    			registration.setName(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME);
    			registration.setLoadOnStartup(webMvcProperties.getServlet().getLoadOnStartup());
    			multipartConfig.ifAvailable(registration::setMultipartConfig);
    			return registration;
    		}
    	}
    }
    

    2.导入了DispatcherServletRegistrationBean组件,先看该类的继承树,看看它继承了哪些关键组件。

    image-20211216090939243

    可以观察到,该类实现了ServletContextInitializer接口,再看接口上的英文注释。

    Interface used to configure a Servlet 3.0+ {@link ServletContext context}

    programmatically. Unlike {@link WebApplicationInitializer}, classes that implement this

    interface (and do not implement {@link WebApplicationInitializer}) will not be

    detected by {@link SpringServletContainerInitializer} and hence will not be

    automatically bootstrapped by the Servlet container.

    This interface is designed to act in a similar way to

    {@link ServletContainerInitializer}, but have a lifecycle that's managed by Spring and

    not the Servlet container.

    For configuration examples see {@link WebApplicationInitializer}.

    大致含义:这个接口不像WebApplicationInitializer一样被SpringServletContainerInitializer自动探测,因此不会自动被servlet容器启动。但是它仍有着被spring管理的生命周期。

    断点调试

    onRefresh

    springboot启动tomcat时候,日志会打印tomcat启动的日志。我们只要在springboot 的run方法上打上断点,不难找到tomcat启动在哪里。

    SpringApplication类的run方法中,这里调用了refreshContext,这一步是刷新ioc容器,这里断点一过,tomcat启动日志就打印了,所以tomcat就在这里面。

    image-20211216092442970

    走到经典的容器刷新方法,断点一走过onRefresh方法,tomcat启动日志就打印了,所以这里就是tomcat启动的地方。

    image-20211216092717329

    createWebServer

    进入onRefresh方法,先调用父类的onRefresh方法,然后调用createWebServer方法:

    	@Override
    	protected void onRefresh() {
    		super.onRefresh();
    		try {
    			createWebServer();
    		}
    		catch (Throwable ex) {
    			throw new ApplicationContextException("Unable to start web server", ex);
    		}
    	}
    

    再看createWebServer方法:

    	private void createWebServer() {
                    //默认情况下,this.webServer和servletContext都为null
    		WebServer webServer = this.webServer;
    		ServletContext servletContext = getServletContext();
    		if (webServer == null && servletContext == null) {
                            //获取WebServerFactory
    			ServletWebServerFactory factory = getWebServerFactory();
                            //从WebServerFactory中获取webServer
    			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();
    	}
    

    那么重点就是ServletWebServerFactory factory = getWebServerFactory();this.webServer = factory.getWebServer(getSelfInitializer());

    getWebServerFactory()

    先看这个getWebServerFactory()方法:

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

    第一步:从容器中获取ServletWebServerFactory组件名,默认情况下,只有在ServletWebServerFactoryAutoConfiguration自动配置的TomcatServletWebServerFactory

    第二步:校验组件名,只有一个组件名的时候,不会抛出异常,如果找不到组件或者组件有多个都会抛出ApplicationContextException

    第三步:从容器中获取到ServletWebServerFactory组件,默认就是TomcatServletWebServerFactory

    getSelfInitializer()

    先看getSelfInitializer(),这个方法比较重要:内部是一个selfInitialize回调方法,并不会立马执行,这里先提一嘴,后面会说到。

    	private org.springframework.boot.web.servlet.ServletContextInitializer getSelfInitializer() {
    		return this::selfInitialize;
    	}
    
    	//同时这个selfInitialize方法不会立刻调用,只是作为一个回调方法,当ServletContextInitializer.onStartUp调用时,才会被调用
    	private void selfInitialize(ServletContext servletContext) throws ServletException {
                    //准备WebApplicationContext,主要做一些校验和设置属性
    		prepareWebApplicationContext(servletContext);
                    //设置ApplicationScope
    		registerApplicationScope(servletContext);
                    //注册一些关于Servlet环境的组件
    		WebApplicationContextUtils.registerEnvironmentBeans(getBeanFactory(), servletContext);
                    //getServletContextInitializerBeans(),主要是创建了一个ServletContextInitializerBeans集合
                    //对ServletContextInitializerBeans集合遍历,是对它内部的sortedList进行遍历,里面存放了容器中的所有ServletContextInitializer
    		for (ServletContextInitializer beans : getServletContextInitializerBeans()) {
                            //调用所有的ServletContextInitializer的onStartup方法
    			beans.onStartup(servletContext);
    		}
    	}
    

    对于这种写法,可能看起来有点费解,因为getSelfInitializer()的返回值是一个ServletContextInitializer,同时它也是一个函数式接口,所以等价于下面这种写法:

    	private org.springframework.boot.web.servlet.ServletContextInitializer getSelfInitializer() {
    		return servletContext -> {
    			prepareWebApplicationContext(servletContext);
    			registerApplicationScope(servletContext);
    			WebApplicationContextUtils.registerEnvironmentBeans(getBeanFactory(), servletContext);
    			for (ServletContextInitializer beans : getServletContextInitializerBeans()) {
    				beans.onStartup(servletContext);
    			}
    		};
    	}
    

    getWebServer

    再看getWebServer方法:

    	@Override
    	public WebServer getWebServer(ServletContextInitializer... initializers) {
    		if (this.disableMBeanRegistry) {
    			Registry.disableRegistry();
    		}
                    //new了一个tomcat
    		Tomcat tomcat = new Tomcat();
    		File baseDir = (this.baseDirectory != null) ? this.baseDirectory : createTempDir("tomcat");
                    //设置baseDir
    		tomcat.setBaseDir(baseDir.getAbsolutePath());
    		Connector connector = new Connector(this.protocol);
    		connector.setThrowOnFailure(true);
                    //添加一个connector
    		tomcat.getService().addConnector(connector);
                    //自定义connector
    		customizeConnector(connector);
    		tomcat.setConnector(connector);
    		tomcat.getHost().setAutoDeploy(false);
                    //配置引擎
    		configureEngine(tomcat.getEngine());
    		for (Connector additionalConnector : this.additionalTomcatConnectors) {
    			tomcat.getService().addConnector(additionalConnector);
    		}
                    //准备Context
    		prepareContext(tomcat.getHost(), initializers);
    		return getTomcatWebServer(tomcat);
    	}
    

    看prepareContext方法:

    	protected void prepareContext(Host host, ServletContextInitializer[] initializers) {
    		File documentRoot = getValidDocumentRoot();
                    //创建TomcatEmbeddedContext
    		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());
    		context.addLifecycleListener(new FixContextListener());
    		context.setParentClassLoader((this.resourceLoader != null) ? this.resourceLoader.getClassLoader()
    				: ClassUtils.getDefaultClassLoader());
    		resetDefaultLocaleMapping(context);
    		addLocaleMappings(context);
    		try {
    			context.setCreateUploadTargets(true);
    		}
    		catch (NoSuchMethodError ex) {
    			// Tomcat is < 8.5.39. Continue.
    		}
    		configureTldPatterns(context);
                    //设置WebappLoader
    		WebappLoader loader = new WebappLoader();
    		loader.setLoaderClass(TomcatEmbeddedWebappClassLoader.class.getName());
    		loader.setDelegate(true);
    		context.setLoader(loader);
    		if (isRegisterDefaultServlet()) {
    			addDefaultServlet(context);
    		}
    		if (shouldRegisterJspServlet()) {
    			addJspServlet(context);
    			addJasperInitializer(context);
    		}
                    //增加LifecycleListener
    		context.addLifecycleListener(new StaticResourceConfigurer(context));
                    //合并Initializers,主要是添加一些其他的ServletContextInitializer
    		ServletContextInitializer[] initializersToUse = mergeInitializers(initializers);
    		host.addChild(context);
                    //配置context
    		configureContext(context, initializersToUse);
    		postProcessContext(context);
    	}
    

    进入configureContext方法:

    	protected void configureContext(Context context, ServletContextInitializer[] initializers) {
                    //创建了TomcatStarter,这个实现了ServletContainerInitializer接口
                    //对servlet3.0规范熟悉的知道,tomcat在servlet容器初始化的时候,会调用所有的ServletContainerInitializer接口的onStartup方法
    		TomcatStarter starter = new TomcatStarter(initializers);
    		if (context instanceof TomcatEmbeddedContext) {
    			TomcatEmbeddedContext embeddedContext = (TomcatEmbeddedContext) context;
    			embeddedContext.setStarter(starter);
    			embeddedContext.setFailCtxIfServletStartFails(true);
    		}
                    //添加到context中
    		context.addServletContainerInitializer(starter, NO_CLASSES);
    		for (LifecycleListener lifecycleListener : this.contextLifecycleListeners) {
    			context.addLifecycleListener(lifecycleListener);
    		}
                    //添加tomcat阀门
    		for (Valve valve : this.contextValves) {
    			context.getPipeline().addValve(valve);
    		}
                    //错误页配置
    		for (ErrorPage errorPage : getErrorPages()) {
    			org.apache.tomcat.util.descriptor.web.ErrorPage tomcatErrorPage = new org.apache.tomcat.util.descriptor.web.ErrorPage();
    			tomcatErrorPage.setLocation(errorPage.getPath());
    			tomcatErrorPage.setErrorCode(errorPage.getStatusCode());
    			tomcatErrorPage.setExceptionType(errorPage.getExceptionName());
    			context.addErrorPage(tomcatErrorPage);
    		}
    		for (MimeMappings.Mapping mapping : getMimeMappings()) {
    			context.addMimeMapping(mapping.getExtension(), mapping.getMimeType());
    		}
    		configureSession(context);
    		new DisableReferenceClearingContextCustomizer().customize(context);
                    //对TomcatContext进行自定义配置
    		for (TomcatContextCustomizer customizer : this.tomcatContextCustomizers) {
    			customizer.customize(context);
    		}
    	}
    

    到这里,看了一大半,有人可能一脸蒙蔽,我是谁,我在哪儿?这里究竟做了哪些关键步骤?

    我来解释一下在getWebServer方法截至运行到prepareContext,springboot究竟做了哪些事情。

    • getSelfInitializer返回的是一个函数式接口,内部主要是拿到springboot中的所有ServletContextInitializer,并依次执行它的onStartup方法
    • 把这个函数式接口作为参数传入到getWebServer方法中
    • getWebServer方法主要做了以下几件事
      • 创建了一个Tomcat实例,设置了一些属性
      • 为tomcat的Host配置Context
      • 创建了一个TomcatStarter并把上面的getSelfInitializer返回的函数式接口设置到它的成员变量中,TomcatStarter实际上是ServletContainerInitializer,tomcat在servlet容器初始化的时候,会调用所有的ServletContainerInitializer接口的onStartup方法
      • 然后把TomcatStarter添加到context中

    断点继续往下走,走到getTomcatWebServer(tomcat),进去:直接new了一个TomcatWebServer,并把前面创建的tomcat传进去。

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

    断点继续往里面走:设置了属性后,直接调用initialize方法:

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

    initialize方法如下:终于在这一步,看到了tomcat.strart()方法,至此,tomcat终于启动。

    	private void initialize() throws WebServerException {
    		synchronized (this.monitor) {
    			try {
    				addInstanceIdToEngineName();
    
    				Context context = findContext();
    				context.addLifecycleListener((event) -> {
    					if (context.equals(event.getSource()) && Lifecycle.START_EVENT.equals(event.getType())) {
    						removeServiceConnectors();
    					}
    				});
    				//tomcat启动
    				this.tomcat.start();
    				rethrowDeferredStartupExceptions();
    
    				try {
    					ContextBindings.bindClassLoader(context, context.getNamingToken(), getClass().getClassLoader());
    				}
    				catch (NamingException ex) {
    				}
    				startDaemonAwaitThread();
    			}
    			catch (Exception ex) {
    				stopSilently();
    				destroySilently();
    				throw new WebServerException("Unable to start embedded Tomcat", ex);
    			}
    		}
    	}
    

    TomcatStarter.onStartup

    tomcat.start()方法启动后,因为TomcatStarter实现了ServletContainerInitializer接口,在前面手动调用了context.addServletContainerInitializer(starter, NO_CLASSES);,把tomcatStart添加到context中,所以这里的TomcatStarter并不是通过SPI机制加载到tomcat中,这里与springmvc处理不一样。

    tomcat启动后,会执行ServletContainerInitializer的onStartUp方法,所以TomcatStarter会被调用:

    	@Override
    	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());
    			}
    		}
    	}
    

    这里的this.initializers有三个,我们只要管前面传进来的getSelfInitializer()方法:这里前面提到的selfInitialize方法才会被调用

    	private void selfInitialize(ServletContext servletContext) throws ServletException {
                    //准备WebApplicationContext,主要做一些校验和设置属性
    		prepareWebApplicationContext(servletContext);
                    //设置ApplicationScope
    		registerApplicationScope(servletContext);
                    //注册一些关于Servlet环境的组件
    		WebApplicationContextUtils.registerEnvironmentBeans(getBeanFactory(), servletContext);
                    //getServletContextInitializerBeans(),主要是创建了一个ServletContextInitializerBeans集合
                    //对ServletContextInitializerBeans集合遍历,是对它内部的sortedList进行遍历,里面存放了容器中的所有ServletContextInitializer
    		for (ServletContextInitializer beans : getServletContextInitializerBeans()) {
                            //调用所有的ServletContextInitializer的onStartup方法
    			beans.onStartup(servletContext);
    		}
    	}
    

    这里getServletContextInitializerBeans()返回了四个ServletContextInitializer:

    image-20211216104613197

    其中第一个,就是配置类中配置的DispatcherServletRegistrationBean,而springboot通过该类注册了DispatcherServlet,我们接下来来看springboot是怎么注册的。

    注册DispatcherServlet

    进入DispatcherServletRegistrationBean的onstart方法,直接进到它的父类RegistrationBean:

    	@Override
    	public final void onStartup(ServletContext servletContext) throws ServletException {
    		String description = getDescription();
    		if (!isEnabled()) {
    			logger.info(StringUtils.capitalize(description) + " was not registered (disabled)");
    			return;
    		}
                    //调用register
    		register(description, servletContext);
    	}
    

    断点进入register方法

    @Override
    protected final void register(String description, ServletContext servletContext) {
        //执行addRegistration
       D registration = addRegistration(description, servletContext);
       if (registration == null) {
          logger.info(StringUtils.capitalize(description) + " was not registered (possibly already registered?)");
          return;
       }
        //调用configure
       configure(registration);
    }
    

    进入addRegistration,终于走进DispatcherServletRegistrationBean类中:直接调用servlet原生方法

    	@Override
    	protected ServletRegistration.Dynamic addRegistration(String description, ServletContext servletContext) {
    		String name = getServletName();
                    //添加servlet
    		return servletContext.addServlet(name, this.servlet);
    	}
    

    这里this.servlet也就是DispatcherServlet,是在前面DispatcherServletAutoConfiguration 自动配置类中传入到DispatcherServletRegistrationBean类中的。

    方法出来后,走进configure方法:这里会给ServletRegistration.Dynamic设置一些属性,这也是javax.servlet的原生方法。

    	@Override
    	protected void configure(ServletRegistration.Dynamic registration) {
    		super.configure(registration);
    		String[] urlMapping = StringUtils.toStringArray(this.urlMappings);
    		if (urlMapping.length == 0 && this.alwaysMapUrl) {
    			urlMapping = DEFAULT_MAPPINGS;
    		}
    		if (!ObjectUtils.isEmpty(urlMapping)) {
    			registration.addMapping(urlMapping);
    		}
    		registration.setLoadOnStartup(this.loadOnStartup);
    		if (this.multipartConfig != null) {
    			registration.setMultipartConfig(this.multipartConfig);
    		}
    	}
    

    至此,tomcat启动了,DispatcherServlet注册到tomcat中,同时RegistrationBean也会注册javax原生的监听器和过滤器,具体实现是ServletListenerRegistrationBeanFilterRegistrationBean,这里不再详细讲解。

    流程图

    流程图如下:

    springboot启动tomcat原理

  • 相关阅读:
    基于perl的网络爬虫
    ios cell展示可滑动的图片
    iOS计算字符串的宽度高度
    swift水波效果
    iOS添加另一个控制器的时候要注意啊
    swift隐藏显示导航栏的底线
    swift集成alamofire的简单封装
    tableview详细介绍
    xmpp xml基本语义
    xmpp SASL 定义
  • 原文地址:https://www.cnblogs.com/wwjj4811/p/15697566.html
Copyright © 2011-2022 走看看