Démarrez-vous, le printemps arrive (partie 2)

Evgeny EvgenyBorisov Borisov (NAYA Technologies) et Kirill tolkkv Tolkachev (Cyan.Finance, Twitter ) continuent de parler de l'utilisation de Spring Boot pour résoudre les problèmes de l'imaginaire Braavos Iron Bank. Dans la deuxième partie, nous nous concentrerons sur les profils et subtilités de lancement de l'application.






La première partie de l'article se trouve ici .


Donc, jusqu'à récemment, le client est venu et a simplement demandé d'envoyer un corbeau. Maintenant, la situation a changé. L'hiver est arrivé, le mur est tombé.


Premièrement, le principe de l'octroi de prêts est en train de changer. Si auparavant, avec une probabilité de 50%, ils ont donné à tout le monde sauf Starks, maintenant ils ne donnent qu'à ceux qui remboursent leurs dettes. Par conséquent, nous modifions les règles d'émission des prêts dans notre logique commerciale. Mais seulement pour les succursales de la banque, qui sont situées là où l'hiver est déjà venu, dans tout le reste, tout reste comme avant. Je vous rappelle qu'il s'agit d'un service qui décide d'accorder ou non un prêt. Nous ferons juste un autre service qui ne fonctionnera qu'en hiver.


Nous passons à notre logique métier:


public class WhiteListBasedProphetService implements ProphetService {  @Override  public boolean willSurvive(String name) {    return false;  } } 

Nous avons déjà une liste de ceux qui remboursent leurs dettes.


 spring: application.name: money-raven jpa.hibernate.ddl-auto: validate ironbank: ---:   -  : -: ,   : true 

Et il y a une classe qui est déjà associée à la propriété - .


 public class ProphetProperties { List<String> ; } 

Comme par le passé, nous l'injectons ici:


 public class WhiteListBasedProphetService implements ProphetService { private final ProphetProperties prophetProperties; @Override public boolean willSurvive(String name) {   return false; } } 

N'oubliez pas l'injection de constructeur (sur les annotations magiques):


 @Service @RequiredArgsConstructor public class WhiteListBasedProphetService implements ProphetService { private final ProphetProperties prophetProperties; @Override public boolean willSurvive(String name) {   return false; } } 

Presque terminé.


Maintenant, nous devons donner uniquement à ceux qui remboursent leurs dettes:


 @Service @RequiredArgsConstructor public class WhiteListBasedProphetService implements ProphetService { private final ProphetProperties prophetProperties; @Override public boolean willSurvive(String name) { return prophetProperties.get().contains(name); } } 

