O Apache Maven é uma ferramenta de automação e gerenciamento de projetos que fornece uma forma padronizada de automação, construção(build) e publicação de aplicações. O Maven é uma ferramenta bastante flexível permitindo estender suas funcionalidades nativas através da adição de plugins.

Quando falamos no Maven a funcionalidade mais lembrada é seu gerenciador de dependências, o que realmente é o que mais facilita a vida do desenvolvedor, mas o Maven possui outras funcionalidade como facilitar a compilação do código e seu empacotamento(JAR, WAR, etc), facilitando a execução de testes unitários e seus relatórios, gerando documentação, etc.

Ainda estimula boas práticas de desenvolvimento como a separação de classes de teste das classes do sistema, uso de convenções de nomes e diretórios, etc.

Alguns conceitos importantes do Maven

Antes de começarmos propriamente com a instalação e uso do maven devemos conhecer alguns conceitos que serão importantes.

Artefato

Um artefato é geralmente um arquivo JAR, mas pode ser de outro tipo, que fica armazenado em um repositório que podemos utilizar como dependência em nossos projetos, inclusive nosso projeto após compilado é considerado um artefato.

Um Artefato é identificado através dos seguintes elementos:

  • Grupo: normalmente o endereço de um domínio ao contrário, por exemplo com.google, org.apache, info.botecodigital, etc.
  • Identificador único do artefato: normalmente o nome do projeto que se está criando ou nome da biblioteca.
  • Número da versão: a versão do projeto, por exemplo: 1.3, 5.2.4. Se o sufixo -SNAPSHOT estiver presente( como 1.8-SNAPSHOT ) significa que o projeto ainda está em desenvolvimento e pode ser alterado.
  • Tipo do projeto:  jar, war, ear.
Repositório Local

Um repositório é um diretório no qual os artefatos baixados de um repositório remoto são armazenados em seu computador, é onde o maven roda. Os artefatos são baixados nele utilizando o comando install do maven, ou quando utilizamos um artefato em nosso projetos e o próprio maven realiza o download automaticamente.

Repositório Remoto

Consiste em uma aplicação, normalmente disponibilizada de forma pública na internet onde criadores de bibliotecas e frameworks, disponibilizam seus artefatos para o publico utilizar. Também é possível criar repositórios remoto privados de empresas dentro da sua intranet utilizando programas como Artifactory.

O maven já vem com um repositório central já configurado onde as dependências são buscadas quando as declaramos em nosso projeto, mas é possível adicionar mais repositórios remotos.

Arquivo POM

O arquivo pom.xml é o arquivo de configuração central do maven e está presente em todo projeto maven. É nele que declaramos as dependências do nosso projeto, os repositórios remotos adicionais, uso de plugin do maven entre outras configurações importantes.

Ciclo de Vida, fases e Goals

O Maven é construído sobre o conceito de ciclo de vida que define o processo de construção e distribuição de um determinado artefato. Existem três ciclos de vida default, clean e site. Os ciclos são compostos por fases que são executadas sequencialmente. Quando é executado um comando para uma fase, todas as anteriores são executadas em sequencia até a fase alvo ser atingida.

Cada ciclo de vida possui várias fases, vamos ver algumas:

  • Ciclo clean: A fase clean remove todos arquivos e diretórios criados pelo Maven como parte do build.
  • Ciclo site: A fase site gera a documentação do projeto.
  • Ciclo default: Compreende um conjunto de fases para lidar com o build e deploy do projeto, sendo elas:
    • validate: valida se o projeto é válido e correto e se todas as informações estão disponíveis
    • compile : compila o código fonte do projeto
    • test: roda testes unitários do projeto utilizando um framework de teste.
    • package: empacota o código compilado no seu formato de ditribuição como JAR.
    • verify: roda verificações nos resultados dos testes de integração para garantir que os critérios de qualidade sejam atendidos.
    • install: instala o pacote dentro do repositório local, para utilizar como dependência de outros projetos localmente.
    • deploy : copia o pacote final para o repositório remoto configurado.

