Récemment, j'ai le temps d'optimiser la plate-forme API ouverte de l'entreprise. Lorsque je tapais le package JAR, j'ai soudainement réalisé que j'étais très habile à utiliser Spring Boot auparavant, mais je n'ai jamais connu le principe de départ du pot produit par Spring Boot. Ensuite, j'ai décompressé le pot cette fois et je l'ai regardé. C'était vraiment différent de ce que j'imaginais. Ce qui suit est une analyse complète du pot dézippé.
L'application de Spring Boot n'est pas publiée. La structure d'une démo plus simple est similaire. De plus, la version de Spring Boot que j'ai utilisée est 1.4.1. Il y a un autre article sur Internet pour analyser le démarrage de Spring Boot Jar. Cela devrait être inférieur à 1,4, et la méthode de démarrage est également très différente de la version actuelle.
Après l'installation de MVN Clean, lorsque nous regardons dans le répertoire cible, nous trouverons deux packages en pot, comme suit:
xxxx.jarxxx.jar.original
Ceci est attribué au mécanisme de plug-in de démarrage de Spring, qui transforme un pot ordinaire en un package JAR exécutable, et xxx.jar.original est un package de pot produit par Maven. Ceux-ci peuvent être trouvés en référence à l'article sur le site officiel du printemps, comme suit:
http://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#execuable-jar
Ce qui suit fait partie de la structure du répertoire du pot produit par l'application Spring Boot, dont la plupart sont omises, et seules les parties importantes sont affichées.
.├fique-inf│ ├fiques ├fiquesl ├fique ├fiques ├fiqueties ├fiqueties ├ - - Properties│ │ - Weibangong. │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ └fique │ │ │ │ │ └fique guru.css│ │ ├fiques └fique-Server-Openapi│ ├── Pom.Properties│ └── Pom.xml└fique ├── lankerdUrlclassloadher.class ├── lanceur.class ├── archive │ ├── archive $ entry.class │ ├── archive $ EntryFilter.class │ ├fique. ExplodedArchive $ filentryiterator $ entryComparator.class ├fique
En plus de la classe que nous avons écrite dans l'application, ce pot a également un package d'organismes distinct. Il faut que l'application Spring Boot utilise le plugin Spring Boot pour entrer ce package, qui est pour améliorer l'étape du package dans le cycle de vie MVN. C'est ce package qui joue un rôle clé dans le processus de démarrage. De plus, les différentes dépendances requises par l'application sont entrées dans le pot et le package supplémentaire de Spring Boot est entré. Ce pot qui peut tout-en-un est également appelé fat.jar. Ici, nous utiliserons toujours Fat.jar pour remplacer le nom du pot.
À l'heure actuelle, nous continuerons à examiner le fichier manifeste.mf dans Meta-Inf, comme suit:
Version manifeste: 1.0IMPLEMENTATION-TIRLE: Open :: Server :: OpenAPIIMPlementation-Version: 1.0-Snapshotarchiver-Version: Plexus ArchiverBuilt-By: XiaxuanImplementation-Vendenor-ID: com.weibangong.openspring-boot-Version, 1.4.1.ReleaseImplementation-Fendet: Pivealin-Software, 1.4.1.ReleaseImplementation-Fendetor: Pivealin-Softaginat, 1.4.1.ReleaseImplementation-Fendetor: Pivealin-Softaginat, 1.4.1.ReleaseImplementation-Fendetor: Pivealin-Software-Softaginat, 1.4.1. Inc.Main-Class: org.springframework.boot.loader.propertieslauncherstart-class: com.weibangong.open.openapi.springbootwebapplicationspring-boot-classe: boot-inf / classes / spring-boot-lib: both-inf / lib / créé: Apache Maven 3.3.9build-jdk: 1.8.0_20IMPLEMENTATION-URL: http://maven.apache.org/open-server-openapi
La classe principale spécifiée ici est un fichier de classe dans le package qui est entré séparément au lieu de notre programme de démarrage, puis le fichier manifeste.mf a une classe de démarrage distincte qui spécifie le programme de démarrage de notre application.
Nous trouvons d'abord la classe org.springframework.boot.loader.propertieslauncher, où la méthode principale est:
public static void main (String [] args) lève une exception {PropertiesLauncher Launcher = new PropertiesLauncher (); args = launcher.getargs (args); launcher.launch (args);}Vérifiez la méthode de lancement. Cette méthode se trouve dans le lanceur de classe Parent et la méthode de lancement de la classe parent est la suivante:
Protected void Launch (String [] args, String MainClass, classloader classloader) lève exception {Thread.currentThread (). SetContextClassLoader (classloader); this.CreateMainMethoDrunner (Mainclass, args, classloader) .run (); } MAINMETHODRUNNER protégé CreateMainMethodRunner (String MainClass, String [] Args, classloader classloader) {return new MayMethoDrunner (mainclass, args); }La méthode de lancement appelle enfin la méthode CreateMainMethoDrunner, qui instancie l'objet MainMethodrunner et exécute la méthode d'exécution. Nous allons au code source MainMethoDrunner, comme suit:
package org.springframework.boot.loader; import java.lang.reflect.Method; public class MainMethodRunner {private final String MainClassname; chaîne finale privée [] args; public MainMethodRunner (String MainClass, String [] args) {this.mainClassName = mainclass; this.args = args == null? null: (String []) args.clone (); } public void run () lève l'exception {class mainclass = thread.currentThread (). getContextClassLoader (). LoadClass (this.mainClassName); Méthode MainMethod = mainclass.getDeclaredMethod ("Main", new Class [] {String []. Class}); mainMethod.invoke ((objet) null, nouvel objet [] {this.args}); }}En vérifiant la méthode d'exécution, il est très facile d'exécuter le pot de démarrage de ressort, et l'analyse est essentiellement terminée.
5. Le processus de démarrage du programme principal
Après avoir parlé du processus de début de JAR, parlons du processus de démarrage et de chargement du programme principal de l'application Spring Boot. Tout d'abord, examinons la méthode principale de l'application Spring Boot.
package cn.com.devh; import org.springframework.boot.springApplication; import org.springframework.boot.autoconfigure.springbootapplication; import org.springframework.cloud.client.discovery.enablediscoveryclient; import; org.springframework.cloud.netflix.eureka.enableeurekaclient; import org.springframework.cloud.netflix.feign.enableFeignClients; / ** * créé par xiaxuan le 17/8/25. * / @ SpringbootApplication @ activerFeignClient @ ENABEREUREKACLIENTPUBLIC Classe A1ServiceApplication {public static void main (String [] args) {SpringApplication.Run (a1serviceApplication.class, args); }}Accédez à la méthode Run dans SpringApplication, comme suit:
/ ** * HELPER STATIQUE qui peut être utilisé pour exécuter un {@Link SpringApplication} à partir de la source * spécifiée à l'aide de paramètres par défaut. * @param source La source pour charger * @param args les arguments d'application (généralement passés d'une méthode principale java) * @return the Running {@Link ApplicationContext} * / public static configurableApplicationContext run (objet source, string ... args) {return run (nouvel objet [] {source}, args); } / ** * HELPER STATIQUE qui peut être utilisé pour exécuter un {@Link SpringApplication} à partir des * sources spécifiées en utilisant les paramètres par défaut et les arguments fournis par l'utilisateur. * @param Sources Les sources pour charger * @param argument les arguments d'application (généralement passés d'une méthode principale Java) * @return the Running {@Link ApplicationContext} * / public static configurableApplicationContext run (object [] Sources, String [] args) {return new SpringApplication (Sources) .run (args); }Ici, l'instanciation de SpringApplication est la clé, nous allons au constructeur de SpringApplication.
/ ** * Créez une nouvelle instance {@Link SpringApplication}. Le contexte de l'application chargera * Beans à partir des sources spécifiées (voir {@Link SpringApplication Class-Level} * Documentation pour plus de détails. L'instance peut être personnalisée avant d'appeler * {@Link #Run (Object,)}. * @Param Sources the Bean Sources * @see #Run (Object, String []) * @see #springApplication (ResourceLoder, Objet ...) * / public Spring (ObjectApplication ... Sources) {Initialise (Sources);} private void Initialise (objet [] Sources) {if (Sources! = Null && Sources.Length> 0) {this.sources.addall (Arrays.Aslist (Sources)); ApplicationContextInitializer.Class));DeduceWebenvironment () dans la méthode Initialize Ici détermine ici s'il est actuellement démarré avec une application Web ou un pot normal, comme suit:
Boolean privé DeduceWebenvironment () {for (String className: web_environment_classes) {if (! classutils.ispresent (className, null)) {return false; }} return true; }Le web_environment_classes est:
chaîne finale statique privée [] web_environment_classes = {"javax.servlet.servlet", "org.springframework.web.context.configurablewebapplicationContext"};Tant que l'un d'eux n'existe pas, l'application actuelle est lancée sous la forme d'un pot normal.
Ensuite, la méthode SETINITIALISERS initialise toutes les applications ContextInitialisers,
/ ** * Définit le {@Link ApplicationContextInitializer} qui sera appliqué au Spring * {@Link ApplicationContext}. * @param initialisers Les initialiseurs pour définir * / public void setInitialisers (collection <? étend ApplicationContextInitializer <? >> initialisers) {this.inializers = new ArrayList <applicationContexInitializer <? >> (); this.inializers.addall (initialiseurs); } setListeners ((collection) getSpringFactoriesInstances (applicationListener.class)) **Cette étape initialise tous les auditeurs.
Revenons à la précédente application Spring (sources) .run (args); et entrez la méthode d'exécution, le code est le suivant:
/ ** * Exécutez l'application Spring, créant et rafraîchissant un nouveau * {@Link ApplicationContext}. * @param args les arguments d'application (généralement passés à partir d'une méthode principale java) * @return a exécuté {@link applicationContext} * / public configurableApplicationContext run (string ... args) {stopwatch stopwatch = new stopwatch (); stopwatch.start (); ConfigurableApplicationContext context = null; configureHeadlessProperty (); SpringApplicationRunListeners auditeurs = getRunListeners (args); auditeurs.started (); essayez {applicationArguments applicationArguments = new defaultApplicationArguments (args); context = createArSrefreshContext (auditeurs, applicationArguments); AfterRefresh (contexte, applicationArguments); écouteurs finis (contexte, null); stopwatch.stop (); if (this.logStartupInfo) {nouveau startupInfologger (this.mainApplicationClass) .logStarted (getApplicationLog (), stopwatch); } Return Context; } catch (Throwable ex) {handlerunfailure (contexte, auditeurs, ex); jeter un nouvel illégalstateException (ex); }}Cette étape effectue une création de contexte CreateArandRefreshContext (auditeurs, applicationArguments),
configurableApplicationContext CreateArandRefreshContext (SpringApplicationRunListeners Auditers, ApplicationArguments ApplicationArguments) {ConfigurableApplicationContex Context; // Créer et configurer l'environnement ConfigurableEnvironment Environment = GetorCreateEenvironment (); ConfigureNenvironment (Environment, ApplicationArgments.getsourceargs ()); auditeurs.environmentPrepared (environnement); if (iswebenvironment (Environment) &&! this.webenvironment) {Environment = convertTostAndAnvironmentment (Environment); } if (this.bannermode! = Banner.Mode.off) {printbanner (environnement); } // Créer, charger, actualiser et exécuter le contexte ApplicationContext = createApplicationContext (); context.setenvironment (environnement); PostProcessApplicationContext (contexte); Appliquer lesInitialiseurs (contexte); écouteurs.ContextPrepared (contexte); if (this.logStartupInfo) {logStartupInfo (context.getParent () == null); LogstartupprofileInfo (contexte); } // Ajouter un singleton beans spécifique de démarrage context.getBeanFactory (). Registersingleton ("SpringApplicationArguments", applicationArguments); // Chargez les sources définies <objet> sources = getSources (); Affirmer.notempty (sources, «les sources ne doivent pas être vides»); charge (contexte, sources.toArray (nouvel objet [sources.size ()])); écouteurs.ContextLoaded (contexte); // actualiser le contexte de rafraîchissement (contexte); if (this.registerShutdownHook) {try {context.registerShutdownHook (); } catch (AccessControlexception ex) {// pas autorisé dans certains environnements. }} Return Context; } // Créer et configurer l'environnement ConfigurableEnvironment Environment = GetorCreateEenvironment (); ConfigureNenvironment (Environment, ApplicationArgments.getsourceargs ());Cette étape effectue la configuration et le chargement de l'environnement.
if (this.bannermode! = Banner.Mode.off) {printbanner (environnement); }Cette étape imprime le logo Spring Boot. Si vous devez le modifier, ajoutez Banner.txt et Banner.txt dans le fichier de ressources pour le changer en modèle dont vous avez besoin.
// Créer, charger, actualiser et exécuter le contexte ApplicationContext = createApplicationContext (); return (configurableApplicationContext) beanutils.instantiate (contextClass)
Le contexte de la création inclut vraiment ce que le conteneur est créé et instancie la classe de réponse, y compris la création de EmbedDedServletContainerFactory, que ce soit pour choisir Jetty ou Tomcat, il y a beaucoup de contenu, donc j'en parlerai la prochaine fois.
if (this.registerShutdownHook) {try {context.registerShutdownHook (); } catch (AccessControlexception ex) {// pas autorisé dans certains environnements. }}Cette étape consiste à enregistrer le contexte actuel et à détruire le conteneur lorsque la commande Kill est reçue.
Fondamentalement, l'analyse de démarrage est terminée, mais il y a encore des détails qui prennent beaucoup de temps à dire. Cela sera discuté dans le billet de blog ultérieur, et c'est tout pour aujourd'hui.
En résumé, le processus de démarrage du Jar de démarrage de Spring est essentiellement les étapes suivantes:
1. Lorsque nous emballons normalement Maven, le plug-in de démarrage de Spring étend le cycle de vie Maven et confère le package lié à Spring Boot dans le pot. Ce pot contient des fichiers de classe liés au programme de démarrage de Spring Boot en plus des pots produits par l'application.
2. J'ai déjà vu le processus de démarrage d'une version légèrement inférieure du Jar de démarrage de Spring. À ce moment-là, je me souviens que le thread actuel a un nouveau fil pour exécuter le programme principal, et maintenant il a été modifié pour utiliser directement la réflexion pour démarrer le programme principal.
Résumer
Ce qui précède est l'analyse du principe de Spring Boot Jar qui vous est présenté par l'éditeur. J'espère que cela vous sera utile. Si vous avez des questions, veuillez me laisser un message et l'éditeur vous répondra à temps. Merci beaucoup pour votre soutien au site Web Wulin.com!