Problemas em aplicações não-modulares
- Não é possível restringir o acesso a pacotes. Se um método for público, ele poderá ser utilizado por toda aplicação.
- Devido a Reflection API é possível violar o encapsulamento mesmo de classes que estejam fora do campo de visão. Se temos um classe
Y
, com modificador de acessoprotected
no pacotea
, e tentarmos importá-la em uma classe do pacoteb
, o compilador irá lançar um erro. Porém, podemos usarClass.forName("a.Y")
da API de reflexão e violaremos oprotected
.
package a; protected class Y {...}
package b; public class X { void violateY() { Class c = Class.forName("a.Y"); Field[] fileds = c.getFields(); Method[] meothods = c.getMethods(); } }
- O processo de compilar e executar uma aplicação não garante que todas as classes necessárias para sua execução estejam presentes no arquivo gerado. Se uma classe utilizada pelo programa não foi declarada no momento de compilação do código, quando a JVM tentar carrega-la em memória um erro será lançado. O problema é que esse erro só é lançado quando a JVM tenta carregar a classe não encontrada. Com essa demora em lançar o erro outros problemas podem ocorrer, simplesmente por que o compilador não avisa que não achou algo que precisa.
Módulos
É o agrupamento de código em alto nível
- Pode agrupar um ou mais pacotes relacionados. Além de recursos como imagens ou arquivos
.xml
.
- As informações do módulo ficam em um arquivo obrigatoriamente com o nome
module-info.java
que deve ficar na raiz do diretório do módulo.
- Um módulo precisa ter um nome único, como classes e pacotes. É recomendado utilizar o nome do pacote principal do seu módulo.
- Se você não explicitamente permitir, até mesmo a API de reflexão não conseguirá acessar os membros do módulo.
- É necessário declarar explicitamente as dependências do módulo.
- Pacotes são, por padrão, não exibidos para fora do módulo. Apenas pacotes explicitamente exportados ficam visíveis.
- Podemos definir serviços que o módulo consome ou exporta.
- Um serviço é um conjunto de módulos.
- Garantem que todos pacotes necessários para a execução do módulo sejam compilados e dispostos no arquivo a ser utilizado.
- Podem ter versões.
- Módulos que pertencem a versão padrão do Java (Java SE) possuem o prefixo
java.
. Quando um módulo não pertence ao Java padrão, ele é específico do JDK utilizado e possuem o prefixojdk.
. Um módulo específico de JDK depende do ambiente em que está sendo executado o programa, e.g. o JDK de windows 10 64 bits é diferente do linux debian 64bits.

JPMS
- É a sigla de Java Plataform Module System
- Desde a versão 9, todas JDK APIs foram reorganizadas em módulos
- Para ter múltiplos módulos em um mesmo diretório, será necessário criar subdiretórios para cada módulo. Isso se deve ao fato de cada módulo precisar ter seu próprio arquivo
module-info.java
.
- Com módulos, é possível gerar uma aplicação com tamanho menor. Os módulos declaram explicitamente oque precisam para serem executados, evitando que código não utilizado vá junto no arquivo final da aplicação.
- Loop de dependências entre módulos não é permitido.
- Com o comando
java --list-modules
é possível listar os módulos disponíveis na máquina.
Dependências
- Com
requires <module>
se declara uma dependência de módulo normal.
- Com
requires transitive <module>
se declara uma dependência que será implicitamente requerida pelos módulos que dependerem desse módulo.
- Com
requires static <module>
se declara uma dependência de compilação. Um módulo requerido de forma estática não precisa ser utilizado em momento de execução.
- A declaração
requires java.base
implicitamente está em todos módulos.
- É possível declarar vários pacotes em uma mesma linha separando-os com ",".
Exportação
- Se um módulo X exportar um pacote, todos métodos públicos dele ficam visíveis para outros módulos que requeiram o módulo X.
- É possível declarar vários pacotes em uma mesma linha separando-os com ",".
- É possível exportar alguns pacotes para módulos específicos com
exports <package one, package two> to <other module one, other module two>
.
- Módulos exportados que não sejam públicos ficam inacessíveis pela API de reflexão.
Abertura
- Faz o mesmo que a diretiva
exports
porém, em pacotes abertos, mesmo conteúdo seu que não é público fica acessível pela API de reflexão.
- Um módulo pode permitir acesso apenas em tempo de execução com a diretiva
opens
.
- A declaração
opens <packages>
especifica pacotes acessíveis em tempo de execução.
- É possível abrir pacotes apenas para módulos específicos
opens <packages> to <modules>
.
- Um módulo pode ter todo seu conteúdo aberto quando é definido utilizando
open module <module name> {...}
Services
Utilizando a classe
ServiceLoader
conseguimos obter em tempo de execução uma instância que implemente uma certa interface, e.g. ServiceLoader.load(MessageService.class)
. O método load
retorna uma instância de cada classe que implementa a interface solicitada. - Possibilitam a um módulo definir uma dependência com sua classe sendo definida dinamicamente. Com isso um módulo pode declarar a dependência em uma interface mas deixar que a classe que for utiliza-la escolha qual implementação da interface quer utilizar.
- Utilizando
provides <service interface> with <classes>
podemos implementações para uma interface de serviço
- Utilizando
uses <service interface>
declaramos que o módulo precisa de uma implementação dessa interface de serviço
- A classe que implementa o serviço é dinamicamente descoberta utilizando a classe
ServiceLoader


Arquivos de módulos com múltiplas versões
Os arquivos chamados JAR de múltiplas versões (Multi-Release JAR) pode ser utilizados para que seu código suporte diferentes versões de Java
- No diretório raiz do módulo pode estar uma versão padrão do módulo ou uma versão não modularizada do código (possibilitando suporte para versões < 9).
- O compilador de Java possibilita que código escrito para uma versão antiga, seja executável em um JVM mais recente. Porém, com JAR de múltiplas versões podemos especificar exatamente qual o pacote utilizado para cada versão Java.

- Quando um aplicação que possuí código modularizado e não modularizado, é possível executa-la utilizando ambas opções
--module-path <path to modules>
e--class-path <path to jars>
. Nesse caso, o código não modularizado será atribuído para um módulo não nomeado. - É possível utilizar um
.jar
não modularizado na opção--module-path
. Isso possibilita que outros módulos requeriam pacotes desse JAR utilizando o nome do arquivo como nome de módulo. Porém, como o nome do arquivo não é garantido de ser único, é recomendado colocar um arquivoMANIFEST.MF
, e, através da propriedadeAutomatic-Module-Name
, definir um nome dado ao JAR caso esse seja utilizado como módulo.
- Em uma aplicação feita inteiramente com módulos, o comando
jlink
consegue construir algo chamado JIMAGE. Uma JIMAGE é uma versão da sua aplicação capaz de rodar sem que o host tenha instalado uma JRE. - A JIMAGE não carrega todos pacotes da JDK, mas apenas o subconjunto que foi utilizado pela aplicação. Isso pode levar facilmente a uma aplicação 10x menor (sem considerar uma aplicação otimizada ou zipada) que com a abordagem tradicional.
- Uma JIMAGE é criada a partir de código Java já compilado, os arquivos
.jar
. - O JDK utilizado para criar a JIMAGE, especifica em qual plataforma essa JIMAGE irá ser executada. É possível, com um mesmo código, criar diferentes imagens para diferentes ambientes.
- A imagem pode ser executada com
<imagem>/bin/java -m <nome do modulo>
ou se foi criada uma imagem com um comando para execução:<imagem>/bin/<comando>