Então se executarmos o maven para a fase install, ele irá automaticamente inferir o ciclo de vida default e executar as fases: validade, compile, test, package , verify e finalmente install.

A lista de fases de cada ciclo de vida pode ser vista aqui.

Agora temos um pequena noção dos conceitos vamos para a prática.

Instalando o maven

No Linux instalar o maven é bastante simples, no Debian podemos utilizar o apt-get

sudo apt-get install maven

No windows é um pouco mais chato primeiro você deve fazer o download na página do projeto.

Página de download do Maven
Página de download do Maven

Após o download descompacte o conteúdo do zip para um diretório de sua escolha. Logo após, para utilizarmos o comando mvn na linha de comando devemos adicionar seu caminho nas variáveis de ambiente. Para isso, no windows 10, entre em Painel de Controle -> Sistema e Segurança -> Sistema. Acesse Configurações avançadas de Sistema, depois Variáveis de Ambiente, em seguida Path e Editar, e adicione uma nova linha com o endereço da basta bin no diretório no qual descompactou o maven.

Sequencia de telas para configurar para configurar variáveis de ambiente
Sequencia de telas para configurar para configurar variáveis de ambiente

Criando um projeto Maven

Para criar um projeto no maven utilizamos algum archetype, eles são uma espécie de projeto pré-definidos com algumas configurações já prontas. Para criar um projeto podemos utilizar o seguinte comando .

mvn archetype:generate

Ele irá realizar alguns downloads se necessário e listará um longa lista de archetypes disponíveis, compreendendo a iniciação de vários tipos de projetos e diversos frameworks. Após a listagem irá nos solicitar a escolha de um archetype por número associado, na tela de exemplo abaixo ele já sugere o archetype org.apache.maven.archetypes:maven-archetype-quickstart pelo seu número 1593 na listagem.

Lista de archetypes
Lista de archetypes

Como pode ter imaginado, não é muito rápido encontrar o número de um archetype nesta longa lista, então se já soubermos o archetype do tipo de projeto que queremos criar podemos utilizar o seguinte comando.

mvn archetype:generate -DarchetypeArtifactId=maven-archetype-quickstart
Criação de Projeto com archetype
Criação de Projeto com archetype

Como vimos acima utilizamos o parâmetro -DarchetypeArtifactId para especificar o archetype escolhido, sem ser necessário a listagem. Durante a criação do projeto o maven solicitou as informações de groupId, artifactId, package, informações que vimos no inicio do post. Estas informações são utilizadas para criar o arquivo pom.xml. Segue abaixo o arquivo criado.

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>info.botecodigital</groupId>
  <artifactId>exemplo-projeto-maven</artifactId>
  <packaging>jar</packaging>
  <version>1.0-SNAPSHOT</version>
  <name>exemplo-projeto-maven</name>
  <url>http://maven.apache.org</url>
  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>3.8.1</version>
      <scope>test</scope>
    </dependency>
  </dependencies>
</project>

Além do arquivo pom.xml, o maven também cria uma estrutura de diretório, esta estrutura pode variar um pouco dependendo do tipo de projeto, mas os principais são:

  • src/main/java: onde fica o código-fonte do projeto.
  • src/main/resources: arquivos auxiliares do sistema, como .properties, XMLs e configurações.
  • src/main/webapp: no caso de uma aplicação web, os arquivos JSP, HTML, JS, CSS vão aqui, incluindo o web.xml.
  • src/test/java: onde os teste unitários ficam, eles são executados automaticamente com o framework de teste configurado(por padrão utiliza JUnit que já vem como dependência do projeto).
  • src/test/resources: arquivos auxiliares usados nos testes, como arquivo de propriedades e configurações separados para os testes.
  • pom.xml: é o arquivo que concentra as configurações do seu projeto.
  • target: é o diretório para onde vai tudo que é construído pelo maven, como arquivos compilados, JARs, WARs, JavaDoc, entre outros.

Estes diretórios são uma convenção do maven e uma boa prática de separação dos artefatos do projeto, e ele trabalha automaticamente com eles, mas é possível utilizar outra estrutura para, por exemplo, utilizar o maven em um projeto já criado e com estrutura diferente. Veja como trocar o diretório dos arquivos fontes:

