Mongo Data Repositories

Home

Monitoring


© 2019-2022 rxmicro.io. Free use of this software is granted under the terms of the Apache License 2.0.

Copies of this entity may be made for Your own use and for distribution to others, provided that You do not charge any fee for such copies and further provided that each copy contains this Copyright Notice, whether distributed in print or electronically.

If You find errors or omissions in this entity, please don’t hesitate to submit an issue or open a pull request with a fix.

The rxmicro.cdi module is an implementation of the Dependency Injection design pattern, that is integrated to the RxMicro framework.

1. Basic Usage

To use the rxmicro.cdi module in the project, the following two steps must be taken:

  • Add the rxmicro-cdi dependency to the pom.xml file:

<dependency>
    <groupId>io.rxmicro</groupId>
    <artifactId>rxmicro-cdi</artifactId>
    <version>${rxmicro.version}</version>
</dependency>
  • Add the rxmicro.cdi module to the module-info.java descriptor:

module examples.cdi.basic {
    requires rxmicro.rest.server.netty;
    requires rxmicro.rest.server.exchange.json;
    requires rxmicro.cdi; (1)
}

After adding the rxmicro.cdi module, You can create a business service:

public interface BusinessService {

    String getValue();
}
public final class BusinessServiceImpl implements BusinessService {

    @Override
    public String getValue() {
        return "IMPL";
    }
}

In order to inject a business service implementation, it is necessary to use the @Inject annotation:

public final class RestController {

    (1)
    @Inject
    BusinessService businessService;

    @PATCH("/")
    void handle() {
        System.out.println(businessService.getValue());
    }
}
1 The usage of the @io.rxmicro.cdi.Inject annotation does not differ fundamentally from that of the @javax.inject.Inject or @com.google.inject.Inject annotations.

The correctness of injection can be checked using REST-based microservice test:

@RxMicroRestBasedMicroServiceTest(RestController.class)
final class RestControllerTest {

    private BlockingHttpClient blockingHttpClient;

    private SystemOut systemOut;

    @Test
    void Should_print_to_system_out() {
        blockingHttpClient.patch("/");

        assertEquals("IMPL", systemOut.asString());
    }
}

Business service implementation can be injected not only into REST controller, but into any component:

public final class BusinessServiceFacade {

    @Inject
    BusinessService businessService;

    public String getValue() {
        return businessService.getValue();
    }
}

Since the injection is performed into any component, it is necessary to use the microservice component testing approach:

@RxMicroComponentTest(BusinessServiceFacade.class)
final class BusinessServiceFacadeTest {

    private BusinessServiceFacade businessServiceFacade;

    @Test
    void Should_invoke_BusinessService_getValue() {
        assertEquals("IMPL", businessServiceFacade.getValue());
    }
}

The project source code used in the current subsection is available at the following link:

When compiling, the RxMicro framework searches for RxMicro Annotations in the source code and generates additional classes necessary for the integral work of the microservice.

When changing the RxMicro Annotations in the source code, DON’T FORGET to recompile the ALL source code, not just the changed file, for the changes to take effect: mvn clean compile.

2. RxMicro Annotations

The RxMicro framework supports the following RxMicro Annotations:

Table 1. Supported RxMicro Annotations.
Annotation Description

@Inject

Indicates the need to inject the component implementation into the annotated class field or method parameter.

(Is a synonym of the @Autowired annotation, and is recommended for developers who have used JEE or Google Guice as CDI implementation in their previous projects.)

@Named

Allows to customize an injection point by specifying a string value or custom annotation. This annotation can also be used to specify a component name.

(Is a synonym of the @Qualifier annotation, and is recommended for developers who have used JEE or Google Guice as CDI implementation in their previous projects.)

@Autowired

Indicates the need to inject the component implementation into the annotated class field or method parameter.

(Is a synonym of the @Inject annotation, and is recommended for developers who have used Spring DI as CDI implementation in their previous projects.)

@Qualifier

Allows to customize an injection point by specifying a string value or custom annotation. This annotation can also be used to specify a component name.

(Is a synonym of the @Named annotation, and is recommended for developers who have used Spring DI as CDI implementation in their previous projects.)

@PostConstruct

Denotes a method, that should be invoked automatically after all dependencies have been injected into the current component.

(In its semantics, it completely corresponds to the @javax.annotation.PostConstruct annotation.)

@Factory

Denotes a factory method or a factory, that creates instances of the specified class.