Mais ici, nous avons un petit problème. Nous avons maintenant deux implémentations: l'ancien et le nouveau service.


 Description Parameter 1 of constructor in com.ironbank.moneyraven.service.TransferMoneyProphecyBackend… - nameBasedProphetService: defined in file [/Users/tolkv/git/conferences/spring-boot-ripper… - WhileListBackendProphetService: defined in file [/Users/tolkv/git/conferences/spring-boot-ripper... 

Il est logique de diviser ces beans en différents profils. Profil d' et profil d' . Laissez notre nouveau service fonctionner uniquement dans le profil :


 @Service @Profile(ProfileConstants.) @RequiredArgsConstructor public class WhiteListBasedProphetService implements ProphetService { private final ProphetProperties prophetProperties; @Override public boolean willSurvive(String name) {   return prophetProperties.get().contains(name); } } 

Et l'ancien service est en .


 @Service @Profile(ProfileConstants.) public class NameBasedProphetService implements ProphetService { @Override public boolean willSurvive(String name) {   return !name.contains("Stark") && ThreadLocalRandom.current().nextBoolean(); } } 

Mais l'hiver arrive lentement. Dans les royaumes à côté du mur brisé, c'est déjà l'hiver. Mais quelque part dans le sud - pas encore. C'est-à-dire Les applications situées dans des succursales et des fuseaux horaires différents doivent fonctionner différemment. Selon les conditions de notre tâche, nous ne pouvons pas effacer l'ancienne implémentation où l'hiver est arrivé et utiliser la nouvelle classe. Nous voulons que les employés de la banque ne fassent rien du tout: nous leur livrerons une application qui fonctionnera en mode été jusqu'à l'arrivée de l'hiver. Et quand l'hiver arrive, ils le redémarrent et c'est tout. Ils n'auront pas à changer le code, à effacer les classes. Par conséquent, nous avons initialement deux profils: une partie du bac est créée lors de l'été, et une partie du bac est créée lorsque l'hiver.


Mais un autre problème apparaît:




Maintenant, nous n'avons plus un seul bean, car nous avons spécifié deux profils et l'application démarre dans le profil par défaut.


Nous avons donc une nouvelle exigence du client.


Iron Law 2. Aucun profil n'est autorisé




Nous ne voulons pas évoquer le contexte si le profil n'est pas activé, car l'hiver est déjà venu, tout est devenu très mauvais. Il y a certaines choses qui devraient arriver ou non, selon que l' ou que l' . Regardez également l'exception, dont le texte est donné ci-dessus. Il n’explique rien. Un profil n'est pas défini, il n'y a donc pas d'implémentation de ProphetService . Dans le même temps, personne n'a dit qu'il était nécessaire de définir un profil.


Par conséquent, nous voulons maintenant visser une pièce supplémentaire dans notre démarreur, qui, lors de la construction du contexte, vérifiera qu'un certain profil est défini. S'il n'est pas défini, nous n'irons pas et ne lancerons pas une telle exception (et pas une exception concernant l'absence de bac).


Pouvons-nous le faire avec notre écouteur d'application? Non. Et il y a trois raisons à cela:


  • L'auditeur à responsabilité unique est responsable de faire voler le corbeau. L'auditeur ne doit pas vérifier si un profil a été activé car l'activation d'un profil affecte non seulement l'auditeur lui-même, mais aussi beaucoup plus.
  • Lorsqu'un contexte est créé, différentes choses se produisent. Et nous ne voulons pas qu'ils commencent à se produire si aucun profil n'a été défini.
  • L'écouteur fonctionne à la toute fin lorsque le contexte est résolu. Et le fait qu'il n'y ait pas de profil, nous le savons bien plus tôt. Pourquoi attendre ces cinq minutes conditionnelles jusqu'à ce que le service augmente presque, puis tout tombe.

De plus, je ne sais toujours pas quels bugs vont apparaître du fait que nous avons commencé à monter sans profil (supposons que je ne connais pas la logique métier). Par conséquent, en l'absence de profil, vous devez faire tomber le contexte à un stade très précoce. Soit dit en passant, si vous utilisez n'importe quel Spring Cloud, cela devient encore plus pertinent pour vous, car l'application fait beaucoup de choses à un stade précoce.


Pour implémenter la nouvelle exigence, il existe ApplicationContextInitializer . C'est une autre interface qui nous permet d'étendre un certain point de Spring en le spécifiant dans spring.factories.




Nous implémentons cette interface, et nous avons un initialiseur de contexte, qui a un ConfigurableApplicationContext :


 public class ProfileCheckAppInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> { @Override public void initialize(ConfigurableApplicationContext applicationContext) { } } 

Avec cela, nous pouvons obtenir l'environnement - la chose que SpringApplication a préparée pour nous. Tous les biens que nous lui avons transmis sont arrivés. Entre autres, ils contiennent également des profils.


S'il n'y a pas de profils là-bas, alors nous devrions lever une exception en disant que vous ne pouvez pas travailler comme ça.


 public class ProfileCheckAppInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> { @Override public void initialize(ConfigurableApplicationContext applicationContext) { if applicationContext.getEnvironment().getActiveProfiles().length == 0 {     throw new RuntimeException("  !");   } } } 

Vous devez maintenant enregistrer ces informations dans spring.factories.


 org.springframework.boot.context.properties.EnableConfigurationProperties=com.ironbank.moneyraven.starter.IronConfiguration org.springframework.context.ApplicationContextInitializer=com.ironbank.moneyraven.starter.ProfileCheckAppInitializer 

D'après ce qui précède, vous pouvez deviner que ApplicationContextInitializer est un point d'extension. ApplicationContextInitializer fonctionne lorsque le contexte commence tout juste à être créé, il n'y a pas encore de bacs.


La question se pose: si nous avons écrit ApplicationContextInitializer , pourquoi ne devrait-il pas, en tant qu'auditeur, être écrit dans une configuration qui s'étire quand même? La réponse est simple: car cela devrait fonctionner beaucoup plus tôt lorsqu'il n'y a pas de contexte et pas de configurations. C'est-à-dire il ne peut pas encore être injecté. Par conséquent, nous le prescrivons comme une pièce distincte.


Une tentative de lancement a montré que tout était tombé assez vite et a indiqué que nous partions sans profil. Essayons maintenant de spécifier un profil, et tout fonctionne - le corbeau est envoyé.


ApplicationContextInitializer - se remplit lorsque le contexte a déjà été créé, mais qu'il n'y a rien d'autre à part l'environnement.




Qui crée l'environnement? Carlson - SpringBootApplication . Il le remplit de diverses méta-informations, qui peuvent ensuite être sorties de leur contexte. La plupart des choses peuvent être injectées via @value , quelque chose peut être obtenu de l'environnement, car nous venons de recevoir des profils.


Par exemple, différentes propriétés viennent ici:


  • que Spring Boot peut construire;
  • qui au démarrage sont transmises via la ligne de commande;
  • systémique;
  • énoncées comme variables d'environnement;
  • prescrit dans les propriétés d'application;
  • enregistré dans d'autres fichiers de propriétés.

Tout cela est collecté et placé dans un objet d'environnement. Il contient également des informations sur les profils actifs. L'objet d'environnement est la seule chose qui existe au moment où Spring Boot commence à créer le contexte.


J'aimerais deviner automatiquement quel sera le profil si les gens oublient de le demander de leurs mains (nous faisons tout pour que les employés de banque qui sont assez impuissants sans programmeurs puissent lancer l'application pour que tout fonctionne pour eux, quoi qu'il arrive). Pour ce faire, nous ajouterons à notre entrée une chose qui devinera le profil - ou pas - en fonction de la température dans la rue. Et une autre nouvelle interface magique nous aidera tous avec cela - EnvironmentPostProcessor , car nous devons le faire avant que ApplicationContextInitializer fonctionne. Et avant ApplicationContextInitializer il n'y a que EnvironmentPostProcessor .


Nous implémentons à nouveau une nouvelle interface. Il n'y a qu'une seule méthode, qui de la même manière que ConfigurableEnvironment lance dans SpringApplication , car nous n'avons pas encore ConfigurableContext (il existe déjà dans SpringInitializer il n'est pas ici; il n'y a que l'environnement).


 public class ResolveProfileEnvironmentPostProcessor implements EnvironmentPostProcessor { @Override public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) { } } 

Dans cet environnement, nous pouvons définir le profil. Mais vous devez d'abord vérifier que personne ne l'a installé auparavant. Par conséquent, nous devons getActiveProfiles vérifier getActiveProfiles . Si les gens savent ce qu'ils font et créent un profil, nous n'essaierons pas de deviner pour eux. Mais s'il n'y a pas de profil, nous essaierons de comprendre par la météo.


Et le second - nous devons comprendre si nous avons un temps d'hiver ou d'été maintenant. Nous reviendrons la température de -300 .


 public class ResolveProfileEnvironmentPostProcessor implements EnvironmentPostProcessor { @Override public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) { if (environment.getActivePrifiles().length == 0 && getTemperature() < -272) {   } } public int getTemperature() { return -300; } } 

