[Pesquisar este blog]

domingo, 17 de dezembro de 2017

Java 9::Jigsaw, a nova modularidade da plataforma (Parte I)

A característica mais esperada da versão 9 do Java é, certamente, o resultado do projeto Jigsaw, que tomou anos de discussões e desenvolvimento, e cuja essência é a melhoria substancial da modularidade da plataforma Java.


Para falar do Jigsaw é necessário, primeiro, discutir a ideia de  modularidade, para, depois, discutir os dois aspectos principais do próprio Jigsaw, que são sua especificação e sua implementação na versão 9 da plataforma Java.

A parte I deste post aborda o conceito de modularidade e a especificação do Jigsaw. A parte II tratará de sua implementação, enquanto a parte III contém um exemplo de seu uso. Estes posts fazem parte de uma série que trata das novidades do Java 9:Primeiras Impressões.

Modularidade

A modularidade é um princípio muito importante no projeto de software que enfatiza a criação de conjuntos de classes ou componentes que possam ser reutilizados em diferentes contextos. Tais conjuntos são considerados os módulos de um sistema de software.

Assim o processo de modularização auxilia: 
  • na ocultação da implementação por meio de encapsulamento forte; 
  • na redução do acoplamento entre componentes; 
  • na simplificação dos contratos entre componentes (de diferentes módulos principalmente); e 
  • na redução e explicitação de dependências entre componentes. 


Modularidade no Java

Até a versão 8, a unidade básica da modularidade no Java eram os arquivos JAR (Java Archives), que nada mais são do que arquivos compactados contendo uma estrutura de subdiretórios e arquivos de classe Java correspondentes a um ou mais pacotes de classes Java, eventualmente incluindo alguns outros tipos de arquivos (até mesmo o próprio código fonte), que podem ser tratados como recursos de aplicações. 

Embora os arquivos JAR sejam capazes de agrupar classes relacionadas, sua organização possui algumas limitações:
  • contratos e dependências explicitas entre os arquivos JAR componentes de uma aplicação;
  • encapsulamento fraco dos elementos contidos nos JARs.

Além disso, o mecanismo de carregamento de classes baseado nos arquivos JAR não impede, restringe ou sequer notifica que múltiplas versões de um mesmo JAR são encontradas no classpath, levando a efeitos indesejáveis e imprevisíveis, um problema sério conhecido como Jar Hell.

Isso sem contar com aplicações que são corretamente compiladas, mas falham em tempo de execução por conta de classes não encontradas (JARs ausentes) ou exibem erros decorrentes de versões incorretas de certas classes (JARs inadequados).

Com estas limitações em mente, os projetistas do Java criaram uma nova construção na linguagem para estabelecer uma nova unidade de modularização que pudesse superar os problemas existentes com os JARs, a qual foi denominada (sem muita criatividade) como module. 

O novo artefato: module

No Java 9, como resultado do projeto Jigsaw, é possível a criação de módulos. Embora os módulos sejam também arquivos JAR, eles são dotados de três propriedades obrigatórias e fundamentais que, explicitamente indicam:
  • o nome do módulo, que é sua identificação (única);
  • suas dependências, isto é, o que este módulo requer; e
  • a definição de sua Application Programming Interface (API), ou seja, o que este módulo fornece/exporta.


O nome do módulo, embora possa ser arbitrário, deve ser único. Por isso é recomendado o uso do esquema conhecido de denominação de pacotes da convenção Java, ou seja, sua URL invertida, tais como org.apache.commons.iocom.google.guava, ou br.gov.sp.cps.fatec.ads.

As dependências de um módulo, ou seja, suas necessidades, são expressas por uma lista de pacotes que precisam ser exportados por um ou mais módulos. Observe que o nome dos módulos exportadores não é necessário, mas apenas os pacotes exigidos. As classes necessárias nestes módulos devem ser públicas.

O módulo também deve indicar os pacotes que são exporta, ou seja, sua API pública. Deve ser notado que classes existentes no módulo, não serão acessíveis externamente se seus pacotes não forem exportados, mesmo que sejam públicas.

Também existem outras duas propriedades, cujo uso esperado não é tão comum, mas que permitem especificar:
  • os serviços consumidos pelo módulo e
  • os serviços providos pelo módulo.

Estas propriedades são usadas apenas quando serviços são providos e consumidos por meio da interface java.util.ServiceLoader.

Estas informações são codificadas num descritor de módulo (module descriptor) que é criado como um arquivo module-info.java, o qual é compilado como module-info.class e empacotado junto os demais elementos do módulo, de modo que a JVM possa recuperar tais informações, permitindo o carregamento de classes e outros recursos, tratando-os como um módulo.

A informação de módulo, contida no arquivo module-info.java, tem a estrutura que segue:

module MODULE_NAME {
   requires MODULE_NAME_1;
   requires MODULE_NAME_2;

   requires MODULE_NAME_N;

   exports PACKAGE_NAME_1;
   exports PACKAGE_NAME_2;

   exports PACKAGE_NAME_N;
}


Como cada módulo contém a informação que necessita dos demais, a questão do Jar Hell se torna coisa do passado, com mostra a figura que segue.


Os módulos (modules) são a construção de mais alto nível no novo sistema de modularização implementado na linguagem, englobando os pacotes (packages). Os desenvolvedores podem organizar seu código em módulos, declarando as dependências existentes entre eles nas definições contidas no arquivo module-info.java.

A nova acessibilidade

Com o novo sistema de modularidade do Java introduzido pelo Jigsaw, a acessibilidade dos tipos existentes sofre uma mudança considerável.

A acessibilidade do Java 8 e das versões anteriores era definida pelos níveis:
  • público, 
  • default
  • protegido e 
  • privado
Tais níveis são indicados por meio dos especificadores de acesso public, protected e private, sendo que a omissão da indicação explícita do nível de acesso é entendida como o nível default, que não tem especificador próprio.

Em resumo, até o Java 8, qualquer tipo público presente no classpath seria acessível por qualquer outro tipo.

No Java 9, com o Jigsaw temos:
  • público para todos que tem acesso ao módulo (exports);
  • público para módulos específicos (comentado na parte 2 do post);
  • público apenas para os demais tipos pertencentes ao módulo, mas não outros externos ao módulo;
  • default;
  • protegido; e
  • privado.
Assim, o novo esquema de modularidade inclui dois níveis extras de acesso público, cujas restrições são associadas ao novo artefato module.

Benefícios esperados

O Jigsaw desempenha um papel central no futuro da plataforma Java, pois provê a base para sua evolução, ao mesmo tempo que garante compatibilidade com o esquema de modularidade anterior, baseado exclusivamente em pacotes.

De fato, um programador pode continuar a utilizar a versão 9 do Java como se o Jigsaw e o novo sistema de modularidade nem existissem, pois sua utilização é completamente transparerente.

Isto mostra que o projeto alcançou um ótimo consenso, consolidando um passo na direção de uma plataforma aderente a boas práticas do projeto de software; compatível com arquiteturas de software mais modernas (microserviços -- microservices); de distribuição melhorada (compartimentos -- containers); e otimizada, pois o novo JRE mínimo tem pouco mais de 15MB.

Na próxima parte deste post será tratada a implementação do Jigsaw na plataforma, ou seja, como estão organizados os módulos a partir da versão 9. A parte III inclui um exemplo de como construir uma aplicação modular.

Para Saber Mais