Springのcomponentをライブラリとして公開する方法

最近ライブラリ作りをたくさんやってます
そこで作りたくなるのがcomponentのライブラリ
今日はその作り方を紹介します
以下、gradleで作ることを前提で話します

辛み:普通に作ると問題がサイレントに起きる

ライブラリを作る上で少し注意すべきことがあるのでそれを書きます
componentライブラリを作るには、普通に考えたら、gradle.buildのdependeciesにcompile 'spring'的なことを書いて作ればイイと思うかもしれませんが、それだとspringのバージョン周りで問題が起きる可能性があります
例えばライブラリのspringのバージョンがv5で、ライブラリを使う側でv4を使っていたら最終的にspringのバージョンは何になるでしょう?
gradleはdependeciesに同じライブラリが複数定義された場合、バージョンが新しい方が採用されます
なのでこの例ではv5になります
つまりライブラリ側が安易に最新バージョンを指定すると、使う側のバージョンがサイレントに上がってしまいます
たぶん不具合が起きない限り、上がったことに気づかない...
怖いですね...

参考: Gradleで陥りやすい問題点の解決策TIPS集
http://kkoudev.github.io/blog/2014/03/30/gradle-tips/

なのでライブラリとしてはバージョンに対して控えめな感じにしておかないといけないです

dependenciesをcompileOnlyにする

解決策は簡単で、依存をcompileOnlyで指定します
これでコンパイル時だけspringのv5に依存して、ライブラリを使う側は依存が見えないようになります
仮にライブラリ側で最新バージョンでないと使えないような機能を使ってたらマズいですが、ただのcomponentを作りたいだけならまぁ問題はおきません

componentライブラリを作る

上記のことを気にしつつ、ライブラリを作ります
とりあえずgradle.buildはこんな感じ
group名やmavenの設定は適当に変えてください

group 'com.naosim'
version '1.0-SNAPSHOT'

apply plugin: 'groovy'
apply plugin: 'java'
apply plugin: 'maven' //ライブラリとして公開するため

sourceCompatibility = 1.8

repositories {
    mavenCentral()
}

dependencies {
    // ライブラリを使う側のバージョンに合わせるためにcompileOnlyにする
    compileOnly group: 'org.springframework', name: 'spring-context', version: '5.0.5.RELEASE'
    compileOnly group: 'org.projectlombok', name: 'lombok', version: '1.16.20'
}

// maven用の設定
uploadArchives {
    repositories {
        mavenDeployer {
            repository url: "file://${rootDir}/docs"
            pom.version = '0.1.0'
            pom.groupId = 'com.naosim'
            pom.artifactId = 'componentlib'
        }
    }
}

作りたいライブラリ

package com.naosim.componentlib;

import org.springframework.stereotype.Component;

@Component
public class SampleComponent {
    public String hello() {
        return "hello";
    }
}

これでライブラリは完成です

テストする

ライブラリができたので、次はテスト
gradleにdependenciesにテスト用のライブラリを追加します

dependencies {
    // ライブラリを使う側のバージョンに合わせるためにcompileOnlyにする
    compileOnly group: 'org.springframework', name: 'spring-context', version: '5.0.5.RELEASE'
    compileOnly group: 'org.projectlombok', name: 'lombok', version: '1.16.20'

    // test
    testCompile 'org.codehaus.groovy:groovy-all:2.3.11'
    testCompile group: 'junit', name: 'junit', version: '4.12'
    testCompile group: 'org.spockframework', name: 'spock-core', version: '1.1-groovy-2.4'
    testCompile group: 'org.spockframework', name: 'spock-spring', version: '1.1-groovy-2.4'
    // spring
    testCompile group: 'org.springframework', name: 'spring-context', version: '5.0.5.RELEASE'
    testCompile("org.springframework.boot:spring-boot-starter-web:2.0.1.RELEASE")
    testCompile group: 'org.springframework.boot', name: 'spring-boot-starter-test', version: '2.0.0.RELEASE'

    testCompileOnly group: 'org.projectlombok', name: 'lombok', version: '1.16.20'
}

次はテストコード側
springを立ち上げるにはApplicationクラスが必要になりますが、それをテスト側に書きます
/src/test/..../Application.java

@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

最後にテストコード

@Unroll
@SpringBootTest
@TestConfiguration
class SampleComponentSpec extends Specification {
    @Autowired
    SampleComponent sut;

    def "test"() {
        expect:
        sut.hello() == "hello"
    }

}

まとめ

componentライブラリの作り方を紹介しました
コード全体をgithubに置いておきます
github.com