Dans cette condition, nous avons l'hiver et nous pouvons établir un nouveau profil. Nous nous souvenons que le profil s'appelle :


 public class ResolveProfileEnvironmentPostProcessor implements EnvironmentPostProcessor { @Override public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {   if (environment.getActivePrifiles().length == 0 && getTemperature() < -272) { environment.setActiveProfiles("");   } else { environment.setActiveProfiles("");   } } public int getTemperature() { return -300; } } 

Maintenant, nous devons spécifier le EnvironmentPostProcessor dans spring.factories.


 org.springframework.boot.context.properties.EnableConfigurationProperties=com.ironbank.moneyraven.starter.IronConfiguration org.springframework.context.ApplicationContextInitializer=com.ironbank.moneyraven.starter.ProfileCheckAppInitializer org.springframework.boot.env.EnvironmentPostProcessor=com.ironbank.moneyraven.starter.ResolveProfileEnvironmentPostProcessor 

En conséquence, l'application démarre sans profil, nous disons que c'est de la production et vérifions dans quel profil elle a commencé avec nous. Par magie, nous avons réalisé que notre profil est l' . Et l'application n'est pas tombée, car l' ApplicationContextInitializer , qui vérifie s'il y a un profil, vient ensuite.
Le résultat:


 public class ResolveProfileEnvironmentPostProcessor implements EnvironmentPostProcessor { @Override public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {   if (getTemperature() < -272) {     environment.setActiveProfiles("");   } else {     environment.setActiveProfiles("");   } } private int getTemperature() {   return -300; } } 

