ข้อแนะนำ การแปลง java project ไปใช้ maven (ทำ pom คลีนๆ)

Posted on
development

poster

หลังจากผ่านสมรภูมิการแปลง java project ซึ่งเป็น library ที่ที่ทำงานใช้กันมากว่า 5 ปี ของเดิมใช้ ant script ตอนนี้อยากจะใช้มันแบบ maven ถึงจะช้าแต่ก็ดีกว่าไม่ทำนะ ;) เลยมาบันทึกเอาไว้หน่อยว่า สิ่งที่ทำบ่อยๆ ตอนนั้นมีอะไรบ้าง

ใช้ mvn dependency:analyze เพื่อตรวจสอบ dependency สำหรับ project เราว่า

  • เราประกาศตัวที่ใช้ (ใส่ dependency) ครบหรือยัง (Used undeclared dependencies)
  • เราเผลอประกาศตัวไหนที่ไม่ได้ใช้ลงไปใน pom หรือเปล่า (Unused declared dependencies)

ตัวอย่าง

...
[INFO] --- maven-dependency-plugin:2.8:analyze (default-cli) @ example-core ---
[WARNING] Used undeclared dependencies found:
[WARNING]    org.springframework:spring-core:jar:3.1.1.RELEASE:compile
[WARNING]    org.springframework:spring-beans:jar:3.1.1.RELEASE:compile
[WARNING] Unused declared dependencies found:
[WARNING]    org.springframework:spring-test:jar:3.1.1.RELEASE:compile
[WARNING]    org.slf4j:slf4j-log4j12:jar:1.7.0:compile
[WARNING]    log4j:log4j:jar:1.2.14:compile
...

หากเห็น Used undeclared dependencies found ในผลลัพธ์ ควรจะกลับมาประกาศ dependency นั้นใน pom

หากเจอ Unused declared dependencies เราอาจจะเปลี่ยน scope ของ dependency ให้เหมาะสม หรือไม่จำเป็นต้องประกาศ เช่น org.springframework:spring-test:jar:3.1.1.RELEASE:compile เราเปลี่ยน scope เป็น test

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-test</artifactId>
    <version>3.1.1.RELEASE</version>
    <scope>test</scope>
</dependency>

การเอา dependency ออก แนะนำให้ comment เอาไว้ก่อน แล้วทดสอบ compile หรือ package ดู หากมี unit test อยู่ เราจะรู้ได้ทันทีว่า เราจะเอามันออกได้หรือไม่ ถ้าไม่ได้แสดงว่าเราควรเปลี่ยน scope ของมัน

ใช้ mvn dependency:tree อยากตรวจสอบว่า jar แต่ละตัวมีความสัมพันธ์ต่อกันอย่างไร

$ mvn dependency:tree

