pátek 1. října 2010

Hygiena kolem projektových souborů Mavenu

Když jsem si tu v předchozím příspěvku postěžoval na to, jak je složité znovu a znovu rozjíždět java projekty z pohledu setupu, měl jsem trochu pocit, že to jsou jenom brekot nad rozlitým mlékem. Abych se vykoupil, rozhodl jsem se dát k dobru praxí ověřená pravidla, týkající se hygieny kolem projektových souborů Mavenu. Samozřejmě pokud mě chcete doplnit vlastními tipy, rád si je poslechnu v komentářích.

Závislosti

Scope

První věc, o které přemýšlejte u každé závislosti je její scope. Hodně problémů projevujících se tím, že se divíte, kde se vám sakra bere tolik závislostí ve WARu je způsobeno tím, že u závislostí není správně scope. Sekundárním efektem je nabobtnalá konfigurace WAR případně Assembly pluginu, která maže tyto závislosti z výsledné binární distribuce.

Pro závislosti, na kterých máte runtime závislost a nepotřebujete je pro kompilaci použijte scope runtime. Pro závislosti potřebné pouze k testům použijte scope test a pro závislosti, které vám poskytuje běhové prostředí, např. Servlet API, použijte scope provided. Více informací o Dependency scope.

Centrální místo pro závislostí

Všechny závislosti a to včetně vašich vlastních definujte v dependencyManagement (neřiká nic o jejích použití) sekci vašeho centrálního POMu, ze kterého dědí všechny ostatní POMy. Pokud žádný takový nemáte, zaveďte si jej. Závislosti uvádějte vždy včetně defaultního scope. Tímto použitím získáte několik výhod. Především všechny závislosti máte na jednom místě.

V dceřiných POMech potom používáte závislosti identifikované jenom groupId a artifactId. Změna verze knihovny je potom zásah v jednom souboru. Další výhodou je právě určení verze knihovny. Díky tomuto použití určujete verzi pro všechny moduly a to včetně tranzitivních závislostí. Nestane se vám, že by se vám dostala jiná verze. Více informací o Dependency Managementu

<dependencyManagement>
 <dependencies>   
  <dependency>
   <groupId>cz.sweb.pichlik</groupId>
   <artifactId>example</artifactId>
   <version>0.1-SNAPSHOT</version>
  </dependency>
  <dependency>
   <groupId>junit</groupId>
   <artifactId>junit</artifactId>
   <version>4.8.1</version>
   <scope>test</scope>
  </dependency>
<dependencies>
  <dependency>
   <groupId>junit</groupId>
   <artifactId>junit</artifactId>
  </dependency>

  <dependency>
   <groupId>cz.sweb.pichlik</groupId>
   <artifactId>example</artifactId>
  </dependency>

Sdílené závislosti

Sdílené závislosti jsou ty, které nadefinujete v sekci dependencies rodičovského POMu. Doporučuji vám se jim obloukem vyhnout kromě třech výjimek - logování, testy a validaci vstupních argumentů. To je minimální průnik všech modulů.

Vyloučení závislostí

Pokud chcete nějakou závislost vyloučit, protože není potřeba, typicky tu zatažená nějakou špatně definovanou 3rd party závislostí, je nejlepším způsobem definovat tuto závislost v dependencyManagement se scopem provided. Samozřejmě tento trik aplikujte pouze za předpokladu, že nikdo tu závislost opravdu nepotřebuje.

 
<dependency>
 <groupId>commons-logging</groupId>
 <artifactId>commons-logging</artifactId>
 <version>1.1.1</version>
 <scope>provided</scope> 
</dependency>

Analýza závislostí

Pokud se chcete podívat na zoubek závislostem, například chcete zkontrolovat jaké závislosti má váš modul, použijte příkaz mvn dependency:tree. Tento příkaz zobrazí původ všech závislostí v textové podobě.

Pokud chcete naopak zjistit, jestli váš modul nějaké závislosti používá, ale nemá je deklarované (vedlejší efekt tranzitivních závislostí), případně naopak, nepoužívá, ale má nadeklarované, pak použijte příkaz mvn dependency:analyze -DignoreNonCompile. Tento příkaz analyzuje přímo byte kód, proto je potřeba, aby byla závislost v lokální Maven cache (prostě jenom mvn install -DskipTests).

Běžné věci

  • Poukud používáte name element v POMu, pak je dobré, aby měl stejnou hodnotu jako artifactId. Důvod je veskrze praktický, Maven vypisuje po každém příkazu summary provedení za jednotlivé moduly. Pokud příkaz pustíte v multimodule projektu a name necháte na lidové tvořivosti, bude se vám těžko dohledávat, o který modul vlastně šlo.
  • S tím souvisí dobrý návyk a to udržovat artifactId unikátní.
  • Nedávejte souboru specifické pro IDE do verzovacího systému. Většina IDE co stojí v Jave za zmínku totiž dokáže číst přímo POMy. Specifické soubory pro IDE jsou většinou nějak spjaté s konkrétním fyzickým umístěním a použitou verzí IDE a proto nepřenosné.
  • Pokud vznikají vazby mezi moduly, které nedávají smysl, což se většinou ještě projevuje rovněž tím, že někde vybublají nechtěné závislosti, je to tím, že vývojáři jsou líní nebo se bojí dělat specializované moduly. Typickým příkladem je modul, kterému pracovně říkám test-support. To je modul, do kterého dávám podporu pro testy jako například mock objekty, specializované předky apod. Tento modul má potom scope test, to znamená, že jeho závislosti nikam neprobublají.