Cinco coisas que você não sabia sobre ... JARs

Um Java Archive é mais do que apenas um pacote configurável de classes

Muitos desenvolvedores™ Java nunca pensam além dos princípios básicos de JARs -- — usando-os apenas para criar pacotes configuráveis de classes e enviando-os aos servidores de produção. No entanto, um JAR é muito mais do que apenas um arquivo ZIP renomeado. Saiba como usar Java Archives em sua capacidade total, incluindo dicas para dependências Spring JAR e arquivos de configuração.

Ted Neward, Principal, Neward & Associates

Ted Neward photoTed Neward é o diretor da Neward & Associates, onde ele dá consultoria, orientação, ensina e faz apresentações sobre Java, .NET, Serviços XML e outras plataformas. Ele reside perto de Seattle, Washington.



17/Mai/2011

Para a maioria dos desenvolvedores Java, os arquivos JAR e seus primos especializados, WARs e EARs, são simplesmente o resultado final de um longo processo de Ant ou Maven. O procedimento padrão é copiar o JAR para o local correto do servidor (ou, mais raramente, para a máquina do usuário) e esquecê-lo.

Na verdade, os JARs podem fazer mais do que armazenar código de origem, mas é necessário saber o que mais é possível, e como pedir. As dicas desta parte da série Cinco coisas mostrarão como aproveitar ao máximo os Java Archives (e, em alguns casos, WARs e EARs também), especialmente no momento da implementação.

Devido ao fato de que muitos desenvolvedores Java usam Spring (e uma vez que a estrutura Spring apresenta alguns desafios específicos ao nosso tradicional uso de JARs), grande parte das dicas abordam especificamente JARs em aplicativos Spring.

Sobre esta série

Então, você acha que possui conhecimento sobre programação Java? A verdade é que a maioria dos desenvolvedores tem apenas algum conhecimento sobre a plataforma Java, aprendendo o suficiente para concluir a tarefa. Nesta série,Ted Neward examina a fundo a funcionalidade da plataforma Java para revelar fatos pouco conhecidos que podem ajudar você a superar até mesmo os desafios de programação mais difíceis.

Começarei com um breve exemplo de um procedimento padrão de arquivo Java Archive, que servirá como base para as dicas posteriores.

Coloque em um JAR

Geralmente, você desenvolveria um arquivo JAR após a compilação da origem do seu código, coletando o código Java (que foi separado por pacote) em uma única coleção por meio do utilitário de linha de comando jar ou, mais comumente, a tarefa jar Ant. O processo é tão direto que não o demonstrarei aqui, de qualquer forma, posteriormente no artigo, voltaremos ao tópico de como os JARs são construídos. Por enquanto, precisamos apenas arquivar Hello, um utilitário de console independente que realiza a tarefa incrivelmente útil de imprimir uma mensagem no console, mostrado na Listagem 1:

Listagem 1. Arquivando o utilitário de console
package com.tedneward.jars;

public class Hello
{
    public static void main(String[] args)
    {
        System.out.println("Howdy!");
    }
}

O utilitário Hello não é muito, mas é uma plataforma útil para explorar arquivos JAR, começando pela execução do código.


1. JARs são executáveis

Linguagens como .NET e C++ historicamente têm a vantagem de serem compatíveis com o S.O., no qual a simples ação de referenciar seu nome na linha de comando (helloWorld.exe) ou clicar duas vezes no respectivo ícone no shell da GUI inicia o aplicativo. Em programação Java, no entanto, um aplicativo ativador —java— autoinicializa o JVM no processo, e temos de passar o argumento de linha de comando (com.tedneward.Hello) indicando a classe cujo método main() desejamos iniciar.

Essas etapas adicionais dificultam a criação de aplicativos fáceis e simples em Java. O usuário final não precisa apenas digitar todos esses elementos na linha de comando, algo que muitos usuários finais evitariam, mas também há grandes chances de esse usuário cometer um erro de digitação e receber um obscuro erro.

A solução é tornar o arquivo JAR "executável", de forma que o ativador Java saiba automaticamente quais classes ativar ao executar o arquivo JAR. Tudo o que precisamos fazer é apresentar uma entrada no manifesto do arquivo JAR (MANIFEST.MF no subdiretório JAR META-INF ), da seguinte forma:

Listagem 2. Mostre-me o ponto de entrada!
Main-Class: com.tedneward.jars.Hello

O manifesto é apenas um conjunto de pares nome-valor. Uma vez que o manifesto pode, às vezes, ser sensível em relação a retornos de linha e espaço em branco, é mais fácil usar o Ant para gerá-lo ao desenvolver o JAR. Na Listagem 3, usei o elemento manifest da tarefa Ant jar para especificar o manifesto:

Listagem 3. Desenvolva o ponto de entrada para mim!
    <target name="jar" depends="build">
        <jar destfile="outapp.jar" basedir="classes">
            <manifest>
                <attribute name="Main-Class" value="com.tedneward.jars.Hello" />
            </manifest>
        </jar>
    </target>

