Análise do Processo de Inicialização e Ciclo de Vida da Classe SpringApplication

A classe SpringApplication é o ponto central para a inicialização de aplicações Spring Boot. Geralmente executada a partir de um método main, ela coordena a criação do contexto, a configuração do ambiente e a execução de listeners.

O Processo de Instalação no Construtor

Ao instanciar uma SpringApplication, o framework realiza diagnósticos preliminares para definir o comportamento da aplicação. O código abaixo ilustra a lógica interna de inicialização:


public SpringApplication(ResourceLoader carregadorRecurso, Class<?>... fontesBase) {
    this.resourceLoader = carregadorRecurso;
    Assert.notNull(fontesBase, "As fontes principais não podem ser nulas.");
    this.primarySources = new LinkedHashSet<>(Arrays.asList(fontesBase));
    
    // Identifica se a aplicação é Web (Servlet ou Reactive) ou Standard
    this.webApplicationType = identificarTipoAplicacao();
    
    // Carrega inicializadores definidos em META-INF/spring.factories
    setInitializers((Collection) localizarInstancias(ApplicationContextInitializer.class));
    
    // Carrega ouvintes (listeners) de eventos do ciclo de vida
    setListeners((Collection) localizarInstancias(ApplicationListener.class));
    
    // Identifica a classe que contém o método main
    this.mainApplicationClass = localizarClassePrincipal();
}

Detecção do Tipo de Ambiente Web

O Spring Boot diferencia o ambiente de execução baseando-se na presença de classes específicas no classpath. Existem três categorias: NONE (não-web), SERVLET (Spring MVC tradicional) e REACTIVE (Spring WebFlux).


private WebApplicationType identificarTipoAplicacao() {
    boolean possuiWebflux = ClassUtils.isPresent("org.springframework.web.reactive.DispatcherHandler", null);
    boolean possuiMvc = ClassUtils.isPresent("org.springframework.web.servlet.DispatcherServlet", null);

    if (possuiWebflux && !possuiMvc) {
        return WebApplicationType.REACTIVE;
    }
    
    String[] classesServlet = { "javax.servlet.Servlet", "org.springframework.web.context.ConfigurableWebApplicationContext" };
    for (String nomeClasse : classesServlet) {
        if (!ClassUtils.isPresent(nomeClasse, null)) {
            return WebApplicationType.NONE;
        }
    }
    return WebApplicationType.SERVLET;
}

O Fluxo do Método run()

O método run é onde a execução propriamente dita acontece. Ele gerencia desde o cronômetro de inicialização até a notificação final de que o serviço está pronto.


public ConfigurableApplicationContext run(String... argumentos) {
    StopWatch cronometro = new StopWatch();
    cronometro.start();

    ConfigurableApplicationContext contexto = null;
    Collection<SpringBootExceptionReporter> relatoresErro = new ArrayList<>();

    // Configura modo headless para ambientes sem interface gráfica
    ajustarPropriedadeHeadless();

    // Notifica listeners que a inicialização começou
    SpringApplicationRunListeners ouvintes = obterOuvintesExecucao(argumentos);
    ouvintes.starting();

    try {
        ApplicationArguments argsApp = new DefaultApplicationArguments(argumentos);
        
        // Prepara o ambiente (propriedades, perfis, etc.)
        ConfigurableEnvironment ambiente = prepararAmbiente(ouvintes, argsApp);
        configurarIgnorarBeanInfo(ambiente);
        
        // Exibe o Banner no console
        Banner bannerExibido = imprimirBanner(ambiente);

        // Instancia o ApplicationContext adequado ao tipo de webApplicationType
        contexto = criarContextoAplicacao();

        // Prepara os analisadores de falhas na inicialização
        relatoresErro = localizarInstancias(SpringBootExceptionReporter.class, 
            new Class[] { ConfigurableApplicationContext.class }, contexto);

        // Configura o contexto antes do refresh (vincula ambiente, injeta beans iniciais)
        prepararContexto(contexto, ambiente, ouvintes, argsApp, bannerExibido);

        // Processo principal de criação de beans e refresh do IoC
        atualizarContexto(contexto);

        posAtualizacao(contexto, argsApp);

        cronometro.stop();
        if (this.logStartupInfo) {
            new StartupInfoLogger(this.mainApplicationClass).logStarted(getLog(), cronometro);
        }

        // Informa que o contexto foi totalmente carregado
        ouvintes.started(contexto);

        // Executa componentes do tipo CommandLineRunner ou ApplicationRunner
        executarRunners(contexto, argsApp);
    } catch (Throwable ex) {
        tratarFalha(contexto, ex, relatoresErro, ouvintes);
        throw new IllegalStateException(ex);
    }

    try {
        ouvintes.running(contexto);
    } catch (Throwable ex) {
        tratarFalha(contexto, ex, relatoresErro, null);
        throw new IllegalStateException(ex);
    }
    return contexto;
}

Diferenças de Contexto: Spring MVC vs. Spring Boot

Uma distinção fundamental entre o modelo tradicional do Spring MVC e o Spring Boot reside na hierarquia de contextos.

Arquitetura Tradicional (Spring MVC)

No Spring MVC legado, é comum encontrar dois contextos distintos:

  • Root WebApplicationContext: Geralmente carregado via ContextLoaderListener, contém beans de serviço e repositórios.
  • Servlet WebApplicationContext: Vinculado a uma DispatcherServlet específica, contenod beans de infraestrutura web (Controllers, ViewResolvers).

O DispatcherServlet não é gerenciado pelo Spring nativamente; ele busca o contexto raiz no ServletContext para estabelecer a relação pai-filho.

Arquitetura Spring Boot

No Spring Boot, por padrão, utiliza-se um único contexto unificado (AnnotationConfigServletWebServerApplicationContext para aplicações Servlet).

Diferente do modelo legado, o DispatcherServlet no Spring Boot é registrado como um Bean dentro do próprio contexto do Spring. Quando o servidor web embutido (como Tomcat) inicia, o Spring Boot injeta o contexto único diretamente no servlet. Isso simplifica a gestão de dependências, permitindo que o DispatcherServlet utilize injeção de dependência padrão para acessar o ApplicationContext.


// Exemplo simplificado da criação do servidor web embutido no contexto
protected void onRefresh() {
    super.onRefresh();
    try {
        // Localiza a fábrica (Tomcat, Jetty ou Undertow) e sobe o servidor
        ServletWebServerFactory fabrica = getWebServerFactory();
        this.webServer = fabrica.getWebServer(getSelfInitializer());
    } catch (Throwable ex) {
        throw new ApplicationContextException("Falha ao iniciar servidor web", ex);
    }
}

Esta abodragem de contexto único facilita testes de integração e reduz a complexidade de configuração, eliminando a necessidade de múltiplos arquivos XML ou classes de configuração redundantes para separar Beans de infraestrutura de Beans de negócio.

Tags: spring-boot java Spring-Framework application-context web-server

Publicado em 6-13 08:14 por Thomas