Nous avons parlé de EnvironmentPostProcessor , qui s'exécute avant ApplicationContextInitializer . Mais qui l'exécute?




Ce monstre le démarre, qui, apparemment, est le fils illégitime d' ApplicationListener et EnvironmentPostProcessor , car il est hérité à la fois d' ApplicationListener et d' EnvironmentPostProcessor . Il s'appelle ConfigFileApplicationListener (pourquoi "ConfigFile" - personne ne sait).


Lui est notre Carlson, c'est-à-dire Spring Application fournit un environnement préparé pour écouter deux événements: ApplicationPreparedEvent et ApplicationEnvironmentPreparedEvent . Nous n'analyserons pas maintenant qui lance ces événements. Il existe une autre couche (à mon avis, elle est déjà complètement superflue, au moins à ce stade du développement de Spring), qui déclenche un événement que l'environnement commence à être créé (Application.yml, les propriétés, les variables d'environnement sont analysées, etc. )
Après avoir reçu ApplicationEnvironmentPreparedEvent , l'auditeur comprend que vous devez configurer l'environnement - recherchez tous les EnvironmentPostProcessor et laissez-les fonctionner.




Après cela, il dit à SpringFactoriesLoader de livrer tout ce que vous avez commandé, à savoir tous les EnvironmentPostProcessor , à spring.factories. Il place ensuite l'ensemble du EnvironmentPostProcessor dans une seule liste.




et comprend que lui aussi est un EnvironmentPostProcessor (simultanément), il se pousse donc là-bas,

en même temps, il les trie, les accompagne et appelle chaque méthode postProcessEnvironment .


De cette façon, tous les postProcessEnvironment sont démarrés à un stade précoce avant SpringApplicationInitializer . Dans ce cas, un EnvironmentPostProcessor incompréhensible appelé ConfigFileApplicationListener démarre également.


Une fois l'environnement mis en place, tout revient à Carlson.


Si l'environnement est prêt, vous pouvez créer un contexte. Et Carlson commence à créer du contexte avec ApplicationInitializer . Ici, nous avons notre propre morceau, qui vérifie que dans le contexte il y a un environnement dans lequel il y a des profils actifs. Sinon, nous tombons, car sinon nous aurons encore des problèmes plus tard. Ensuite, les démarreurs fonctionnent, avec toutes les configurations habituelles déjà.