@Resource

Indicates the need to inject the external resource into the annotated class field or method parameter.

3. All Beans are Singletons!

The RxMicro framework focuses on creating microservice projects. One of the key features of microservices is their simplicity. That’s why singleton scope was chosen as the main and only one.

Thus, all CDI components are singletons!

It means that when starting a microservice project, only one instance of the component implementation class is created and injected into all necessary injection points.

If it is necessary to inject a separate implementation class instance (prototype scope) to each injection point, then You must use the factory that creates instances of the given class!

This behavior can be checked with the following code:

public final class BusinessServiceImpl implements BusinessService1, BusinessService2 {
}
public final class BusinessServiceFacade {

    @Inject
    BusinessService1 businessService1;

    @Inject
    BusinessService2 businessService2;
}
@RxMicroComponentTest(BusinessServiceFacade.class)
final class BusinessServiceFacadeTest {

    private BusinessServiceFacade facade;

    @Test
    void Should_use_singleton_instance() {
        assertSame(facade.businessService1, facade.businessService2); (1)
    }
}
1 Since the BusinessServiceImpl class implements two interfaces, the same implementation instance is injected when injecting different types of interfaces!

The project source code used in the current subsection is available at the following link:

When compiling, the RxMicro framework searches for RxMicro Annotations in the source code and generates additional classes necessary for the integral work of the microservice.

When changing the RxMicro Annotations in the source code, DON’T FORGET to recompile the ALL source code, not just the changed file, for the changes to take effect: mvn clean compile.

4. Constructor Injection

The RxMicro framework also supports constructor injection:

public final class BusinessServiceFacade {

    private final BusinessService businessService;

    (1)
    @Inject
    public BusinessServiceFacade(final BusinessService businessService) {
        System.out.println(businessService.getClass().getSimpleName());
        this.businessService = businessService;
    }

}
1 To enable constructor injection, it is necessary to create a constructor with parameters and annotate it by the @Inject or @Autowired annotation.

When using the constructor injection mechanism, this constructor is automatically invoked by the RxMicro framework:

@RxMicroComponentTest(BusinessServiceFacade.class)
final class BusinessServiceFacadeTest {

    private SystemOut systemOut;

    @Test
    void Should_support_constructor_injection() {
        assertEquals(BusinessServiceImpl.class.getSimpleName(), systemOut.asString());
    }
}

Constructor injection requires more code to be written, but also allows You to create final fields as injection points.

The project source code used in the current subsection is available at the following link:

When compiling, the RxMicro framework searches for RxMicro Annotations in the source code and generates additional classes necessary for the integral work of the microservice.

When changing the RxMicro Annotations in the source code, DON’T FORGET to recompile the ALL source code, not just the changed file, for the changes to take effect: mvn clean compile.

5. Method Injection

The RxMicro framework also supports injection with setters (method injection):

public final class BusinessServiceFacade {

    private BusinessService businessService;

    (1)
    @Inject
    public void setBusinessService(final BusinessService businessService) {
        System.out.println(businessService.getClass().getSimpleName());
        this.businessService = businessService;
    }

}
1 To enable the injection with setters, it is necessary to create setter and annotate it by the @Inject or @Autowired annotation.

When using the injection mechanism with setters, this method is invoked automatically by the RxMicro framework:

@RxMicroComponentTest(BusinessServiceFacade.class)
final class BusinessServiceFacadeTest {

    private SystemOut systemOut;

    @Test
    void Should_support_constructor_injection() {
        assertEquals(BusinessServiceImpl.class.getSimpleName(), systemOut.asString());
    }
}

The project source code used in the current subsection is available at the following link:

When compiling, the RxMicro framework searches for RxMicro Annotations in the source code and generates additional classes necessary for the integral work of the microservice.

When changing the RxMicro Annotations in the source code, DON’T FORGET to recompile the ALL source code, not just the changed file, for the changes to take effect: mvn clean compile.

6. Ambiguity Resolving

If there are two or more implementations of the same interface in the current project module, the problem of ambiguity resolving may arise in the process of dependencies injection. In the field with the interface type potentially can be injected an implementation of any of the child classes of this interface, that’s why such a problem occurs.

In order to solve such problems definitively, the RxMicro framework uses the ambiguity resolving algorithm by default.

6.1. Default Ambiguity Resolving

