No Maven, as dependências são transmitidas indiretamente entre projetos. Por exemplo, se o projeto A depende de B e B depende de C, o projeto A também recebe C automaticamente. Contudo, nem sempre é desejável incluir todas as dependências transitivas. O Maven oferece dois mecanismos para controlar esse comportamento: dependências opcionais e exclusões de dependência.
- Dependências Opcionais
Uma dependência opcional é declarada quando o projeto atual utiliza apenas uma pequena parte de uma biblioteca, e essa biblioteca não é essencial para a maioria dos consumidores. Por exemplo, um fraemwork de persistência que suporta vários bancos de dados (MySQL, PostgreSQL, Oracle) pode marcar cada driver como opcional, pois quem usa o framework normalmente precisa apenas de um deles.
Razões para usar dependências opcionais:
- Economia de espaço em disco e memória;
- Evitar problemas de licenciamento;
- Prevenir conflitos de classpath.
Exemplo de declaração no pom.xml do projeto A (que depende de B):
<project>
...
<dependencies>
<dependency>
<groupId>com.exemplo</groupId>
<artifactId>projeto-b</artifactId>
<version>2.0</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>
</dependencies>
</project>
Quando um terceiro projeto X declara dependência de A, o Maven não incluirá B automaticamente. Caso X precise de funcionalidades específicas de B, ele deve adicionar explicitamente a dependência para B no seu próprio pom.xml.
- Exclusões de Dependência
As exclusões permitem remover dependências transitivas indesejadas. Por exemplo, suponha que A depende de B e B depende de C, mas A não quer usar C. A declaração de dependência de B em A pode excluir C.
Exemplo: A → B, B → C. No pom.xml de A:
<project>
...
<dependencies>
<dependency>
<groupId>com.exemplo</groupId>
<artifactId>projeto-b</artifactId>
<version>2.0</version>
<scope>compile</scope>
<exclusions>
<exclusion>
<groupId>com.exemplo</groupId>
<artifactId>projeto-c</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
</project>
Para dependências em cadeia mais longas, como A → B → D → E, é possível excluir E diretamente na declaração de B (a dependência mais próxima de A). Não é necessário excluir em cada nível intermediário.
Exemplo: Excluindo o artefato E da árvore de dependências de B:
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>com.exemplo</groupId>
<artifactId>projeto-a</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
...
<dependencies>
<dependency>
<groupId>com.exemplo</groupId>
<artifactId>projeto-b</artifactId>
<version>1.0-SNAPSHOT</version>
<exclusions>
<exclusion>
<groupId>com.exemplo</groupId>
<artifactId>projeto-e</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
</project>
Esses mecanismos dão ao desenvolvedor controle fino sobre quais bibliotecas são efetivamente empregadas no projeto final.