© 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 thepom.xml
file:
<dependency>
<groupId>io.rxmicro</groupId>
<artifactId>rxmicro-cdi</artifactId>
<version>${rxmicro.version}</version>
</dependency>
-
Add the
rxmicro.cdi
module to themodule-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 When changing the |
2. RxMicro Annotations
The RxMicro framework supports the following RxMicro Annotations
:
Annotation | Description |
---|---|
Indicates the need to inject the component implementation into the annotated class field or method parameter. (Is a synonym of the |
|
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 |
|
Indicates the need to inject the component implementation into the annotated class field or method parameter. (Is a synonym of the |
|
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 |
|
(In its semantics, it completely corresponds to the |
|
Denotes a factory method or a factory, that creates instances of the specified class. |
|
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 ( |
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 When changing the |
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 When changing the |
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 When changing the |
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 toproductionBusinessService
. -
For the
io.rxmicro.examples.cdi.DevelopmentBusinessService
class, the name is equal todevelopmentBusinessService
. -
For the
io.rxmicro.ProductionBusinessService
class, the name is equal toproductionBusinessService
. -
For the
ProductionBusinessService
class, the name is equal toproductionBusinessService
.
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:
and four implementation classes:
then despite the same names for different classes:
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 When changing the |
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 When changing the |
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 When changing the |
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 When changing the |
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 When changing the |
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 When changing the |
In many frameworks implementing the Dependency Injection design template, besides the For a microservice project that uses the RxMicro framework, there is no need for the |
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 When changing the |
9.2. All Supported RxMicro Components
This section describes all supported RxMicro components that can be injected in any class using the injection mechanisms.
Name | Feature |
---|---|
Config instance. |
Any class that extends the basic configuration class: (For example: |
Mongo repository. |
The interface annotated by the |
Mongo client. |
Predefined type: |
PostgreSQL repository. |
The interface annotated by the |
R2DBC connection factory. |
Predefined type: |
R2DBC connection pool. |
Predefined type: |
REST client. |
The interface annotated by the |
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 @Factory annotation. |
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 @Factory
annotation, 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 When changing the |
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:
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 When changing the |
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:
-
/home/rxmicro/config.json
; -
/home/rxmicro/config.properties
; -
classpath:config.json
; -
classpath:config.properties
;
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 hasjava.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 hasjava.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 hasjava.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 hasjava.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:
-
If the dependency is found, it will be successfully injected.
-
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 When changing the |
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 When changing the |