README
The
REFCODES.ORG
codes represent a group of artifacts consolidating parts of my work in the past years. Several topics are covered which I consider useful for you, programmers, developers and software engineers.
What is this repository for?
This artifact provides a reactor for breaking up dependencies between a Java software system’s components (modules), which enables them to be developed and tested independently (dependency injection and inversion of control) from each other (decoupling). The reactor wires components together to form an interacting application by using dependency injection and inversion of control (IoC), which are two techniques that help achieve decoupling by allowing components to depend on abstractions instead of concrete implementations.
Why yet another dependency injection and inversion of control toolkit? Short answer: Because solving this kind of puzzle is fun :-)1. Longer answer: The various
REFCODES.ORG
artifacts all follow the same design principle of being a toolkit aiming at low resource consumption and focusing on simplicity - this artifact is that missing piece in the puzzle for building light weight Java applications with decoupled components. In comparison to full blown frameworks with their hunger for resources, this toolkit (artifact) aims at low resource consumption (e.g. for limited devices) and at being non-invasive (you can go without any annotations).
With this artifact, an application is constructed from a bag full of components, which’s layout depends on the components’ dependency requirements and other constraints such as profiles or claims. The <<requires>>
dependencies in the diagrams below denote the dependencies as declared by the constructors of the depending components. For example as of an activated profile, probably being “TEST” or “PROD”, different component constellations are constructed:
Quick start archetype
For a jump start into developing Java driven command line tools, I created some fully pre-configured Maven Archetypes available on Maven Central. Those Maven Archetypes already provide means to directly create native executables, bundles as well as launchers and support out of the box command line argument parsing as well as out of the box property file handling.
Use the refcodes-archetype-alt-decoupling
archetype to create a bare metal dependency injection and inversion of control (IoC
) driven Java application breaking up dependencies between components of a software system:
Please adjust
my.corp
with your actualGroup-ID
andmyapp
with your actualArtifact-ID
:
How do I get set up?
To get up and running, include the following dependency (without the three dots “…”) in your pom.xml
:
1
2
3
4
5
6
7
8
9
<dependencies>
...
<dependency>
<artifactId>refcodes-decoupling</artifactId>
<groupId>org.refcodes</groupId>
<version>3.3.9</version>
</dependency>
...
</dependencies>
The artifact is hosted directly at Maven Central. Jump straight to the source codes at Bitbucket. Read the artifact’s javadoc at refcodes-decoupling.
How it works
The
refcodes-decoupling
artifact provides imperative (you get what you code) and tiny (no annotations required) means to declare your software components’ dependencies by decoupling the components from each other using dependency injection and inversion of control (IoC). These two techniques help achieve decoupling by allowing components to depend on abstractions instead of concrete implementations.
Main players here are the types Dependnecy
, DependnecyBuilder
, Reactor
as well as Context
(alongside its offsprings ApplicationReactor
, ApplicationContext
and the CtxHelper
):
Dependency
: Describes a component to be provided to or wired together with other components.DependnecyBuilder
: Fluently declares aDependency
which is added to theReactor
.Reactor
: Analyzes allDependencies
and produces aContext
when wiring theDependencies
’s components.Context
: Contains the wired components as of theDependencies
created by theReactor
.ApplicationReactor
: An extension of theReactor
with event bus and application properties support.ApplicationContext
: An extension of theContext
with application lifecycle support.CtxHelper
: An application helper for using theApplicationReactor
and theApplicationContext
.
A Reactor
instance provides means to create DependnecyBuilder
instances which are used to fluently declare Dependencies
. Dependencies
describe the components of your application. A Dependency
is described (amongst others) by its type and an InstanceMode
. The InstanceMode
tells the Reactor
whether we have a singleton and whether the Dependency
is mandatory. A mandatory Dependency
’s component is created anyway, if it is not mandatory, then it is only created in case another Dependency
requires it. Basically, a Dependency
is something, from which an instance (component) of a given type can be constructed. The constructors of your Dependencies
’ types tell the Reactor
, which Dependency
components are to be wired together. Wiring the components together is done by constructor injection and results in a Context
instance, representing the application as it has been wired together. Claims
on required Dependencies
can be declared for a Dependency
to be satisfied: Such a Dependency
can only be created when all its Claims
can be satisfied. In addition, a FactoryClaim
can be declared upon a Dependency
, claiming the factory for fabricating the Dependency
’s instances (components). Also an InitializerClaim
can be declared claiming a Dependency
for initializing the Dependency
’s instances (components) after creation.
Snippets of interest
Below find some code snippets which demonstrate the various aspects of using the refcodes-decoupling
artifact (and , if applicable, its offsprings). See also the example source codes of the refcodes-decoupling
artifact as well as the refcodes-decoupling-ext-application
artifact’s example source codes for further information on the usage of this artifact.
Dependency injection using inversion of control
Setting up an application configured by an application.ini
configuration file, consisting of a view
, a controller
(alongside an according configuration
), a service
and a repository
(alongside yet another according configuration
) being wired together as of their constructor parameters (optionally loosely coupled by an event bus2) is as easy is this:
1
2
3
4
5
6
7
8
ApplicationReactor theReactor = new ApplicationReactor( "application.ini" );
theReactor.addDependency( View.class );
theReactor.addDependency( Controller.class );
theReactor.addDependency( Service.class );
theReactor.addDependency( Repository.class );
theReactor.addConfiguration( Controller.Config.class, "controller" ).withInstanceMetrics( InstanceMode.SINGLETON_ON_DEMAND ); // Populate record from properties file
theReactor.addConfiguration( Repository.Config.class, "repository" ).withInstanceMetrics( InstanceMode.SINGLETON_ON_DEMAND ); // Populate record from properties file
ApplicationContext theCtx = theReactor.createContext();
In this example, which has been derived from the quick start archetype, the
Controller.Config
type and theRepository.Config
type (lines 6 and 7) are actuallyRecord
types, usingPOJO
types as of theJavaBeans
conventions would also have been possible.
Decoupling with profiles
Given a baseline of components where we have Service
component as well as an alternate TestService
extensions of it for testing purposes, which results in two candidates matching the Service
type. The Service
is declared for the “PROD” profile, the TestService
is declared for the “TEST” profile:
1
2
3
4
5
6
7
8
9
ApplicationReactor theReactor = new ApplicationReactor( "application.ini" );
theReactor.addDependency( View.class );
theReactor.addDependency( Controller.class );
theReactor.addDependency( Service.class ).withAddProfile("PROD");
theReactor.addDependency( TestService.class ).withAddProfile("TEST");
theReactor.addDependency( Repository.class ).withInstanceMetrics( InstanceMode.SINGLETON_ON_DEMAND );
theReactor.addConfiguration( Controller.Config.class, "controller" ).withInstanceMetrics( InstanceMode.SINGLETON_ON_DEMAND ); // Populate record from properties file
theReactor.addConfiguration( Repository.Config.class, "repository" ).withInstanceMetrics( InstanceMode.SINGLETON_ON_DEMAND ); // Populate record from properties file
ApplicationContext theCtx = theReactor.createContext("PROD");
Creating the Context
as above results in a “PROD” context being created. For testing, we would create the Context
with a profile “TEST” (as of ApplicationContext theCtx = theReactor.createContext("TEST");
), resulting in a “TEST” context being created.
As the
Repository
is only created on demand (InstanceMode.SINGLETON_ON_DEMAND
) at line 7, it is not instantiated when creating theContext
with the “TEST” profile (see also the three diagrams above)!
You may place a line runtime.profiles=PROD
or runtime.profiles=TEST
into the application.ini
properties file to select the according profile, or you may override the profile setting of the properties file by launching your application with the java system property -Druntime.profiles=PROD
or -Druntime.profiles=TEST
.
Note that the ApplicationContext
automatically provides application properties as well as an event bus (here being an instance of the ApplicationBus
class) if required!
Declaring claims upon your dependencies
Given a ComponentQ
, a ComponentQ1
and a ComponentQ2
. The ComponentQ
depends on a ComponentQ1
and a ComponentQ2
. The components ComponentQ1
and a ComponentQ2
are injected using ComponentQ
’s constructor:
1
2
3
public ComponentQ( ComponentQ1 q1, ComponentQ2 q2 ) {
...
}
Declaring Claims
upon your Dependencies
ensures that the according claimed Dependencies
will be provided to your Dependencies
’ components:
1
2
3
4
5
6
ApplicationReactor theReactor = new ApplicationReactor( "application.ini" );
theReactor.addDependency( ComponentQ.class ).withAlias( "Q" ).withAddClaim( ComponentQ1.class, "Q1" );
theReactor.addDependency( new ComponentQ1( "Q1, take this one" ) ).withAlias( "Q1" );
theReactor.addDependency( new ComponentQ1( "Another Q1, don't pick it" ) );
theReactor.addDependency( new ComponentQ2( "Q2" ) );
ApplicationContext theCtx = theReactor.createContext();
In line 2 ComponentQ
claims a Dependency
of type ComponentQ1
having an alias “Q1”. In lines 3 and 4 we declare two Dependencies
of type ComponentQ1
, which would be ambiguous for the Reactor
, as it would not know which of those two to provide to ComponentQ
. The ambiguousness is resolved by specifically claiming the Dependency
with alias “Q1” (line 2) and by assigning the required alias to the Dependency
in question (line 3).
Using factories to create dependencies
Considering the above example, the components are now to be created by factories: A shortcut for doing so is to declare a FactoryClaim
fabricating a Dependency
’s component(s) by the Reactor
type:
1
2
3
4
5
ApplicationReactor theReactor = new ApplicationReactor( "application.ini" );
theReactor.addDependency( ComponentQ.class, QFactory.class ).withInstanceMetrics( InstanceMode.INSTANCE_IS_MANDATORY );
theReactor.addDependency( ComponentQ1.class, () -> new ComponentQ1( "Q1" ) ).withInstanceMetrics( InstanceMode.INSTANCE_ON_DEMAND );
theReactor.addDependency( ComponentQ2.class, () -> new ComponentQ2( "Q2" ) ).withInstanceMetrics( InstanceMode.INSTANCE_ON_DEMAND );
ApplicationContext theCtx = theReactor.createContext();
In lines 3 and 4, the components ComponentQ1
and ComponentQ2
are to be fabricated by a simple factory just producing the according instances. In line 2, we declare a factory QFactory
to produce ComponentQ
. As ComponentQ
depends on ComponentQ1
alongside ComponentQ2
(see previous example), the QFactory
needs to be provided with those two components for it to fabricate component ComponentQ
. Therefore the QFactory
is considered a component as well being provided ComponentQ1
and ComponentQ2
by the Reactor
. For the Reactor
to use the QFactory
to create ComponentQ
components, the QFactory
must implement the Factory
interface:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class QFactory implements Factory<ComponentQ> {
private ComponentQ1 q1;
private ComponentQ2 q2;
public QFactory( ComponentQ1 q1, ComponentQ2 q2 ) {
this.q1 = q1;
this.q2 = q2;
}
@Override
public ComponentQ create() {
return new ComponentQ( q1.getAlias() + "+" + q2.getAlias() );
}
}
In line 1 the Factory
interface is declared for implementation and at line 12 the according create()
method is provided. Instead of the QFactory
factory having to implement the Factory
interface alongside the create()
method, an arbitrary factory method (instead of the create()
method) can be called with providing an according lambda expression invoking the required method:
1
2
3
4
5
ApplicationReactor theReactor = new ApplicationReactor( "application.ini" );
theReactor.addDependency( ComponentQ.class, QFactory.class, ( f ) -> f.fabricateQ() ).withInstanceMetrics( InstanceMode.INSTANCE_IS_MANDATORY );
theReactor.addDependency( new ComponentQ1( "Q1" ) );
theReactor.addDependency( new ComponentQ2( "Q2" ) );
ApplicationContext theCtx = theReactor.createContext();
In line 2 we explicitly declare the method fabricateQ()
to be invoked instead of the create()
method (which is defined by the Factory
interface). The QFactory
now must provide an according fabricateQ()
method without the need to implement any interface.
Initializing dependencies after creation
A Dependency
may claim an InitializerClaim
for configuring a component after creation:
1
2
3
4
5
ApplicationReactor theReactor = new ApplicationReactor( "application.ini" );
theReactor.addDependency( ComponentQ.class );
theReactor.addDependency( ComponentQ1.class ).withInitializer( ComponentQ2.class, ( t, d ) -> { t.setAlias( "Hello " + d.getAlias() ); return t; } );
theReactor.addDependency( new ComponentQ2( "ComponentQ2" ) );
ApplicationContext theCtx = theReactor.createContext();
In line 3 an InitializerClaim
is declared which initializes ComponentQ1
after construction with data from ComponentQ2
(the Reactor
takes care to create the Dependencies
’ components in the correct order). To make things easier, ComponentQ2
could implement the Initializer
interface’s initialize(...)
method which then takes care of initialization:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class ComponentQ2 implements Initializer<ComponentQ1> {
private String _alias;
public ComponentQ2( String aAlias ) {
_alias = aAlias;
}
public String getAlias() {
return _alias;
}
/**
* {@inheritDoc}
*/
@Override
public ComponentQ1 initialize( ComponentQ1 aInstance ) {
aInstance.setAlias( "Hello " + getAlias() );
return aInstance;
}
}
In line 1, the Initializer
is declared to be implemented by ComponentQ2
and in line 17 the initialize(...)
method is being provided. Initializing ComponentQ1
now is as easy as this:
1
2
3
4
5
ApplicationReactor theReactor = new ApplicationReactor( "application.ini" );
theReactor.addDependency( ComponentQ.class );
theReactor.addDependency( ComponentQ1.class ).withInitializer( ComponentQ2.class );
theReactor.addDependency( new ComponentQ2( "ComponentQ2" ) );
ApplicationContext theCtx = theReactor.createContext();
Intercepting the creation of instances
By adding an DependencyInterceptor
to your Reactor
you may tweak upon any instances created for a Dependency
. The DependencyInterceptor
defines the method intercept
to intercept instance creation:
1
2
3
4
5
6
7
8
9
10
11
ApplicationReactor theReactor = new ApplicationReactor( "application.ini" );
theReactor.addDependency( ComponentQ.class );
theReactor.addDependency( ComponentQ1.class );
theReactor.addDependency( ComponentQ2.class );
theReactor.addInterceptor( ( t, d ) -> {
if ( t instanceof ComponentQ2 q2 ) {
q2.setAlias( "Intercepted and modified after creation!" );
}
return t;
} );
ApplicationContext theCtx = theReactor.createContext();
As the DependencyInterceptor
is a functional interface, we can directly provide a lambda expression (line 5) in place of a DependencyInterceptor
instance (t
in the above lambda expression stands for the created instance of the give type as defined by the Dependency
d
).
Using the archetype’s CTX-Helper
The refcodes-archetype-alt-decoupling
archetype (see the quick start archetype) makes use of the CTX-Helper (context helper) defined in the the refcodes-archetype
artifact, which bundles dependency injection and inversion of control (IoC) functionality together with functionality configuring your application.
When using the CTX-Helper, the first half of the code is about configuring your command line arguments’ syntax (see also refcodes-cli: Parse your args[]: Using the REFCODES.ORG toolkit made easy ) and other stuff needed to get your application up and running properly:
public static void main( String args[] ) { [...]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
public static void main( String args[] ) {
Flag theInitFlag = initFlag();
ConfigOption theConfigOption = configOption();
Flag theVerboseFlag = verboseFlag();
Flag theSysInfoFlag = sysInfoFlag();
Flag theHelpFlag = helpFlag();
Flag theDebugFlag = debugFlag();
Term theArgsSyntax = cases(
optional( theConfigOption, theVerboseFlag, theDebugFlag ),
and( theInitFlag, optional( theConfigOption, theVerboseFlag, theDebugFlag) ),
xor( theHelpFlag, and( theSysInfoFlag, any ( theVerboseFlag ) ) )
);
Example[] theExamples = examples(
example( "Run application (more verbose)", theVerboseFlag ),
example( "Run by using the config file (more verbose)", theConfigOption, theVerboseFlag ),
example( "Initialize default config file", theInitFlag, theVerboseFlag),
example( "Initialize specific config file", theConfigOption, theInitFlag, theVerboseFlag),
example( "To show the help text", theHelpFlag ),
example( "To print the system info", theSysInfoFlag )
);
CtxHelper theCtxHelper = CtxHelper.builder().
withArgs( args ).
// withArgs( args, ArgsFilter.D_XX ).
withArgsSyntax( theArgsSyntax ).
withExamples( theExamples ).
withFilePath( DEFAULT_CONFIG ). // Must be the name of the default (template) configuration file below "/src/main/resources"
withResourceClass( Main.class ).
withName( NAME ).
withTitle( TITLE ).
withDescription( DESCRIPTION ).
withLicense( LICENSE_NOTE ).
withCopyright( COPYRIGHT ).
withBannerFont( BANNER_FONT ).
withBannerFontPalette( BANNER_PALETTE ).
withLogger( LOGGER ).build();
The main concern of the CTX-Helper, beyond configuring your command line arguments’ syntax and more, is to configure your dependencies which are to be wired together:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
try {
theCtxHelper.addDependency( View.class );
theCtxHelper.addDependency( Controller.class );
theCtxHelper.addDependency( Service.class );
theCtxHelper.addDependency( Repository.class );
theCtxHelper.addConfiguration( Controller.Config.class, "controller" ).withInstanceMetrics( InstanceMode.SINGLETON_ON_DEMAND ); // Populate record from properties file
theCtxHelper.addConfiguration( Repository.Config.class, "repository" ).withInstanceMetrics( InstanceMode.SINGLETON_ON_DEMAND ); // Populate record from properties file
ApplicationContext theCtx = theCtxHelper.createContext();
if ( theCtxHelper.isVerbose() ) {
// Show some insights on the created context |-->
for ( Object e : theCtx.getInstances() ) {
LOGGER.printSeparator();
LOGGER.info( "Instance = " + e );
}
LOGGER.printTail();
System.out.println( theCtx.toSchema() );
// Show some insights on the created context <--|
}
}
catch ( Throwable e ) {
theCtxHelper.exitOnException( e );
}
}
The dependency schema
As of the REFCODES.ORG
’s Schema
mechanism, you may create an outline of your dependencies as created at runtime by retrieving an according DependencySchema
(as of theCtx.toSchema()
at line 16 of the above code snippet). The produced schema provides you insights on your application and looks similar to the one below:
Note that the ApplicationContext
automatically provides application properties as well as an event bus (here being an instance of the ApplicationBus
class) if required!
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
ApplicationContext: {
DESCRIPTION: "Context managing dependencies between modules.",
TYPE: "org.refcodes.decoupling.ext.application.ApplicationContext",
Dependency: {
ALIAS: "view",
COMMENT: "Dependency [alias=view, tags=[], profiles=[], type=class club.funcodes.View, instances=[View [applicationBus=org.refcodes.eventbus.ext.application.ApplicationBus@24fcf36f]], instanceMetrics=SINGLETON_IS_MANDATORY]",
DEPENDENCY: "club.funcodes.View",
DESCRIPTION: "Singleton dependency of type <View>",
INSTANCES: { "View [applicationBus=org.refcodes.eventbus.ext.application.ApplicationBus@24fcf36f]" },
MANDATORY: true,
SINGLETON: true,
SIZE: 1,
TYPE: "org.refcodes.decoupling.Dependency",
Dependency: {
ALIAS: "applicationBus",
COMMENT: "Dependency [alias=applicationBus, tags=[], profiles=[], type=class org.refcodes.eventbus.ext.application.ApplicationBus, instances=[org.refcodes.eventbus.ext.application.ApplicationBus@24fcf36f], instanceMetrics=SINGLETON_ON_DEMAND]",
DEPENDENCY: "org.refcodes.eventbus.ext.application.ApplicationBus",
DESCRIPTION: "Singleton dependency of type <ApplicationBus>",
INSTANCES: { "org.refcodes.eventbus.ext.application.ApplicationBus@24fcf36f" },
MANDATORY: false,
SINGLETON: true,
SIZE: 1,
TYPE: "org.refcodes.decoupling.Dependency"
}
},
Dependency: {
ALIAS: "controller",
COMMENT: "Dependency [alias=controller, tags=[], profiles=[], type=class club.funcodes.Controller, instances=[Controller [service=Service [repository=Repository [repositoryConfig=Config[url=192.168.1.198:3306, user=maintainer, secret=Secret123]]], controllerConfig=Config[port=80, protocol=https]]], instanceMetrics=SINGLETON_IS_MANDATORY]",
DEPENDENCY: "club.funcodes.Controller",
DESCRIPTION: "Singleton dependency of type <Controller>",
INSTANCES: { "Controller [service=Service [repository=Repository [repositoryConfig=Config[url=192.168.1.198:3306, user=maintainer, secret=Secret123]]], controllerConfig=Config[port=80, protocol=https]]" },
MANDATORY: true,
SINGLETON: true,
SIZE: 1,
TYPE: "org.refcodes.decoupling.Dependency",
Dependency: {
ALIAS: "applicationBus",
COMMENT: "Dependency [alias=applicationBus, tags=[], profiles=[], type=class org.refcodes.eventbus.ext.application.ApplicationBus, instances=[org.refcodes.eventbus.ext.application.ApplicationBus@24fcf36f], instanceMetrics=SINGLETON_ON_DEMAND]",
DEPENDENCY: "org.refcodes.eventbus.ext.application.ApplicationBus",
DESCRIPTION: "Singleton dependency of type <ApplicationBus>",
INSTANCES: { "org.refcodes.eventbus.ext.application.ApplicationBus@24fcf36f" },
MANDATORY: false,
SINGLETON: true,
SIZE: 1,
TYPE: "org.refcodes.decoupling.Dependency"
},
Dependency: {
ALIAS: "service",
COMMENT: "Dependency [alias=service, tags=[], profiles=[], type=class club.funcodes.Service, instances=[Service [repository=Repository [repositoryConfig=Config[url=192.168.1.198:3306, user=maintainer, secret=Secret123]]]], instanceMetrics=SINGLETON_IS_MANDATORY]",
DEPENDENCY: "club.funcodes.Service",
DESCRIPTION: "Singleton dependency of type <Service>",
INSTANCES: { "Service [repository=Repository [repositoryConfig=Config[url=192.168.1.198:3306, user=maintainer, secret=Secret123]]]" },
MANDATORY: true,
SINGLETON: true,
SIZE: 1,
TYPE: "org.refcodes.decoupling.Dependency",
Dependency: {
ALIAS: "repository",
COMMENT: "Dependency [alias=repository, tags=[], profiles=[], type=class club.funcodes.Repository, instances=[Repository [repositoryConfig=Config[url=192.168.1.198:3306, user=maintainer, secret=Secret123]]], instanceMetrics=SINGLETON_IS_MANDATORY]",
DEPENDENCY: "club.funcodes.Repository",
DESCRIPTION: "Singleton dependency of type <Repository>",
INSTANCES: { "Repository [repositoryConfig=Config[url=192.168.1.198:3306, user=maintainer, secret=Secret123]]" },
MANDATORY: true,
SINGLETON: true,
SIZE: 1,
TYPE: "org.refcodes.decoupling.Dependency",
Dependency: {
ALIAS: "repositoryConfig",
COMMENT: "Dependency [alias=repositoryConfig, tags=[], profiles=[], type=class club.funcodes.Repository$Config, instances=[Config[url=192.168.1.198:3306, user=maintainer, secret=Secret123]], instanceMetrics=SINGLETON_ON_DEMAND]",
DEPENDENCY: "club.funcodes.Repository$Config",
DESCRIPTION: "Singleton dependency of type <Config>",
FACTORY: FactoryClaim [alias=applicationProperties, type=interface org.refcodes.properties.Properties],
INSTANCES: { "Config[url=192.168.1.198:3306, user=maintainer, secret=Secret123]" },
MANDATORY: false,
SINGLETON: true,
SIZE: 1,
TYPE: "org.refcodes.decoupling.Dependency",
FactoryClaim: {
ALIAS: "applicationProperties",
COMMENT: "FactoryClaim [alias=applicationProperties, type=interface org.refcodes.properties.Properties]",
DEPENDENCY: "org.refcodes.properties.Properties",
DESCRIPTION: "A factory being claimed to be applied by a dependency when creating its instances.",
FACTORY: "org.refcodes.decoupling.ext.application.ApplicationReactor$$Lambda$52/0x0000000800252d90",
TYPE: "org.refcodes.decoupling.FactoryClaim"
}
}
}
},
Dependency: {
ALIAS: "controllerConfig",
COMMENT: "Dependency [alias=controllerConfig, tags=[], profiles=[], type=class club.funcodes.Controller$Config, instances=[Config[port=80, protocol=https]], instanceMetrics=SINGLETON_ON_DEMAND]",
DEPENDENCY: "club.funcodes.Controller$Config",
DESCRIPTION: "Singleton dependency of type <Config>",
FACTORY: FactoryClaim [alias=applicationProperties, type=interface org.refcodes.properties.Properties],
INSTANCES: { "Config[port=80, protocol=https]" },
MANDATORY: false,
SINGLETON: true,
SIZE: 1,
TYPE: "org.refcodes.decoupling.Dependency",
FactoryClaim: {
ALIAS: "applicationProperties",
COMMENT: "FactoryClaim [alias=applicationProperties, type=interface org.refcodes.properties.Properties]",
DEPENDENCY: "org.refcodes.properties.Properties",
DESCRIPTION: "A factory being claimed to be applied by a dependency when creating its instances.",
FACTORY: "org.refcodes.decoupling.ext.application.ApplicationReactor$$Lambda$52/0x0000000800252d90",
TYPE: "org.refcodes.decoupling.FactoryClaim"
}
}
},
Dependency: {
ALIAS: "service",
COMMENT: "Dependency [alias=service, tags=[], profiles=[], type=class club.funcodes.Service, instances=[Service [repository=Repository [repositoryConfig=Config[url=192.168.1.198:3306, user=maintainer, secret=Secret123]]]], instanceMetrics=SINGLETON_IS_MANDATORY]",
DEPENDENCY: "club.funcodes.Service",
DESCRIPTION: "Singleton dependency of type <Service>",
INSTANCES: { "Service [repository=Repository [repositoryConfig=Config[url=192.168.1.198:3306, user=maintainer, secret=Secret123]]]" },
MANDATORY: true,
SINGLETON: true,
SIZE: 1,
TYPE: "org.refcodes.decoupling.Dependency",
Dependency: {
ALIAS: "repository",
COMMENT: "Dependency [alias=repository, tags=[], profiles=[], type=class club.funcodes.Repository, instances=[Repository [repositoryConfig=Config[url=192.168.1.198:3306, user=maintainer, secret=Secret123]]], instanceMetrics=SINGLETON_IS_MANDATORY]",
DEPENDENCY: "club.funcodes.Repository",
DESCRIPTION: "Singleton dependency of type <Repository>",
INSTANCES: { "Repository [repositoryConfig=Config[url=192.168.1.198:3306, user=maintainer, secret=Secret123]]" },
MANDATORY: true,
SINGLETON: true,
SIZE: 1,
TYPE: "org.refcodes.decoupling.Dependency",
Dependency: {
ALIAS: "repositoryConfig",
COMMENT: "Dependency [alias=repositoryConfig, tags=[], profiles=[], type=class club.funcodes.Repository$Config, instances=[Config[url=192.168.1.198:3306, user=maintainer, secret=Secret123]], instanceMetrics=SINGLETON_ON_DEMAND]",
DEPENDENCY: "club.funcodes.Repository$Config",
DESCRIPTION: "Singleton dependency of type <Config>",
FACTORY: FactoryClaim [alias=applicationProperties, type=interface org.refcodes.properties.Properties],
INSTANCES: { "Config[url=192.168.1.198:3306, user=maintainer, secret=Secret123]" },
MANDATORY: false,
SINGLETON: true,
SIZE: 1,
TYPE: "org.refcodes.decoupling.Dependency",
FactoryClaim: {
ALIAS: "applicationProperties",
COMMENT: "FactoryClaim [alias=applicationProperties, type=interface org.refcodes.properties.Properties]",
DEPENDENCY: "org.refcodes.properties.Properties",
DESCRIPTION: "A factory being claimed to be applied by a dependency when creating its instances.",
FACTORY: "org.refcodes.decoupling.ext.application.ApplicationReactor$$Lambda$52/0x0000000800252d90",
TYPE: "org.refcodes.decoupling.FactoryClaim"
}
}
}
},
Dependency: {
ALIAS: "repository",
COMMENT: "Dependency [alias=repository, tags=[], profiles=[], type=class club.funcodes.Repository, instances=[Repository [repositoryConfig=Config[url=192.168.1.198:3306, user=maintainer, secret=Secret123]]], instanceMetrics=SINGLETON_IS_MANDATORY]",
DEPENDENCY: "club.funcodes.Repository",
DESCRIPTION: "Singleton dependency of type <Repository>",
INSTANCES: { "Repository [repositoryConfig=Config[url=192.168.1.198:3306, user=maintainer, secret=Secret123]]" },
MANDATORY: true,
SINGLETON: true,
SIZE: 1,
TYPE: "org.refcodes.decoupling.Dependency",
Dependency: {
ALIAS: "repositoryConfig",
COMMENT: "Dependency [alias=repositoryConfig, tags=[], profiles=[], type=class club.funcodes.Repository$Config, instances=[Config[url=192.168.1.198:3306, user=maintainer, secret=Secret123]], instanceMetrics=SINGLETON_ON_DEMAND]",
DEPENDENCY: "club.funcodes.Repository$Config",
DESCRIPTION: "Singleton dependency of type <Config>",
FACTORY: FactoryClaim [alias=applicationProperties, type=interface org.refcodes.properties.Properties],
INSTANCES: { "Config[url=192.168.1.198:3306, user=maintainer, secret=Secret123]" },
MANDATORY: false,
SINGLETON: true,
SIZE: 1,
TYPE: "org.refcodes.decoupling.Dependency",
FactoryClaim: {
ALIAS: "applicationProperties",
COMMENT: "FactoryClaim [alias=applicationProperties, type=interface org.refcodes.properties.Properties]",
DEPENDENCY: "org.refcodes.properties.Properties",
DESCRIPTION: "A factory being claimed to be applied by a dependency when creating its instances.",
FACTORY: "org.refcodes.decoupling.ext.application.ApplicationReactor$$Lambda$52/0x0000000800252d90",
TYPE: "org.refcodes.decoupling.FactoryClaim"
}
}
},
Dependency: {
ALIAS: "applicationProperties",
COMMENT: "Dependency [alias=applicationProperties, tags=[], profiles=[], type=class org.refcodes.properties.PropertiesPrecedenceComposite, instances=[org.refcodes.properties.PropertiesPrecedenceComposite@625732], instanceMetrics=SINGLETON_BY_DEFAULT]",
DEPENDENCY: "org.refcodes.properties.PropertiesPrecedenceComposite",
DESCRIPTION: "Singleton dependency of type <PropertiesPrecedenceComposite>",
INSTANCES: { "org.refcodes.properties.PropertiesPrecedenceComposite@625732" },
MANDATORY: true,
SINGLETON: true,
SIZE: 1,
TYPE: "org.refcodes.decoupling.Dependency"
},
Dependency: {
ALIAS: "applicationContext",
COMMENT: "Dependency [alias=applicationContext, tags=[], profiles=[], type=class org.refcodes.decoupling.ext.application.ApplicationContext, instances=[org.refcodes.decoupling.ext.application.ApplicationContext@15bbf42f], instanceMetrics=SINGLETON_BY_DEFAULT]",
DEPENDENCY: "org.refcodes.decoupling.ext.application.ApplicationContext",
DESCRIPTION: "Singleton dependency of type <ApplicationContext>",
INSTANCES: { "org.refcodes.decoupling.ext.application.ApplicationContext@15bbf42f" },
MANDATORY: true,
SINGLETON: true,
SIZE: 1,
TYPE: "org.refcodes.decoupling.Dependency"
}
}
The schema above lists all Dependencies
as well as the construction insights of each individual Dependency
in a JSON
alike format (when printed via the schema’s #toString()
method).
Only required
Dependencies
andDependencies
without any components (instances) created are listed directly below the root hierarchy, all otherDependencies
are shown as part of their parents.
Examples
See the example source codes of the refcodes-decoupling
artifact as well as the refcodes-decoupling-ext-application
artifact’s example source codes for further information on the usage of this artifact.
See also
- Publish … subscribe … observe … event-bus?
- Dead simple Java application configuration
- All-in-one Java configuration properties at hand
- Automatically obfuscate your Java application’s configuration
- The canonical model, an ace upon your sleeve
- refcodes-eventbus: Observer + Publish/Subscribe = Message broker
- refcodes-properties: Managing your application’s configuration
- refcodes-archetype: Using the REFCODES.ORG toolkit made easy
- refcodes-cli: Parse your args[]
Contribution guidelines
- Report issues
- Finding bugs
- Helping fixing bugs
- Making code and documentation better
- Enhance the code
Who do I talk to?
- Siegfried Steiner (steiner@refcodes.org)
Terms and conditions
The REFCODES.ORG
group of artifacts is published under some open source licenses; covered by the refcodes-licensing
(org.refcodes
group) artifact - evident in each artifact in question as of the pom.xml
dependency included in such artifact.