読者です 読者をやめる 読者になる 読者になる

SpringBootをGradleでビルド

SpringBootをGradleでビルド

Gradleを使ってビルド周りの自動化を進めていきたいということで試しにSpringBootのプロジェクトをGradleでビルドした際のメモ。開発環境にはEclipseを使用しています。

SpringBootプロジェクト作成準備

EclipseでSpringBoot作成のための以下のプラグインをインストールしておきます

□spring tool suite
https://spring.io/tools/sts

□Gradle IDE Pack
http://marketplace.eclipse.org/content/gradle-ide-pack

eclipsekでSpringBootのプロジェクト作成

File → New → Spring → Spring Starter Projectを選択します。 それからTypeに"Gradle(STS)“を選択することでGradleでビルドが行えるSpringBootのプロジェクトが作成できます。

ちなみに、作成後のbuild.gradleは以下のようになっております。

buildscript {
    ext {
        springBootVersion = '1.5.2.RELEASE'
    }
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
    }
}

apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'org.springframework.boot'

version = '0.0.1-SNAPSHOT'
sourceCompatibility = 1.8

repositories {
    mavenCentral()
}


dependencies {
    compile('org.springframework.boot:spring-boot-starter')
    testCompile('org.springframework.boot:spring-boot-starter-test')
}

プロジェクト作成

動作確認用のプロジェクトとして単純なTCPサーバとクライアントのプロジェクトを作成したいと思います。

TCPサーバのファイル構成

├── build.gradle
├── gradlew
├── gradlew.bat
└── src
    ├── main
    │   ├── java
    │   │   └── jp
    │   │       └── co
    │   │           └── xxxx
    │   │               └── mycat
    │   │                   ├── ServerApp.java
    │   │                   ├── config
    │   │                   │   └── AppProperties.java
    │   │                   └── tcp
    │   │                       └── TcpServer.java
    │   └── resources
    │       ├── application.properties
    │       ├── development
    │       │   └── application.properties
    │       └── production
    │           └── application.properties
    └── test
        └── java
            └── jp
                └── co
                    └── xxxx
                        └── mycat
                            └── ServerAppTests.java

□クライアントのファイル構成

├── build.gradle
├── gradlew
├── gradlew.bat
└── src
    ├── main
    │   ├── java
    │   │   └── jp
    │   │       └── co
    │   │           └── xxxx
    │   │               └── mycat
    │   │                   ├── ClientApp.java
    │   │                   ├── config
    │   │                   │   └── AppProperties.java
    │   │                   └── tcp
    │   │                       └── TcpClient.java
    │   └── resources
    │       ├── application.properties
    │       ├── development
    │       │   └── application.properties
    │       └── production
    │           └── application.properties
    └── test
        └── java
            └── jp
                └── co
                    └── xxxx
                        └── mycat
                            └── ServerAppTests.java

動作する内容としてはサーバはリクエストを受け取ったらプロパティにセットされた値を返すだけといったシンプルなものです。以下にコードをのせときます。
TcpServer.java

package jp.co.xxxx.mycat.tcp;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

import jp.co.xxxx.mycat.config.AppProperties;


@Component
@ConfigurationProperties(prefix = "app")
public class TcpServer {
    @Autowired
    private AppProperties appProp;

    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd hh:mm");

    public void run() throws Exception{
        sdf.setTimeZone(appProp.getTimezone());
        try(ServerSocket server = new ServerSocket(appProp.getPort())){
                System.out.println("クライアントからの接続を待ちます:" + appProp.getPort());
                Socket socket = server.accept();
                System.out.println("クライアント接続");
                System.out.println(appProp.getName());
                System.out.println("access time:" + sdf.format(new Date(Calendar.getInstance().getTimeInMillis())));

                serverReceive(socket);
                serverSend( socket, appProp.getServersend());
                socket.close();
                System.out.println("通信を終了しました");
        }catch(Exception ex){
            ex.printStackTrace();
        }
    }

    public void serverReceive(Socket socket) throws IOException{
        int ch;
        String text = "";
        InputStream input = socket.getInputStream();
        while((ch = input.read()) != 0){
            text += (char)ch;
        }
        System.out.println("server receive:" + text);
    }

    public void serverSend(Socket socket, String sendText) throws IOException{
        OutputStream output = socket.getOutputStream();
        for (char ch: sendText.toCharArray()) {
            output.write(ch);
        }
        output.write(0);
        System.out.println("server send:" + sendText);
    }
}

