Caminho de classe, incluindo JAR dentro de um JAR

134

É possível especificar um Java classpathque inclua um arquivo JAR contido em outro arquivo JAR?

Paul Reiners
fonte

Respostas:

94

Se você está tentando criar um único jar que contém seu aplicativo e suas bibliotecas necessárias, há duas maneiras (que eu conheço) de fazer isso. O primeiro é o One-Jar , que usa um carregador de classe especial para permitir o aninhamento de frascos. O segundo é o UberJar , (ou Shade ), que explode as bibliotecas incluídas e coloca todas as classes no jarro de nível superior.

Também devo mencionar que UberJar e Shade são plugins para Maven1 e Maven2, respectivamente. Como mencionado abaixo, você também pode usar o plug-in de montagem (que na realidade é muito mais poderoso, mas muito mais difícil de configurar corretamente).

Steve Moyer
fonte
81
Então aqui estamos, 5 anos depois. Parece que isso ainda é verdade. Muito triste :(
T3rm1
3
A melhor maneira que conheço nos dias de hoje é usar o artefato de jar do IntelliJ. Extrai todas as classes dos frascos dependentes e as coloca em seu único frasco.
enl8enmentnow
2
Isso não é possível em algumas situações, como quando seu usando implementações JCE como BouncyCastle que precisa ser assinado
debuti
1
@ BrainSlugs83 ... O que você está tentando fazer? Acho que o carregador de classe e o empacotador do One-Jar ainda é a resposta para incluir um Jar em um projeto Jar (para Java SE). O uso do UberJar elimina o problema extraindo arquivos de classe no seu Jar.
9788 Steve Jobs Moyer
1
O link UberJar precisa de credenciais de login. Existe alguma página da web sucessora?
Thomas Weller
50

Você NÃO deseja usar as soluções "explodir conteúdo JAR". Eles definitivamente tornam mais difícil ver as coisas (já que tudo é explodido no mesmo nível). Além disso, pode haver conflitos de nomenclatura (não deve acontecer se as pessoas usarem pacotes adequados, mas você nem sempre pode controlar isso).

O recurso que você deseja é um dos 25 principais RFEs da Sun : RFE 4648386 , que a Sun, em sua infinita sabedoria, designou como de baixa prioridade. Só podemos esperar que a Sun acorde ...

Enquanto isso, a melhor solução que eu encontrei (que eu gostaria que a Sun copiasse no JDK) é usar o carregador de classes personalizado JarClassLoader .

Bruce Willis
fonte
4
Na verdade, é quase garantido que os conflitos de nomes acontecem com coisas como a configuração do log4j e os textos de licença.
Michael Borgwardt
1
Infelizmente, o JarClassLoader é GPLv3 / comercial, portanto provavelmente não será "copiado pelo sol (agora oracle)" e não poderá ser usado comercialmente, a menos que você tenha influência política interna e tempo para comprar algo. Concordo, no entanto, que os frascos que vomitam no diretório atual são uma coisa ruim.
Gus
+1 para a idéia nesta resposta (embora eu não esteja marcando a resposta com +1): você não pode empacotar nenhum arquivo JAR assinado, portanto, essa técnica não pode ser usada em muitas situações (por exemplo, Java's activation.jar)
Christopher Schultz
31

Após algumas pesquisas, encontrei um método que não requer o maven ou qualquer extensão / programa de terceiros.

Você pode usar "Caminho da classe" no seu arquivo de manifesto.

Por exemplo:

Criar arquivo de manifesto MANIFEST.MF

Manifest-Version: 1.0
Created-By: Bundle
Class-Path: ./custom_lib.jar
Main-Class: YourMainClass

Compile todas as suas aulas e execute jar cfm Testing.jar MANIFEST.MF *.class custom_lib.jar

csignifica criar archive findica que você deseja especificar o arquivo vé para entrada detalhada msignifica que passaremos o arquivo de manifesto personalizado

Certifique-se de incluir a lib no pacote jar. Você deve conseguir executar o jar da maneira normal.

com base em: http://www.ibm.com/developerworks/library/j-5things6/

todas as outras informações necessárias sobre o caminho da classe que você encontra aqui

Tezar
fonte
19
Esta resposta não funciona. do documento do oracle (vinculado por banana acima): "O cabeçalho Class-Path aponta para classes ou arquivos JAR na rede local, não arquivos JAR no arquivo JAR"
sebnukem
Qualquer coisa conhecida se isso também pode ser usado em uma configuração do Android.
Mostowski Collapse
2
Ele funciona no cenário de jar executável (testado), mas pode não funcionar quando você deseja incluir um jar em um jar de biblioteca.
Cychih
5
Oh não, não funciona, uma vez que eu me mudei custom_lib.jarpara longe, o frasco não pode ser executado mais :(
Cychih
Como vários jarros especificados no caminho de classe?
Abhishek Tiwari
22

Use a tag zipgroupfileset (usa os mesmos atributos de uma tag do conjunto de arquivos ); descompactará todos os arquivos no diretório e será adicionado ao seu novo arquivo. Mais informações: http://ant.apache.org/manual/Tasks/zip.html

Essa é uma maneira muito útil de contornar o problema do jar-in-a-jar - eu sei porque pesquisei essa questão exata do StackOverflow ao tentar descobrir o que fazer. Se você deseja empacotar um jar ou uma pasta de jars no seu jar com o Ant, esqueça todos esses caminhos de classe ou plug-ins de terceiros, tudo o que você precisa fazer é o seguinte (no Ant):

<jar destfile="your.jar" basedir="java/dir">
  ...
  <zipgroupfileset dir="dir/of/jars" />
</jar>
ecbrodie
fonte
8

Se você estiver construindo com ant (estou usando ant do eclipse), basta adicionar os arquivos jar adicionais dizendo ao ant para adicioná-los ... Não é necessariamente o melhor método para manter um projeto mantido por várias pessoas, mas ele funciona para um projeto de uma pessoa e é fácil.

por exemplo, meu destino que estava criando o arquivo .jar era:

<jar destfile="${plugin.jar}" basedir="${plugin.build.dir}">
    <manifest>
        <attribute name="Author" value="ntg"/>
        ................................
        <attribute name="Plugin-Version" value="${version.entry.commit.revision}"/>
    </manifest>
</jar>

Acabei de adicionar uma linha para torná-lo:

<jar ....">
    <zipgroupfileset dir="${external-lib-dir}" includes="*.jar"/>
    <manifest>
        ................................
    </manifest>
</jar>

Onde

<property name="external-lib-dir" 
          value="C:\...\eclipseWorkspace\Filter\external\...\lib" />

foi o dir com os frascos externos. E é isso...

ntg
fonte
6

Não sem escrever seu próprio carregador de classes. Você pode adicionar jars ao caminho de classe do jar, mas eles devem ser colocados juntos, não contidos no jar principal.

Joe Skora
fonte
2

Você precisa criar um carregador de classes personalizado para fazer isso ou uma biblioteca de terceiros que suporte isso. Sua melhor aposta é extrair o jar do tempo de execução e adicioná-los ao caminho de classe (ou tê-los já adicionados ao caminho de classe).

James Schek
fonte
2

Eu uso o maven para minhas compilações java, que tem um plugin chamado maven assembly plugin .

Ele faz o que você pede, mas como algumas das outras sugestões descrevem - basicamente explodindo todos os frascos dependentes e recombinando-os em um único frasco

Vinnie
fonte
A Maven assembly plug-in é bastante doloroso para uso ... UberJar e Sombra estão Maven1 e Maven2 plugins (um fato que eu deveria ter mencionado acima, e irá fazê-lo agora)
Steve Moyer
2

Se você eclipsou o IDE, basta exportar seu JAR e escolher "Bibliotecas necessárias para o pacote no JAR gerado". O eclipse incluirá automaticamente os JARs dependentes necessários no JAR gerado, bem como gerou algum carregador de classes customizado do eclipse que carrega esses JARs automaticamente.

amralieg
fonte
1

Eu estava prestes a aconselhar a extrair todos os arquivos no mesmo nível e, em seguida, criar um jarro com o resultado, pois o sistema de pacotes deve mantê-los bem separados. Essa seria a maneira manual, suponho que as ferramentas indicadas por Steve farão isso muito bem.

PhiLho
fonte
1

Bem, existe uma maneira muito fácil se você estiver usando o Eclipse.

Exporte seu projeto como um arquivo Jar "Executável" (clique com o botão direito do mouse na pasta do projeto no Eclipse, selecione "Exportar ..."). Ao definir as configurações de exportação, selecione "Extrair bibliotecas necessárias para o jar gerado". Lembre-se de selecionar "Extrair ..." e não "Empacotar bibliotecas necessárias ...".

Além disso : você deve selecionar uma configuração de execução nas suas configurações de exportação. Portanto, você sempre pode criar um main () vazio em alguma classe e usá-lo para sua configuração de execução.

De qualquer forma, não é garantido que funcione 100% do tempo - pois você verá uma mensagem pop-up solicitando que você verifique as licenças dos arquivos Jar incluídos e algo sobre não copiar arquivos de assinatura. No entanto, faço isso há anos e nunca encontrei um problema.

CM_2014
fonte
0

A extração para um Uber-dir funciona para mim, pois todos devemos usar root: \ java e ter código de saída em pacotes com controle de versão. Ou seja, ca.tecreations-1.0.0. A assinatura está correta porque os frascos estão intactos no local baixado. Assinaturas de terceiros intactas, extraídas para c: \ java. Aqui está o meu diretor de projeto. executar a partir do iniciador para java -cp c: \ java Launcher

Tim de Vries
fonte
Então, jarros em c: \ java \ jars. Origem, binários e recursos em c: \ java. Exclua backups, propriedades, keystore_private. Descompacte em c: \ java e execute com a ferramenta de formação de caminho de classe, talvez ca.tecreations.system.tools.SystemTool ou classe java comparável. Execute isso.
Tim de Vries