Aplicativo Maven de múltiplos módulos com módulos Java
1. Visão geral
OJava Platform Module System (JPMS) adiciona mais confiabilidade, melhor separação de interesses e encapsulamento mais forte para aplicativos Java. No entanto, não é uma ferramenta de construção, portanto,it lacks the ability for automatically managing project dependencies.
Claro, podemos nos perguntar se podemosuse well-established build tools, like Maven or Gradle, em aplicações modularizadas.
Na verdade, nós podemos! Neste tutorial,we’ll learn how to create a multi-module Maven application using Java modules.
2. Encapsulando módulos Maven em módulos Java
Comomodularity and dependency management are not mutually exclusive concepts in Java,, podemos integrar perfeitamente oJPMS, por exemplo, com o Maven, aproveitando assim o melhor dos dois mundos.
Em um projeto Maven multimódulo padrão, adicionamos um ou mais módulos Maven filho, colocando-os na pasta raiz do projeto e declarando-os no POM pai, na seção<modules>.
Por sua vez, editamos o POM de cada módulo filho e especificamos suas dependências por meio das coordenadas <groupId>, <artifactId>e <version> padrão.
O mecanismoreactor no Maven - responsável por lidar com projetos multimódulos - cuida de construir todo o projeto na ordem certa.
Nesse caso, usaremos basicamente a mesma metodologia de design, mas com uma variante sutil, porém fundamental:we’ll wrap each Maven module into a Java module by adding to it the module descriptor file,module-info.java.
3. O Módulo Maven Pai
Para demonstrar como a modularidade e o gerenciamento de dependência funcionam bem juntos, construiremos um projeto Maven multimódulo de demonstração básico,whose functionality will be narrowed to just fetching some domain objects from a persistence layer.
Para manter o código simples, usaremos umMap simples como a estrutura de dados subjacente para armazenar os objetos de domínio. Obviamente, podemos facilmente mudar ainda mais adiante para um banco de dados relacional completo.
Vamos começar definindo o módulo pai Maven. Para fazer isso, vamos criar um diretório raiz do projeto chamado, por exemplo,multimodulemavenproject (mas poderia ser qualquer outra coisa), e adicionar a ele o arquivopom.xml pai:
Há alguns detalhes que devem ser observados na definição do POM pai.
Em primeiro lugar, como estamos usando Java 11,we’ll need at least Maven 3.5.0 on our system, as Maven supports Java 9 and higher from that version onward.
Como podemos ver, o móduloEntity não tem nenhuma dependência com outros módulos, nem requer artefatos Maven adicionais, pois inclui apenas a classeUser.
Agora, precisamosencapsulate the Maven module into a Java module. Para conseguir isso, vamos simplesmente colocar o seguinte arquivo descritor de módulo (module-info.java) no diretórioentitymodule/src/main/java:
Por fim, vamos adicionar o módulo filho do Maven ao POM pai:
entitymodule
4.2. Módulo Mavendaomodule
Vamos criar um novo módulo Maven que conterá uma interface simples. Isso é conveniente para definir um contrato abstrato para buscar tipos genéricos da camada de persistência.
Na verdade, há um motivo muito convincente para colocar essa interface em um módulo Java separado. By doing so, we have an abstract, highly-decoupled contract, which is easy to reuse in different contexts. No núcleo, esta é uma implementação alternativa deDependency Inversion Principle, que produz um design mais flexível.
Portanto, vamos criar a estrutura de diretóriodaomodule/src/main/java/com/example/dao no diretório raiz do projeto e adicionar a interfaceDao<T>:
public interface Dao {
Optional findById(int id);
List findAll();
}
O novo módulo não requer outros módulos ou artefatos, então vamos apenas embrulhá-lo em um módulo Java. Vamos criar o descritor do módulo no diretóriodaomodule/src/main/java:
A seguir, vamos definir o módulo Maven que contém uma implementação da interfaceDao.
No diretório raiz do projeto, vamos criar a estrutura de diretóriouserdaomodule/src/main/java/com/example/userdao e adicionar a seguinte classeUserDao:
public class UserDao implements Dao {
private final Map users;
// standard constructor
@Override
public Optional findById(int id) {
return Optional.ofNullable(users.get(id));
}
@Override
public List findAll() {
return new ArrayList<>(users.values());
}
}
Simplificando, a classeUserDao fornece uma API básica que nos permite buscar objetosUser da camada de persistência.
Para manter as coisas simples, usamos aMap como a estrutura de dados de apoio para persistir os objetos de domínio. Claro, é possível fornecer uma implementação mais completa que usa, por exemplo,Hibernate’s entity manager.
Nesse caso, as coisas são um pouco diferentes, pois o módulouserdaomodule requer os módulos entitymoduleedaomodule. É por isso que os adicionamos como dependências no arquivopom.xml.
Ainda precisamos encapsular esse módulo Maven em um módulo Java. Então, vamos adicionar o seguinte descritor de módulo no diretóriouserdaomodule/src/main/java:
Por fim, precisamos adicionar este novo módulo ao POM pai:
entitymoduledaomoduleuserdaomodule
Em uma visualização de alto nível, é fácil ver quethe pom.xml file and the module descriptor play different roles. Mesmo assim, eles se complementam muito bem.
Digamos que precisamos atualizar as versões dos artefatos entitymoduleedaomodule Maven. Podemos fazer isso facilmente sem precisar alterar as dependências no descritor do módulo. Maven cuidará de incluir os artefatos certos para nós.
Da mesma forma, podemos alterar a implementação do serviço que o módulo fornece modificando a diretiva“provides..with” no descritor do módulo.
Ganhamos muito quando usamos os módulos Maven e Java juntos. The former brings the functionality of automatic, centralized dependency management, while the latter provides the intrinsic benefits of modularity.
4.4. Módulo Mavenmainappmodule
Além disso, precisamos definir o módulo Maven que contém a classe principal do projeto.
Como fizemos antes, vamos criar a estrutura de diretóriomainappmodule/src/main/java/mainapp no diretório raiz e adicionar a ela a seguinte classeApplication:
public class Application {
public static void main(String[] args) {
Map users = new HashMap<>();
users.put(1, new User("Julie"));
users.put(2, new User("David"));
Dao userDao = new UserDao(users);
userDao.findAll().forEach(System.out::println);
}
}
O métodomain() da classeApplication é bastante simples. Primeiro, ele preenche umHashMap com alguns objetosUser. Em seguida, ele usa uma instânciaUserDao para buscá-los emMap,e os exibe no console.
Além disso, também precisamos definir o arquivopom.xml do módulo:
As dependências do módulo são autoexplicativas. Então, só precisamos colocar o módulo dentro de um módulo Java. Portanto, na estrutura de diretóriomainappmodule/src/main/java, vamos incluir o descritor do módulo:
Finalmente, vamos adicionar este módulo ao POM pai:
entitymoduledaomoduleuserdaomodulemainappmodule
Com todos os módulos filhos Maven já instalados e perfeitamente encapsulados em módulos Java, é assim que a estrutura do projeto se parece:
multimodulemavenproject (the root directory)
pom.xml
|-- entitymodule
|-- src
|-- main
| -- java
module-info.java
|-- com
|-- example
|-- entity
User.class
pom.xml
|-- daomodule
|-- src
|-- main
| -- java
module-info.java
|-- com
|-- example
|-- dao
Dao.class
pom.xml
|-- userdaomodule
|-- src
|-- main
| -- java
module-info.java
|-- com
|-- example
|-- userdao
UserDao.class
pom.xml
|-- mainappmodule
|-- src
|-- main
| -- java
module-info.java
|-- com
|-- example
|-- mainapp
Application.class
pom.xml
5. Executando o aplicativo
Por fim, vamos executar o aplicativo, de dentro de nosso IDE ou de um console.
Como podemos esperar, devemos ver alguns objetosUser impressos no console quando o aplicativo é inicializado:
User{name=Julie}
User{name=David}
6. Conclusão
Neste tutorial, aprendemos de forma pragmáticahow to put Maven and the JPMS to work side-by-side, in the development of a basic multi-module Maven project that uses Java modules.
Como de costume, todos os exemplos de código mostrados neste tutorial estão disponíveisover on GitHub.