□TcpClient.java

package jp.co.xxxx.mycat.tcp;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

import jp.co.xxxx.mycat.config.AppProperties;


@Component
@ConfigurationProperties(prefix = "app")
public class TcpClient {
    @Autowired
    private AppProperties appProp;

    public void run() throws Exception{
        try(Socket socket = new Socket(appProp.getHost(), appProp.getPort())){
            clientSend(socket, appProp.getClientsend());
            clientReceive(socket);
        } catch(Exception ex){
            ex.printStackTrace();
        }
    }

    public void clientSend(Socket socket, String clientSendText) throws IOException{
        OutputStream output = socket.getOutputStream();
        for (char ch: clientSendText.toCharArray()) {
            output.write(ch);
        }
        output.write(0);
        System.out.println("client send:" + clientSendText);
    }

    public void clientReceive(Socket socket) throws IOException{
        int ch;
        String text = "";
        InputStream input = socket.getInputStream();
        while((ch = input.read()) != 0){
            text += (char)ch;
        }
        System.out.println("client receive:" + text);
    }
}

この時のプロパティの読み込みに使っているAppProperties.javaは以下のようになっています。

package jp.co.xxxx.mycat.config;

import java.text.SimpleDateFormat;
import java.util.TimeZone;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

@Component
@ConfigurationProperties(prefix = "app")
public class AppProperties {
    private String name;
    private TimeZone timezone;
    private String serversend;
    private String clientsend;

    private String host;
    private int port;

    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public TimeZone getTimezone() {
        return timezone;
    }
    public void setTimezone(TimeZone timezone) {
        this.timezone = timezone;
    }
    public String getServersend() {
        return serversend;
    }
    public void setServersend(String serversend) {
        this.serversend = serversend;
    }
    public String getClientsend() {
        return clientsend;
    }
    public void setClientsend(String clientsend) {
        this.clientsend = clientsend;
    }
    public String getHost() {
        return host;
    }
    public void setHost(String host) {
        this.host = host;
    }
    public int getPort() {
        return port;
    }
    public void setPort(int port) {
        this.port = port;
    }
}

ビルド

“./gradlew build"でビルドを行うとsrc/resource/application.propertiesがプロパティファイルで読み込まれて使用されます。 ビルド時に読み込むプロパティファイルを変更したいのであれば以下のようにbuild.gradleを修正したら行えるようになります。

buildscript {
    ext {
        springBootVersion = '1.5.2.RELEASE'
    }
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
    }
}

apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'org.springframework.boot'

version = '0.0.1-SNAPSHOT'
sourceCompatibility = 1.8

repositories {
    mavenCentral()
}


dependencies {
    compile('org.springframework.boot:spring-boot-starter')
    testCompile('org.springframework.boot:spring-boot-starter-test')
}

sourceSets {
    generated {
        java {
            srcDirs = ['src/main/generated/']
        }
    }
    main {
        resources {
            srcDirs = [ 'src/main/resources/' ]
        }
    }
}

//設定ファイルの切り替え(本番用・テストサーバ用)
def environment = project.hasProperty( 'env' ) ? env : 'dev'
if( environment.equals( 'prod' ))
    sourceSets.main.resources.srcDirs 'src/main/resources/production'
else
    sourceSets.main.resources.srcDirs 'src/main/resources/development'
println "Target environment: $environment"

上記ではsourceSetsを変更する箇所が新しく追加されています。例えば"./gradle build"でビルドを行う場合はsrc/main/resources/developmentがプロパティファイルとして読み込まれるようになります。 production環境のプロパティファイルを読み込ませる場合は、以下のコマンドを実行することで可能になります。

./gradlew -Penv=prod clean build

実行

ビルドを行った後はプロジェクトの"build/libs"に成果物のjarが出力されるので、実行できるか試してみたいと思います。
まずはサーバ、クライアント側のjarを順に実行してビルド時に読み込んだプロパティファイルが反映されているか確認できました。 今回は調べていませんが多分読み込むリソースファイルを動的に変更すると言ったこともできると思います。

設定ファイルの読み込みを動的に変更するのが簡単で環境ごとのビルドを一つのスクリプトで管理できそうなきがするのでGradleは良さそうな気がしました。