If there are two or more implementations of the same interface in the current project module, then the RxMicro framework implicitly sets the name for each implementation. This name corresponds to a simple class name starting with a small letter, for example:

  • For the io.rxmicro.examples.cdi.ProductionBusinessService class, the name is equal to productionBusinessService.

  • For the io.rxmicro.examples.cdi.DevelopmentBusinessService class, the name is equal to developmentBusinessService.

  • For the io.rxmicro.ProductionBusinessService class, the name is equal to productionBusinessService.

  • For the ProductionBusinessService class, the name is equal to productionBusinessService.

The ambiguity resolving problem may occur only between classes implementing the same interface!.

This means that if there are two following interfaces in the project:

  • package1.BusinessService and

  • package2.BusinessService;

and four implementation classes:

  • package1.impl.ProductionBusinessService implements package1.BusinessService;

  • package1.impl.DevelopmentBusinessService implements package1.BusinessService;

  • package2.impl.ProductionBusinessService implements package2.BusinessService;

  • package2.impl.DevelopmentBusinessService implements package2.BusinessService;

then despite the same names for different classes:

  • productionBusinessService for package1.impl.ProductionBusinessService and package2.impl.ProductionBusinessService;

  • developmentBusinessService for package1.impl.DevelopmentBusinessService and package2.impl.DevelopmentBusinessService;

no implementation errors will occur! Everything will work correctly as the same component names are created for different data types!

Thus, for ProductionBusinessService and DevelopmentBusinessService implementation classes, the following names are set accordingly: productionBusinessService and developmentBusinessService:

public final class ProductionBusinessService implements BusinessService {

    @Override
    public String getValue() {
        return "PRODUCTION";
    }
}
public final class DevelopmentBusinessService implements BusinessService {

    @Override
    public String getValue() {
        return "DEVELOPMENT";
    }
}

When injecting implementations, the RxMicro framework reads the name of the class or method parameter field. If the names of the class fields correspond to the names of implementation components, the successful injection is performed:

If there is only one implementation class, in the current module, then regardless of the class field names or method parameters, the instance of this class will be successfully injected!

In other words, the ambiguity resolving algorithm is enabled by default only if there are such ambiguities!

public final class BusinessServiceFacade {

    @Inject
    BusinessService productionBusinessService; (1)

    @Inject
    BusinessService developmentBusinessService; (2)

    public String getValue(final boolean production) {
        if (production) {
            return productionBusinessService.getValue();
        } else {
            return developmentBusinessService.getValue();
        }
    }
}
1 In the productionBusinessService field, the ProductionBusinessService class instance is injected.
2 In the developmentBusinessService field, the DevelopmentBusinessService class instance is injected.

This behavior can be tested with the following test:

@RxMicroComponentTest(BusinessServiceFacade.class)
final class BusinessServiceFacadeTest {

    private BusinessServiceFacade businessServiceFacade;

    @Test
    void Should_return_PRODUCTION() {
        assertEquals("PRODUCTION", businessServiceFacade.getValue(true));
    }

    @Test
    void Should_return_DEVELOPMENT() {
        assertEquals("DEVELOPMENT", businessServiceFacade.getValue(false));
    }
}

The project source code used in the current subsection is available at the following link:

When compiling, the RxMicro framework searches for RxMicro Annotations in the source code and generates additional classes necessary for the integral work of the microservice.

When changing the RxMicro Annotations in the source code, DON’T FORGET to recompile the ALL source code, not just the changed file, for the changes to take effect: mvn clean compile.

6.2. The @Named (@Qualifier) Annotation Usage

In case if the ambiguity resolving algorithm by default does not meet the needs of a business task, the developer can set up the implementation process using the following annotations: @Named or @Qualifier:

public final class BusinessServiceFacade {

    @Inject
    (1)
    @Named("productionBusinessService")
    BusinessService businessService1;

    @Inject
    (2)
    @Named("developmentBusinessService")
    BusinessService businessService2;

    public String getValue(final boolean production) {
        if (production) {
            return businessService1.getValue();
        } else {
            return businessService2.getValue();
        }
    }
}
1 To inject the ProductionBusinessService class instance to the businessService1 field, in the @Named annotation parameter, You need to specify the productionBusinessService name.
(This is an implicit name that is set by the RxMicro framework for the ProductionBusinessService class!)
2 To inject the DevelopmentBusinessService class instance to the businessService2 field, in the @Named annotation parameter, You need to specify the developmentBusinessService name.
(This is an implicit name that is set by the RxMicro framework for the DevelopmentBusinessService class!)

