SpringBoot의 시작 프로세스 분석(2)

18877 단어
SpringApplication 시작 프로세스의run () 방법을 분석합니다. 코드는 다음과 같습니다.
    public ConfigurableApplicationContext run(String... args) {
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        ConfigurableApplicationContext context = null;
        FailureAnalyzers analyzers = null;
        configureHeadlessProperty();
        SpringApplicationRunListeners listeners = getRunListeners(args);
        listeners.starting();
        try {
            ApplicationArguments applicationArguments = new DefaultApplicationArguments(
                    args);
            ConfigurableEnvironment environment = prepareEnvironment(listeners,
                    applicationArguments);
            Banner printedBanner = printBanner(environment);
            context = createApplicationContext();
            analyzers = new FailureAnalyzers(context);
            prepareContext(context, environment, listeners, applicationArguments,
                    printedBanner);
            refreshContext(context);
            afterRefresh(context, applicationArguments);
            listeners.finished(context, null);
            stopWatch.stop();
            if (this.logStartupInfo) {
                new StartupInfoLogger(this.mainApplicationClass)
                        .logStarted(getApplicationLog(), stopWatch);
            }
            return context;
        }
        catch (Throwable ex) {
            handleRunFailure(context, listeners, analyzers, ex);
            throw new IllegalStateException(ex);
        }
    }

보아하니 방법이 비교적 긴 것 같지만 우리는 주로 가동 절차, 어떤 감시, 실패 분석을 하는 것을 보고 나중에 이야기하기 때문에 이 방법의 관건적인 몇 단계는 다음과 같다.
1 context = createApplicationContext();
2 prepareContext(context,environment,listeners,applicationArguments,printedBanner);
3 refreshContext(context);
4 afterRefresh(context, applicationArguments);

맞아요. 관건적인 방법은 바로 이 네 가지입니다. 우선createApplicationContext () 방법을 분석하고 코드는 다음과 같습니다.
  public static final String DEFAULT_WEB_CONTEXT_CLASS = "org.springframework."
            + "boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext";
    public static final String DEFAULT_CONTEXT_CLASS = "org.springframework.context."
            + "annotation.AnnotationConfigApplicationContext";


    protected ConfigurableApplicationContext createApplicationContext() {
        Class> contextClass = this.applicationContextClass;
        if (contextClass == null) {
            try {
                contextClass = Class.forName(this.webEnvironment
                        ? DEFAULT_WEB_CONTEXT_CLASS : DEFAULT_CONTEXT_CLASS);
            }
            catch (ClassNotFoundException ex) {
                throw new IllegalStateException(
                        "Unable create a default ApplicationContext, "
                                + "please specify an ApplicationContextClass",
                        ex);
            }
        }
        return (ConfigurableApplicationContext) BeanUtils.instantiate(contextClass);
    }

웹 환경에서Configurable Application Context를 불러오고 만들었습니다. 쉽게 말하면 Application Context를 만들었습니다. 즉 Bean의 용기를 만들었습니다. (BeanFactory)
그리고prepareContext() 방법을 보면 이 방법은 설정, 파라미터, 감청기, 그리고banner 정보를 응용 프로그램 Context에 주입하는 것을 알 수 있다.
    private void prepareContext(ConfigurableApplicationContext context,
            ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
            ApplicationArguments applicationArguments, Banner printedBanner) {
        context.setEnvironment(environment);
        postProcessApplicationContext(context);
        applyInitializers(context);
        listeners.contextPrepared(context);
        if (this.logStartupInfo) {
            logStartupInfo(context.getParent() == null);
            logStartupProfileInfo(context);
        }

        // Add boot specific singleton beans
        context.getBeanFactory().registerSingleton("springApplicationArguments",
                applicationArguments);
        if (printedBanner != null) {
            context.getBeanFactory().registerSingleton("springBootBanner", printedBanner);
        }

        // Load the sources
        Set sources = getSources();
        Assert.notEmpty(sources, "Sources must not be empty");
        load(context, sources.toArray(new Object[sources.size()]));
        listeners.contextLoaded(context);
    }

방법3은 시작의 관건 함수입니다. 이 단계에서springboot는 이미 시작되었습니다. 비교적 복잡하기 때문에 잠시 후에 말씀드리겠습니다.그래서 우리는afterRefresh(context,applicationArguments)를 먼저 보았는데 그 세부 사항은 다음과 같다.
    protected void afterRefresh(ConfigurableApplicationContext context,
            ApplicationArguments args) {
        callRunners(context, args);
    }

    private void callRunners(ApplicationContext context, ApplicationArguments args) {
        List runners = new ArrayList();
        runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());
        runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());
        AnnotationAwareOrderComparator.sort(runners);
        for (Object runner : new LinkedHashSet(runners)) {
            if (runner instanceof ApplicationRunner) {
                callRunner((ApplicationRunner) runner, args);
            }
            if (runner instanceof CommandLineRunner) {
                callRunner((CommandLineRunner) runner, args);
            }
        }
    }    

  private void callRunner(CommandLineRunner runner, ApplicationArguments args) {
        try {
            (runner).run(args.getSourceArgs());
        }
        catch (Exception ex) {
            throw new IllegalStateException("Failed to execute CommandLineRunner", ex);
        }
    }