<project>
    ...
  <build>
    <sourceDirectory>fontes</sourceDirectory>
    <directory>bin</directory>
    <testSourceDirectory>testesdir</testSourceDirectory>
  </build>
    ...
</project>

No trecho acima trocamos o diretório src/main/java para fontes, o diretório target para bin, e src/test/java para testesdir.

Adicionando uma dependência

Como falado no inicio do post, o principal recurso do maven é o gerenciamento de dependências. É muito comum em projetos necessitarmos de uma biblioteca e esta biblioteca necessitar de outras bibliotecas. Gerenciar isto manualmente pode ser um pesadelo, então o maven fornece um mecanismo de gerenciamento automático onde podemos declarar um dependência ficando a cargo do maven baixar ela e outras bibliotecas das quais esta necessita(dependências transitivas).

Para adicionar uma dependência primeiro temos que saber suas informações, um lugar bom para pesquisar é o MVNRepository que é um índice das dependências disponíveis no repositório do maven.

Nele podemos pesquisar pela biblioteca que desejamos e pegar o código/configuração pronto para inserir no pom.xml. Para um exemplo simples vamos inserir a dependência da biblioteca commons-lang3. Então primeiramente vamos buscá-lo.

Busca pela biblioteca log4j
Busca pela biblioteca log4j
Selecionando a versão da biblioteca
Selecionando a versão da biblioteca
Pegando o código de dependência para colocar no pom.xml
Pegando o código de dependência para colocar no pom.xml

Com o código em mãos adicione no seu pom.xml, as configurações das dependências devem ser adicionada dentro da marcação dependencies.

<project>
...
<dependencies>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>3.8.1</version>
        <scope>test</scope>
    </dependency>

    <dependency>
        <groupId>org.apache.commons</groupId>
        <artifactId>commons-lang3</artifactId>
        <version>3.10</version>
    </dependency>
  </dependencies>
...
</project>

Para configurar uma dependência é necessários três informações, seu groupId, artifactId e version. Outra configuração que pode ser fornecida é o scope. que pode ter os seguintes valores:

  • compile*: Dependência necessária para compilação. Significa assim que é necessária para os testes e para runtime (projeto rodando). *É o scope padrão das dependências se nenhum for informado.
  • test: Dependência necessária somente para testes. Significa que esta dependência só é utilizada no código de teste, portanto não necessário para o runtime.
  • runtime: Dependência não necessária durante a compilação, mas para rodar o projeto.
  • provided: Dependência necessária para compilação e runtime, mas não empacotada com o projeto para distribuição. Esta dependência será fornecida pelo usuário. Um exemplo desta dependência é servlet-api, em geral fornecida pelos servidores de aplicação.
  • system: Similar a provided, porém necessita ser indicado explicitamente a localização do arquivo JAR. Pouco utilizada.
  • import: Dependência utilizada para centralizar projetos com vários módulos.

Com a dependência adicionada em nosso projeto vamos usá-lo em uma classe:

package info.botecodigital;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

public class App 
{
    private static final Logger logger = LogManager.getLogger(App.class);
    
    public static void main( String[] args )
    {
        logger.error("Algo de errado não está certo");
    }
}

Podemos então utilizar o goal mvn install para criar um jar do nosso projeto.

mvn install

Após a execução deste comando você irá encontra o arquivo exemplo-projeto-maven-1.0-SNAPSHOT.jar dentro da pasta target. E deve estar se perguntando, onde estão as dependências baixadas? Por padrão o maven não as copia para o diretório target, mas podemos utilizar o comando dependency:copy-dependencies para forçar ele a copiar as dependências para a pasta dependency

mvn install dependency:copy-dependencies

Após isso basta executar com o comando

java -cp target\exemplo-projeto-maven-1.0-SNAPSHOT.jar;target\dependency\* info.botecodigital.App