L'image ci-dessus reflète que le printemps ne va pas bien non plus. Ces étrangers s'y rencontrent périodiquement, la responsabilité unique n'est pas respectée et vous devez monter avec précaution.


Maintenant, nous voulons parler un peu de l'autre côté de cette étrange créature, qui est à l'écoute d'un côté et EnvironmentPostProcessor de l'autre.




Comme EnvironmentPostProcessor il peut charger application.yml, les propriétés d'application, toutes sortes de variables d'environnement, les arguments de commande, etc. Et en tant qu'auditeur, il peut écouter deux événements:


  • ApplicationPreparedEvent
  • ApplicationEnvironmentPreparedEvent

La question est:




Tous ces événements ont eu lieu dans l'ancien printemps. Et ceux dont nous avons parlé ci-dessus sont des événements de Spring Boot (événements spéciaux qu'il a ajoutés pour son cycle de vie). Et il y en a plein. Ce sont les principaux:


  • ApplicationStartingEvent
  • ApplicationEnvironmentPreparedEvent
  • ApplicationPreparedEvent
  • ContextRefreshedEvent
  • EmbeddedServletContainerInitializedEvent
  • ApplicationReadyEvent
  • ApplicationFailedEvent

Cette liste est loin d'être tout. Mais il est important que certains d'entre eux soient liés à Spring Boot et une partie à Spring (bon vieux ContextRefreshedEvent , etc.).


La mise en garde est que tous ces événements ne peuvent pas être reçus dans l'application (les simples mortels - différentes grand-mères - ne peuvent pas simplement écouter les événements complexes que Spring Boot lance). Mais si vous connaissez les mécanismes secrets de spring.factories et définissez votre écouteur d'application au niveau de spring.factories, alors ces événements dès la première étape du démarrage de l'application vous parviennent.




Par conséquent, vous pouvez influencer le démarrage de votre demande à un stade assez précoce. La plaisanterie, cependant, est qu'une partie de ce travail est effectuée dans d'autres entités - telles que EnvironmentPostProcessor et ApplicationContextInitializer .


Vous pourriez tout faire sur les auditeurs, mais ce serait gênant et moche. Si vous souhaitez écouter tous les événements que Spring lance, et pas seulement ContextRefreshedEvent et ContextStartedEvent , vous n'avez pas besoin de définir l'écouteur, comme un bean, de la manière habituelle (sinon il est créé trop tard). Il doit également être enregistré via spring.factories, puis il sera créé beaucoup plus tôt.


Soit dit en passant, lorsque nous avons examiné cette liste, il n'était pas clair pour nous quand ContextStartedEvent et ContextStoppedEvent déclenchés.




Il s'est avéré que ces événements ne fonctionnent jamais du tout. Nous nous sommes longtemps interrogés sur les événements à intercepter pour comprendre que l'application avait vraiment démarré. Et il s'est avéré que les événements dont nous parlions apparaissent maintenant lorsque vous extrayez avec force des méthodes du contexte:


  • ctx.start(); -> ContextStartedEvent
  • ctx.stop(); -> ContextStoppedEvent

C'est-à-dire SpringApplication.run ne SpringApplication.run que si nous SpringApplication.run , obtenons le contexte, en tirons ctx.start(); ou ctx.stop(); . Il n'est pas très clair pourquoi cela est nécessaire. Mais, encore une fois, ils vous ont donné un point d'extension.


Le printemps a-t-il quelque chose à voir avec cela? Si oui, quelque part il devrait y avoir une exception:


  • ctx.stop(); (1)
  • ctx.start(); (2)
  • ctx.close(); (3)
  • ctx.start(); (4)