Tudo o que o usuário precisa fazer para executar o arquivo JAR agora é especificar seu nome do arquivo como uma linha de comando, viajava -jar outapp.jar. No caso de alguns shells da GUI, também funciona clicar duas vezes no arquivo JAR.


2. JARs podem incluir informações de dependência

Parece que o utilitário Hello foi divulgado, por isso surgiu a necessidade de variar a implementação. Os contêineres de injeção de dependência (DI), como Spring ou Guice, identificam diversos detalhes para nós, mas ainda há um obstáculo: a modificação do código para incluir o uso do contêiner de DI pode levar a resultados semelhantes à Listagem 4:

Listagem 4. Hello, Spring world!
package com.tedneward.jars;

import org.springframework.context.*;
import org.springframework.context.support.*;

public class Hello
{
    public static void main(String[] args)
    {
        ApplicationContext appContext =
            new FileSystemXmlApplicationContext("./app.xml");
        ISpeak speaker = (ISpeak) appContext.getBean("speaker");
        System.out.println(speaker.sayHello());
    }
}

Mais sobre o Spring

Esta dica pressupõe que você esteja familiarizado com a injeção de dependência e com a estrutura Spring. Consulte a seção Recursos se for necessário rever algum tópico.

Como a opção -jar no ativador sobrescreve tudo o que acontece na opção da linha de comando -classpath , o Spring precisa estar no CLASSPATH e na variável de ambiente quando você executar este código. Felizmente, os JARs permitem que uma declaração de outras dependências JARs apareça no manifesto, o que implicitamente cria o CLASSPATH sem que você tenha que declará-lo, conforme é mostrado na Listagem 5:

Listagem 5. Hello, Spring CLASSPATH!
    <target name="jar" depends="build">
        <jar destfile="outapp.jar" basedir="classes">
            <manifest>
                <attribute name="Main-Class" value="com.tedneward.jars.Hello" />
                <attribute name="Class-Path" 
                    value="./lib/org.springframework.context-3.0.1.RELEASE-A.jar 
                      ./lib/org.springframework.core-3.0.1.RELEASE-A.jar 
                      ./lib/org.springframework.asm-3.0.1.RELEASE-A.jar 
                      ./lib/org.springframework.beans-3.0.1.RELEASE-A.jar 
                      ./lib/org.springframework.expression-3.0.1.RELEASE-A.jar 
                      ./lib/commons-logging-1.0.4.jar" />
            </manifest>
        </jar>
    </target>

Observe que o atributo Class-Path contém uma referência relativa aos arquivos JAR dos quais o aplicativo depende. Também é possível escrever isso como uma referência absoluta ou inteiramente sem um prefixo, situação em que seria presumido que os arquivos JAR estavam no mesmo diretório do aplicativo JAR.

Infelizmente, o atributo value para o atributo Ant Class-Path deve aparecer em uma linha, porque o manifesto JAR não é compatível com a ideia de vários atributos Class-Path . Por isso, todas aquelas dependências devem aparecer em uma linha no arquivo de manifesto. Evidentemente, não ficará muito bonito, mas poder executar o java -jar outapp.jar vale a pena!


3. JARs podem ser referenciados implicitamente

Se você tiver vários diferentes utilitários de linha comando (ou outros aplicativos) que usam a estrutura Spring, pode ser mais fácil colocar os arquivos JAR Spring em um local comum que possa ser referenciado por todos os utilitários. Faça isso para evitar ter várias cópias de JARs ocupando todo o sistema de arquivos. Por padrão, a localização comum do Java Runtime para JARs, conhecida como "diretório de extensão", é o subdiretório lib/ext no local do JRE instalado.

O JRE é um local customizável, mas é tão raramente customizado em um ambiente Java específico que é inteiramente seguro pressupor que lib/ext é um local seguro para armazenar JARs, e que eles estarão implicitamente disponíveis no CLASSPATH do ambiente Java.


4. Java 6 permite curingas do caminho de classe

