1. 程式人生 > 其它 >SpringApplication原始碼剖析(一)——構造方法

SpringApplication原始碼剖析(一)——構造方法

技術標籤:Springspringjava

SpringApplication原始碼剖析(一)——構造方法

之前通過看了AbstractApplicationContext.class、DefaultListableBeanFactory.class、AbstractBeanFactory.class幾個類基本瞭解到了SpringIoC相關特性的原理具體程式碼實現。從這篇文章開始將打算了解以下SpringBoot應用的啟動過程。SpringBoot應用入口是SpringApplication.run()方法,因此從這個方法為入口,看一下相關的原始碼。

構造方法

在閱讀run()方法的原始碼時,發現有些類屬性是在構造方法中初始化的,因此先寫一篇關於構造方法的筆記,記錄以下幾個後面會用到的比較特殊的屬性。

構造方法原始碼:

public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
        this.sources = new LinkedHashSet();
        this.bannerMode = Mode.CONSOLE;
        this.logStartupInfo = true;
        this.addCommandLineProperties = true;
        this.addConversionService =
true; this.headless = true; this.registerShutdownHook = true; this.additionalProfiles = Collections.emptySet(); this.isCustomEnvironment = false; this.lazyInitialization = false; this.applicationContextFactory = ApplicationContextFactory.DEFAULT; this
.applicationStartup = ApplicationStartup.DEFAULT; this.resourceLoader = resourceLoader; Assert.notNull(primarySources, "PrimarySources must not be null"); this.primarySources = new LinkedHashSet(Arrays.asList(primarySources)); this.webApplicationType = WebApplicationType.deduceFromClasspath(); this.bootstrappers = new ArrayList(this.getSpringFactoriesInstances(Bootstrapper.class)); this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class)); this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class)); this.mainApplicationClass = this.deduceMainApplicationClass(); }
    this.sources = new LinkedHashSet();

spring應用的源source?

    this.bannerMode = Mode.CONSOLE;

banner的模式,banner是指SpringBoot啟動的時候列印的:
banner
一共三種列舉:LOG、CONSOLE、OFF

    this.addCommandLineProperties = true;

若addCommandLineProperties設定為true,則如果啟動時命令列傳入的引數不為空,則把引數以SimpleCommandLinePropertySource物件例項形式新增到environment.getPropertySources()中
相關程式碼:

protected void configurePropertySources(ConfigurableEnvironment environment, String[] args) {
        MutablePropertySources sources = environment.getPropertySources();
        DefaultPropertiesPropertySource.ifNotEmpty(this.defaultProperties, sources::addLast);
        if (this.addCommandLineProperties && args.length > 0) {
            String name = "commandLineArgs";
            if (sources.contains(name)) {
                PropertySource<?> source = sources.get(name);
                CompositePropertySource composite = new CompositePropertySource(name);
                composite.addPropertySource(new SimpleCommandLinePropertySource("springApplicationCommandLineArgs", args));
                composite.addPropertySource(source);
                sources.replace(name, composite);
            } else {
                sources.addFirst(new SimpleCommandLinePropertySource(args));
            }
        }
    }
    this.addConversionService = true;

相關程式碼主要有兩處:
1、設定環境變數時,若addConversionService為true,則把sharedInstance注入到ConfigurableEnvironment中

protected void configureEnvironment(ConfigurableEnvironment environment, String[] args) {
        if (this.addConversionService) {
            ConversionService conversionService = ApplicationConversionService.getSharedInstance();
            environment.setConversionService((ConfigurableConversionService)conversionService);
        }

        this.configurePropertySources(environment, args);
        this.configureProfiles(environment, args);
    }

2、若addConversionService為true,則把sharedInstance注入到beanFactory中

protected void postProcessApplicationContext(ConfigurableApplicationContext context) {
       ......
        if (this.addConversionService) {
            context.getBeanFactory().setConversionService(ApplicationConversionService.getSharedInstance());
        }
    }
    this.registerShutdownHook = true;

若設定為true,則起一條執行緒監聽當前執行緒發生shutdown時呼叫AbstractApplicationContext.doClose()方法。
相關程式碼:

private void refreshContext(ConfigurableApplicationContext context) {
        if (this.registerShutdownHook) {
            try {
                context.registerShutdownHook();
            } catch (AccessControlException var3) {
            }
        }

        this.refresh((ApplicationContext)context);
    }
