Java 9 na prática: Jigsaw
Há muito tempo se diz sobre modularizar a plataforma Java. É um plano que começou desde antes do Java 7, foi uma possibilidade no Java 8 e por fim, para permitir mais tempo de desenvolvimento, revisão e testes, foi movido para o Java 9.
O projeto Jigsaw, como foi chamado, é composto por uma série de JEPs. Algumas delas inclusive já disponíveis no Java 8, como os conhecidos Compact Profiles. A idéia por trás do projeto não é só criar um sistema de módulos, que poderemos usar em nossos projetos, mas também aplicá-lo em toda a plataforma e JDK em busca de melhor organização e desempenho.
we propose to design and implement a standard module system for the Java SE Platform and to apply that system to the Platform itself, and to the JDK. The module system should be powerful enough to modularize the JDK and other large legacy code bases, yet still be approachable by all developers.
Neste novo post da serie Java 9 prático não só veremos que o projeto já existe e está integrado aos últimos builds, como também vamos explorar e implementar nosso próprio projeto modular. Tudo ao estilo hands on, claro — preparem seus compiladores!
Preparando o seu ambiente para o Java 9 modular
O Jigsaw foi integrado ao build do JDK 9 desde a versão ea+111, portanto tudo que você precisa para usá-lo é baixar uma versão igual ou superior a ela. Eu recomendo bastante que, mesmo que você já tenha uma versão relativamente atual, baixe sempre a última antes de qualquer teste. Você pode fazer o download aqui. A versão que estou usando neste post é do build 134 (mais atual de agora).
Criando um hello world tradicional
No lugar de já começar criando o projeto modular com Jigsaw, vamos criar um projeto da forma tradicional e depois migrá-lo, assim a diferença entre as duas abordagens deve ficar bastante clara. Nos exemplos do post vou fazer tudo pela linha de comando e ir compartilhando os snippets, mas para acompanhar, você pode e deve usar o gerenciador de arquivos do seu sistema operacional e qualquer editor de sua preferência.
Os passos são simples, começando pela criação dos diretórios do projeto e de seus pacotes. O projeto se chamará hello-jigsaw e as classes Java devem ficar no pacote br.com.caelum.jigsaw
. Podemos fazer tudo com o comando:
mkdir -p hello-jigsaw/src/br/com/caelum/jigsaw
Claro que, se você preferir, pode criar usando o gerenciador de arquivos do seu sistema operacional favorito. O importante é que, no final, a estrutura de diretórios fique assim:
hello-jigsaw └── src └── br └── com └── caelum └── jigsaw
Tudo ok. Então agora podemos criar a classe JigSaw.java
, dentro de src/br/com/caelum/jigsaw
:
package br.com.caelum.jigsaw; class JigSaw { public static void main (String... args) { System.out.println("Olá Java 9 modular!"); } }
Aqui tem um vídeo com todos os passos desse processo, caso queira dar uma olhada. E também vou deixar um link para fazer download ou navegar pelos arquivos com esse estado do post.
Compilando e executando, ainda da forma atual
Para compilar o projeto podemos usar o comando javac
e pronto. Nada de especial aqui.
javac src/br/com/caelum/jigsaw/JigSaw.java
Feito isso, caso não exista nenhum erro, podemos executar com:
java -cp src/ br.com.caelum.jigsaw.JigSaw
Repare que, como já criamos dentro de um pacote e da pasta source, foi preciso passar o parâmetro -cp (classpath) apontando para ela e também usar o nome completo da classe, que inclui o pacote. Talvez você não esteja tão acostumado com isso, afinal a maioria de nós não compila classes na linha de comando no dia a dia, mas não tem nenhuma novidade por enquanto.
Ao executar, o output, conforme esperado, será a mensagem: Olá Java 9 modular!.
Você pode ver aqui o vídeo do processo de compilação e execução.
Agora finalmente vamos para a parte divertida, que será migrar o projeto para que seja modular!
Migrando para um projeto modular
A migração não é nada complicada. Você precisa basicamente criar uma nova pasta com o nome do seu módulo, que por convenção, é o pacote base do seu projeto. Você pode pensar no nome do módulo como algo parecido com o groupId
de um projeto do Maven. É aberto, você pode escrever qualquer coisa, mas idealmente deve seguir uma convenção.
Em nosso caso o módulo será chamado de br.com.caelum.jigsaw
:
mkdir src/br.com.caelum.jigsaw
Ok. O próximo passo será mover todos os arquivos do projeto, de dentro da pasta src, para dentro deste novo diretório do módulo. Você pode fazer isso com o comando mv do terminal ou simplesmente mover a pasta br pelo seu gerenciador de arquivos com tudo que tem dentro para o diretório do módulo. Na linha de comando seria assim:
mv src/br/ src/br.com.caelum.jigsaw/
Agora falta criar um arquivo chamado module-info.java
, também dentro deste diretório do módulo — tudo ficará nele a partir de agora. Esse arquivo será o responsável pela declaração do módulo e futuramente as suas dependências. A configuração é mínima:
module br.com.caelum.jigsaw { // não precisa escrever nada aqui, pelo menos por enquanto }
Tudo pronto, no final de todos os passos nosso projeto deve ficar assim:
hello-jigsaw └── src └── br.com.caelum.jigsaw ├── br │ └── com │ └── caelum │ └── jigsaw │ └── JigSaw.java └── module-info.java
Vou deixar aqui os links do post nesta fase também, caso queira acompanhar:
[video, download do código, navegar pelo projeto]
Nosso projeto agora é modular! Nada complicado, não é mesmo? Vamos ver agora como compilar e executar um projeto com esse novo formato.
Compilando um projeto modular
O comando javac
para um projeto modular é um pouco diferente. Ele terá a seguinte estrutura:
javac -d <1> <2> <3>
Onde <1> é o diretório onde ficará o código compilado, <2> é o caminho para o seu arquivo module-info.java
, e <3> é o caminho completo para o seu arquivo .java.
O comando completo para compilar esse nosso projeto ficará assim:
javac -d mods/br.com.caelum.jigsaw src/br.com.caelum.jigsaw/module-info.java src/br.com.caelum.jigsaw/br/com/caelum/jigsaw/JigSaw.java
Execute e, se nenhum erro de compilação for exibido, você verá que a nova pasta mods
foi criada com todo o código compilado. Inclusive os metadados do module-info. O projeto ficou assim:
hello-jigsaw └── mods │ └── br.com.caelum.jigsaw │ ├── br │ │ └── com │ │ └── caelum │ │ └── jigsaw │ │ └── JigSaw.class │ └── module-info.class └── src └── br.com.caelum.jigsaw ├── br │ └── com │ └── caelum │ └── jigsaw │ └── JigSaw.java └── module-info.java
Agora temos o diretório src, com o código fonte, e o diretório mods
, com o código compilado.
Executando um projeto modular
Para executar você usará o comando java
, como sempre, mas agora com esse formato:
java –module-path <1> -m <2>
Onde <1> é o nome do diretório onde está o código compilado do nosso módulo, ou seja, a pasta /mods. E <2> o nome completo (full qualified name) da classe que será executada. Um detalhe importante é que, agora que estamos trabalhando de forma modular, o nome da classe recebe também o nome do módulo como prefixo, logo antes do nome do pacote.
O comando para executar a classe JigSaw ficará assim:
java –module-path mods/ -m br.com.caelum.jigsaw/br.com.caelum.jigsaw.JigSaw
E a saída, assim como esperado, será o print: Olá Java 9 modular!
Aqui vão os links de [video, download do código, navegar pelo projeto]
Adicionando um alert do Swing
Por enquanto usamos apenas um System.out
no projeto, apenas recursos do pacote padrão java.lang
. Vamos experimentar usar um importe de alguma outra API do próprio Java pra ver o que acontece? Uma forma simples seria usando um alert do Swing para imprimir a mensagem:
package br.com.caelum.jigsaw; import javax.swing.JOptionPane; class JigSaw { public static void main (String... args) { JOptionPane.showMessageDialog(null,"Olá Java 9 modular!"); } }
Maravilha, agora basta compilar utilizando o mesmo comando de antes e ops…
src/br.com.caelum.jigsaw/br/com/caelum/jigsaw/JigSaw.java:3: error: package javax.swing does not exist import javax.swing.JOptionPane; ^ src/br.com.caelum.jigsaw/br/com/caelum/jigsaw/JigSaw.java:8: error: cannot find symbol JOptionPane.showMessageDialog(null,"Olá Java 9 modular!"); ^ symbol: variable JOptionPane location: class JigSaw 2 errors
Você pode ver aqui um video com esses passos e o erro gerado.
O que houve? A mensagem diz que o pacote javax.swing
não existe! Claro que o Swing não foi removido no Java 9… mas a partir do momento em que compilamos nosso projeto modular, o compilador só usará os módulos que declararmos como dependência no arquivo module-info.java
. Até então não tinhamos nenhuma.
O JRE modular
A primeira coisa que eu fiz ao baixar o build do Java 9 com Jigsaw foi abrir o diretório onde ele está instalado e ver o que mudou. Se você fizer isso aí na sua casa, vai perceber que no lugar da pasta jre/lib, onde ficavam todos os .jar
s da JRE, você encontrará um diretório jmods
com todos os módulos. E não são poucos.
The original goal of this Project was to design and implement a module system focused narrowly upon the goal of modularizing the JDK, and to apply that system to the JDK itself. We expected the resulting module system to be useful to developers for their own code, and it would have been fully supported for that purpose, but it was not at the outset intended to be an official part of the Java SE Platform Specification.
O pensamento foi: se vamos modularizar o próprio JDK e esse sistema de módulos pode ser útil aos desenvolvedores, por que não torná-lo parte oficial do Java SE? E assim foi feito, todo o código foi reorganizado em módulos. Você pode listar todos eles executando o seguinte comando:
java --list-modules
Você pode ver todos na imagem deste link. Um detalhe que você deve ter percebido é que, assim como não era preciso importar o java.lang
para usar as classes System
, String
e outras, você também não precisa declarar nenhum módulo. Essas classes estão presentes no módulo padrão, java.base
. Outro detalhe importante é que você não é obrigado a usar Java modular, claro. Você só precisa declarar de quais módulos a sua aplicação depende se ela for modular… caso exista o arquivo module-info
.
Declarando a dependência entre módulos
As classes do Swing
, como o JOptionPane
que usamos, estão em um módulo chamado java.desktop
. Para ensinar ao compilador que precisamos desse módulo em nosso projeto, basta declará-lo no arquivo module-info.java
. A mudança é simples, o código ficará assim:
module br.com.caelum.jigsaw { requires java.desktop; }
E pronto! Você não precisa de nenhuma configuração adicional, basta usar o mesmo comando para compilar e agora ele saberá de onde vem o pacote javax.swing
.
javac -d mods/br.com.caelum.jigsaw src/br.com.caelum.jigsaw/module-info.java src/br.com.caelum.jigsaw/br/com/caelum/jigsaw/JigSaw.java
Zero erros. Agora executamos o código e o alert será exibido:
java –module-path mods/ -m br.com.caelum.jigsaw/br.com.caelum.jigsaw.JigSaw
Aqui vão os links de [video, download do código, navegar pelo projeto]
É claro que, assim como você não precisa executar os comandos javac
e java
manualmente na linha de comando hoje, passando o classpath e caminho para as dependências do projeto, você também não vai precisar fazer isso com o projeto modular. As IDEs vão adicionar suporte para que isso fique transparente e para que você não se preocupe tanto com os detalhes.
O post foi um overview rápido sobre um assunto bastante abrangente, então, sem dúvida alguma novos posts entrando em detalhes que não foram discutidos aqui virão em breve. Não deixe de deixar suas perguntas e sugestões de assuntos aqui como comentário, são elas que estão guiando os posts da serie Java 9.
E aí, o que achou do Jigsaw? Tem outros assuntos que gostaria de ler aqui no blog sobre Java 9?
Programação, Mobile, Front-end, Design & UX, Infraestrutura e Business
Senti falta da resposta à pergunta: como voce compararia um projeto baseado nesta modularizaçao, a um projeto baseado em modulos maven (por exemplo)? quais vantagens e desvantagens…
Oi Icaro, legal o ponto que você levantou!
A grande vantagem do modular em Java é que você só vai usar o que precisa da JRE — já que agora ela também é modular e você precisa declarar quais módulos usa em seu projeto!
Em outras palavras, terá uma JRE pequena só com o que você usa.
A vantagem disso é uma melhora de desempenho considerável, principalmente na inicialização.
Rodrigo Turini, ótimo hands on. Não sei se na época que você fez esse artigo era diferente mas tive problema construindo o seu exemplo, deu erro quando fui executar:
java -modulepath mods/ -m br.com.caelum.jigsaw/br.com.caelum.jigsaw.JigSaw
No site do proprio java descobri que o module path estava errado, o correto é:
java –module-path mods/ -m br.com.caelum.jigsaw/br.com.caelum.jigsaw.JigSaw
Esqueci de dizer no próprio help do java: java –help, está com erro também:
…
-modulepath …
Uma lista separada por : de diretórios, cada um
sendo um diretório de módulos.
…
Ou eu fiz alguma coisa errado?
Baixeo o jdk-9-ea+136_linux-x86_bin.tar.gz
Oi Ricardo
Tem razão, isso mudou no build 136! Já atualizei pra `–module-path`.
Obrigado pelo aviso, assim o post sempre fica atualizado (;
Desculpe a ignorância, mas o propósito do jigsaw é parecido com osgi?
Oi Hugo
É isso mesmo, o JigSaw é uma solução nativa da linguagem para modularização.
A grande diferença entre ambos é que o JigSaw é usado pra modularizar o próprio JRE. Quando você for criar um projeto modular, com ele, pode escolher apenas os “pedacinhos” da JRE que precisar, diferente da abordagem atual.
É muito provável que em um futuro próximo as duas soluções possam ser utilizadas em conjunto. OSGI existe a anos e é muito bem consolidado.
Excelente artigo introdutório. Mas fiquei com uma dúvida em relação a modularização: o projeto Jigsaw vai modularizar o Java e possibilitar que os projetos se tornem menores (mais eficientes em relação ao uso da JRE). Isso possibilitará que os projetos desenvolvidos em Java 9 sejam módulos de um projeto maior?
oi Rogério
Será uma possibilidade sim. Você pode, no lugar de ter 1 único projeto com todo o código, ter vários projetinhos pequenos (módulos) que se integram em um projeto maior.
Olá Rodrigo,
Você sabe dizer se o Java 9 também terá suporte para modularização de aplicações Web? Ou seja, se vai existir a possibilidade de modularizar um war em outros sub Wars?
Olá Rodrigo,
Interessante a idéia de modularizar, porém achei meio burocrático, essa ideia de ter q criar uma outra estrutura para q isso funcione. Na minha opinião a solução deveria ser mais convention over configuration. Quem sabe em versões futuras melhorem isso até com utilização de annotations para facilitar nossa vida. O que vc achou?
Oi Dênis
Fazendo assim, manual, realmente parece bastante burocrático!
mas no fim das contas as IDEs vão fazer o trabalho pesado. Tenho certeza que, pouco tempo depois de sair o JigSaw –ou talvez antes–, as principais do mercado já vão suportar isso e facilitar o trabalho.
Claro que, com o tempo, novos recursos devem aparecer e como você disse muitas convenções podem ser adotadas, etc, para que tudo fique ainda mais simples (;
O Netbeans já tem um suporte beta que deixa tudo transparente. Sei que não é a IDE mais popular, mas pra testar a facilidade de programar com módulos ele é ideal pra avaliar.
Java swing não vai ter mais?
oi Marco
boa dica sobre o NetBeans! Como ele é da própria Oracle, costuma estar a frente nessas atualizações de novas features.
O Intellij também já tem um suporte bem interessante.
Oi Tiago
O Java swing continuará existindo sim! Ele está no módulo *java.desktop*
“O pensamento foi: se vamos criar um sistema de módulos na plataforma Java, por que não modularizar o próprio JDK?”
Pelo texto em inglês, parece que foi o contrário: já que iam modularizar o próprio JDK e esse sistema de módulos seria útil aos desenvolvedores, por que não torná-lo parte oficial do do Java SE?
Oi Marcus
muito bem observado! já atualizei no texto com as suas palavras.
obrigado pelo aviso
Legal, obrigado pela dedicação para atualizar o post!
Caramba… deve ter dado um puta trabalho modularizar a jdk
pra quem gostou e quer acompanhar, saiu um post no blog da caelum sobre o mínimo que você deve saber de Java 9
http://blog.caelum.com.br/o-minimo-que-voce-deve-saber-de-java-9/
e o livro da casa do código, lançado junto com a nova versão da linguagem
https://www.casadocodigo.com.br/products/livro-java9