Boot dich selbst, der Frühling kommt (Teil 2)

Evgeny EvgenyBorisov Borisov (NAYA Technologies) und Kirill tolkkv Tolkachev (Cyan.Finance, Twitter ) sprechen weiterhin über die Verwendung von Spring Boot zur Lösung der Probleme der imaginären Braavos Iron Bank. Im zweiten Teil konzentrieren wir uns auf die Profile und Feinheiten beim Starten der Anwendung.






Den ersten Teil des Artikels finden Sie hier .


Also kam der Kunde bis vor kurzem und verlangte nur, einen Raben zu schicken. Jetzt hat sich die Situation geändert. Der Winter ist gekommen, die Mauer ist gefallen.


Erstens ändert sich das Prinzip der Kreditvergabe. Wenn sie früher mit einer Wahrscheinlichkeit von 50% an alle außer Starks ausgegeben haben, geben sie jetzt nur an diejenigen aus, die Schulden zurückzahlen. Daher ändern wir die Regeln für die Ausgabe von Krediten in unserer Geschäftslogik. Aber nur für Filialen der Bank, die sich dort befinden, wo bereits der Winter gekommen ist, bleibt im Übrigen alles wie bisher. Ich erinnere Sie daran, dass dies ein Dienst ist, der entscheidet, ob ein Darlehen gewährt wird oder nicht. Wir werden nur einen weiteren Service anbieten, der nur im Winter funktioniert.


Wir gehen zu unserer Geschäftslogik:


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

Wir haben bereits eine Liste derjenigen, die Schulden zurückzahlen.


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

Und es gibt eine Klasse, die bereits mit Eigentum verbunden ist - .


 public class ProphetProperties { List<String> ; } 

Wie in früheren Zeiten injizieren wir es einfach hier:


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

Denken Sie an die Konstruktorinjektion (über magische Anmerkungen):


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

Fast fertig.


Jetzt dürfen wir nur an diejenigen ausgeben, die Schulden zurückzahlen:


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

Aber hier haben wir ein kleines Problem. Jetzt haben wir zwei Implementierungen: den alten und den neuen Dienst.


 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... 

Es ist logisch, diese Beans in verschiedene Profile aufzuteilen. und . Lassen Sie unseren neuen Service nur im 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); } } 

Und der alte Service ist im .


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

Aber der Winter kommt langsam. In den Königreichen neben der zerbrochenen Mauer ist bereits Winter. Aber irgendwo im Süden - noch nicht. Das heißt, Anwendungen in verschiedenen Branchen und Zeitzonen sollten unterschiedlich funktionieren. Entsprechend den Bedingungen unserer Aufgabe können wir die alte Implementierung, in der der Winter gekommen ist, nicht löschen und die neue Klasse verwenden. Wir möchten, dass die Bankangestellten überhaupt nichts tun: Wir liefern ihnen eine Anwendung, die im Sommermodus funktioniert, bis der Winter kommt. Und wenn der Winter kommt, starten sie ihn einfach neu und das wars. Sie müssen den Code nicht ändern und keine Klassen löschen. Daher haben wir zunächst zwei Profile: Ein Teil des Behälters wird im Sommer und ein Teil des Behälters im Winter erstellt.


Aber ein anderes Problem tritt auf:




Jetzt haben wir keine einzige Bean, da wir zwei Profile angegeben haben und die Anwendung im Standardprofil startet.


Wir haben also eine neue Anforderung vom Kunden.


Eisengesetz 2. Kein Profil ist erlaubt




Wir wollen den Kontext nicht erhöhen, wenn das Profil nicht aktiviert ist, da der Winter bereits gekommen ist und alles sehr schlecht geworden ist. Es gibt bestimmte Dinge, die passieren sollten oder nicht, je nachdem, ob der oder der . Schauen Sie sich auch die Ausnahme an, deren Text oben angegeben ist. Er erklärt nichts. Ein Profil ist nicht definiert, daher gibt es keine Implementierung von ProphetService . Gleichzeitig sagte niemand, dass es notwendig sei, ein Profil festzulegen.


