Spring Bootで動作するAzure Functionsプロジェクト作成(Gradleプロジェクト)

プロジェクト作成

1. 必要なツールのインストール

〇Node.js(Windows 64bit版 LTS v14.17.6)

以下のサイトからインストール。
https://nodejs.org/ja/download/

〇Azure Functions Core Tools

コマンドプロンプトで以下のコマンドを実行する。参考

npm i -g azure-functions-core-tools@3 --unsafe-perm true

2. GitHubページからソースをダウンロードする

https://github.com/Azure-Samples/hello-spring-function-azure/tree/gradle
ダウンロードしたプロジェクトは解凍してEclipseにインポートする。


警告の除去と依存関係の追加

上記のGitHubページからダウンロードしたファイルをEclipseに取り込むと警告が出てくるので、推奨されている代替クラスに差し替える。
推奨されているクラスに差し替えてファンクションを動作させるためにはspring-boot-starter-webfluxの依存関係の追加が必要となるため、build.gradleに以下の記述を追加する。

dependencies {
    // ↓ 追加
    implementation 'org.springframework.boot:spring-boot-starter-webflux:2.4.4'
    // ↑ 追加
    implementation 'org.springframework.cloud:spring-cloud-function-adapter-azure:3.1.2'
    testImplementation 'org.springframework.boot:spring-boot-starter-test:2.4.4'
    compileOnly 'org.springframework.cloud:spring-cloud-starter-function-web:3.1.2'
}

Handlerクラスが継承しているクラスをorg.springframework.cloud.function.adapter.azure.AzureSpringBootRequestHandlerからorg.springframework.cloud.function.adapter.azure.FunctionInvokerに差し替える。

// 変更前
public class HelloHandler extends AzureSpringBootRequestHandler<User, Greeting> {
}

// 変更後
public class HelloHandler extends FunctionInvoker<User, Greeting> {
}

ファンクションのローカルでの起動

1. build.gradleファイルを開き、resourceGroupappNameを使用しているものに書き換える

azurefunctions {
    resourceGroup = "sample"
    appName = "java-functions"
    appSettings {
        WEBSITE_RUN_FROM_PACKAGE = '1'
        FUNCTIONS_EXTENSION_VERSION = '~3'
        FUNCTIONS_WORKER_RUNTIME = 'java'
        MAIN_CLASS = 'com.example.DemoApplication'
    }
    authentication {
        type = 'azure_cli'
    }
    deployment {
        type = 'run_from_blob'
    }
}

2. プロジェクトをビルドする

gradlew azureFunctionsPackage

3. ファンクションを起動する

gradlew azureFunctionsRun

起動すると以下のようなログが出力される

C:\\pleiades-2021-06\\pleiades\\workspace\\hello-spring-function-azure-gradle>gradlew azureFunctionsRun

> Task :azureFunctionsPackage

Step 1 of 8: Searching for Azure Functions entry points
1 Azure Functions entry point(s) found.

Step 2 of 8: Generating Azure Functions configurations
Generation done.

Step 3 of 8: Validating generated configurations
Validation done.

Step 4 of 8: Saving host.json
Successfully saved to C:\\pleiades-2021-06\\pleiades\\workspace\\hello-spring-function-azure-gradle\\build\\azure-functions\\java-functions\\host.json

Step 5 of 8: Saving local.settings.json
Successfully saved to C:\\pleiades-2021-06\\pleiades\\workspace\\hello-spring-function-azure-gradle\\build\\azure-functions\\java-functions\\local.settings.json

Step 6 of 8: Saving configurations to function.json
Starting processing function: hello
Successfully saved to C:\\pleiades-2021-06\\pleiades\\workspace\\hello-spring-function-azure-gradle\\build\\azure-functions\\java-functions\\hello\\function.json

Step 7 of 8: Copying JARs to staging directory: C:\\pleiades-2021-06\\pleiades\\workspace\\hello-spring-function-azure-gradle\\build\\azure-functions\\java-functions
Copied successfully.
Step 8 of 8: Installing function extensions if needed
Skip install Function extension for HTTP Trigger Functions
Successfully built Azure Functions.

> Task :azureFunctionsRun
Azure Function App's staging directory found at: C:\\pleiades-2021-06\\pleiades\\workspace\\hello-spring-function-azure-gradle\\build\\azure-functions\\java-functions

Azure Functions Core Tools
Core Tools Version:       3.0.3734 Commit hash: 61192bb28820be76916f85209916152801483456  (64-bit)
Function Runtime Version: 3.1.4.0

Functions:

        hello: [GET,POST] http://localhost:7071/api/hello

For detailed output, run func with --verbose flag.
[2021-09-15T04:37:05.344Z] Host lock lease acquired by instance ID \\'0000000000000000000000006956DF61'.

起動中にhttp://localhost:7071/api/helloGETPOSTでリクエストを送るとファンクションが実行される。
最初にリクエストを送ったときにSpring Bootが起動する。 ファンクションの停止はCtrl + cで行う。


Eclipseデバッグする

1. プロジェクトのbuild.gradleに以下の記述を追加する

 azurefunctions {
    resourceGroup = "sample"
    appName = "java-functions"
    appSettings {
        WEBSITE_RUN_FROM_PACKAGE = '1'
        FUNCTIONS_EXTENSION_VERSION = '~3'
        FUNCTIONS_WORKER_RUNTIME = 'java'
        MAIN_CLASS = 'com.example.DemoApplication'
    }
    authentication {
        type = 'azure_cli'
    }
    // ↓の記述を追加
    localDebug = "transport=dt_socket,server=y,suspend=n,address=5005"
    // ↑の記述を追加
    deployment {
        type = 'run_from_blob'
    }
}

2. ファンクションを起動する

gradlew azureFunctionsRun

3. 起動後に以下の操作を行う。

  1. functionsプロジェクトを指定して右クリック→「デバッグ」→「デバッグの構成」を押下
  2. 「リモートJavaアプリケーション」を選択して、「新規の起動構成」を押下
  3. プロジェクトがfunctionsプロジェクトになっていることを確認し、接続プロパティのホストにlocalhostを、ポートに5005を指定する
  4. デバッグボタンを押下する

以降ファンクションを停止するまではブレークポイントで止まるようになる。


ファンクションでDIする

HandlerクラスはFunction起動時に(Spring Bootが起動する前に)動作しているので、DIを使用することはできないので注意。

1. Configクラスの作成

DIしたいクラスをbean登録するためのConfigクラスを作成する。
この時、Functionクラスも必ずbean登録する。 Functionクラスのbean登録名はHandlerクラスの@FunctionName("hello")で付けた名前と同じにする。

@Import({ HogehogeConfiguration.class })
@SpringBootApplication
public class HelloConfiguration {

    @Bean
    public HelloService helloService() {
        return new HelloServiceImpl();
    }

    // Functionクラスのbean登録名とHandlerクラスの@FunctionName("hello")の属性に
    // 指定した名前は一致させる
    @Bean
    public HelloFunction hello(HelloService service) {
        return new HelloFunction(service);
    }
}

2. インジェクション

実際にDIを使用するクラスでは、フィールドインジェクション、セッターインジェクション、コンストラクタインジェクションのいずれかを使用することでインジェクション可能。
以下にコンストラクタインジェクションの例を示す。

public class HelloFunction implements Function<User, Greeting> {

    private HelloService service;

    public HelloFunction(HelloService service){
        this.service.service;
    }

    public Greeting apply(User user) {
        service.doSomething();
        return new Greeting("Hello, " + user.getName() + "!\\n");
    }
}