Para evitar grandes variáveis de ambiente CLASSPATH , (que os desenvolvedores Java deveriam ter abandonado há anos) e/ou parâmetros -classpath de linha de comando, o Java 6 apresentou a noção do curinga do caminho de classe. Em vez de ter de ativar qualquer e todo arquivo JAR explicitamente listado em um argumento, o curinga do caminho de classe permite especificar lib/* e todos os arquivos JAR listados nesse diretório (não recursivamente) no caminho de classe.

Infelizmente, o curinga do caminho de classe não se aplica à entrada do manifesto do atributo Class-Path discutida anteriormente. Mas ele realmente facilita a ativação de aplicativos Java (incluindo servidores) para tarefas de desenvolvedor, como ferramentas de geração de código ou ferramentas de análise.


5. JARs contêm mais do que apenas código

O Spring, assim como muitas partes do ecossistema Java, depende de um arquivo de configuração que descreve como o ambiente deve ser estabelecido. Como foi afirmado, o Spring depende de um arquivo app.xml que reside no mesmo diretório do arquivo JAR — mas é absolutamente comum que os desenvolvedores se esqueçam de copiar o arquivo de configuração juntamente com o arquivo JAR.

Alguns arquivos de configuração podem ser editados por um administrador do sistema, mas um significativo número deles (como mapeamentos Hibernate) estão bastante fora do domínio do administrador do sistema, o que leva a erros de implementação. Uma solução lógica seria empacotar o arquivo de configuração junto com o código— e essa solução é factível, pois o JAR é basicamente um ZIP disfarçado. Basta incluir os arquivos de configuração na tarefa Ant ou na linha de comando jar ao desenvolver um JAR.

Os JARs também incluem outros tipos de arquivos, não apenas arquivos de configuração. Por exemplo, se meu componente SpeakEnglish desejasse acessar um arquivo de propriedades, eu poderia configurá-lo conforme a Listagem 6:

Listagem 6. Responder aleatoriamente
package com.tedneward.jars;

import java.util.*;

public class SpeakEnglish
    implements ISpeak
{
    Properties responses = new Properties();
    Random random = new Random();

    public String sayHello()
    {
        // Pick a response at random
        int which = random.nextInt(5);
        
        return responses.getProperty("response." + which);
    }
}

Inserir responses.properties no arquivo JAR significa que há um arquivo a menos com o qual se preocupar sendo implantado com o arquivo JAR. Para fazer isso, é necessário apenas incluir o arquivo responses.properties durante a etapa JAR.

Uma vez que você tiver armazenado suas propriedades em um JAR, pode ficar imaginando como obtê-las de volta. Se os dados desejados estiverem colocalizados dentro do mesmo arquivo JAR, como é o caso no exemplo anterior, não se incomode tentando adivinhar a localização do arquivo do JAR e abra-o com um objeto JarFile . Em vez disso, deixe que a classe ClassLoader o localize como um "recurso" dentro do arquivo JAR, usando o método ClassLoader getResourceAsStream() mostrado na Listagem 7:

Listagem 7. ClassLoader localiza um Recurso
package com.tedneward.jars;

import java.util.*;

public class SpeakEnglish
    implements ISpeak
{
    Properties responses = new Properties();
    // ...

    public SpeakEnglish()
    {
        try
        {
            ClassLoader myCL = SpeakEnglish.class.getClassLoader();
            responses.load(
                myCL.getResourceAsStream(
                    "com/tedneward/jars/responses.properties"));
        }
        catch (Exception x)
        {
            x.printStackTrace();
        }
    }
    
    // ...
}

É possível seguir este procedimento para qualquer tipo de recurso: arquivo de configuração, arquivo de áudio, arquivo gráfico, você decide. Praticamente qualquer tipo de arquivo pode ser empacotado em um JAR, obtido como um InputStream (via ClassLoader) e usado da maneira que você desejar.


Conclusão

Este artigo abordou as cinco principais coisas que a maioria dos desenvolvedores Java não sabe sobre JARs — pelo menos com base em indícios históricos e casuais. Observe que todas essas dicas relacionadas a JARs são igualmente válidas para WARs. Algumas dicas (os atributos Class-Path e Main-Class , especificamente) são menos animadoras no caso de WARs, porque o ambiente do servlet seleciona o conteúdo inteiro de um diretório e tem um ponto de entrada predefinido. Ainda assim, consideradas coletivamente, essas dicas nos transportam para além do paradigma "OK, comece copiando tudo neste diretório...". Ao fazer isso, elas também simplificam muito a implementação de aplicativos Java.

A seguir, nesta série: Cinco coisas que você não sabia sobre monitoramento de desempenho para aplicativos Java.


Download

DescriçãoNomeTamanho
Sample code for this articlej-5things6-src.zip10KB

Recursos

Aprender

Discutir

Comentários

developerWorks: Conecte-se

Los campos obligatorios están marcados con un asterisco (*).


Precisa de um ID IBM?
Esqueceu seu ID IBM?


Esqueceu sua senha?
Alterar sua senha

Ao clicar em Enviar, você concorda com os termos e condições do developerWorks.

 


A primeira vez que você entrar no developerWorks, um perfil é criado para você. Informações no seu perfil (seu nome, país / região, e nome da empresa) é apresentado ao público e vai acompanhar qualquer conteúdo que você postar, a menos que você opte por esconder o nome da empresa. Você pode atualizar sua conta IBM a qualquer momento.

Todas as informações enviadas são seguras.

Elija su nombre para mostrar



Ao se conectar ao developerWorks pela primeira vez, é criado um perfil para você e é necessário selecionar um nome de exibição. O nome de exibição acompanhará o conteúdo que você postar no developerWorks.

Escolha um nome de exibição de 3 - 31 caracteres. Seu nome de exibição deve ser exclusivo na comunidade do developerWorks e não deve ser o seu endereço de email por motivo de privacidade.

Los campos obligatorios están marcados con un asterisco (*).

(Escolha um nome de exibição de 3 - 31 caracteres.)

Ao clicar em Enviar, você concorda com os termos e condições do developerWorks.

 


Todas as informações enviadas são seguras.


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=80
Zone=Tecnologia Java
ArticleID=658254
ArticleTitle=Cinco coisas que você não sabia sobre ... JARs
publish-date=05172011