Spring Dependency Injection

Introduction

While you are encouraged to use CDI annotations for injection, Quarkus provides a compatibility layer for Spring dependency injection in the form of the spring-di extension.  

This step explains how your Quarkus application can leverage the well known Dependency Injection annotations included in the Spring Framework.

 

Let’s proceed to create some beans using various Spring annotations.

Creating the Functional Interface

First you'll create a StringFunction interface that some of your beans will implement and which will be injected into another bean later on. This functional interface provides target types for lambda expressions and method references you'll define.  

Note: Functional Interfaces are part of the base Java platform, and are not Spring-specific.
Create a new file in the project at:
src/main/java/org/acme/quickstart/StringFunction.java

Add the following to that file:

package org.acme.quickstart;

import java.util.function.Function;

public interface StringFunction extends Function<String, String> {

}

With the interface in place, you'll add an AppConfiguration class which will use the Spring’s Java configuration style for defining a bean. It will be used to create a StringFunction bean that will capitalize the text passed as parameter.  

Create a new file at:
src/main/java/org/acme/quickstart/AppConfiguration.java

Enter the following implementation to that file:

package org.acme.quickstart;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class AppConfiguration {

    @Bean(name = "capitalizeFunction")
    public StringFunction capitalizer() {
        return String::toUpperCase;
    }
}

Creating Functions

The next step is to define another bean that will implement StringFunction using Spring’s stereotype annotation style. This bean will effectively be a no-op bean that simply returns the input as is. 

Create a new file at:
src/main/java/org/acme/quickstart/NoOpSingleStringFunction.java

Add the following code to the new file:

package org.acme.quickstart;

import org.springframework.stereotype.Component;

@Component("noopFunction")
public class NoOpSingleStringFunction implements StringFunction {

    @Override
    public String apply(String s) {
        return s;
    }
}

Adding Injectable Configuration

Quarkus also provides support for injecting configuration values using Spring’s @Value annotation. Configuration parameters are added to the file:  

src/main/resources/application.properties

 

Open that file and add the following value:
taste.message = tastes great

You'll also need to add a new Spring Bean to use this configuration value. Create a new file at:

src/main/java/org/acme/quickstart/MessageProducer.java

 

Edit the new class and enter the following code:

 

package org.acme.quickstart;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

@Service
public class MessageProducer {

    @Value("${taste.message}")
    String message;

    public String getTaste() {
        return message;
    }
}

Bringing Everything Together

The final bean you'll create ties together all of the previous beans.  

 
Create a new file for this final bean at:
src/main/java/org/acme/quickstart/TasterBean.java

Enter the following code in the new file:

package org.acme.quickstart;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

@Component
public class TasterBean {

    private final MessageProducer messageProducer;

    @Autowired
    @Qualifier("noopFunction")
    StringFunction noopStringFunction;

    @Autowired
    @Qualifier("capitalizeFunction")
    StringFunction capitalizerStringFunction;

    @Value("${taste.suffix:!}")
    String suffix;

    public TasterBean(MessageProducer messageProducer) {
        this.messageProducer = messageProducer;
    }

    public String taste(String fruitName) {
        final String initialValue = fruitName + ": " + messageProducer.getTaste() + " " + suffix;
        return noopStringFunction.andThen(capitalizerStringFunction).apply(initialValue);
    }
}
In the code above, you'll see that both field injection and constructor injection are being used (note that constructor injection does not need the @Autowired annotation since there is a single constructor). Furthermore, the @Value annotation on suffix has also a default value defined, which in this case will be used since we have not defined taste.suffix in the application.properties file.

 

This new TasterBean class has a method taste() which will report how each fruit tastes. It also uses our functions noopStringFunction and capitalizerStringFunction that we've injected via @Autowired to both do nothing and also transform the result into all capital letters.

 

With the data model, repository, and accessor beans in place, in the final step where you'll expose your fruits to the outside world via Spring Web annotations.