• SpringApplication.run(xxx.class, args)背后的东东——实现原理


    现在springboot已经成为web应用开发的事实标准,所以为了能更好的应用springboot特性,有必要深入研究下背后的实现原理。

    在分析之前,先抛出几个问题,后续章节也是围绕解决这几个问题展开:

    1、springboot是如何依赖几个注解就零配置启动spring容器?

    2、springboot开发出来的jar,如何实现内嵌tomcat?

    3、springboot开发经常用到的springmvc中的dispathservlet是如何关联到内嵌tomcat?

    首先咱们直接从main方法上的  @SpringBootApplication 组合注解开始,里面实现零配置的注解就是  @EnableAutoConfiguration ,它利用 @Import 一个 AutoConfigurationImportSelector,该类主要利用类SPI的机制,获取META-INFO下面需要自动装配的 @Configuration,至于SPI后续将详细介绍。

    说到这里,估计大家会有一个疑问,springboot如何一开始就知道这个重要 @Import  注解呢,肯定有个注解解析的地方,下面需要说的就是spring容器的启动(跟着main方法走):

    context = this.createApplicationContext()
    

      这里是创建重要的ApplicationContext,其实就是BeanFactory,不过springboot会自动推断创建针对servlet应用的 AnnotationConfigServletWebServerApplicationContext。

    org.springframework.context.support.AbstractApplicationContext#refresh
    

      熟悉spring源码的同学就会知道,这里就是刷新容器的地方,

    org.springframework.context.support.AbstractApplicationContext#invokeBeanFactoryPostProcessors
    org.springframework.context.annotation.ConfigurationClassParser#processImports
    

    这个方法里面就有对 @Import 注解做解析

      解析逻辑比较复杂,有兴趣可以自己看下,说白了就是开头讲的找到需要自动装配的 @Configuration 对应的类,这里列出来三个重要的如下:

    @Configuration(proxyBeanMethods = false)
    @ConditionalOnWebApplication(type = Type.SERVLET)
    @ConditionalOnClass({ Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class })
    @ConditionalOnMissingBean(WebMvcConfigurationSupport.class)
    @AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10)
    @AutoConfigureAfter({ DispatcherServletAutoConfiguration.class, TaskExecutionAutoConfiguration.class,
    		ValidationAutoConfiguration.class })
    public class WebMvcAutoConfiguration {
    

      

    @AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
    @Configuration(proxyBeanMethods = false)
    @ConditionalOnWebApplication(type = Type.SERVLET)
    @ConditionalOnClass(DispatcherServlet.class)
    @AutoConfigureAfter(ServletWebServerFactoryAutoConfiguration.class)
    public class DispatcherServletAutoConfiguration {
    

      

    @Configuration(proxyBeanMethods = false)
    @AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
    @ConditionalOnClass(ServletRequest.class)
    @ConditionalOnWebApplication(type = Type.SERVLET)
    @EnableConfigurationProperties(ServerProperties.class)
    @Import({ ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class,
    		ServletWebServerFactoryConfiguration.EmbeddedTomcat.class,
    		ServletWebServerFactoryConfiguration.EmbeddedJetty.class,
    		ServletWebServerFactoryConfiguration.EmbeddedUndertow.class })
    public class ServletWebServerFactoryAutoConfiguration {
    

      其实这三个都是通过 @AutoConfigureAfter 依赖关系加载的,加载这些的目的就是为了getBean时可以拿到具体的实例对象。

    咱们再回到刷新spring器中的方法中

    org.springframework.context.support.AbstractApplicationContext#onRefresh
    

      该方法对应servlet的实现是

    org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext#onRefresh
    

      里面就是创建WebServer,可以理解为就是咱们的tomcat,不过它是由工厂方法创建的

    protected ServletWebServerFactory getWebServerFactory() {
            String[] beanNames = this.getBeanFactory().getBeanNamesForType(ServletWebServerFactory.class);
    

      核心代码来了,这里就会触发getBean,去拿到 ServletWebServerFactory的实例,也就会触发Bean的生命周期调用,

    @Configuration(proxyBeanMethods = false)
    class ServletWebServerFactoryConfiguration {
    
    	@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;
    		}
    
    	}
    

      其实就是到这里来获取 TomcatServletWebServerFactory 实例,这个也就是前面自动配置 @Import扫描出来的 ServletWebServerFactoryAutoConfiguration 导入的beandefinition

    这个工厂实现了 ErrorPageRegistry 接口

    org.springframework.boot.web.server.ErrorPageRegistry
    

      而ServletWebServerFactory在getBean的生命周期中会触发BeanPostProcessor实现类的调用

    public class ErrorPageRegistrarBeanPostProcessor implements BeanPostProcessor, BeanFactoryAware {
    
    	private ListableBeanFactory beanFactory;
    
    	private List<ErrorPageRegistrar> registrars;
    
    	@Override
    	public void setBeanFactory(BeanFactory beanFactory) {
    		Assert.isInstanceOf(ListableBeanFactory.class, beanFactory,
    				"ErrorPageRegistrarBeanPostProcessor can only be used with a ListableBeanFactory");
    		this.beanFactory = (ListableBeanFactory) beanFactory;
    	}
    
    	@Override
    	public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
    		if (bean instanceof ErrorPageRegistry) {
    			postProcessBeforeInitialization((ErrorPageRegistry) bean);
    		}
    		return bean;
    	}
    

      也就是实例化 TomcatServletWebServerFactory 之前,会去先实例化 ErrorPageRegistrar

    	private Collection<ErrorPageRegistrar> getRegistrars() {
    		if (this.registrars == null) {
    			// Look up does not include the parent context
    			this.registrars = new ArrayList<>(
    					this.beanFactory.getBeansOfType(ErrorPageRegistrar.class, false, false).values());
    			this.registrars.sort(AnnotationAwareOrderComparator.INSTANCE);
    			this.registrars = Collections.unmodifiableList(this.registrars);
    		}
    		return this.registrars;
    	}
    

      跟着代码,实例化 ErrorPageRegistrar 又会先去实例化  DispatcherServletPath,它只有一个实现类 DispatcherServletRegistrationBean

    protected ErrorPageCustomizer(ServerProperties properties, DispatcherServletPath dispatcherServletPath) {
    			this.properties = properties;
    			this.dispatcherServletPath = dispatcherServletPath;
    		}
    

      

    public DispatcherServletRegistrationBean(DispatcherServlet servlet, String path) {
    		super(servlet);
    		Assert.notNull(path, "Path must not be null");
    		this.path = path;
    		super.addUrlMappings(getServletUrlMapping());
    	}
    

      这里就看到熟悉的 DispatcherServlet了,有人可能会问,DispatcherServlet哪里来的?其实又要回到前面扫描处理的三个自动装配类中的 DispatcherServletAutoConfiguration

    至此,我们终于通过spring容器的启动,将DispatcherServlet实例化出来了,同理负责创建tomcat容器的TomcatServletWebServerFactory也实例化出来了。

    下面就是负责创建内嵌tomcat了,以及建立tomcat与DispatcherServlet实例的关联

    org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory#getWebServer
    

      

    tomcat容器启动后,因为它实现了servlet规范,内部会回调实现了所有实现了ServletContainerInitializer类的onStartup方法

    javax.servlet.ServletContainerInitializer#onStartup
    

      具体代码需要关联tomcat源码,这里只是简单记录下,后续再深入分析tomcat实现原理

    org.apache.catalina.core.StandardContext#startInternal
    
    while(var27.hasNext()) {
                    Entry entry = (Entry)var27.next();
    
                    try {
                        ((ServletContainerInitializer)entry.getKey()).onStartup((Set)entry.getValue(), this.getServletContext());
                    } catch (ServletException var22) {
                        log.error(sm.getString("standardContext.sciFail"), var22);
                        ok = false;
                        break;
                    }
                }
    

          而TomcatStarter正好实现了ServletContainerInitializer接口,所以tomcat启动会触发这个方法的调用  

    org.springframework.boot.web.embedded.tomcat.TomcatStarter#onStartup
    

      方法内部调用了spring中实现了ServletContextInitializer接口的类的onStartup方法,恰好又是DispatcherServletRegistrationBean,而它的父类RegistrationBean就将DispatcherServlet绑定到tomcat容器的ServletContext上了

    org.springframework.boot.autoconfigure.web.servlet.DispatcherServletRegistrationBean
    org.springframework.boot.web.servlet.RegistrationBean#onStartup
    

      

    最后,tomcat容器启动了,spring容器也启动了,访问tomcat的请求也可以通过关联的DispatcherServlet进行分发处理了,咱们前面的三个问题也应该有答案了。

  • 相关阅读:
    Pass360[最新].cpp
    在任意的远程桌面的session中运行指定的程序
    C++内存泄露的检测(三)
    在release模式下debug
    [转]VC Studio 使用技巧大全
    C++内存泄露的检测(一)
    获取系统用户所对应的配置路径
    关于Debug和Release之本质区别的讨论
    GDB 的使用
    struct termios
  • 原文地址:https://www.cnblogs.com/abingtech/p/15836150.html
Copyright © 2020-2023  润新知