Spring Boot啟動過程(一)


  之前在排查一個線上問題時,不得不仔細跑了很多遍Spring Boot的代碼,於是整理一下,我用的是1.4.3.RELEASE。

  首先,普通的入口,這沒什么好說的,我就隨便貼貼代碼了:

SpringApplication.run(Application.class, args);
-->

  public static ConfigurableApplicationContext run(Object source, String... args) {
    return run(new Object[] { source }, args);
  }

  public static ConfigurableApplicationContext run(Object[] sources, String[] args) {
    return new SpringApplication(sources).run(args);
  }

   也就是一個靜態方法,調用了構造函數創建實例,構造的參數是Object數組,這里new這個數組的時候傳入了一個元素就是啟動類的類對象實例(一般就是“new Object[] { Application.class” }),構造函數里調用了一個initialize方法。

  SpringApplication的initialize方法,首先在Object數組有值的情況下將數組放入一個final的類實例私有Object的Set集合中;然后用deduceWebEnvironment方法判斷當前應用環境是否是web環境,判斷邏輯是看Classpath是否同時存在javax.servlet.Servlet和org.springframework.web.context.ConfigurableWebApplicationContext,缺一就認為不是。然后,調用setInitializers方法,設置類實例的私有List<ApplicationContextInitializer<?>>類型變量initializers:

    public void setInitializers(
            Collection<? extends ApplicationContextInitializer<?>> initializers) {
        this.initializers = new ArrayList<ApplicationContextInitializer<?>>();
        this.initializers.addAll(initializers);
    }

  設置的時候會先new,也就是說這方法每次都是整體更換,不會追加。這個方法的參數都是各個模塊中配置在META-INF/spring.factories中的key為org.springframework.context.ApplicationContextInitializer的值,這些類都是接口ApplicationContextInitializer<C extends ConfigurableApplicationContext>的泛型實現。

    private <T> Collection<? extends T> getSpringFactoriesInstances(Class<T> type,
            Class<?>[] parameterTypes, Object... args) {
        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
        // Use names and ensure unique to protect against duplicates
        Set<String> names = new LinkedHashSet<String>(
                SpringFactoriesLoader.loadFactoryNames(type, classLoader));
        List<T> instances = createSpringFactoriesInstances(type, parameterTypes,
                classLoader, args, names);
        AnnotationAwareOrderComparator.sort(instances);
        return instances;
    }

  使用SpringFactoriesLoader.loadFactoryNames方法去取上面說的被配置的ApplicationContextInitializer的名字放進Set<String>中,並用反射創建這些名字的實例。

  

  setInitializers方法之后又是setInitializers,參數同上都是getSpringFactoriesInstances方法獲取,只不過這次參數Class<T> type泛型類型是org.springframework.context.ApplicationListener。

  

   initialize方法的最后一個步是設置實例的Class<?>類型私有屬性mainApplicationClass,獲取設置值的方法deduceMainApplicationClass:

    private Class<?> deduceMainApplicationClass() {
        try {
            StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();
            for (StackTraceElement stackTraceElement : stackTrace) {
                if ("main".equals(stackTraceElement.getMethodName())) {
                    return Class.forName(stackTraceElement.getClassName());
                }
            }
        }
        catch (ClassNotFoundException ex) {
            // Swallow and continue
        }
        return null;
    }

  實例化SpringApplication后調用了它的run實例方法(注意不是上面的靜態方法)。一進run方法首先啟動了StopWatch,這個StopWatch的功能在類的注釋寫可,大概意思是這是個簡單的秒表,用於在開發過程中方便程序員調試性能等,非線程安全,不建議用於生產。configureHeadlessProperty設置使用Headless,對於只有遠程登錄使用的服務器來說這樣性能要好一些。接着是加載用於這個run方法啟動過程的監聽器,依然是getSpringFactoriesInstances方法,這次的類型是org.springframework.boot.SpringApplicationRunListener:

# Run Listeners
org.springframework.boot.SpringApplicationRunListener=\
org.springframework.boot.context.event.EventPublishingRunListener

  

    SpringApplicationRunListeners(Log log,
            Collection<? extends SpringApplicationRunListener> listeners) {
        this.log = log;
        this.listeners = new ArrayList<SpringApplicationRunListener>(listeners);
    }

  先是加載所有可用監聽,然后初始化SpringApplicationRunListeners對象,最后循環啟動所有SpringApplicationRunListener監聽。啟動監聽的方法:

    @Override
    public void started() {
        this.initialMulticaster
                .multicastEvent(new ApplicationStartedEvent(this.application, this.args));
    }

  ApplicationStartedEvent實例化傳了兩個參數,先看第一個參數this.application是怎么來的,實例的SpringApplication的run方法中,用於獲取SpringApplicationRunListener,也就是前面說的getSpringFactoriesInstances被調用時:

    private SpringApplicationRunListeners getRunListeners(String[] args) {
        Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
        return new SpringApplicationRunListeners(logger, getSpringFactoriesInstances(
                SpringApplicationRunListener.class, types, this, args));
    }

  getSpringFactoriesInstances方法的參數包含SpringApplication.class和this,這兩個參數被傳入createSpringFactoriesInstances方法:

  

  可以看到,是通過反射創建實例的時候,將SpringApplication中的this傳進來EventPublishingRunListener構造的,然后EventPublishingRunListener構造:

    public EventPublishingRunListener(SpringApplication application, String[] args) {
        this.application = application;
        this.args = args;
        this.initialMulticaster = new SimpleApplicationEventMulticaster();
        for (ApplicationListener<?> listener : application.getListeners()) {
            this.initialMulticaster.addApplicationListener(listener);
        }
    }

  最后在構造ApplicationStartedEvent時傳給它的基類EventObject的protected不可序列化屬性source。實例化ApplicationStartedEvent后instance.getClass()並包裝為ResolvableType類型以保存類型信息,並將它和event作為參數傳入SimpleApplicationEventMulticaster的multicastEvent方法。multicastEvent首先獲取ApplicationListener,使用getApplicationListeners方法,這個方法中拋開對listener做了一些緩存類工作外,主要就是將事件和對應的監聽器做了下是否支持的驗證,返回通過了retrieveApplicationListeners中通過了supportsEvent驗證的監聽器集合,這里就體現出了ResolvableType的作用,它保存了類型的信息同時對泛型類型也支持。

   得到了這些匹配的監聽器后,判斷當前Executor是否被設置過,如果為null則同步循環執行所有:invokeListener(listener, event);如果不為null則:

                executor.execute(new Runnable() {
                    @Override
                    public void run() {
                        invokeListener(listener, event);
                    }
                });

  監聽器執行的時候也會先判斷是否是該由自己處理的事件,例如:

    @Override
    public void onApplicationEvent(ApplicationEvent event) {
        if (event instanceof ApplicationEnvironmentPreparedEvent) {
            onApplicationEnvironmentPreparedEvent(
                    (ApplicationEnvironmentPreparedEvent) event);
        }
        if (event instanceof ApplicationPreparedEvent) {
            onApplicationPreparedEvent(event);
        }
    }

  監聽啟動后,只准備一些啟動參數,和環境變量prepareEnvironment方法先是讀取了應用的啟動參數和profile配置,然后用listeners.environmentPrepared(environment)傳給監聽器:

    public void environmentPrepared(ConfigurableEnvironment environment) {
        this.initialMulticaster.multicastEvent(new ApplicationEnvironmentPreparedEvent(
                this.application, this.args, environment));
    }

   接着判斷如果environment是org.springframework.web.context.ConfigurableWebEnvironment的實例,但webEnvironment不是true,也就是說存在org.springframework.web.context.ConfigurableWebEnvironmen但不存在javax.servlet.Servlet的情況,會多執行一步environment = convertToStandardEnvironment(environment)轉換。

  之后的printBanner就不細說了,如果你在resource下自定義了一個banner.txt文件,啟動時會輸出內容,否則輸出:

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v1.4.3.RELEASE)

   接着創建ConfigurableApplicationContext實例,方法也很簡單,如果是web環境就BeanUtils.instantiate一個org.springframework.boot.context.embedded. AnnotationConfigEmbeddedWebApplicationContext的實例並強轉為ConfigurableApplicationContext,否則用org.springframework.context.annotation. AnnotationConfigApplicationContext的實例強轉。

  創建FailureAnalyzers實例,記錄了ConfigurableApplicationContext實例中需要關注的部分,如果啟動出錯了可以據此分析,可以配置,具體的邏輯依然是老方法spring.factories:

  不同的Analyzer關注不同的部分,自己可以擴展配置,最后prepareFailureAnalyzers方法給所有Analyzer實例setBeanFactory(context.getBeanFactory()),一旦啟動過程進入catch,被注冊的Analyzer實例的analyze方法就會被觸發執行,分析結果會被loggedExceptions.add(exception)加入到拋出的異常中:

    private FailureAnalysis analyze(Throwable failure, List<FailureAnalyzer> analyzers) {
        for (FailureAnalyzer analyzer : analyzers) {
            FailureAnalysis analysis = analyzer.analyze(failure);
            if (analysis != null) {
                return analysis;
            }
        }
        return null;
    }