Outra forma seria criar um fat jar, com as dependências dentro dele, para isso temos que utilizar um plugin do maven, o maven-assembly-plugin, que deve ser adicionado dentro da marcação plugins, e esta dentro da marcação build, veja como ficaria:

<project>
  ...
  <dependencies>
  ...
  </dependencies>
  ..
  <build>
    <plugins>
      <plugin>
        <artifactId>maven-assembly-plugin</artifactId>
        <configuration>
          <archive>
            <manifest>
              <mainClass>info.botecodigital.App</mainClass>
            </manifest>
          </archive>
          <descriptorRefs>
            <descriptorRef>jar-with-dependencies</descriptorRef>
          </descriptorRefs>
        </configuration>
      </plugin>
    </plugins>

  </project>

Como vimos adicionamos o plugin ao nosso pom.xml informando seu artifactId(maven-assembly-plugin), configurando o manifest do pacote para que a classe main seja info.botecodigital.App, deixando assim o jar executável. Na configuração descriptorRef definimos que o jar deverá conter as dependências com a opção jar-with-dependencies.

Agora para gerar o jar utilizamos o seguinte comando:

mvn compile assembly:single

Ele irá gerar um outro jar com o nome de exemplo-projeto-maven-1.0-SNAPSHOT-jar-with-dependencies.jar, que podemos executar com o comando:

java -jar target\exemplo-projeto-maven-1.0-SNAPSHOT-jar-with-dependencies.jar

Podemos visualizar todas as dependências de um projeto utilizando o comando:

mvn dependency:list

Segue um exemplo para um projeto com um pouco mais de dependências utilizando o archetype: org.springframework.boot:spring-boot-sample-simple-archetype

Lista de dependências de um projeto spring boot
Lista de dependências de um projeto spring boot

Também podemos ver a árvore de dependências do projeto, isto ajuda a entender quais dependências dependem de quais. Fazemos isso com o seguinte comando:

mvn dependency:tree
Árvore de dependências de um projeto Spring aboot
Árvore de dependências de um projeto Spring boot
Executando testes no maven

O maven como você já viu acima, também executa os teste unitário, para isto basta criá-los dentro da pasta de teste src/test/java. Vejamos como exemplo duas classes de teste.

package info.botecodigital;
import junit.framework.TestCase;
public class AppTest extends TestCase{ 
    public void testApp1(){
        int x = 2+ 2;
        assertEquals( 4 , x );
    }
    public void testApp2(){
        String nome = "Rodrigo";
        assertEquals( "Rodrigo" , nome );
    }
}
package info.botecodigital;
import junit.framework.TestCase;
public class App2Test extends TestCase{  
    public void testApp4(){
        int x = 10 + 10;
        assertEquals( 19 , x );
    }
}

Tanto a classe AppTest e App2Test estão no diretório src/test/java/info/botecodigital. Para excuta-las basta o comando:

mvn test
Execução de teste unitário pelo maven
Execução de teste unitário pelo maven

Lembrando que para executar automaticamente os testes, as classes devem seguir um determinado padrão de nome: "**/Test*.java", "**/*Test.java", "**/*Tests.java", "**/*TestCase.java".

Caso nossas classes de teste não sigam o padrão acima devemos inclui-las explicitamente para o teste com:

mvn -Dtest=AppTest,App2Test test

Bom para finalizar outra configuração importante para é qual versão do java iremos utilizar para compilar nosso projeto isto pode ser feito através da marcação de properties:

<properties>
    <maven.compiler.source>1.8</maven.compiler.source>
    <maven.compiler.target>1.8</maven.compiler.target>
</properties>

O maven pode ser meio confuso no inicio, mas ele agrega muita organização e permite um gerenciamento mais fácil das dependências de um projeto, embora mesmo assim possa ocorrer alguns problemas como duas dependências exigirem versões diferentes de uma biblioteca.

Ele também é suportados ela maioria das IDE, permitindo realizar suas operações diretamente por ela sem ser necessário o uso da linha de comando.

É claro que o maven ainda disponibiliza vários outros recursos interessantes e muito mais configurações, mas espero ter passado o básico para poder começar a utilizar. Então T++ pessoal.