Recentemente, tenho tempo para otimizar a plataforma de API aberta da empresa. Quando eu estava digitando o pacote de jar, de repente percebi que era muito habilidoso em usar a bota da primavera antes, mas nunca conheci o princípio inicial do frasco produzido pela Spring Boot. Então eu descompactei a jarra desta vez e olhei para ela. Foi realmente diferente do que eu imaginava. A seguir, é apresentada uma análise completa do frasco desconfiado.
A aplicação da bota de primavera não é publicada. A estrutura de uma demonstração mais simples é semelhante. Além disso, a versão da bota de primavera que usei é 1.4.1. Há outro artigo na Internet para analisar a inicialização do Spring Boot Jar. Isso deve estar abaixo de 1.4, e o método de inicialização também é muito diferente da versão atual.
Após a instalação limpa do MVN, quando procurarmos no diretório de destino, encontraremos dois pacotes JAR, como segue:
xxxx.jarxxx.jar.original
Isso é atribuído ao mecanismo de plug-in de inicialização da mola, que transforma um frasco comum em um pacote de frasco executável, e xxx.jar.original é um pacote JAR produzido pelo MAVEN. Eles podem ser encontrados em referência ao artigo no site oficial da primavera, como segue:
http://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#executable-jar
A seguir, faz parte da estrutura do diretório do frasco produzido pelo aplicativo de inicialização da mola, a maioria das quais é omitida, e apenas as partes importantes são exibidas.
.├── BOOT-INF│ ├── classes│ ├── application-dev.properties│ │ ├── application-prod.properties│ │ ├── application.properties│ │ │ │ └── com│ │ │ │ └── weibangong│ │ │ └── open│ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ └ └─ Open│ │ │ │ │ │ │ │ └—rup swaggerconfig.class│ │ │ │ │ │ └ └stoCconfig.class│ │ │ │ │ │ │ └ └stoCconfig.class│ │ │ │ │ │ │ └ └sto -a. │ │ │ │ │ │ └── SwaggerConfig.class│ │ │ │ │ ├── oauth2│ │ │ │ ├── controller│ │ │ │ │ ├── AccessTokenController.class│ │ ├── logback-spring.xml│ │ └── static│ ├── css│ │ │ └─— GURU.CSS│ │ ├— Imagens│ │ ├── FBCover1200x628.png│ │ └── newbannerBoots_2.png│ └── Lib│ ├sto. com.weibangong.open│ └── servidor aberto-openapi│ ├── pom.properties│ └── pom.xml└── org └── springframework └── bota └─ carregador ├stoChIleSearchOUNHER $ 1.class ├lltIuncher └UCHIBLETERSHIBLETER ├stoChIleSearcher. LançouDurlClassLoader $ 1.class ├── lançouDurlClassLoader.class ├── lançador.class ├── Arquivo │ ├── Arquivo $ Entry.class │ ├— Arquivo $ RoduStFilter.class │ ├— Arquivo.class │─ ├─ ExplodedFilter $ 1.Class │ ├── Archive.class │ ├─ ├ ├ ├ ├ - ExplodedFilter $ 1.class │ ├── Archive.Class │ ├─ ├ ├ ├ ├ ├ ├ - Explodedfilter ├── Explodedarchive $ FileEntRyIterator $ ingressComparator.class ├── explodedarchive $ FileEntryiterator.class
Além da aula que escrevemos no aplicativo, esse frasco também possui um pacote de org separado. Deve ser que o aplicativo de inicialização da mola use o plug -in de inicialização do Spring para inserir este pacote, que é para aprimorar o estágio do pacote no ciclo de vida da MVN. É este pacote que desempenha um papel fundamental no processo de inicialização. Além disso, as várias dependências exigidas pelo aplicativo são inseridas no frasco e o pacote adicional de inicialização da mola é inserido. Esse frasco que pode tudo em um também é chamado de gordura. Aqui sempre usaremos o gordo.jar para substituir o nome da jarra.
Neste momento, continuaremos analisando o arquivo manifesto.mf no meta-inf, como segue:
Manifest-Version: 1.0Implementation-Title: Open :: Server :: OpenApiimplementation-Version: 1.0-SnapshoTarchiver-Versão: Plexus Archiverbuilt-by: Xiaxuanimplement-Version-Vendor-Id: com.weibangong. Inc.Main-Class: org.springframework.boot.loader.propertieslauncherTart-class: com.weibangong.open.openapi.springbootwebapplicationspring-boot-classes: boot-inf/classes/spring-boot-lib: boot-inf/lib-by-by: MAV: 1.8.0_20Implementation-url: http://maven.apache.org/open-sherver-openapi
A classe principal especificada aqui é um arquivo de classe no pacote inserido separadamente, em vez do nosso programa de inicialização e, em seguida, o arquivo manifest.mf possui uma classe inicial separada que especifica o programa de inicialização do nosso aplicativo.
Primeiro, encontramos a classe org.springframework.boot.loader.propertieslauncher, onde o método principal é:
public static void main (string [] args) lança exceção {Propertieslauncher iniciador = new Propertieslauncher (); args = iniciador.getargs (args); lançador.launch (args);}Verifique o método de lançamento. Este método é encontrado no iniciador da classe pai e o método de lançamento da classe pai é o seguinte:
lançamento de void protegido (string [] args, string mainClass, classLoader classLoader) lança Exceção {thread.currentThread (). SetContextClassLoadeler (classloader); this.createmainMethodRunner (mainClass, args, classe de classe) .run (); } mainMethodRunner protegido CreateMainMethOrunner (String mainClass, string [] args, classLoader classLoader) {return new mainMethodRunner (mainClass, args); }O método de lançamento finalmente chama o método CreateMainMethOdRunner, que instancia o objeto MainMethodRunner e executa o método RUN. Nós vamos ao código -fonte mainMethodrunner, como segue:
pacote org.springframework.boot.loader; importar java.lang.reflect.method; classe pública mainmethodrunner {private final string mainClassName; String final privada [] args; public mainMethodRunner (string mainClass, string [] args) {this.mainClassName = mainClass; this.args = args == null? null: (string []) args.clone (); } public void run () lança Exceção {classe mainClass = thread.currentThread (). getContextClassLoader (). loadclass (this.mainClassName); Método mainMethod = mainClass.getDecLaredMethod ("main", nova classe [] {string []. Class}); mainmethod.invoke ((objeto) null, novo objeto [] {this.args}); }}Verificando o método de execução, é muito fácil executar o frasco de inicialização da mola e a análise acabou basicamente.
5. O processo de inicialização do programa principal
Depois de falar sobre o processo de início do JAR, vamos falar sobre o processo de inicialização e carregamento do programa principal no aplicativo de inicialização da primavera. Primeiro, vejamos o método principal do aplicativo de inicialização da primavera.
pacote cn.com.devh; importar org.springframework.boot.springApplication; importar org.springframework.boot.autoconfigure.springbootApplication; importação org.springframework.cloud.client.discovery.enablediscoverclient; org.springframework.cloud.netflix.eureka.enableeurekaclient; importar org.springframework.cloud.netflix.feign.enablefeignlients;/*** Criado por xiaxuan em 17/8/25. */@SpringBoOTApplication@enableFeignClients@EnableeureKaclientPublic Classe A1ServiceApplication {public Static void main (String [] args) {springapplication.run (a1ServiceApplication.class, args); }}Vá para o método de execução em Springapplication, como segue:
/** * Helper estático que pode ser usado para executar um {@link springApplication} na fonte * especificada usando configurações padrão. * @param fonte a fonte para carregar * @param args os argumentos do aplicativo (geralmente passados de um método principal java) * @return the Running {@link ApplicationContext} */ public static configurableApplicationContext Run (fonte do objeto, string ... args) {Return run (novo objeto [] {},}, fonte); } /** * Helper estático que pode ser usado para executar uma {@link SpringApplication} das fontes * especificadas usando configurações padrão e argumentos fornecidos pelo usuário. * @param obtém as fontes para carregar * @param args os argumentos do aplicativo (geralmente passados de um método principal java) * @return the Running {@link ApplicationContext} */ public static configurableApplicationContext run (object [] fontes, string [] args) {return springAppatication (fontes) .r (] fontes, args) {return springAppatication (fontes) .r) }Aqui, a instanciação da SpringApplication é a chave, vamos ao construtor de aplicativos Spring.
/*** Crie uma nova instância {@link SpringApplication}. O contexto do aplicativo carregará * os grãos das fontes especificadas (consulte {@link SpringApplication-Level} * documentação para obter detalhes. A instância pode ser personalizada antes de chamar * {@link #run (string ...)}. * @Param fontes as fontes de bEapplation * @see #run (objeto, string []) * @see #srunationicslation Fontes) {Inicialize (fontes); ApplicationContextInitializer.class);DeduceWebEnvironment () No método Initialize aqui determina se está atualmente iniciado com um aplicativo da web ou um frasco normal, como segue:
Private boolean deducewebenvironment () {for (string className: web_environment_classes) {if (! classutils.ispresent (ClassName, null)) {return false; }} retornar true; }O web_environment_classes é:
String final estática privada [] web_environment_classes = {"javax.servlet.servlet", "org.springframework.web.context.configurablewebapplicationContext"};Enquanto qualquer um deles não existir, o aplicativo atual será iniciado na forma de um frasco normal.
Em seguida, o método Setinitializers inicializa todos os ApplicationContextinitializers,
/** * Define o {@link ApplicationContextInitializer} que será aplicado ao Spring * {@link ApplicationContext}. * @Param Initializers Os inicializadores para definir */ public void setInitializers (coleção <? Extend ApplicationContextInitializer <? >> Inicializadores) {this.initializers = new ArrayList <ApplicationContextinitializer <? >> (); this.initializers.addall (inicializadores); } setListeners ((coleção) getsPringFacoriesInstances (ApplicationListener.class)) **Esta etapa inicializa todos os ouvintes.
Vamos voltar à aplicação anterior de Spring (fontes) .run (args); e insira o método de execução, o código é o seguinte:
/** * Execute o aplicativo Spring, criando e atualizando um novo * {@link ApplicationContext}. * @param args os argumentos do aplicativo (geralmente passados de um método principal java) * @return um {@link ApplicationContext} */ public configurableApplicationContext run (string ... args) {stopwatch stopwatch = new stopwatch (); stopwatch.start (); ConfigurableApplicationContext context = null; ConfigureheadlessProperty (); SpringApplicationRunListeners ouvintes = getRunListeners (args); ouvintes.started (); tente {ApplicationArguments ApplicationArguments = new DefaultApplicationArguments (args); context = CreateAndRefreshContext (ouvintes, ApplicationAnduments); AfterRefresh (contexto, ApplicationArguments); ouvintes.finished (contexto, nulo); stopwatch.stop (); if (this.logstartupinfo) {new startupInfologger (this.mainApplicationClass) .LogStard (getApplicationLog (), StopWatch); } retornar contexto; } catch (throwable ex) {handlerunfailure (contexto, ouvintes, ex); lançar uma nova ilegalStateException (Ex); }}Esta etapa executa a criação de contexto CreateAndRefreshContext (ouvintes, ApplicationArguments),
private configurableApplicationContext CreateAndRefreshContext (SpringApplicationRunListeners ouvintes, ApplicationArguments ApplicationArguments) {ConfigurableApplicationContext Contexto; // Crie e configure o ambiente de ambiente configurável e ambiente = getorCreateenvironment (); configureEnvironment (ambiente, ApplicationArguments.getSourCeargs ()); ouvintes. if (iswebenvironment (ambiente) &&! this.webenvironment) {Environment = converttostandardenvironment (ambiente); } if (this.banNermode! = Banner.mode.off) {printBanner (ambiente); } // Crie, carregue, atualize e execute o ApplicationContext context = CreateApplicationContext (); context.setEnvironment (ambiente); PostprocessApplicationContext (contexto); aplicar initializadores (contexto); ouvintes.ContextPrepared (contexto); if (this.logstartupinfo) {logstartupinfo (context.getParent () == null); logstartupprofileInfo (contexto); } // Adicione a bota específica de singleton beans context.getBeanFactory (). Registersingleton ("SpringApplicationArguments", ApplicationArguments); // Carregar o conjunto de fontes <Object> fontes = getsources (); Assert.NotEmpty (fontes, "fontes não devem estar vazias"); carga (contexto, fontes.toArray (novo objeto [fontes.size ()])); ouvintes.ContextLoaded (contexto); // Atualizar a atualização do contexto (contexto); if (this.registershutdowhook) {try {context.registershutdownook (); } catch (accessControlexception ex) {// não permitido em alguns ambientes. }} retornar contexto; } // Crie e configure o ambiente de ambiente configurável e ambiente = getorCreateenvironment (); configureEnvironment (ambiente, ApplicationArguments.getSourCeargs ());Esta etapa realiza a configuração e o carregamento do ambiente.
if (this.BanNerMode! = Banner.mode.off) {printBanner (ambiente); }Esta etapa imprime o logotipo da inicialização da primavera. Se você precisar alterá -lo, adicione Banner.txt e Banner.txt ao arquivo de recursos para alterá -lo para o padrão necessário.
// Criar, carregar, atualizar e executar o ApplicationContext Context = CreateApplicationContext (); return (ConfigurableApplicationContext) beanutils.instantiate (ContextClass)
O contexto de criação realmente inclui o que o contêiner é criado e instancia a classe de resposta, incluindo a criação de incorporação incorporada, se deve escolher o jetty ou o tomcat, há muito conteúdo, então falarei sobre isso na próxima vez.
if (this.registershutdowhook) {try {context.registershutdownook (); } catch (accessControlexception ex) {// não permitido em alguns ambientes. }}Esta etapa é registrar o contexto atual e destruir o contêiner quando o comando Kill for recebido.
Basicamente, a análise de inicialização acabou, mas ainda há alguns detalhes que consomem muito tempo para dizer. Isso será discutido no post subsequente do blog, e isso é tudo para hoje.
Em resumo, o processo de inicialização do Spring Boot Jar é basicamente as seguintes etapas:
1. Quando empacotamos normalmente, o plug-in de inicialização da mola expande o ciclo de vida do Maven e transmite o pacote relacionado à inicialização da mola no frasco. Esse frasco contém arquivos de classe relacionados ao programa de inicialização de inicialização da primavera, além dos frascos produzidos pelo aplicativo.
2. Eu já vi o processo de inicialização de uma versão ligeiramente mais baixa do frasco de inicialização da mola antes. Naquela época, lembro -me de que o thread atual possui um novo thread para executar o programa principal e agora foi alterado para usar diretamente a reflexão para iniciar o programa principal.
Resumir
O exposto acima é a análise do princípio do jarra de inicialização da primavera introduzido pelo editor. Espero que seja útil para você. Se você tiver alguma dúvida, deixe -me uma mensagem e o editor responderá a você a tempo. Muito obrigado pelo seu apoio ao site wulin.com!