... seine vier debuggen.
Inspiriert von einem Bericht von Vladimir Plizgi ( Spring Boot 2: Was sie nicht in Versionshinweisen schreiben ), beschloss ich, über meine Erfahrungen mit Spring Booth, seine Funktionen und Fallstricke zu sprechen, die ich auf meinem Weg kennengelernt habe.
Federdaten jpa
Wladimir's Bericht widmet sich der Migration zu Spring Booth 2 und den auftretenden Schwierigkeiten. Im ersten Teil beschreibt er Kompilierungsfehler, daher werde ich auch damit beginnen.
Ich denke, viele kennen das Spring Data Framework und seine Derivate für verschiedene Data Warehouses. Ich habe nur mit einem von ihnen gearbeitet - Spring Data JPA. Die in diesem Framework verwendete Hauptschnittstelle - JpaRepository
- wurde in Version 2 erheblich geändert. *.
Betrachten Sie die am häufigsten verwendeten Methoden:
Tatsächlich gibt es weitere Änderungen, und nach dem Wechsel zu Version 2 * werden alle zu Kompilierungsfehlern.
Als eines unserer Projekte das Problem der Migration auf Spring Booth 2 ansprach und die ersten Schritte unternommen wurden (Ersetzen der Version in pom.xml), wurde das gesamte Projekt buchstäblich rot: Dutzende von Repositorys und Hunderte von Aufrufen ihrer Methoden führten zur Migration "(mit neuen Methoden) würde jede zweite Datei einbinden. Stellen Sie sich den einfachsten Code vor:
SomeEntity something = someRepository.findOne(someId); if (something == null) { something = someRepository.save(new SomeEntity()); } useSomething(something);
Damit das Projekt einfach zusammenkommt, müssen Sie
- Rufen Sie eine andere Methode auf
- Ändern Sie den Typ der Variablen in
Optional<SomeEntity>
- Ersetzen Sie die Prüfung der leeren Referenz durch die
Optional::orElseGet
Optional::isPresent
oder Optional::orElse
/ Optional::orElseGet
- Tests reparieren
Glücklicherweise gibt es einen einfacheren Weg, da Spring Date Benutzern bisher beispiellose Möglichkeiten bietet, Repositorys an ihre Bedürfnisse anzupassen. Wir müssen lediglich die Schnittstelle und Klasse definieren (sofern sie noch nicht definiert wurden), die die Grundlage aller Repositorys unseres Projekts bilden. Dies geschieht folgendermaßen:
Alle unsere Repositorys erben von dieser Schnittstelle. Jetzt Implementierung:
public class BaseJpaRepositoryImpl<T, ID extends Serializable> extends SimpleJpaRepository<T, ID> implements BaseJpaRepository<T, ID> { private final JpaEntityInformation<T, ?> entityInfo; private final EntityManager entityManager; public BaseJpaRepositoryImpl(JpaEntityInformation<T, ?> entityInfo, EntityManager entityManager) { super(entityInfo, entityManager); this.entityInfo = entityInfo; this.entityManager = entityManager; } @Override public T findOne(ID id) { return findById(id).orElse(null);
Der Rest ist im Bild und in der Ähnlichkeit. Alle Kompilierungsfehler verschwinden, der Code funktioniert wie zuvor und nur 2 Klassen werden geändert und nicht 200. Jetzt können Sie die veraltete API im Verlauf der Entwicklung langsam durch eine neue ersetzen, da die Idee alle Aufrufe von @Deprecated-Methoden sorgfältig gelb färbt.
Letzter Schlag - wir informieren Spring, dass von nun an Repositories über unserer Klasse aufgebaut werden sollten:
@Configuration @EnableJpaRepositories(repositoryBaseClass = BaseJpaRepositoryImpl.class) public class SomeConfig{ }
In einem ruhigen Pool befinden sich Mülleimer
Spring Booth basiert auf zwei Konzepten - Starter und automatische Konfiguration. Im Leben ist die Konsequenz daraus eine wichtige Eigenschaft: Die Bibliothek, die in den Klassenpfad der Anwendung gelangt ist, wird vom SB gescannt. Wenn darin eine Klasse gefunden wird, die eine bestimmte Einstellung enthält, wird sie ohne Ihr Wissen und ohne ausdrückliche Anweisungen aktiviert. Wir zeigen dies anhand eines einfachen Beispiels.
Viele verwenden die gson- Bibliothek, um Objekte in JSON-Zeichenfolgen umzuwandeln und umgekehrt. Sie können diesen Code oft sehen:
@Component public class Serializer { public <T> String serialize(T obj) { return new Gson().toJson(obj); } }
Bei häufigen Zugriffen lädt dieser Code zusätzlich den Garbage Collector, den wir nicht wollen. Besonders kluge Leute erstellen ein Objekt und verwenden es:
@Configuration public class SomeConfig { @Bean public Gson gson() { return new Gson(); } } @Component @RequiredArgsConstructor public class Serializer { private final Gson gson; public String serialize(T obj) { return gson.toJson(obj); } }
... ohne zu merken, dass der Spring Booth alles selbst machen kann. Standardmäßig wird der SB mit der org.springframework.boot:spring-boot-autoconfigure
, die viele Klassen mit dem Namen *AutoConfiguration
, z.
@Configuration @ConditionalOnClass(Gson.class) @EnableConfigurationProperties(GsonProperties.class) public class GsonAutoConfiguration { @Bean @ConditionalOnMissingBean public GsonBuilder gsonBuilder(List<GsonBuilderCustomizer> customizers) { GsonBuilder builder = new GsonBuilder(); customizers.forEach((c) -> c.customize(builder)); return builder; } @Bean @ConditionalOnMissingBean public Gson gson(GsonBuilder gsonBuilder) { return gsonBuilder.create(); } }
Die Einstellung ist einfach wie eine Schiene: Wenn die Anwendung eine Gson
Klasse hat und keine manuell definierte Bean dieses Typs vorhanden ist, wird eine Standardimplementierung erstellt. Dies ist eine enorme Leistung, die es einer Abhängigkeit und einigen Anmerkungen ermöglicht, eine vollwertige Konfiguration zu erstellen, deren Beschreibung XML-Blätter und zahlreiche handgeschriebene Fächer verwendet.
Dies ist jedoch eine große Hinterlist des Sicherheitsrates. Hinterlist liegt in der Geheimhaltung, weil nach vorgefertigten Skripten viel Arbeit unter der Haube geschieht. Dies ist gut für eine typische Anwendung, aber das Verlassen der ausgetretenen Pfade kann Probleme verursachen - der Konvoi schießt ohne Vorwarnung. Viele Bohnen werden ohne unser Wissen hergestellt, das Beispiel mit Gson zeigt dies gut. Sei vorsichtig mit der Klasse und deinen Abhängigkeiten, denn ...
Alles, was in Ihren Klassenpfad gelangt, kann gegen Sie verwendet werden.
Ein Fall aus dem Leben. Es gibt zwei Anwendungen: Eine verwendet LdapTemplate
, die andere hat es einmal verwendet. Beide Anwendungen hängen vom core
, in dem einige gemeinsame Klassen herausgenommen werden, und die für beide Anwendungen gemeinsamen Bibliotheken wurden sorgfältig in pom.xml gefaltet.
Nach einiger Zeit wurden ab dem zweiten Projekt alle Verwendungen von LdapTemplate
als unnötig herausgeschnitten. Aber die Bibliothek org.springramework:spring-ldap
blieb im core
. Dann schlug der SB zu und org.springramework.ldap:ldap-core
wurde zu org.springramework.boot:spring-boot-starter-data-ldap
.
Aus diesem Grund wurden in den Protokollen interessante Meldungen angezeigt:
Multiple Spring Data modules found, entering strict repository configuration mode! Spring Data LDAP - Could not safely identify store assignment for repository candidate interface ....
Abhängigkeit von der core
org.springramework.boot:spring-boot-starter-data-ldap
um in ein Projekt org.springramework.boot:spring-boot-starter-data-ldap
passen, in dem LDAP überhaupt nicht verwendet wird. Was bedeutet es zu zögern? Schlagen Sie den Klassenpfad :). Weiter mit allen Haltestellen:
- Spring Boot-Hinweise
org.springramework.boot:spring-boot-starter-data-ldap
im Klassenpfad - Jedes Repository wird nun überprüft, ob es für die Verwendung mit Spring Data JPA oder Spring Data LDAP geeignet ist
- Reorganisation von Abhängigkeiten und
org.springramework.boot:spring-boot-starter-data-ldap
unnötiger org.springramework.boot:spring-boot-starter-data-ldap
reduziert die org.springramework.boot:spring-boot-starter-data-ldap
der Anwendung um durchschnittlich 20 (!) Sekunden von insgesamt 40-50 Sekunden
Ein aufmerksamer und kritischer Leser wird wahrscheinlich fragen: Warum mussten Sie org.springramework.ldap:ldap-core
in org.springramework.boot:spring-boot-starter-data-ldap
wenn Spring Data LDAP nicht verwendet wird, sondern nur eines verwendet wird Klasse org.springframework.ldap.LdapTempate
?
Antwort: Es war ein Fehler. Tatsache ist, dass vor Version 2.1.0.M1 die automatische Optimierung für LDAP ungefähr so aussah
@Configuration @ConditionalOnClass({ContextSource.class}) @EnableConfigurationProperties({LdapProperties.class}) public class LdapAutoConfiguration { private final LdapProperties properties; private final Environment environment; public LdapAutoConfiguration(LdapProperties properties, Environment environment) { this.properties = properties; this.environment = environment; } @Bean @ConditionalOnMissingBean public ContextSource ldapContextSource() { LdapContextSource source = new LdapContextSource(); source.setUserDn(this.properties.getUsername()); source.setPassword(this.properties.getPassword()); source.setBase(this.properties.getBase()); source.setUrls(this.properties.determineUrls(this.environment)); source.setBaseEnvironmentProperties(Collections.unmodifiableMap(this.properties.getBaseEnvironment())); return source; } }
Wo ist das LdapTemplate
? Aber er ist nicht :). Genauer gesagt, liegt es aber woanders:
@Configuration @ConditionalOnClass({LdapContext.class, LdapRepository.class}) @AutoConfigureAfter({LdapAutoConfiguration.class}) public class LdapDataAutoConfiguration { @Bean @ConditionalOnMissingBean({LdapOperations.class}) public LdapTemplate ldapTemplate(ContextSource contextSource) { return new LdapTemplate(contextSource); } }
Daher wurde angenommen, dass Sie LdapTemplate
in Ihrer Anwendung erhalten LdapTemplate
, LdapTemplate
Sie die @ConditionalOnClass({LdapContext.class, LdapRepository.class})
, die möglich ist, wenn die Abhängigkeit spring-boot-starter-data-ldap
zum Klassenpfad hinzugefügt wird.
Eine andere Möglichkeit: Bestimmen Sie diese Bohne mit Ihren Händen, die Sie nicht möchten (denn warum brauchen wir dann SB)? org.springramework.boot:spring-boot-starter-data-ldap
kamen darauf, nachdem sie org.springramework.boot:spring-boot-starter-data-ldap
durch org.springramework.ldap:ldap-core
.
Das Problem wird hier behoben: https://github.com/spring-projects/spring-boot/pull/13136 . Wesentliche Änderung: Die LdapTemplate
Bean- LdapTemplate
nach LdapAutoConfiguration
. Jetzt können Sie LDAP verwenden, ohne an spring-boot-starter-data-ldap
LDAP gebunden zu spring-boot-starter-data-ldap
und den LdapTemplate
Bin manuell zu definieren.
Hood Aufregung
Wenn Sie den Ruhezustand verwenden, sind Sie wahrscheinlich mit dem Open-in-View- Antimuster vertraut. Wir werden nicht auf seine Beschreibung eingehen, indem wir darauf verweisen, dass es ausreichend detailliert beschrieben wird, und in anderen Quellen werden seine schädlichen Wirkungen sehr detailliert beschrieben.
Ein spezielles Flag ist für das Ein- und Ausschalten dieses Modus verantwortlich:
spring: jpa: open-in-view: true
In SB Version 1. * war es standardmäßig aktiviert, während der Benutzer nicht darüber informiert wurde. In Version 2. * ist es weiterhin aktiviert, aber jetzt wird eine Warnung in das Protokoll geschrieben:
WARN spring.jpa.open-in-view is enabled by default. Therefore, database queries may be performed during view rendering. Explicitly configure spring.jpa.open-in-view to disable this warning.
Ursprünglich gab es eine Aufforderung, diesen Modus zu deaktivieren. Der epische Srach zum Thema enthält mehrere Dutzend (!) Detaillierte Kommentare, einschließlich von Oliver Hörke (Spring Date-Entwickler), Vlad Michalce (Hibernate-Entwickler), Phil Web und Vedran Pavic (Spring-Entwickler) mit Vor- und Nachteilen.
Sie waren sich einig, dass sich das Verhalten nicht ändern wird, aber eine Warnung angezeigt wird (die beobachtet wird). Es gibt auch einen ziemlich häufigen Tipp, um diesen Modus zu deaktivieren:
spring: jpa: open-in-view: false
Das ist alles, schreiben Sie über Ihren Rechen und die interessanten Merkmale des Sicherheitsrates - dies ist wirklich ein unerschöpfliches Thema.