This behavior can be tested with the following test:

@RxMicroComponentTest(BusinessServiceFacade.class)
final class BusinessServiceFacadeTest {

    private BusinessServiceFacade businessServiceFacade;

    @Test
    void Should_return_PRODUCTION() {
        assertEquals("PRODUCTION", businessServiceFacade.getValue(true));
    }

    @Test
    void Should_return_DEVELOPMENT() {
        assertEquals("DEVELOPMENT", businessServiceFacade.getValue(false));
    }
}

The project source code used in the current subsection is available at the following link:

When compiling, the RxMicro framework searches for RxMicro Annotations in the source code and generates additional classes necessary for the integral work of the microservice.

When changing the RxMicro Annotations in the source code, DON’T FORGET to recompile the ALL source code, not just the changed file, for the changes to take effect: mvn clean compile.

An implicitly created name for an implementation class can be set explicitly using the @Named or @Qualifier annotations:

@Named("Production")
public final class ProductionBusinessService implements BusinessService {

    @Override
    public String getValue() {
        return "PRODUCTION";
    }
}
@Named("Development")
public final class DevelopmentBusinessService implements BusinessService {

    @Override
    public String getValue() {
        return "DEVELOPMENT";
    }
}

When using explicit names for implementation classes, it is necessary to specify these explicit names as the qualifier of an injection point:

public final class BusinessServiceFacade {

    @Inject
    (1)
    @Named("Production")
    BusinessService businessService1;

    @Inject
    (2)
    @Named("Development")
    BusinessService businessService2;

    public String getValue(final boolean production) {
        if (production) {
            return businessService1.getValue();
        } else {
            return businessService2.getValue();
        }
    }
}
1 To inject the ProductionBusinessService class instance to the businessService1 field, in the @Named annotation parameter, You need to specify the Production name.
(This name is explicitly set for the ProductionBusinessService class, using the @Named annotation!)
2 To inject the DevelopmentBusinessService class instance to the businessService2 field, in the @Named annotation parameter, You need to specify the Development name.
(This name is explicitly set for the DevelopmentBusinessService class, using the @Named annotation!)

The project source code used in the current subsection is available at the following link:

When compiling, the RxMicro framework searches for RxMicro Annotations in the source code and generates additional classes necessary for the integral work of the microservice.

When changing the RxMicro Annotations in the source code, DON’T FORGET to recompile the ALL source code, not just the changed file, for the changes to take effect: mvn clean compile.

6.3. Custom Annotations Usage

When using string names for implementation classes and injection points, the developer may make a mistake. Since the compiler does not check the specified names, the error can be detected only during runtime.

If such a situation is unacceptable, custom annotations should be used as qualifiers:

@Documented
@Retention(CLASS)
@Target({FIELD, METHOD, TYPE})
(1)
@Named("")
public @interface EnvironmentType {

    Type value(); (2)

    enum Type {

        PRODUCTION,

        DEVELOPMENT
    }
}
1 For a custom annotation to be defined by the RxMicro framework as a qualifier, it must be annotated by the @Named or @Qualifier annotation with an empty string value.
2 To control the component names with the compiler, it is recommended to use enumerations.

After creating the custom annotation that serves as a qualifier, it is necessary to annotate the implementation classes by it

@EnvironmentType(EnvironmentType.Type.PRODUCTION)
public final class ProductionBusinessService implements BusinessService {

    @Override
    public String getValue() {
        return "PRODUCTION";
    }
}
@EnvironmentType(EnvironmentType.Type.DEVELOPMENT)
public final class DevelopmentBusinessService implements BusinessService {

    @Override
    public String getValue() {
        return "DEVELOPMENT";
    }
}

and injection points:

public final class BusinessServiceFacade {

    @Inject
    (1)
    @EnvironmentType(EnvironmentType.Type.PRODUCTION)
    BusinessService businessService1;

    @Inject
    (2)
    @EnvironmentType(EnvironmentType.Type.DEVELOPMENT)
    BusinessService businessService2;

    public String getValue(final boolean production) {
        if (production) {
            return businessService1.getValue();
        } else {
            return businessService2.getValue();
        }
    }
}

When using custom annotations, the result of ambiguity resolving will be the same as when using string names:

@RxMicroComponentTest(BusinessServiceFacade.class)
final class BusinessServiceFacadeTest {

    private BusinessServiceFacade businessServiceFacade;