En fait, ce sera sur la dernière ligne, car après ctx.close(); rien ne peut être fait avec le contexte. Mais appelez ctx.stop(); avant ctx.start(); - vous pouvez (Spring ignore simplement ces événements - ils sont juste pour vous).


Écrivez à vos auditeurs, écoutez-vous, ctx.stop(); vos lois, que faire sur ctx.stop(); , et que faire sur ctx.start(); .


Au total, le diagramme d'interaction et de cycle de vie de l'application ressemble à ceci:




Les couleurs ici montrent différentes périodes de la vie.


  • Blue is Spring Boot, l'application a déjà démarré. Cela signifie que les demandes de service Tomcat qui lui parviennent des clients sont traitées, tout le contexte est définitivement levé, tous les beans fonctionnent, les bases de données sont connectées, etc.
  • Vert - un événement ContextRefreshedEvent arrivé et le contexte est créé. À partir de ce moment, par exemple, les écouteurs d'application commencent à fonctionner, que vous implémentez soit en définissant l'annotation ApplicationListener, soit via l'interface éponyme avec un générique qui écoute certains événements. Si vous souhaitez recevoir plus d'événements, vous devez écrire le même ApplicationListener dans spring.factories (le Spring habituel fonctionne ici). Une barre indique où commence le rapport Spring Ripper .
  • À un stade antérieur, SpringApplication fonctionne, ce qui prépare le contexte pour nous. C'est le travail de préparation de l'application que nous avons fait lorsque nous étions des développeurs Spring réguliers. Par exemple, WebXML configuré.
  • Mais il y a des étapes encore plus anciennes. Il montre qui, où et pour qui cela fonctionne.
  • Il y a encore une étape grise dans laquelle il est impossible de se caler en aucune façon. Il s'agit de l'étape à laquelle SpringApplication sort de la boîte (entrez uniquement dans le code).

Si vous avez remarqué, lors du rapport en deux parties, nous sommes allés de droite à gauche: nous sommes partis de la toute fin, avons vissé la configuration qui volait du démarreur, puis avons ajouté ce qui suit, etc. Parlons maintenant rapidement de toute la chaîne dans la direction opposée.
Vous écrivez dans votre SpringApplication.run principal. Il trouve différents auditeurs, leur lance un événement qu'il a commencé à construire. Après cela, les écouteurs trouvent EnvironmentPostProcessor , laissez-les configurer l'environnement. Une fois l'environnement mis en place, nous commençons à construire le contexte (Carlson entre). Carlson crée le contexte et permet à tous les initialiseurs d'application de faire quelque chose avec ce contexte. Nous avons un point d'extension. Après cela, le contexte est déjà configuré et la même chose commence à se produire que dans l'application Spring habituelle, lorsque le contexte est créé - BeanFactoryPostProcessor , BeanPostProcessor , les beans sont configurés. C'est ce que fait le printemps ordinaire.


Comment courir


Nous avons terminé de discuter du processus de rédaction d'une demande.


Mais nous avions encore une chose que les développeurs n'aiment pas. Ils n'aiment pas penser comment, finalement, leur demande va commencer. L'administrateur l'exécutera-t-il dans Tomcat, JBoss ou dans WebLogic? Il faut juste que ça marche. Si cela ne fonctionne pas, dans le pire des cas, le développeur devra à nouveau configurer quelque chose


Quelles sont donc nos méthodes de lancement?


  • guerre tomcat;
  • idée;
  • java -jar/war .

Tomcat n'est pas une tendance de masse, nous n'en parlerons pas en détail.


L'idée est également, en principe, peu intéressante. C'est juste un peu plus compliqué que je ne le dirai ci-dessous. Mais dans Idea, en principe, il ne devrait pas y avoir de problèmes. Elle voit quel genre de dépendances le démarreur apportera.
Si nous utilisons java -jar , le principal problème est de créer un chemin de classe avant de lancer l'application.