여기 보니까 생각나는 거 없어요?맞아요. 저희만의 현실적인 Application Runner나 Command Line Runner 같은 종류가 바로 이곳에서 호출되어 실행되고 있습니다.
마지막으로 가장 중요한refreshContext(context)를 살펴보겠습니다. 관련 코드는 다음과 같습니다.
    private void refreshContext(ConfigurableApplicationContext context) {
        refresh(context);
        if (this.registerShutdownHook) {
            try {
                context.registerShutdownHook();
            }
            catch (AccessControlException ex) {
                // Not allowed in some environments.
            }
        }
    }

    protected void refresh(ApplicationContext applicationContext) {
        Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext);
        ((AbstractApplicationContext) applicationContext).refresh();
    }


    public void refresh() throws BeansException, IllegalStateException {
        synchronized (this.startupShutdownMonitor) {
            // Prepare this context for refreshing.
            prepareRefresh();

            // Tell the subclass to refresh the internal bean factory.
            ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

            // Prepare the bean factory for use in this context.
            prepareBeanFactory(beanFactory);

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

                // Invoke factory processors registered as beans in the context.
                invokeBeanFactoryPostProcessors(beanFactory);

                // Register bean processors that intercept bean creation.
                registerBeanPostProcessors(beanFactory);

                // Initialize message source for this context.
                initMessageSource();

                // Initialize event multicaster for this context.
                initApplicationEventMulticaster();

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

                // Check for listener beans and register them.
                registerListeners();

                // Instantiate all remaining (non-lazy-init) singletons.
                finishBeanFactoryInitialization(beanFactory);

                // Last step: publish corresponding event.
                finishRefresh();
            }

            catch (BeansException ex) {
                if (logger.isWarnEnabled()) {
                    logger.warn("Exception encountered during context initialization - " +
                            "cancelling refresh attempt: " + ex);
                }

                // Destroy already created singletons to avoid dangling resources.
                destroyBeans();

                // Reset 'active' flag.
                cancelRefresh(ex);

                // Propagate exception to caller.
                throw ex;
            }

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

마지막으로 Abstract Application Context에 떨어졌습니다.refresh 방법 중입니다.
이 방법의 매 단계에는 명확한 주석이 있다.
prepare Refresh () 리셋 준비, 응용 프로그램의 시작 시간 설정, active 로고 설정, 속성 초기화 작업 수행.
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();BeanFactory를 가져옵니다.
prepare BeanFactory (beanFactory) 는 일부 BeanFactory의 초기화 작업을 실행합니다.
postProcess BeanFactory (beanFactory) 에서 BeanFactory의 백업 프로세서를 설정합니다.(Spring에서 Bean의 라이프 사이클 참조)
invokeBeanFactoryPostProcessors(beanFactory)가 BeanFactory의 백업 프로세서를 호출합니다.
registerBeanPostProcessors(beanFactory)는 용기 내의 모든 Bean을 실례화하기 전에 Bean의 전(후) 프로세서를 등록합니다.(BeanPostProcessor 참조)
initMessageSource() 설정 messgeSource
initApplication Event Multicaster() Context 브로드캐스트 초기화
onRefresh () 는 하위 클래스의 실현 방식에 따라 다른 일을 합니다.EmbeddedWebApplicationContext는 내장된 Tomcat을 만들고 시작합니다.
registerListeners () 감청기 만들기
finishBeanFactoryInitialization(beanFactory) 실례화 BeanFactory에서 게으름 피우지 않고 불러오는 단례 Bean.이 방법은 비교적 관건이다.
finishRefresh () 는 성명 주기와 관련된 작업을 초기화하거나 시작합니다. Context가 새로 고쳤다는 메시지를 발표합니다.
이상은springboot가 시작되는 대략적인 절차입니다. 시작하는 절차가 어떤 것인지 대충 알 수 있습니다.각 단계의 해석이 특별히 상세하지 않고 틀릴 수도 있다.
앞으로 차츰차츰 상세한 분석을 진행할 수 있을 것이다.
문제를 야기하다
빈팩토리가 뭐야?
Tomcat과 BeanFactory의 라이프 사이클에 대한 문제입니다.
Spring의 이벤트 수신 메커니즘
Tomcat 및 서브렛 및 Springboot 연관

좋은 웹페이지 즐겨찾기