Tuesday, October 16, 2018

Modular Projects no NetBeans 9.0

NetBeans 9 é a primeira release desde que a IDE passou a fazer parte da família Apache, cujo nome oficial agora é Apache NetBeans.

O NetBeans 9 também é a primeira versão desta IDE a conter suporte para o sistema de modularização da plataforma java, recurso que foi incluido a partir da versão 9 do java. Um módulo na plataforma Java consiste em:
  • Uma coleção de pacotes
  • Uma lista de quais pacotes estarão acessíveis para outros módulos
  • Uma lista de todos os módulos dos quais o módulo atual depende
Um módulo, portanto, pode controlar seletivamente quais pacotes estão disponíveis, isso reforça o encapsulamento e o gerenciamento de dependências, evitando problemas recorrentes na configuração do class path de seu projeto, tais como classes duplicadas ou não encontradas (ClassNotFoundException).



Java Modular Project

O NetBeans 9 apresenta uma nova opção de projeto, que é o Java Modular Project.
Caso queira modularizar sua aplicação, você deve escolher essa opção ao criar um novo projeto. 


Vamos criar um novo Java Modular Project que faz uso do módulo java.xml.bind, popularmente conhecido como JAXB API, ou seja, nosso projeto de exemplo converte uma classe java em um arquivo xml (você pode conferir todos os módulos disponíveis no javadoc do Java 10). Clique em Next e dê o nome ao novo projeto de JavaModular.

Clique com o botão direito na raiz do projeto JavaModular, navegue por new / module:


Dê o nome ao novo módulo de modular.test. (nomes de módulo devem ser globalmente únicos, a melhor forma de garantr isso é utilizar a mesma abordagem para nomeação de pacotes). O NetBeans já cria automaticamente o arquivo de configuração obrigatório em todos os módulos, chamado de module-info.java.

Clique com o botão direito no módulo que você acabou de criar e adicione um novo pacote chamado com:


Repare na estrutura do projeto. No pacote com vamos criar a classe Pais, que será convertida em um arquivo XML:
package com;

import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;

@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class Pais {
 
 @XmlElement
 private String nome;
 @XmlElement
 private double area;

 public Pais() {
 }

 public Pais(String nome, double area) {
  this.nome = nome;
  this.area = area;
 }
    //getters e setters...

Ao criar as delcarações de import para os pacotes javax.xml, ocorre um erro de compilação, package javax.xml.bind.annotation is not visible. Isso porque esses pacotes estão em um módulo separado. Precisamos declarar explicitamente no arquivo module-info que nosso módulo modular.test precisa desses pacotes, os quais pertencem ao módulo java.xml.bind. Para isso utilizamos a palavra chave requires dentro do arqivo de configuração do módulo:

module modular.test{
 
    requires java.xml.bind;

}

Agora no pacote com crie a classe Main na qual instanciaremos um objeto da classe Pais:
package com;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;

public class Main {

 public static void main(String[] args) throws JAXBException {
 
  Pais brasil = new Pais("Brasil", 8_600_000);
  JAXBContext context = JAXBContext.newInstance(Pais.class);
  Marshaller m = context.createMarshaller();
  m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
  m.marshal(brasil, System.out);
 }
}

Até aqui vimos claramente como a modularização reforça o encapsulamento. Um módulo só pode acessar pacotes explicitamente exportados por outros módulos, ou seja, só conseguimos usar as classes do pacote java.xml.bind porque o módulo java.xml exporta tais pacotes. Por exemplo, se quisermos que o pacote que acabamos de criar com esteja disponível para uso em outros módulos, então no arquivo module-info adicionamos a declração exports para esse pacote:

 exports com;

Reflection API em Módulos

Outro ponto interessante a notar é que frameworks de mapeamento como o JAXB, que estamos usando neste exemplo, ou o JPA, que mapeia classes Java para tabelas no banco de dados, entre outros,  utilizam a API Reflection para fazer o mapeamento das propriedades das classes, mesmo que essas propriedades sejam privadas. Em um ambiente modular, tentar acessar campos privados via reflection não é 100% garantido. Sim, isso significa que um sistema desenvolvido no Java 8 (ou mais antigo) que faz uso de APIs como JAXB, JPA ou outros frameworks que utilizam extensivamente a API Reflection, pode não rodar no ambiente Java 9 (ou mais novo).

Tente executar a classe Main e comprove. Uma exceção é lançada porque o JAXB tenta acessar os campos privados da classe Pais via reflection.

Para liberar o acesso via reflection não basta exportar sua classe, é necessário utilizar a declaração opens para determinada classe ou pacote. O arquivo module-info portanto deve ficar como segue:
module modular.test {
    exports com;
    requires java.xml.bind;
    opens com;
}

Agora sim podemos executar a classe Main. Ela imprime na tela a o objeto Pais no formato xml:
<?xml version="1.0" encoding="UTF-8"?>
<pais>
    <nome>Brasil</nome>
    <area>8600000.0</area>
</pais>

         

No comments:

Post a Comment