An enterprise application is made up of four layers:
- Presentation
- Application or Service Layer
- Domain or Business layer
- Data Access or Persistence Layer
In ‘First Aspect of Layering Using Spring BeanFactoryLocator’, we have seen how to approach layering using spring.
One can have separate application Context for each of the above layers.
In this article, we will further modularize the code by pushing the concerns dealing with layering to a framework, so that the application module can just focus on the domain and avoid getting into the technicalities of layering.
However the challenge lies in the framework module to allow placeholder of XML context files which the application module can override with its own set of context files. Let’s start with our example but first a bit about the setup.
This example uses the following frameworks:
Dependencies
Add the following dependencies:
spring-core
spring-context
spring-beans
Composite of Context Files
Each layer will have its own ApplicationContext
definition and beanRefFactory.xml
will contain the respective ApplicationContext
beans with their context files. If you want to know more about the layering, please read my article ‘First Aspect of Layering Using Spring BeanFactoryLocator’ Spring BeanFactoryLocator’.
If I want to fetch a bean from a domain layer, all I have to do is use DomainContext
. There will be one context class for each layer. For simplicity, we will only show the context for the domain layer here. You can fetch a domain object like below:
DomainObject1 frameworkDomain1 = (DomainObject1) DomainContext.getBean("domain1");
The bean that defines domain layer’s ApplicationContext
.
<bean id="DOMAIN" class="org.springframework.context.support.ClassPathXmlApplicationContext"> <constructor-arg index="0" ref="compositeList" /> </bean>
Note that the list of XML files that the domain layer is made up of are a composite of framework module and application module’s domain context files. This is defined by bean compositeList
.
<bean id="compositeList" class="com.javarticles.CompositeList"> <property name="frameworkLocationList" ref="frameworkContextList" /> <property name="appLocationList" ref="appDomainContextList" /> </bean>
frameworkContextList
and appDomainContextList are beans representing List
of context XML files.
<bean id="frameworkContextList" class="java.util.ArrayList"> <constructor-arg index="0"> <list> <value>frameworkDomainContext1.xml</value> <value>frameworkDomainContext2.xml</value> </list> </constructor-arg> </bean> <bean id="appDomainContextList" class="java.util.ArrayList"> </bean>
appDomainContextList
is an empty list and will be overridden by the application module.
How do we allow overriding the bean?
We use an import
of another beanRefFactory.xml
which will be application specific.
<import resource="classpath*:appBeanRefFactory.xml" />
Here is the complete beanRefFactory.xml
.
beanRefFactory.xml:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="DOMAIN" class="org.springframework.context.support.ClassPathXmlApplicationContext"> <constructor-arg index="0" ref="compositeList" /> </bean> <bean id="compositeList" class="com.javarticles.CompositeList"> <property name="frameworkLocationList" ref="frameworkContextList" /> <property name="appLocationList" ref="appDomainContextList" /> </bean> <bean id="frameworkContextList" class="java.util.ArrayList"> <constructor-arg index="0"> <list> <value>frameworkDomainContext1.xml</value> <value>frameworkDomainContext2.xml</value> </list> </constructor-arg> </bean> <bean id="appDomainContextList" class="java.util.ArrayList"> </bean> <import resource="classpath*:appBeanRefFactory.xml" /> </beans>
For the framework module to successfully load the beanRefFactory.xml
, we will have an empty appBeanRefFactory.xml
file.
Let’s make sure we can find a framework specific domain object.
SpringFrameworkContextExample:
package com.javarticles; import com.javarticles.business.DomainContext; import com.javarticles.business.DomainObject1; import com.javarticles.business.DomainObject2; public class SpringFrameworkContextExample { public static void main(String[] args) { DomainObject1 frameworkDomain1 = (DomainObject1) DomainContext.getBean("domain1"); System.out.println(frameworkDomain1); DomainObject2 frameworkDomain2 = (DomainObject2) DomainContext.getBean("domain2"); System.out.println(frameworkDomain2); } }
Output:
Framework Domain Object1 Framework Domain Object2
Override Bean
Application module will provide the actual appBeanRefFactory.xml
.
appBeanRefFactory.xml:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="appDomainContextList" class="java.util.ArrayList"> <constructor-arg> <list> <value>appDomainContext1.xml</value> <value>appDomainContext2.xml</value> </list> </constructor-arg> </bean> </beans>
Each domain context file contains domain specific beans.
appDomainContext1.xml:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="appDomain1" class="com.javarticles.business.DomainObject1"> <constructor-arg index="0" value="Application Domain Object1" /> </bean> </beans>
appDomainContext2.xml:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="appDomain2" class="com.javarticles.business.DomainObject2"> <constructor-arg index="0" value="Application Domain Object2" /> </bean> </beans>
Let’s now fetch application specific domain objects.
SpringAppContextExample:
package com.javarticles; import com.javarticles.business.DomainContext; import com.javarticles.business.DomainObject1; import com.javarticles.business.DomainObject2; public class SpringAppContextExample { public static void main(String[] args) { DomainObject1 frameworkDomain1 = (DomainObject1) DomainContext.getBean("domain1"); System.out.println(frameworkDomain1); DomainObject2 frameworkDomain2 = (DomainObject2) DomainContext.getBean("domain2"); System.out.println(frameworkDomain2); DomainObject1 appDomain1 = (DomainObject1) DomainContext.getBean("appDomain1"); System.out.println(appDomain1); DomainObject2 appDomain2 = (DomainObject2) DomainContext.getBean("appDomain2"); System.out.println(appDomain2); } }
Output:
Framework Domain Object1 Framework Domain Object2 Application Domain Object1 Application Domain Object2
Download the source code
This was an example about pushing spring specific stuff to a framework module and making the application module extend the framework to plugin its own context files.
You can download the source code here: springFrameworkContextExample.zip and springAppContextExample.zip