public void registerShutdownHook() {
        if (this.shutdownHook == null) {
            this.shutdownHook = new Thread("SpringContextShutdownHook") {
                public void run() {
                    synchronized(AbstractApplicationContext.this.startupShutdownMonitor) {
                        AbstractApplicationContext.this.doClose();
                    }
                }
            };
            Runtime.getRuntime().addShutdownHook(this.shutdownHook);
        }
    }
    this.lazyInitialization = false;

設定bean定義是否預設為懶載入,若設定為true,則會新增LazyInitializationBeanFactoryPostProcessor到context的beanFactoryPostProcessor列表中,遍歷所有BeanDefinition,若沒有設定lazyInit屬性,則預設修改為true。

    this.applicationContextFactory = ApplicationContextFactory.DEFAULT;

預設的applicationContext工廠,根據webApplicationType返回對應的context。

ApplicationContextFactory DEFAULT = (webApplicationType) -> {
        try {
            switch(webApplicationType) {
            case SERVLET:
                return new AnnotationConfigServletWebServerApplicationContext();
            case REACTIVE:
                return new AnnotationConfigReactiveWebServerApplicationContext();
            default:
                return new AnnotationConfigApplicationContext();
            }
        } catch (Exception var2) {
            throw new IllegalStateException("Unable create a default ApplicationContext instance, you may need a custom ApplicationContextFactory", var2);
        }
    };
    this.primarySources = new LinkedHashSet(Arrays.asList(primarySources));

SpringApplication建構函式傳入的class引數,儲存在primarySource屬性中

    this.webApplicationType = WebApplicationType.deduceFromClasspath();

根據不同的webApplicationType生成對應的ApplicationContext、Enviroment屬性。判斷條件:
1、若存在DispatcherHandler,則webApplicationType=REACTIVE
2、若存在"javax.servlet.Servlet"、"org.springframework.web.context.ConfigurableWebApplicationContext"都有載入,則返回SERVLET,(SERVLET_INDICATOR_CLASSES = new String[]{“javax.servlet.Servlet”, “org.springframework.web.context.ConfigurableWebApplicationContext”};)
3、否則返回NONE
相關判斷程式碼:

static WebApplicationType deduceFromClasspath() {
        if (ClassUtils.isPresent("org.springframework.web.reactive.DispatcherHandler", (ClassLoader)null) && !ClassUtils.isPresent("org.springframework.web.servlet.DispatcherServlet", (ClassLoader)null) && !ClassUtils.isPresent("org.glassfish.jersey.servlet.ServletContainer", (ClassLoader)null)) {
            return REACTIVE;
        } else {
            String[] var0 = SERVLET_INDICATOR_CLASSES;
            int var1 = var0.length;

            for(int var2 = 0; var2 < var1; ++var2) {
                String className = var0[var2];
                if (!ClassUtils.isPresent(className, (ClassLoader)null)) {
                    return NONE;
                }
            }

            return SERVLET;
        }
    }
    this.bootstrappers = new ArrayList(this.getSpringFactoriesInstances(Bootstrapper.class));

獲取所有Bootstrapper.class型別bean,在BoostrapperContext初始化階段會呼叫所有的Boostrapper的initialize方法初始化context:

private DefaultBootstrapContext createBootstrapContext() {
        DefaultBootstrapContext bootstrapContext = new DefaultBootstrapContext();
        this.bootstrappers.forEach((initializer) -> {
            initializer.intitialize(bootstrapContext);
        });
        return bootstrapContext;
    }
    this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));

獲取所有ApplicationContextInitializer.class型別bean,在ApplicationContext初始化階段會呼叫所有initialzer初始化context,發生在BeanDefinition載入之前。

    this.mainApplicationClass = this.deduceMainApplicationClass();

根據呼叫棧找到main方法所在類。

private Class<?> deduceMainApplicationClass() {
        try {
            StackTraceElement[] stackTrace = (new RuntimeException()).getStackTrace();
            StackTraceElement[] var2 = stackTrace;
            int var3 = stackTrace.length;

            for(int var4 = 0; var4 < var3; ++var4) {
                StackTraceElement stackTraceElement = var2[var4];
                if ("main".equals(stackTraceElement.getMethodName())) {
                    return Class.forName(stackTraceElement.getClassName());
                }
            }
        } catch (ClassNotFoundException var6) {
        }

        return null;
    }