    @Test
    void Should_return_PRODUCTION() {
        assertEquals("PRODUCTION", businessServiceFacade.getValue(true));
    }

    @Test
    void Should_return_DEVELOPMENT() {
        assertEquals("DEVELOPMENT", businessServiceFacade.getValue(false));
    }
}

The project source code used in the current subsection is available at the following link:

When compiling, the RxMicro framework searches for RxMicro Annotations in the source code and generates additional classes necessary for the integral work of the microservice.

When changing the RxMicro Annotations in the source code, DON’T FORGET to recompile the ALL source code, not just the changed file, for the changes to take effect: mvn clean compile.

7. Dependency Injection Using Spring Style

The developers with a rich experience in using Spring in their previous projects can instead of the following annotations: @Inject and @Named use the @Autowired and @Qualifier annotations respectively, which are absolutely similar in features:

@Qualifier("Production")
public final class ProductionBusinessService implements BusinessService {

    @Override
    public String getValue() {
        return "PRODUCTION";
    }
}
@Qualifier("Development")
public final class DevelopmentBusinessService implements BusinessService {

    @Override
    public String getValue() {
        return "DEVELOPMENT";
    }
}
public final class BusinessServiceFacade {

    @Autowired
    (1)
    @Qualifier("Production")
    BusinessService businessService1;

    @Autowired
    (2)
    @Qualifier("Development")
    BusinessService businessService2;

    public String getValue(final boolean production) {
        if (production) {
            return businessService1.getValue();
        } else {
            return businessService2.getValue();
        }
    }
}
1 To inject the ProductionBusinessService class instance in the businessService1 field, it is necessary in the @Qualifier annotation parameter specify the Production name.
(This is the name that is set explicitly for the ProductionBusinessService class by the @Qualifier annotation!)
2 To inject the DevelopmentBusinessService class instance in the businessService2 field, it is necessary in the @Qualifier annotation parameter specify the Development name.
(This is the name that is set explicitly for the DevelopmentBusinessService class by the @Qualifier annotation!)

The project source code used in the current subsection is available at the following link:

When compiling, the RxMicro framework searches for RxMicro Annotations in the source code and generates additional classes necessary for the integral work of the microservice.

When changing the RxMicro Annotations in the source code, DON’T FORGET to recompile the ALL source code, not just the changed file, for the changes to take effect: mvn clean compile.

8. @PostConstruct

If You need to run some code while creating a class instance, Java provides a special method for doing so, namely the constructor. However, when using the dependency injection mechanisms, the dependencies are injected after creating an instance, and accordingly after invoking the constructor. (Except for the constructor injection mechanism!). In order to run some code while creating an instance, but only after introducing all dependencies into this instance, the RxMicro framework provides a special @PostConstruct annotation.

Thus, if there is a class method, annotated by the @PostConstruct annotation, then this method is automatically invoked after all dependencies are injected into the created instance of this class.

public final class BusinessService2Facade {

    @Inject
    BusinessService businessService;

    @PostConstruct
    void anyName() { (1)
        System.out.println(businessService.getClass().getSimpleName());
    }
}
1 After injection of the implementation class instance in the businessService field, the RxMicro framework will automatically invoke the anyName method, since this method is annotated by the @PostConstruct annotation.

For the convenience of developers, the RxMicro framework introduces an additional convention:

If there is a method with the postConstruct name in the class, this method may not be annotated by the @PostConstruct annotation!

The method with the specified name will be invoked automatically after all dependencies are injected:

public final class BusinessService1Facade {

    @Inject
    BusinessService businessService;

    void postConstruct() { (1)
        System.out.println(businessService.getClass().getSimpleName());
    }

}

Thus, for any method in the class to be defined by the RxMicro framework as the method to be invoked automatically after all dependencies are injected, it is necessary to:

  • to annotate this method by the @PostConstruct annotation;

  • or has the predefined postConstruct name.

The postConstruct method must meet the following requirements:

  • This method should be a single method in the class.

  • The method must not be static.

  • The method must not be abstract.

  • The method must be non-native.

  • The method must not be synchronized.

  • The method must not contain parameters.

  • The method must return the void type.

The facts of invoking the postConstruct and anyName methods can be checked using the following tests:

@RxMicroComponentTest(BusinessService1Facade.class)
final class BusinessService1FacadeTest {

    private SystemOut systemOut;