例如:NoSuchBeanDefinitionFailureAnalyzer
    @Override
    protected FailureAnalysis analyze(Throwable rootFailure,
            NoSuchBeanDefinitionException cause, String description) {
        if (cause.getNumberOfBeansFound() != 0) {
            return null;
        }
        List<AutoConfigurationResult> autoConfigurationResults = getAutoConfigurationResults(
                cause);
        StringBuilder message = new StringBuilder();
        message.append(String.format("%s required %s that could not be found.%n",
                description == null ? "A component" : description,
                getBeanDescription(cause)));
        if (!autoConfigurationResults.isEmpty()) {
            for (AutoConfigurationResult provider : autoConfigurationResults) {
                message.append(String.format("\t- %s%n", provider));
            }
        }
        String action = String.format("Consider %s %s in your configuration.",
                (!autoConfigurationResults.isEmpty()
                        ? "revisiting the conditions above or defining" : "defining"),
                getBeanDescription(cause));
        return new FailureAnalysis(message.toString(), action, cause);
    }

   prepareContext方法中postProcessApplicationContext會在this.beanNameGenerator存在的情況下加載自定義命名策略,然后在this.resourceLoader存在的情況下為context設置resourceLoader和classLoader。applyInitializers方法調用之前加載的Initializer的實例並執行其initialize方法,例如加載環境變量信息、注冊EmbeddedServletContainerInitializedEvent的監聽、注冊CachingMetadataReaderFactoryPostProcessor等。listeners.contextPrepared(context)由於EventPublishingRunListener的contextPrepared是空的,先不說了。logStartupInfo部分初始化了logger,然后根據配置情況打印了啟動或運行以及profile是否配置的日志:

    protected void logStartupInfo(boolean isRoot) {
        if (isRoot) {
            new StartupInfoLogger(this.mainApplicationClass)
                    .logStarting(getApplicationLog());
        }
    }

    protected Log getApplicationLog() {
        if (this.mainApplicationClass == null) {
            return logger;
        }
        return LogFactory.getLog(this.mainApplicationClass);
    }

    public void logStarting(Log log) {
        Assert.notNull(log, "Log must not be null");
        if (log.isInfoEnabled()) {
            log.info(getStartupMessage());
        }
        if (log.isDebugEnabled()) {
            log.debug(getRunningMessage());
        }
    }

    protected void logStartupProfileInfo(ConfigurableApplicationContext context) {
        Log log = getApplicationLog();
        if (log.isInfoEnabled()) {
            String[] activeProfiles = context.getEnvironment().getActiveProfiles();
            if (ObjectUtils.isEmpty(activeProfiles)) {
                String[] defaultProfiles = context.getEnvironment().getDefaultProfiles();
                log.info("No active profile set, falling back to default profiles: "
                        + StringUtils.arrayToCommaDelimitedString(defaultProfiles));
            }
            else {
                log.info("The following profiles are active: "
                        + StringUtils.arrayToCommaDelimitedString(activeProfiles));
            }
        }
    }

   接着prepareContext中注冊啟動參數(applicationArguments)到bean工廠,包括logger、commandLineArgs等。然后加載bean定義的來源並根據其中配置加載bean,這里的sources就是初始化啟動類時傳進來的那個sources:

    BeanDefinitionLoader(BeanDefinitionRegistry registry, Object... sources) {
        Assert.notNull(registry, "Registry must not be null");
        Assert.notEmpty(sources, "Sources must not be empty");
        this.sources = sources;
        this.annotatedReader = new AnnotatedBeanDefinitionReader(registry);
        this.xmlReader = new XmlBeanDefinitionReader(registry);
        if (isGroovyPresent()) {
            this.groovyReader = new GroovyBeanDefinitionReader(registry);
        }
        this.scanner = new ClassPathBeanDefinitionScanner(registry);
        this.scanner.addExcludeFilter(new ClassExcludeFilter(sources));
    }

  注意下面的sources是待加載的,和上面這段不是同一個:

    public int load() {
        int count = 0;
        for (Object source : this.sources) {
            count += load(source);
        }
        return count;
    }

    private int load(Object source) {
        Assert.notNull(source, "Source must not be null");
        if (source instanceof Class<?>) {
            return load((Class<?>) source);
        }
        if (source instanceof Resource) {
            return load((Resource) source);
        }
        if (source instanceof Package) {
            return load((Package) source);
        }
        if (source instanceof CharSequence) {
            return load((CharSequence) source);
        }
        throw new IllegalArgumentException("Invalid source type " + source.getClass());
    }

  類型不同加載過程不同,其中Class<?>加載過程大概是通過BeanDefinitionLoader調用AnnotatedBeanDefinitionReader的registerBean方法:

    public void registerBean(Class<?> annotatedClass, String name, Class<? extends Annotation>... qualifiers) {
        AnnotatedGenericBeanDefinition abd = new AnnotatedGenericBeanDefinition(annotatedClass);
        if (this.conditionEvaluator.shouldSkip(abd.getMetadata())) {
            return;
        }

        ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(abd);
        abd.setScope(scopeMetadata.getScopeName());
        String beanName = (name != null ? name : this.beanNameGenerator.generateBeanName(abd, this.registry));
        AnnotationConfigUtils.processCommonDefinitionAnnotations(abd);
        if (qualifiers != null) {
            for (Class<? extends Annotation> qualifier : qualifiers) {
                if (Primary.class == qualifier) {
                    abd.setPrimary(true);
                }
                else if (Lazy.class == qualifier) {
                    abd.setLazyInit(true);
                }
                else {
                    abd.addQualifier(new AutowireCandidateQualifier(qualifier));
                }
            }
        }

        BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(abd, beanName);
        definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
        BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, this.registry);
    }

  可以看到有生成方法名,設置默認注入的實例、延遲以及過濾等等,注入的過程包括初始化一些信息,如構造、內部類、注解等:

    protected AbstractBeanDefinition(ConstructorArgumentValues cargs, MutablePropertyValues pvs) {
        setConstructorArgumentValues(cargs);
        setPropertyValues(pvs);
    }

    public StandardAnnotationMetadata(Class<?> introspectedClass, boolean nestedAnnotationsAsMap) {
        super(introspectedClass);
        this.annotations = introspectedClass.getAnnotations();
        this.nestedAnnotationsAsMap = nestedAnnotationsAsMap;
    }

   其他三種比如有的有輸入流什么的就不細總結了,這部分介紹Spring IOC的相關文章應該不少。

   prepareContext方法最后listeners.contextLoaded(context),加載監聽器到context並廣播ApplicationPreparedEvent事件。

==========================================================

咱最近用的github:https://github.com/saaavsaaa

微信公眾號:

                      


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM