Zależności cieniowania to proces włączania i zmieniania nazw zależności (przenoszenie klas i przepisywanie pod wpływem kodu i zasobów) w celu utworzenia prywatnej kopii, którą pakujesz razem z własnym kodem .
Pojęcie to zwykle kojarzy się z słoikami Uber-Jars (zwanymi także słoikami tłuszczowymi ).
Istnieje pewne zamieszanie w związku z tym terminem , ze względu na wtyczkę cienia maven, która pod tą pojedynczą nazwą robi 2 rzeczy (cytowanie własnej strony):
Ta wtyczka zapewnia możliwość spakowania artefaktu w słoik uber-jar, w tym jego zależności oraz przyciemnienia - tj. Zmiany nazwy - pakietów niektórych zależności.
Tak więc część cieniująca jest w rzeczywistości opcjonalna: wtyczka pozwala uwzględniać zależności w słoiku (gruby słoik) i opcjonalnie zmieniać zależności (cień) .
Dodanie innego źródła :
Cieniowanie biblioteki polega na pobraniu plików zawartości wspomnianej biblioteki, włożeniu ich do własnego słoika i zmianie ich pakietu . Różni się to od pakowania, które po prostu wysyła pliki bibliotek do własnego słoika bez przenoszenia ich do innego pakietu.
Z technicznego punktu widzenia zależności są zacienione. Ale często określa się zależności „gruby słoik-z-zacienionymi” jako „zacieniony słoik”, a jeśli ten słoik jest klientem dla innego systemu, można go nazwać „zacienionym klientem”.
Oto tytuł problemu Jira dla HBase, który podałeś w swoim pytaniu:
Opublikuj artefakt klienta z zacienionymi zależnościami
Więc w tym poście staram się przedstawić 2 koncepcje bez ich łączenia.
Dobry
Słoiki Uber-jars są często używane do wysyłania aplikacji jako pojedynczego pliku (ułatwia wdrożenie i uruchomienie). Można ich także używać do wysyłania bibliotek wraz z niektórymi (lub wszystkimi) ich zależnościami, które są zacienione , aby uniknąć konfliktów, gdy są używane przez inne aplikacje (które mogą korzystać z różnych wersji tych bibliotek).
Istnieje kilka sposobów budowania słoików Uber-Jars, ale maven-shade-plugin
idzie o krok dalej dzięki funkcji przenoszenia klas :
Jeśli plik JAR uber zostanie ponownie wykorzystany jako zależność innego projektu, bezpośrednie włączenie klas z zależności artefaktu do pliku JAR uber może powodować konflikty ładowania klas z powodu duplikacji klas na ścieżce klasy. Aby rozwiązać ten problem, można przenieść klasy zawarte w zacienionym artefakcie, aby utworzyć prywatną kopię ich kodu bajtowego.
(Uwaga historyczna: Jar Jar Links wcześniej oferował tę funkcję relokacji)
Dzięki temu możesz uczynić zależności biblioteki szczegółowymi implementacjami , chyba że udostępnisz klasy z tych bibliotek w interfejsie API.
Powiedzmy, że mam projekt, ACME Quantanizer ™, który zapewnia DecayingSyncQuantanizer
klasę i zależy od commons-rng Apache ( oczywiście, aby właściwie skwantyzować, potrzebujesz XorShift1024Star
duh).
Jeśli użyję wtyczki shadow maven do wytworzenia słoika i zajrzę do środka, zobaczę te pliki klas:
com/acme/DecayingSyncQuantanizer.class
org/apache/commons/rng/RandomProviderState.class
org/apache/commons/rng/RestorableUniformRandomProvider.class
...
org/apache/commons/rng/core/source64/XorShift1024Star.class
org/apache/commons/rng/core/util/NumberFactory.class
Teraz, jeśli użyję funkcji przenoszenia klasy:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.0.0</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<relocations>
<relocation>
<pattern>org.apache.commons</pattern>
<shadedPattern>com.acme.shaded.apachecommons</shadedPattern>
</relocation>
</relocations>
</configuration>
</execution>
</executions>
</plugin>
Zawartość słoika wygląda następująco:
com/acme/DecayingSyncQuantanizer.class
com/acme/shaded/apachecommons/rng/RandomProviderState.class
com/acme/shaded/apachecommons/rng/RestorableUniformRandomProvider.class
...
com/acme/shaded/apachecommons/rng/core/source64/XorShift1024Star.class
com/acme/shaded/apachecommons/rng/core/util/NumberFactory.class
To nie tylko zmiana nazw plików, przepisuje kod bajtowy, który odwołuje się do przeniesionych klas (więc moje własne klasy i klasy wspólne są przekształcane).
Ponadto wtyczka Shade wygeneruje również nowy POM ( dependency-reduced-pom.xml
), w którym zależności zacienione zostaną usunięte z <dependencies>
sekcji. Pomaga to wykorzystać zacieniony słoik jako zależność dla innego projektu. Możesz więc opublikować ten słoik zamiast podstawowego lub oba (używając kwalifikatora dla zacienionego słoika).
To może być bardzo przydatne ...
Źli
... ale wiąże się to również z wieloma problemami. Agregowanie wszystkich zależności w jedną „przestrzeń nazw” w słoiku może powodować bałagan i wymagać cieniowania i bałaganu przy użyciu zasobów.
Na przykład: jak postępować z plikami zasobów, które zawierają nazwy klas lub pakietów? Pliki zasobów, takie jak deskryptory usługodawców, pod którymi wszystkie żyją META-INF/services
?
Wtyczka cieniowania oferuje transformatory zasobów, które mogą w tym pomóc:
Agregowanie klas / zasobów z kilku artefaktów w jeden plik JAR Uber jest proste, o ile nie zachodzi na siebie nakładanie się. W przeciwnym razie wymagana jest logika do łączenia zasobów z kilku plików JAR. Tutaj rozpoczynają się transformatory zasobów .
Ale wciąż jest bałagan, a problemy są prawie niemożliwe do przewidzenia (dość często problemy odkrywa się na etapie produkcji). Zobacz, dlaczego przestaliśmy budować słoiki z tłuszczem .
Podsumowując, wdrażanie grubego słoika jako samodzielnej aplikacji / usługi jest nadal bardzo powszechne, musisz tylko wiedzieć o problemach, a dla niektórych z nich możesz potrzebować cieniowania lub innych sztuczek.
Brzydki
Istnieje wiele trudniejszych problemów (debugowanie, testowalność, kompatybilność z OSGi i egzotycznymi modułami ładującymi ...).
Ale co ważniejsze, kiedy tworzysz bibliotekę, różne problemy, które Twoim zdaniem można kontrolować, stają się teraz nieskończenie bardziej skomplikowane, ponieważ słoik będzie używany w wielu różnych kontekstach (w przeciwieństwie do grubego słoika, który wdrażasz jako samodzielną aplikację / usługę w kontrolowanym środowisku).
Na przykład ElasticSearch zwykł zaciemniać niektóre zależności w słoikach, które wysyłali, ale postanowili przestać to robić :
Przed wersją 2.0 Elasticsearch był dostarczany jako plik JAR z pewnymi (ale nie wszystkimi) typowymi zależnościami zacienionymi i spakowanymi w tym samym artefakcie. Pomogło to użytkownikom Java, którzy osadzają Elasticsearch we własnych aplikacjach, uniknąć konfliktów wersji modułów takich jak Guava, Joda, Jackson itp. Oczywiście nadal istniała lista innych niecieniowanych zależności, takich jak Lucene, które nadal mogą powodować konflikty.
Niestety, cieniowanie jest złożonym i podatnym na błędy procesem, który rozwiązał problemy dla niektórych osób, a jednocześnie stworzył problemy dla innych. Cieniowanie utrudnia programistom i autorom wtyczek prawidłowe pisanie i debugowanie kodu, ponieważ podczas kompilacji zmienia się nazwy pakietów. W końcu testowaliśmy Elasticsearch bez cieniowania, a następnie wysyłaliśmy zacieniony słoik i nie lubimy niczego, czego nie testujemy.
Zdecydowaliśmy się wysłać Elasticsearch bez cieniowania od wersji 2.0.
Pamiętaj, że one również odnoszą się do zacienionych zależności , a nie zacienionego słoika