Qu'ont fait les gens en 2001? Ils ont écrit java -jar quel pot doit être exécuté, puis un espace, classpath=... et les scripts y ont été indiqués. Dans notre cas, il y a 150 Mo de diverses dépendances ajoutées par les démarreurs. Et tout cela devrait être fait manuellement. Naturellement, personne ne fait ça. Nous écrivons simplement: java -jar , quel pot doit être exécuté et c'est tout. D'une manière ou d'une autre, le chemin de classe est toujours en cours de construction. Nous en parlerons maintenant.


Commençons par la préparation du fichier jar afin qu'il puisse même être lancé sans Tomcat. Avant de créer java -jar , vous devez créer un bocal. Ce pot devrait évidemment être inhabituel, une sorte d'analogue de guerre, où tout sera à l'intérieur, y compris le Tomcat intégré.


 <build> <plugins>    <plugin>       <groupId>org.springframework.boot</groupId>       <artifactId>spring-boot-maven-plugin</artifactId>    </plugin> </plugins> </build> 

Lorsque nous avons téléchargé le projet, quelqu'un a déjà enregistré un plug-in dans notre POM. Ici, en passant, vous pouvez lancer des configurations, mais plus à ce sujet plus tard. jar, Maven Gradle , jar . :




:




war-.


, .


, jar. java -jar , , , , org.springframework.boot . . org.springframework.boot package. META-INF




Spring Boot MANIFEST ( Maven Gradle), main class, jar-.


, jar- : -, main-. java -jar -jar, , main-class-.


, , MANIFEST, main-class , main ( Idea). , . class path? java -jar , main, , — main, . MANIFEST JarLauncher.




C'est-à-dire , , JarLauncher. , main, class path.
, main? property — Start-class .


C'est-à-dire . class path jar. , — org.springframework.boot — class path. org.springframework.boot.loader.JarLauncher main-class. , main-class . class path, BOOT-INF ( lib class , ).


RavenApplication, properties class BOOT-INF , , Tomcat , BOOT-INF/lib/ . JarLauncher classpath, — , start-class . Spring, ContextSpringApplication — flow, .


, start-class-? , . , .


, . property, mainClass , MANIFEST Start-Class , mainClass — JarLauncher.


, mainClass, ? . Spring boot plugin – mainClass:


  • – . — main class;
  • – , mainClass @SpringBootApplication , , , ;
  • — exception , main class, , jar- . C'est-à-dire , , . , , main class.
  • @SpringBootApplication — .

JarLauncher . Tomcat WarLauncher, war- , jar-.


, java -jar . ? Tu peux. .


 <build> <plugins>    <plugin>       <groupId>org.springframework.boot</groupId>       <artifactId>spring-boot-maven-plugin</artifactId>       <configuration>          <executable>true</executable>       </configuration>    </plugin> </plugins> </build> 

<configuration> <executable>true</executable> Gradle , :


 springBoot { executable = true } 

jar executable jar. .


, . Windows , exe-, . Spring Boot, .. jar, . , .
?


(jar — zip-, ):




Spring Boot - .


-, jar-. , , — #!/bin/bash . .


. exit 0 - — zip-.




, zip- — 0xf4ra . , , .




(, ..).


jar :


  • — ;
  • , " bash" ( #!/bin/bash );
  • bash ;
  • exit 0 ;
  • java -jar — jar-, ;
  • java -jar zip- jar-, , , .

Conclusions


, Spring Boot — , , .


-, . , Spring, Spring — Spring Boot. , , — , , , . , , Spring, Spring Boot .


-, @SpringBootApplication , best practice, Spring-.


— , , . property environment variable, var arg , , JSON. @value , . configuration properties , , , . , Spring . , , .


. , . Spring, Spring Boot . - , , , .




Minute de publicité. 19-20 Joker 2018, « [Joker Edition]» , «Micronaut vs Spring Boot, ?» . , Joker . .

Source: https://habr.com/ru/post/fr425333/


All Articles