[INFO] Scanning for projects...
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] Building Example 1.1
[INFO] ------------------------------------------------------------------------
[INFO]
[INFO] --- maven-dependency-plugin:2.8:tree (default-cli) @ example-web ---
[INFO] com.example:example-web:jar:1.1
[INFO] +- javax.servlet:servlet-api:jar:2.5:provided
[INFO] +- org.springframework:spring-context:jar:3.1.1.RELEASE:compile
[INFO] |  +- org.springframework:spring-aop:jar:3.1.1.RELEASE:compile
[INFO] |  +- org.springframework:spring-expression:jar:3.1.1.RELEASE:compile
[INFO] |  \- org.springframework:spring-asm:jar:3.1.1.RELEASE:compile
[INFO] +- org.springframework:spring-core:jar:3.1.1.RELEASE:compile
[INFO] +- org.springframework:spring-beans:jar:3.1.1.RELEASE:compile
[INFO] +- org.springframework:spring-web:jar:3.1.1.RELEASE:compile
[INFO] |  \- aopalliance:aopalliance:jar:1.0:compile
[INFO] +- org.springframework:spring-webmvc:jar:3.1.1.RELEASE:compile
[INFO] |  \- org.springframework:spring-context-support:jar:3.1.1.RELEASE:compile
[INFO] +- javax.persistence:com.springsource.javax.persistence:jar:2.0.0:compile
[INFO] +- org.eclipse.persistence:javax.persistence:jar:2.0.0:compile
[INFO] +- org.freemarker:freemarker:jar:2.3.18:compile
[INFO] +- commons-io:commons-io:jar:1.4:compile
[INFO] +- javax.validation:validation-api:jar:1.0.0.GA:compile
[INFO] +- commons-beanutils:commons-beanutils:jar:1.7.0:compile
[INFO] |  \- commons-logging:commons-logging:jar:1.0.3:compile
[INFO] +- junit:junit:jar:4.10:compile
[INFO] |  \- org.hamcrest:hamcrest-core:jar:1.1:compile
[INFO] +- net.sourceforge.json:com.springsource.net.sf.json:jar:2.2.2:compile
[INFO] |  +- org.apache.commons:com.springsource.org.apache.commons.beanutils:jar:1.7.0:compile
[INFO] |  +- org.apache.commons:com.springsource.org.apache.commons.collections:jar:3.2.0:compile
[INFO] |  +- org.apache.commons:com.springsource.org.apache.commons.logging:jar:1.1.1:compile
[INFO] |  \- net.sourceforge.ezmorph:com.springsource.net.sf.ezmorph:jar:1.0.5:compile
[INFO] +- org.apache.commons:com.springsource.org.apache.commons.lang:jar:2.5.0:compile
[INFO] +- org.hibernate:com.springsource.org.hibernate.validator:jar:4.1.0.GA:compile
[INFO] |  \- javax.validation:com.springsource.javax.validation:jar:1.0.0.GA:compile
[INFO] +- org.mockito:com.springsource.org.mockito:jar:1.8.4:compile
[INFO] |  +- org.objenesis:com.springsource.org.objenesis:jar:1.2.0:compile
[INFO] |  \- org.hamcrest:com.springsource.org.hamcrest.core:jar:1.1.0:compile
[INFO] +- org.slf4j:slf4j-api:jar:1.7.0:compile
[INFO] +- org.slf4j:slf4j-log4j12:jar:1.7.0:compile
[INFO] +- log4j:log4j:jar:1.2.14:compile
[INFO] +- com.example:example-persistence:jar:1.1:compile
[INFO] |  +- org.springframework:spring-tx:jar:3.1.1.RELEASE:compile
[INFO] |  +- org.springframework:spring-jdbc:jar:3.1.1.RELEASE:compile
[INFO] |  +- org.springframework:spring-orm:jar:3.1.1.RELEASE:compile
[INFO] |  +- org.springframework:spring-test:jar:3.1.1.RELEASE:compile
[INFO] |  +- javax.transaction:transaction-api:jar:1.1:compile
[INFO] |  +- org.hibernate:com.springsource.org.hibernate:jar:3.3.2.GA:compile
[INFO] |  |  +- net.sourceforge.cglib:com.springsource.net.sf.cglib:jar:2.2.0:compile
[INFO] |  |  +- org.antlr:com.springsource.antlr:jar:2.7.6:compile
[INFO] |  |  +- org.dom4j:com.springsource.org.dom4j:jar:1.6.1:compile
[INFO] |  |  +- org.jboss.javassist:com.springsource.javassist:jar:3.9.0.GA:compile
[INFO] |  |  +- org.objectweb.asm:com.springsource.org.objectweb.asm:jar:1.5.3:compile
[INFO] |  |  \- org.slf4j:com.springsource.slf4j.api:jar:1.5.6:compile
[INFO] |  +- org.hibernate:hibernate-annotations:jar:3.4.0.GA:compile
[INFO] |  |  +- org.hibernate:ejb3-persistence:jar:1.0.2.GA:compile
[INFO] |  |  +- org.hibernate:hibernate-commons-annotations:jar:3.1.0.GA:compile
[INFO] |  |  +- org.hibernate:hibernate-core:jar:3.3.0.SP1:compile
[INFO] |  |  \- dom4j:dom4j:jar:1.6.1:compile
[INFO] |  |     \- xml-apis:xml-apis:jar:1.0.b2:compile
[INFO] |  +- commons-dbcp:commons-dbcp:jar:1.2.2:compile
[INFO] |  |  \- commons-pool:commons-pool:jar:1.3:compile
[INFO] |  +- org.aspectj:aspectjweaver:jar:1.6.5:compile
[INFO] |  +- org.slf4j:slf4j-jcl:jar:1.4.2:compile
[INFO] |  +- org.hsqldb:hsqldb:jar:1.8.0.10:compile
[INFO] |  \- org.dbunit:dbunit:jar:2.2:compile
[INFO] |     +- junit-addons:junit-addons:jar:1.4:compile
[INFO] |     |  +- xerces:xercesImpl:jar:2.6.2:compile
[INFO] |     |  \- xerces:xmlParserAPIs:jar:2.6.2:compile
[INFO] |     +- poi:poi:jar:2.5.1-final-20040804:compile
[INFO] |     +- commons-collections:commons-collections:jar:3.1:compile
[INFO] |     \- commons-lang:commons-lang:jar:2.1:compile
[INFO] +- com.example:example-template:jar:1.1:compile
[INFO] \- com.example:example-core:jar:1.1:compile
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 2.783 s
[INFO] Finished at: 2016-02-11T10:13:10+07:00
[INFO] Final Memory: 13M/309M

ใช้ property มาช่วยให้การประกาศ dependency สะอาดตา และ maintain ง่ายขึ้น

เช่น หากต้องประกาศ spring-*** เยอะขนาดนี้ จะเห็นว่ามี 3.1.1.RELEASE ซ้ำๆกันเต็มไปหมด

    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>3.1.1.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-core</artifactId>
        <version>3.1.1.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-tx</artifactId>
        <version>3.1.1.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-jdbc</artifactId>
        <version>3.1.1.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-orm</artifactId>
        <version>3.1.1.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-test</artifactId>
        <version>3.1.1.RELEASE</version>
        <scope>test</scope>
    </dependency>

เราสามารถจะทำแบบนี้ได้ ประกาศเลข version ใน properties tag เวลาจะเปลี่ยน version ก็มาแก้ไขที่นี่ที่เดียว ครั้งเดียวด้วย

<properties>
...
        <spring.framework.version>3.1.1.RELEASE</spring.framework.version>
</properties>

แล้วเอา ${spring.framework.version} มาแทนที่ 3.1.1.RELEASE ในส่วนที่ประกาศ dependency

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>${spring.framework.version}</version>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-core</artifactId>
    <version>${spring.framework.version}</version>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-tx</artifactId>
    <version>${spring.framework.version}</version>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-jdbc</artifactId>
    <version>${spring.framework.version}</version>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-orm</artifactId>
    <version>${spring.framework.version}</version>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-test</artifactId>
    <version>${spring.framework.version}</version>
    <scope>test</scope>
</dependency>

ศึกษาเพิ่มเติม kyleblaney’s maven best practice