Dependências de sombreamento é o processo de inclusão e renomeação de dependências (realocando as classes e reescrevendo os códigos e recursos afetados) para criar uma cópia privada que você agrupa junto com seu próprio código .
O conceito é geralmente associado aos uber-jarros (também conhecidos como jarros de gordura ).
Há alguma confusão sobre o termo , devido ao plugin maven shade, que sob esse nome único faz duas coisas (citando sua própria página):
Este plugin fornece a capacidade de empacotar o artefato em um uber-jar, incluindo suas dependências e sombrear - ou seja, renomear - os pacotes de algumas das dependências.
Portanto, a parte de sombreamento é realmente opcional: o plug-in permite incluir dependências em seu jar (jar de gordura) e, opcionalmente, renomear dependências (sombreadas) .
Adicionando outra fonte :
Para sombrear uma biblioteca é pegar os arquivos de conteúdo da referida biblioteca, colocá-los em seu próprio jarro e alterar o pacote deles . Isso é diferente do empacotamento, que é simplesmente enviar os arquivos das bibliotecas ao lado do seu próprio jar sem realocá-los para um pacote diferente.
Tecnicamente falando, as dependências são sombreadas. Mas é comum referir-se a um jar de gordura com dependências sombreadas como "jar sombreado" e, se esse jar for um cliente para outro sistema, poderá ser chamado de "cliente sombreado".
Aqui está o título do problema do Jira para o HBase que você vinculou na sua pergunta:
Publicar um artefato do cliente com dependências sombreadas
Então, neste post, estou tentando apresentar os 2 conceitos sem confundi-los.
O bom
Os Uber-jars geralmente são usados para enviar um aplicativo como um único arquivo (facilita a implantação e a execução). Eles também podem ser usados para enviar bibliotecas junto com algumas (ou todas) de suas dependências sombreadas , para evitar conflitos quando usadas por outros aplicativos (que podem usar versões diferentes dessas bibliotecas).
Existem várias maneiras de criar uber-jars, mas maven-shade-plugin
vai um passo além com seu recurso de realocação de classe :
Se o JAR uber for reutilizado como uma dependência de algum outro projeto, a inclusão direta de classes das dependências do artefato no JAR uber poderá causar conflitos de carregamento de classe devido a classes duplicadas no caminho da classe. Para solucionar esse problema, é possível realocar as classes incluídas no artefato sombreado para criar uma cópia privada de seu bytecode.
(Nota histórica: Jar Jar Links ofereceu esse recurso de realocação antes)
Portanto, com isso, você pode tornar as dependências da sua biblioteca um detalhe de implementação , a menos que exponha classes dessas bibliotecas na sua API.
Digamos que eu tenha um projeto, o ACME Quantanizer ™, que fornece DecayingSyncQuantanizer
classe e depende do Apache commons-rng (porque é claro que para quantanizar adequadamente você precisa de um XorShift1024Star
, duh).
Se eu usar o plugin shadow maven para produzir um uber-jar e olhar para dentro, vejo esses arquivos de classe:
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
Agora, se eu usar o recurso de realocação de classe:
<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>
O conteúdo do uber-jar é assim:
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
Não se trata apenas de renomear arquivos, ele reescreve o código de código que faz referência às classes realocadas (assim, minhas próprias classes e classes commons-rng são transformadas).
Além disso, o plug-in Shade também gerará um novo POM ( dependency-reduced-pom.xml
) no qual as dependências sombreadas são removidas da <dependencies>
seção. Isso ajuda a usar o frasco sombreado como uma dependência para outro projeto. Portanto, você pode publicar esse jar em vez do base, ou ambos (usando um qualificador para o jar sombreado).
Então isso pode ser muito útil ...
O mal
... mas também apresenta uma série de questões. A agregação de todas as dependências em um único "espaço para nome" dentro do jar pode ficar confusa e exigir sombreamento e confusão de recursos.
Por exemplo: como lidar com arquivos de recursos que incluem nomes de classe ou pacote? Arquivos de recursos, como descritores de provedores de serviços, nos quais todos vivem META-INF/services
?
O plugin de sombra oferece transformadores de recursos que podem ajudar com isso:
A agregação de classes / recursos de vários artefatos em um JAR uber é direta, desde que não haja sobreposição. Caso contrário, é necessário algum tipo de lógica para mesclar recursos de vários JARs. É aqui que entram os transformadores de recursos .
Mas ainda é confuso e os problemas são quase impossíveis de prever (muitas vezes você descobre os problemas da maneira mais difícil na produção). Veja por que paramos de construir jarros de gordura .
Em suma, a implantação de um jar de gordura como um aplicativo / serviço independente ainda é muito comum, você só precisa estar ciente das dicas e, para algumas delas, pode ser necessário sombreamento ou outros truques.
O feio
Existem muitos problemas mais difíceis (depuração, testabilidade, compatibilidade com OSGi e carregadores de classes exóticos ...).
Mais importante, porém, quando você produz uma biblioteca, os vários problemas que você pensou que poderia controlar agora estão se tornando infinitamente mais complicados, porque seu jar será usado em muitos contextos diferentes (diferente de um jar de gordura que você implanta como aplicativo / serviço independente em um ambiente controlado).
Por exemplo, o ElasticSearch costumava ocultar algumas dependências dos jarros enviados, mas eles decidiram parar de fazer isso :
Antes da versão 2.0, o Elasticsearch era fornecido como um JAR com algumas (mas não todas) dependências comuns sombreadas e empacotadas no mesmo artefato. Isso ajudou os usuários de Java que incorporaram o Elasticsearch em seus próprios aplicativos a evitar conflitos de versão de módulos como Guava, Joda, Jackson etc. É claro que ainda havia uma lista de outras dependências não sombreadas, como Lucene, que ainda podiam causar conflitos.
Infelizmente, o sombreamento é um processo complexo e passível de erros, que resolveu problemas para algumas pessoas enquanto cria problemas para outras. O sombreamento torna muito difícil para os desenvolvedores e autores de plugins escrever e depurar o código corretamente porque os pacotes são renomeados durante a compilação. Por fim, costumávamos testar o Elasticsearch sem sombreamento e enviar o frasco sombreado, e não gostamos de enviar nada que não estamos testando.
Decidimos enviar o Elasticsearch sem sombreamento a partir de 2.0.
Observe que eles também se referem a dependências sombreadas , não a jarras sombreadas