    @Test
    void Should_invoke_postConstruct_method() {
        assertEquals(BusinessServiceImpl.class.getSimpleName(), systemOut.asString());
    }
}
@RxMicroComponentTest(BusinessService2Facade.class)
final class BusinessService2FacadeTest {

    private SystemOut systemOut;

    @Test
    void Should_invoke_postConstruct_method() {
        assertEquals(BusinessServiceImpl.class.getSimpleName(), systemOut.asString());
    }
}

The project source code used in the current subsection is available at the following link:

When compiling, the RxMicro framework searches for RxMicro Annotations in the source code and generates additional classes necessary for the integral work of the microservice.

When changing the RxMicro Annotations in the source code, DON’T FORGET to recompile the ALL source code, not just the changed file, for the changes to take effect: mvn clean compile.

In many frameworks implementing the Dependency Injection design template, besides the postConstruct methods, there are also the preDestroy methods. These methods are invoked automatically, and usually clear resources when deleting an instance from the CDI container.

For a microservice project that uses the RxMicro framework, there is no need for the preDestroy methods. Since the instances are removed from the CDI container when the java process ends. And upon completion of any process in the operating system, the operating system automatically clears all occupied resources!

9. RxMicro Components Injection

9.1. Basic Usage

Besides custom classes, the RxMicro framework supports the RxMicro components injection.

For example, if a declarative REST client is declared in the project:

@RestClient
public interface RESTClient {

    @PATCH("/")
    CompletableFuture<Void> patch();
}

then instead of getting an explicit reference to the implementation class instance using the RestClientFactory.getRestClient(Class<?>), the developer can use the dependency injection mechanism:

public final class BusinessServiceFacade {

    (1)
    @Inject
    RESTClient restClient;

    (2)
    @Inject
    RestClientConfig config;

    void postConstruct() {
        System.out.println(restClient.getClass().getSimpleName());
        System.out.println(config.getClass().getSimpleName());
    }
}
1 To get a reference to the REST client implementation class, the developer can use the @Inject annotation.
2 Besides injecting REST clients, the RxMicro framework also supports the configuration injection.
(A list of all supported RxMicro components that can be injected is described in the Section 9.2, “All Supported RxMicro Components”.)

The project source code used in the current subsection is available at the following link:

When compiling, the RxMicro framework searches for RxMicro Annotations in the source code and generates additional classes necessary for the integral work of the microservice.

When changing the RxMicro Annotations in the source code, DON’T FORGET to recompile the ALL source code, not just the changed file, for the changes to take effect: mvn clean compile.

9.2. All Supported RxMicro Components

This section describes all supported RxMicro components that can be injected in any class using the injection mechanisms.

Table 2. All Supported RxMicro Components.
Name Feature

Config instance.

Any class that extends the basic configuration class: Config.

(For example: MongoConfig, HttpClientConfig, HttpServerConfig, etc.)

Mongo repository.

The interface annotated by the @MongoRepository annotation.

Mongo client.

Predefined type: com.mongodb.reactivestreams.client.MongoClient

PostgreSQL repository.

The interface annotated by the @PostgreSQLRepository annotation.

R2DBC connection factory.

Predefined type: io.r2dbc.spi.ConnectionFactory.

R2DBC connection pool.

Predefined type: io.r2dbc.pool.ConnectionPool.

REST client.

The interface annotated by the @RestClient annotation.

The source code of the project demonstrating all supported for injection RxMicro components is available at the following link:

10. Factory Method

When using the dependency injection mechanisms, the RxMicro framework creates instances of the specified classes and injects references to them to injection points. For successful implementation of this behavior, each class, the instance of which should be injected, must contain an accessible constructor without parameters or a constructor annotated by the @Inject or @Autowired annotation.

In other words, the RxMicro framework determines the instance of which class should be created and creates this instance automatically at the start of the CDI container. If it is necessary to get more control over creation of the implementation instance, it is necessary to use the Factory Method template:

public final class BusinessServiceFacade {

    @Inject
    BusinessService businessService;

    (1)
    @Factory
    static BusinessServiceFacade create() {
        System.out.println("Use factory method");
        return new BusinessServiceFacade();
    }

    (2)
    private BusinessServiceFacade() {
    }

    void postConstruct() {
        System.out.println(businessService.getClass().getSimpleName());
    }

}
1 The class must contain the static method annotated by the @Factoryannotation.
2 The private constructor restricts the possibility of creating the instance of this class externally. Thus, the instance of this class can only be created using the create() factory method.

