refcodes-decoupling: Breaking up dependencies between components

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 actual Group-ID *and myapp with your a

mvn archetype:generate \
  -DarchetypeGroupId=org.refcodes \
  -DarchetypeArtifactId=refcodes-archetype-alt-decoupling \
  -DarchetypeVersion=3.3.5 \
  -DgroupId=my.corp \
  -DartifactId=myapp \
  -Dversion=0.0.1

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.5</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):

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 serviceand 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 the Repository.Config type (lines 6 and 7) are actually Record types, using POJO types as of the JavaBeans 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 the Context 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 and Dependencies without any components (instances) created are listed directly below the root hierarchy, all other Dependencies 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

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.

  1. Yes, I am well aware that the definition of fun can be quite controversial 🙂. 

  2. If stated as a constructor argument, an event bus (see also the recommended reading) as defined by the refcodes-eventbus-ext-application artifact is created as singleton and provided as dependency.