Dependency Injection on Azure Functions Java - Azure/azure-functions-java-worker GitHub Wiki

Function instance injector for dependency injection

azure-function-java-spi contains an interface FunctionInstanceInjector

/**
 * Copyright (c) Microsoft Corporation. All rights reserved.
 * Licensed under the MIT License. See License.txt in the project root for
 * license information.
 */
package com.microsoft.azure.functions.spi.inject;

/**
 * The instance factory used by DI framework to initialize function instance.
 *
 * @since 1.0.0
 */
public interface FunctionInstanceInjector {
    /**
     * This method is used by DI framework to initialize the function instance. This method takes in the customer class and returns
     * an instance create by the DI framework, later customer functions will be invoked on this instance.
     * @param functionClass the class that contains customer functions
     * @param <T> customer functions class type
     * @return the instance that will be invoked on by azure functions java worker
     * @throws Exception any exception that is thrown by the DI framework during instance creation
     */
    <T> T getInstance(Class<T> functionClass) throws Exception;
}

By implementing this interface, you can return an instance of your function class and your functions will be invoked on this instance. This gives frameworks like Spring, Quarkus, Google Juice, Dagger, etc the ability to create the function instance and register it into their IOC container. This means you can use those Dependency Injection frameworks to manage your functions naturally.

Below are examples using FunctionInstanceInjector to integrate with Google Juice and Dagger. Examples on github

Google Juice

The FunctionGuiceFactory class that implement FunctionInstanceInjector interface

package com.azfs.dihook;

import com.google.inject.Guice;
import com.azfs.BasicModule;
import com.microsoft.azure.functions.spi.inject.FunctionInstanceInjector;

public class FunctionGuiceFactory implements FunctionInstanceInjector {
    @Override
    public <T> T getInstance(Class<T> functionClass) throws Exception {
        return Guice.createInjector(new BasicModule()).getInstance(functionClass);
    }
}

Function.class that contains the functions. We use @Inject annotation to autowire the Communicator here which used latter to send out an message.

package com.azfs;

import com.google.inject.Inject;
import com.microsoft.azure.functions.ExecutionContext;
import com.microsoft.azure.functions.HttpMethod;
import com.microsoft.azure.functions.HttpRequestMessage;
import com.microsoft.azure.functions.HttpResponseMessage;
import com.microsoft.azure.functions.HttpStatus;
import com.microsoft.azure.functions.annotation.AuthorizationLevel;
import com.microsoft.azure.functions.annotation.FunctionName;
import com.microsoft.azure.functions.annotation.HttpTrigger;

import java.util.Optional;

/**
 * Azure Functions with HTTP Trigger.
 */
public class Function {
    /**
     * This function listens at endpoint "/api/HttpExample". Two ways to invoke it using "curl" command in bash:
     * 1. curl -d "HTTP Body" {your host}/api/HttpExample
     * 2. curl "{your host}/api/HttpExample?name=HTTP%20Query"
     */

    @Inject
    public Communicator communicator;

    @FunctionName("HttpExample")
    public HttpResponseMessage run(
            @HttpTrigger(
                name = "req",
                methods = {HttpMethod.GET, HttpMethod.POST},
                authLevel = AuthorizationLevel.ANONYMOUS)
                HttpRequestMessage<Optional<String>> request,
            final ExecutionContext context) {
        context.getLogger().info("Java HTTP trigger processed a request.");

        // Parse query parameter
        final String query = request.getQueryParameters().get("name");
        final String name = request.getBody().orElse(query);

        //use the injected communicator to send out message
        communicator.sendMessage(context);

        if (name == null) {
            return request.createResponseBuilder(HttpStatus.BAD_REQUEST).body("Please pass a name on the query string or in the request body").build();
        } else {
            return request.createResponseBuilder(HttpStatus.OK).body("Hello, " + name).build();
        }
    }
}

You can find full code for this example at here

Dagger

MyFunctionInstanceInjector implement the 'FunctionInstanceInjector ' interface to return an instance of the Function.class.

package com.azfs.dihook;

import com.azfs.component.DaggerFunctionComponent;
import com.microsoft.azure.functions.spi.inject.FunctionInstanceInjector;

public class MyFunctionInstanceInjector implements FunctionInstanceInjector {
    @Override
    public <T> T getInstance(Class<T> aClass) throws Exception {
        return (T) DaggerFunctionComponent.create().buildFunction();
    }
}

The Function.class that contains the functions. We use @Inject annotation on the constructor to inject the communicator.

package com.azfs;

import com.azfs.model.Communicator;
import com.microsoft.azure.functions.ExecutionContext;
import com.microsoft.azure.functions.HttpMethod;
import com.microsoft.azure.functions.HttpRequestMessage;
import com.microsoft.azure.functions.HttpResponseMessage;
import com.microsoft.azure.functions.HttpStatus;
import com.microsoft.azure.functions.annotation.AuthorizationLevel;
import com.microsoft.azure.functions.annotation.FunctionName;
import com.microsoft.azure.functions.annotation.HttpTrigger;

import javax.inject.Inject;
import java.util.Optional;

/**
 * Azure Functions with HTTP Trigger.
 */
public class Function {
    /**
     * This function listens at endpoint "/api/HttpExample". Two ways to invoke it using "curl" command in bash:
     * 1. curl -d "HTTP Body" {your host}/api/HttpExample
     * 2. curl "{your host}/api/HttpExample?name=HTTP%20Query"
     */

    private final Communicator communicator;

    @Inject
    public Function(Communicator communicator) {
        this.communicator = communicator;
    }

    @FunctionName("HttpExample")
    public HttpResponseMessage run(
            @HttpTrigger(
                name = "req",
                methods = {HttpMethod.GET, HttpMethod.POST},
                authLevel = AuthorizationLevel.ANONYMOUS)
                HttpRequestMessage<Optional<String>> request,
            final ExecutionContext context) {
        context.getLogger().info("Java HTTP trigger processed a request.");

        // Parse query parameter
        final String query = request.getQueryParameters().get("name");
        final String name = request.getBody().orElse(query);

        communicator.communicate(context);

        if (name == null) {
            return request.createResponseBuilder(HttpStatus.BAD_REQUEST).body("Please pass a name on the query string or in the request body").build();
        } else {
            return request.createResponseBuilder(HttpStatus.OK).body("Hello, " + name).build();
        }
    }
}

You can find full code for this example at here

This feature will be soon adopted by Spring and Quarkus for better user experience when using those frameworks on azure functions.

⚠️ **GitHub.com Fallback** ⚠️