If the RxMicro framework detects a method in the class, annotated by the @Factoryannotation, then this method is used instead of the constructor when creating the instance of this class:

@RxMicroComponentTest(BusinessServiceFacade.class)
final class BusinessServiceFacadeTest {

    private SystemOut systemOut;

    @Test
    void Should_support_constructor_injection() {
        assertEquals(
                List.of(
                        "Use factory method",
                        BusinessServiceImpl.class.getSimpleName()
                ),
                systemOut.asStrings()
        );
    }
}

The factory method must meet the following requirements:

  • The method must be static.

  • The method must be non-native.

  • The method must not be synchronized.

  • The method must return the class instance in which it is declared.

  • The method must not contain parameters.

The project source code used in the current subsection is available at the following link:

When compiling, the RxMicro framework searches for RxMicro Annotations in the source code and generates additional classes necessary for the integral work of the microservice.

When changing the RxMicro Annotations in the source code, DON’T FORGET to recompile the ALL source code, not just the changed file, for the changes to take effect: mvn clean compile.

11. Factory Class

The RxMicro framework supports creation of factory classes, that can be used to create instances of other types.

By using factory classes, it is possible to get the following benefits:

  • Create dynamic classes. (For example, using the Proxy class.)

  • Implement a prototype scope.

To create a factory class, it is necessary:

  • Create a class implementing the Supplier interface.

  • Annotate this class by the @Factoryannotation.

  • Implement the get method, which should return the instance of the created class.

For example, to create a dynamic class, it is necessary to use the following factory class:

@Factory
public final class BusinessServiceFactoryImpl implements Supplier<BusinessService> {

    @Override
    public BusinessService get() {
        final Object o = new Object();
        return (BusinessService) Proxy.newProxyInstance(
                BusinessService.class.getClassLoader(),
                new Class[]{BusinessService.class},
                (proxy, method, args) -> {
                    if ("getValue".equals(method.getName())) {
                        return "PROXY";
                    } else {
                        return method.invoke(o, args);
                    }
                }
        );
    }
}

Injection of an instance created by the factory class does not differ from injection of an instance automatically created by the RxMicro framework:

public final class BusinessServiceFacade {

    @Inject
    BusinessService businessService;

    public String getValue() {
        return businessService.getValue();
    }

}

When invoking the getValue method, the dynamic class returns a predefined value:

@RxMicroComponentTest(BusinessServiceFacade.class)
final class BusinessServiceFacadeTest {

    private BusinessServiceFacade businessServiceFacade;

    @Test
    void Should_support_factory_classes() {
        assertEquals("PROXY", businessServiceFacade.getValue());
    }
}

The project source code used in the current subsection is available at the following link:

When compiling, the RxMicro framework searches for RxMicro Annotations in the source code and generates additional classes necessary for the integral work of the microservice.

When changing the RxMicro Annotations in the source code, DON’T FORGET to recompile the ALL source code, not just the changed file, for the changes to take effect: mvn clean compile.

12. External resource injection

12.1. Basic Usage

Indicates the need to inject the external resource into the annotated class field or method parameter.

External resource is:

  • File.

  • Directory.

  • Classpath resource.

  • URL Resource.

  • etc.

The RxMicro framework uses blocking API to inject resource during startup!

To inject external resource it is necessary to use @Resource annotation:

public class Component {

    @Resource("classpath:resources.properties")
    Map<String, String> resources;

    public Map<String, String> getResources() {
        return resources;
    }
}

Content of the classpath:resources.properties is:

name=value

After startup external resource is injected successful:

@RxMicroComponentTest(Component.class)
final class ComponentTest {

    private Component component;

    @Test
    void Should_the_resources_be_injected() {
        assertEquals(Map.of("name", "value"), component.getResources());
    }
}

12.2. Additional Info

To customize resource injection mechanism it is necessary to use @Resource annotation.

Example of valid resource paths:

If converter class is not specified, the RxMicro framework tries to detect valid resource converter automatically using the following algorithm:

  • io.rxmicro.cdi.resource.ClasspathJsonArrayResourceConverter is used if:

    • Resource path starts with classpath: prefix.

    • Resource path ends with json extension.

    • Annotated by @Resource annotation field has java.util.List<Object> type.

  • io.rxmicro.cdi.resource.ClasspathJsonObjectResourceConverter is used if:

    • Resource path starts with classpath: prefix.

    • Resource path ends with json extension.

    • Annotated by @Resource annotation field has java.util.Map<String, Object> type.

  • io.rxmicro.cdi.resource.ClasspathPropertiesResourceConverter is used if:

    • Resource path starts with classpath: prefix.

    • Resource path ends with properties extension.

  • io.rxmicro.cdi.resource.FileJsonArrayResourceConverter is used if:

    • Resource path starts with file:// prefix or prefix is missing.

    • Resource path ends with json extension.

    • Annotated by @Resource annotation field has java.util.List<Object> type.

  • io.rxmicro.cdi.resource.FileJsonObjectResourceConverter is used if:

    • Resource path starts with file:// prefix or prefix is missing.

    • Resource path ends with json extension.

    • Annotated by @Resource annotation field has java.util.Map<String, Object> type.

  • io.rxmicro.cdi.resource.FilePropertiesResourceConverter is used if:

    • Resource path starts with file:// prefix or prefix is missing.

    • Resource path ends with properties extension.

13. Optional Injection

By default, all injection points are required. Thus, if during the process of dependencies injection, the RxMicro framework does not find a suitable instance, an error will occur.

If the current project allows the situation when a suitable instance may be missing, then the optional injection mode should be used:

public final class BusinessServiceFacade {

    (1)
    @Inject(optional = true)
    BusinessService productionBusinessService = null;

    (2)
    @Autowired(required = false)
    BusinessService developmentBusinessService = new BusinessService() {
        @Override
        public String toString() {
            return "DefaultImpl";
        }
    };

    void postConstruct() {
        System.out.println(productionBusinessService);
        System.out.println(developmentBusinessService);
    }
}
1 When using the @Inject annotation, the optional = true parameter must be set to enable the optional injection mode.
2 When using the @Autowired annotation, the required = false parameter must be set to enable the optional injection mode.

If the optional injection mode is enabled, the RxMicro framework uses the following injection algorithm:

  1. If the dependency is found, it will be successfully injected.

  2. If there’s no dependency, nothing happens.
    (In this case, the behaviour appears to be as if the field is not annotated by any annotation!)

The correctness of this algorithm can be checked with the following test:

@RxMicroComponentTest(BusinessServiceFacade.class)
final class BusinessServiceFacadeTest {

    private SystemOut systemOut;

    @Test
    void Should_support_optional_injection() {
        assertEquals(
                List.of(
                        "null",
                        "DefaultImpl"
                ),
                systemOut.asStrings()
        );
    }
}

The project source code used in the current subsection is available at the following link:

When compiling, the RxMicro framework searches for RxMicro Annotations in the source code and generates additional classes necessary for the integral work of the microservice.

When changing the RxMicro Annotations in the source code, DON’T FORGET to recompile the ALL source code, not just the changed file, for the changes to take effect: mvn clean compile.

14. Multibindings

The RxMicro framework supports Multibindings.

Multibindings is a function of the CDI container, that allows You to find all implementations of this type and inject them.

For example, if there are two implementation classes of the BusinessService interface:

public final class ProductionBusinessService implements BusinessService {

    @Override
    public String getValue() {
        return "PRODUCTION";
    }
}
public final class DevelopmentBusinessService implements BusinessService {

    @Override
    public String getValue() {
        return "DEVELOPMENT";
    }
}

then the instances of these classes can be injected by the RxMicro framework into the java.util.Set type field:

public final class BusinessServiceFacade {

    @Inject
    Set<BusinessService> businessServices;

    void postConstruct() {
        System.out.println(
                businessServices.stream()
                        .map(s -> s.getClass().getSimpleName())
                        .collect(joining(", "))
        );
    }
}

After successful injection, the businessServices field will contain the ProductionBusinessService and DevelopmentBusinessService class instances:

@RxMicroComponentTest(BusinessServiceFacade.class)
final class BusinessServiceFacadeTest {

    private SystemOut systemOut;

    @Test
    void Should_support_multibinder() {
        assertEquals(
                "DevelopmentBusinessService, ProductionBusinessService",
                systemOut.asString()
        );
    }
}

The project source code used in the current subsection is available at the following link:

When compiling, the RxMicro framework searches for RxMicro Annotations in the source code and generates additional classes necessary for the integral work of the microservice.

When changing the RxMicro Annotations in the source code, DON’T FORGET to recompile the ALL source code, not just the changed file, for the changes to take effect: mvn clean compile.

Mongo Data Repositories

Home

Monitoring