Aus diesem Grund möchten wir jetzt ein zusätzliches Teil in unseren Starter schrauben, das beim Erstellen des Kontexts überprüft, ob ein bestimmtes Profil festgelegt ist. Wenn es nicht gesetzt ist, werden wir nicht hochgehen und nur eine solche Ausnahme auslösen (und keine Ausnahme über das Fehlen eines Behälters).


Können wir dies mit unserem Anwendungs-Listener tun? Nein. Dafür gibt es drei Gründe:


  • Der Hörer mit Einzelverantwortung ist dafür verantwortlich, dass der Rabe fliegt. Der Listener sollte nicht prüfen, ob ein Profil aktiviert wurde, da das Aktivieren eines Profils nicht nur den Listener selbst betrifft, sondern auch vieles mehr.
  • Wenn ein Kontext erstellt wird, passieren verschiedene Dinge. Und wir möchten nicht, dass sie auftreten, wenn kein Profil festgelegt wurde.
  • Listener arbeitet ganz am Ende, wenn der Kontext aufgelöst wird. Und die Tatsache, dass es kein Profil gibt, wissen wir viel früher. Warum auf diese bedingten fünf Minuten warten, bis der Service fast steigt und dann alles fällt?

Außerdem weiß ich immer noch nicht, welche Fehler auftreten werden, da wir ohne Profil zu steigen begannen (angenommen, ich kenne die Geschäftslogik nicht). In Ermangelung eines Profils ist es daher erforderlich, den Kontext sehr früh zu reduzieren. Wenn Sie eine Spring Cloud verwenden, wird dies übrigens für Sie noch relevanter, da die Anwendung viele Dinge in einem frühen Stadium erledigt.


Um die neue Anforderung zu implementieren, gibt es ApplicationContextInitializer . Dies ist eine weitere Schnittstelle, mit der wir einen bestimmten Punkt des Frühlings erweitern können, indem wir ihn in spring.factories angeben.




Wir implementieren diese Schnittstelle und haben einen Context Initializer mit einem ConfigurableApplicationContext :


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

Damit können wir die Umwelt schaffen - das, was SpringApplication für uns vorbereitet hat. Das gesamte Eigentum, das wir ihm übergeben haben, ist dort angekommen. Sie enthalten unter anderem auch Profile.


Wenn dort keine Profile vorhanden sind, sollten wir eine Ausnahme auslösen, die besagt, dass Sie so nicht arbeiten können.


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

Jetzt müssen Sie dieses Zeug in spring.factories registrieren.


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

Anhand der obigen Ausführungen können Sie erraten, dass ApplicationContextInitializer ein Erweiterungspunkt ist. ApplicationContextInitializer funktioniert, wenn der Kontext gerade erst erstellt wird. Es sind noch keine Bins vorhanden.


Die Frage stellt sich: Wenn wir ApplicationContextInitializer , warum sollte es als Listener nicht in einer Konfiguration geschrieben werden, die sich sowieso erstreckt? Die Antwort ist einfach: Weil es viel früher funktionieren sollte, wenn es keinen Kontext und keine Konfigurationen gibt. Das heißt, es kann noch nicht injiziert werden. Deshalb schreiben wir es als separates Stück vor.


Ein Startversuch zeigte, dass alles schnell genug gefallen war und berichtete, dass wir ohne Profil starteten. Versuchen wir nun, ein Profil anzugeben, und alles funktioniert - der Rabe wird gesendet.


ApplicationContextInitializer - Wird erfüllt, wenn der Kontext bereits erstellt wurde, aber nichts anderes als die Umgebung darin enthalten ist.




Wer schafft die Umwelt? Carlson - SpringBootApplication . Er füllt es mit verschiedenen Meta-Informationen, die dann aus dem Kontext gezogen werden können. Die meisten Dinge können über @value injiziert werden, etwas kann aus der Umgebung bezogen werden, da wir gerade Profile erhalten haben.


Hier kommen zum Beispiel verschiedene Eigenschaften:


  • welcher Spring Boot bauen kann;
  • die beim Start über die Kommandozeile übertragen werden;
  • systemisch;
  • als Umgebungsvariablen geschrieben;
  • vorgeschrieben in Anwendungseigenschaften;
  • in einigen anderen Eigenschaftendateien registriert.

All dies wird gesammelt und in ein Umgebungsobjekt gesetzt. Es enthält auch Informationen darüber, welche Profile aktiv sind. Das Umgebungsobjekt ist das einzige, was zum Zeitpunkt der Erstellung des Kontexts durch Spring Boot vorhanden ist.


Ich würde gerne automatisch erraten, wie das Profil aussehen wird, wenn die Leute vergessen haben, es mit ihren Händen zu fragen (wir tun alles, damit Bankangestellte, die ohne Programmierer hilflos genug sind, die Anwendung starten können, damit alles für sie funktioniert, egal was passiert). Zu diesem Zweck fügen wir unserem Starter etwas hinzu, das das Profil - ob oder nicht - abhängig von der Temperatur auf der Straße errät. Und eine weitere neue magische Oberfläche wird uns allen dabei helfen - EnvironmentPostProcessor , da wir dies tun müssen, bevor ApplicationContextInitializer funktioniert. Und vor ApplicationContextInitializer gibt es nur EnvironmentPostProcessor .


Wir implementieren wieder eine neue Schnittstelle. Es gibt nur eine Methode, die ConfigurableEnvironment auf die gleiche Weise in SpringApplication , da ConfigurableContext noch nicht vorhanden ist (sie ist bereits in SpringInitializer nicht hier; es gibt nur Umgebung).


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

In dieser Umgebung können wir das Profil festlegen. Aber zuerst müssen Sie überprüfen, ob es noch niemand installiert hat. Daher müssen wir getActiveProfiles überprüfen. Wenn die Leute wissen, was sie tun, und ein Profil erstellen, werden wir nicht versuchen, für sie zu raten. Aber wenn es kein Profil gibt, werden wir versuchen, das Wetter zu verstehen.


Und das zweite - wir müssen verstehen, ob wir jetzt Winter- oder Sommerwetter haben. Wir werden die Temperatur von -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; } } 

Unter dieser Bedingung haben wir Winter und können ein neues Profil erstellen. Wir erinnern uns, dass das Profil heißt:


 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; } } 

Jetzt müssen wir den EnvironmentPostProcessor in spring.factories angeben.


 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 

Infolgedessen startet die Anwendung ohne Profil. Wir sagen, dass es sich um eine Produktion handelt, und prüfen, in welchem ​​Profil sie bei uns gestartet wurde. Magischerweise haben wir erkannt, dass unser Profil . Und die Anwendung ist nicht gefallen, da als nächstes der ApplicationContextInitializer folgt, der prüft, ob ein Profil vorhanden ist.
Das Ergebnis:


 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; } } 

Wir haben über EnvironmentPostProcessor , der vor ApplicationContextInitializer . Aber wer leitet es?




Dieser Freak startet es, was anscheinend der uneheliche Sohn von ApplicationListener und EnvironmentPostProcessor , da es sowohl von ApplicationListener als auch von EnvironmentPostProcessor geerbt wird. Es heißt ConfigFileApplicationListener (warum "ConfigFile" - niemand weiß).


Er ist unser Carlson, d.h. Spring Application bietet eine vorbereitete Umgebung zum Abhören von zwei Ereignissen: ApplicationPreparedEvent und ApplicationEnvironmentPreparedEvent . Wir werden jetzt nicht analysieren, wer diese Ereignisse auslöst. Es gibt noch eine weitere Ebene (meiner Meinung nach ist sie zumindest in dieser Phase der Frühjahrsentwicklung bereits völlig überflüssig), die ein Ereignis auslöst, bei dem die Erstellung der Umgebung beginnt (Application.yml, Eigenschaften, Umgebungsvariablen werden analysiert usw.). )
Nachdem der Listener ApplicationEnvironmentPreparedEvent , muss er die Umgebung konfigurieren. Suchen Sie den gesamten EnvironmentPostProcessor und lassen Sie ihn arbeiten.




Danach weist er SpringFactoriesLoader an, alles, was Sie bestellt haben, nämlich alle EnvironmentPostProcessor , an spring.factories zu liefern. Anschließend wird der gesamte EnvironmentPostProcessor in einer Liste zusammengefasst.




und versteht, dass auch er (gleichzeitig) ein EnvironmentPostProcessor , deshalb drängt er sich dorthin,

Gleichzeitig werden sie sortiert, mit ihnen postProcessEnvironment und die postProcessEnvironment jeder Methode postProcessEnvironment .


Auf diese Weise werden alle postProcessEnvironment frühzeitig vor SpringApplicationInitializer . In diesem Fall wird auch ein unverständlicher EnvironmentPostProcessor namens ConfigFileApplicationListener .


Wenn die Umgebung eingerichtet ist, kehrt alles wieder zu Carlson zurück.


Wenn die Umgebung bereit ist, können Sie einen Kontext erstellen. Und Carlson beginnt, mit dem ApplicationInitializer einen Kontext aufzubauen. Hier haben wir unser eigenes Stück, das überprüft, ob es im Kontext eine Umgebung gibt, in der es aktive Profile gibt. Wenn nicht, fallen wir, weil wir sonst später noch Probleme haben werden. Dann arbeiten die Starter mit allen üblichen Konfigurationen bereits.


Das Bild oben zeigt, dass der Frühling auch nicht gut läuft. Solche Außerirdischen treffen sich dort regelmäßig, einzelne Verantwortung wird nicht respektiert und Sie müssen vorsichtig klettern.


Jetzt wollen wir ein wenig über die andere Seite dieser seltsamen Kreatur sprechen, die auf der einen Seite Listener und auf der anderen EnvironmentPostProcessor .




Wie EnvironmentPostProcessor es application.yml, Anwendungseigenschaften, alle Arten von Umgebungsvariablen, Befehlsargumente usw. laden. Und als Zuhörer kann er zwei Ereignisse anhören:


  • ApplicationPreparedEvent
  • ApplicationEnvironmentPreparedEvent

Die Frage ist:




Alle diese Ereignisse waren im alten Frühling. Und diejenigen, über die wir oben gesprochen haben, sind Ereignisse von Spring Boot (besondere Ereignisse, die er für seinen Lebenszyklus hinzugefügt hat). Und es gibt eine ganze Reihe von ihnen. Dies sind die wichtigsten:


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

Diese Liste ist weit von allem entfernt. Es ist jedoch wichtig, dass sich einige von ihnen auf Spring Boot beziehen und sich auf Spring beziehen (gutes altes ContextRefreshedEvent usw.).


Die Einschränkung ist, dass nicht alle diese Ereignisse in der Anwendung enthalten sind (bloße Sterbliche - verschiedene Großmütter - können nicht nur die komplexen Ereignisse hören, die Spring Boot auslöst). Wenn Sie jedoch die geheimen Mechanismen von spring.factories kennen und Ihren Application Listener auf der Ebene von spring.factories definieren, werden Sie von diesen Ereignissen ab dem frühesten Stadium des Anwendungsstarts erfasst.




Dadurch können Sie den Start Ihrer Bewerbung frühzeitig beeinflussen. Der Witz ist jedoch, dass ein Teil dieser Arbeit in anderen Entitäten ausgeführt wird - wie z. B. EnvironmentPostProcessor und ApplicationContextInitializer .


Sie könnten alles mit Zuhörern machen, aber es wäre unpraktisch und hässlich. Wenn Sie alle Ereignisse abhören möchten, die Spring ContextRefreshedEvent , und nicht nur ContextRefreshedEvent und ContextStartedEvent , müssen Sie den Listener nicht wie üblich wie eine Bean registrieren (andernfalls wird er zu spät erstellt). Es muss auch über spring.factories registriert werden, dann wird es viel früher erstellt.


Übrigens, als wir uns diese Liste angesehen haben, war uns nicht klar, wann ContextStartedEvent und ContextStoppedEvent überhaupt ContextStartedEvent .




Es stellte sich heraus, dass diese Ereignisse überhaupt nicht funktionieren. Wir haben lange darüber nachgedacht, welche Ereignisse abgefangen werden sollten, um zu verstehen, dass die Anwendung wirklich gestartet wurde. Und es stellte sich heraus, dass die Ereignisse, über die wir gesprochen haben, jetzt angezeigt werden, wenn Sie Methoden mit Nachdruck aus dem Kontext ziehen:


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

Das heißt, SpringApplication.run werden nur SpringApplication.run , wenn wir SpringApplication.run , den Kontext ctx.start(); und ctx.start(); daraus ziehen ctx.start(); oder ctx.stop(); . Es ist nicht sehr klar, warum dies notwendig ist. Aber sie gaben Ihnen wieder einen Erweiterungspunkt.


Hat der Frühling etwas damit zu tun? Wenn ja, sollte es irgendwo eine Ausnahme geben:


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

In der Tat wird es in der letzten Zeile sein, weil nach ctx.close(); Mit dem Kontext kann nichts gemacht werden. Rufen Sie jedoch ctx.stop(); vor ctx.start(); - Sie können (Spring ignoriert diese Ereignisse einfach - sie sind nur für Sie).


Schreiben Sie Ihren Zuhörern, hören Sie sich selbst zu, ctx.stop(); Sie sich Ihre Gesetze, was Sie auf ctx.stop(); tun ctx.stop(); und was auf ctx.start(); zu tun ist ctx.start(); .


Insgesamt sieht das Interaktions- und Anwendungslebenszyklusdiagramm ungefähr so ​​aus:




Die Farben hier zeigen verschiedene Lebensabschnitte.


  • Blau ist Spring Boot, die Anwendung wurde bereits gestartet. Dies bedeutet, dass Tomcat-Serviceanfragen, die von Clients eingehen, verarbeitet werden, der gesamte Kontext definitiv ausgelöst wird, alle Beans funktionieren, die Datenbanken verbunden sind usw.
  • Grün - ein Ereignis ContextRefreshedEvent eingetroffen und der Kontext wird erstellt. Ab diesem Moment arbeiten beispielsweise Anwendungslistener, die Sie entweder durch Festlegen der ApplicationListener-Annotation oder über die gleichnamige Schnittstelle mit einem Generikum implementieren, das auf bestimmte Ereignisse wartet. Wenn Sie weitere Ereignisse empfangen möchten, müssen Sie denselben springListener in spring.factories schreiben (der übliche Frühling funktioniert hier). Ein Balken zeigt an, wo der Spring Ripper- Bericht beginnt.
  • Zu einem früheren Zeitpunkt funktioniert SpringApplication, das den Kontext für uns vorbereitet. Dies ist die Aufgabe der Vorbereitung der Anwendung, die wir als reguläre Spring-Entwickler erstellt haben. Zum Beispiel konfiguriertes WebXML.
  • Es gibt aber noch frühere Stadien. Es zeigt wer, wo und für wen es funktioniert.
  • Es gibt immer noch eine graue Phase, in der es unmöglich ist, sich in irgendeiner Weise zu verkeilen. Dies ist die Phase, in der SpringApplication sofort ausgeführt wird (nur in den Code einsteigen).

Wenn Sie während des zweiteiligen Berichts bemerkt haben, sind wir von rechts nach links gegangen: Wir haben ganz am Ende angefangen, die vom Starter geflogene Konfiguration verschraubt und dann Folgendes hinzugefügt usw. Lassen Sie uns nun schnell die gesamte Kette in die entgegengesetzte Richtung sprechen.
Sie schreiben in Ihre Hauptdatei SpringApplication.run . Er findet verschiedene Zuhörer, wirft ihnen ein Ereignis zu, das er zu bauen begann. Danach finden die Listener EnvironmentPostProcessor und lassen sie die Umgebung konfigurieren. Sobald die Umgebung eingerichtet ist, beginnen wir mit dem Aufbau des Kontexts (Carlson tritt ein). Carlson erstellt den Kontext und ermöglicht allen Anwendungsinitialisierern, etwas mit diesem Kontext zu tun. Wir haben einen Erweiterungspunkt. Danach ist der Kontext bereits konfiguriert und dann beginnt das gleiche wie in der üblichen Spring-Anwendung, wenn der Kontext erstellt wird - BeanFactoryPostProcessor , BeanPostProcessor , die Beans werden konfiguriert. Das macht der gewöhnliche Frühling.


Wie man läuft


Wir haben den Prozess des Schreibens einer Bewerbung abgeschlossen.


Aber wir hatten noch eine Sache, die Entwickler nicht mögen. Sie denken nicht gerne darüber nach, wie ihre Bewerbung am Ende beginnen wird. Wird der Administrator es in Tomcat, JBoss oder in WebLogic ausführen? Es muss einfach funktionieren. Wenn es nicht funktioniert, muss der Entwickler im schlimmsten Fall etwas neu konfigurieren


Was sind unsere Startmethoden?


  • Katerkrieg;
  • Idee;
  • java -jar/war .

Tomcat ist kein Massentrend, wir werden nicht im Detail darüber sprechen.


Idee ist auch im Prinzip nicht sehr interessant. Es ist nur ein bisschen kniffliger als ich weiter unten sagen werde. Aber in der Idee sollte es im Prinzip keine Probleme geben. Sie sieht, welche Abhängigkeiten der Starter mit sich bringen wird.
Wenn wir java -jar , besteht das Hauptproblem darin, einen Klassenpfad zu erstellen, bevor wir die Anwendung starten.


Was haben die Leute 2001 gemacht? Sie schrieben java -jar welches jar ausgeführt werden sollte, dann wurden dort ein Leerzeichen, ein classpath=... und Skripte angegeben. In unserem Fall wurden 150 MB verschiedener Abhängigkeiten hinzugefügt. Und das alles müsste manuell gemacht werden. Das macht natürlich niemand. Wir schreiben einfach: java -jar , welches Glas soll ausgeführt werden und das java -jar . Irgendwie wird der Klassenpfad noch gebaut. Wir werden jetzt darüber sprechen.


Beginnen wir mit der Vorbereitung der JAR-Datei, damit sie auch ohne Tomcat gestartet werden kann. Bevor Sie java -jar , müssen Sie ein Glas bauen. Dieses Glas sollte offensichtlich ungewöhnlich sein, eine Art Kriegsanalog, in dem sich alles befindet, einschließlich des eingebetteten Tomcat.


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

Als wir das Projekt heruntergeladen haben, hat bereits jemand ein Plug-In in unserem POM registriert. Hier können Sie übrigens Konfigurationen werfen, aber dazu später mehr. 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.




Das heißt, , , JarLauncher. , main, class path.
, main? property — Start-class .


Das heißt, . 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- . Das heißt, , , . , , main class.
  • @SpringBootApplication — .

JarLauncher . Tomcat WarLauncher, war- , jar-.


, java -jar . ? Du kannst. .


 <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-, , , .


, 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 der Werbung. 19-20 Joker 2018, « [Joker Edition]» , «Micronaut vs Spring Boot, ?» . , Joker . .

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


All Articles