A complex J2EE app is composed of multiple layers.
For example
- Presentation
- Application or Service Layer
- Domain or Business layer
- Data Access or Persistence Layer
Each layer may have its own ApplicationContext
definition.
We will begin exploring layering using an example of SingletonBeanFactoryLocator
which will be used to load these contexts.
Layers in an enterprise application
Layers as Enum
The layers in an enterprise application:
Let’s have the layers as enum
.
Layers:
package com.javarticles; public enum Layers { PRESENTATION, APPLICATION, DOMAIN, PERSISTENCE }
Context file per Layer
Define the factory bean for each layer in beanRefFactory.xml
. For example, below we have created one for the Domain layer. It is composed of context files DomainContext1.xml
and DomainContext2.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> <list> <value>DomainContext1.xml</value> <value>DomainContext2.xml</value> </list> </constructor-arg> </bean> </beans>
DomainContext1.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="domain1" class="com.javarticles.business.DomainObject1"> <constructor-arg index="0" value="D1"/> </bean> </beans>
DomainContext2.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="domain2" class="com.javarticles.business.DomainObject2"> <constructor-arg index="0" value="D2"/> </bean> </beans>
Domain Layer
In our domain layer , we have simple beans DomainObject1
and DomainObject2
.
DomainObject1:
package com.javarticles.business; public class DomainObject1 { private String s; public DomainObject1(String s) { this.s = s; } public String getS() { return s; } }
DomainObject2:
package com.javarticles.business; public class DomainObject2 { private String s; public DomainObject2(String s) { this.s = s; } public String getS() { return s; } }
Create BeanFactoryLocator
We will call SingletonBeanFactoryLocator.getInstance()
to create the topmost bean factory which will contain the layer specific bean factories. We can either pass the location path of the resources to it or just leave it empty in which case it will load beanRefFactory.xml
file from the classpath.
bfl.useBeanFactory("DOMAIN")
will return us the Bean Factory for the domain layer. We will cache the bean factory as we load so on next time we can simply return it from cache. Note that SingletonBeanFactoryLocator
itself caches the topmost factory so further caching will only help us avoid calling topmostbeanFactory.getBean("layer key")
.
LayerContext:
package com.javarticles.business; import java.util.EnumMap; import java.util.Map; import org.springframework.beans.factory.ListableBeanFactory; import org.springframework.beans.factory.access.BeanFactoryLocator; import org.springframework.beans.factory.access.BeanFactoryReference; import org.springframework.beans.factory.access.SingletonBeanFactoryLocator; import com.javarticles.Layers; public class LayerContext { protected static ListableBeanFactory createBeanFactory(Layers factoryKey) { if (BEAN_FACTORY_BY_LAYER.containsKey(factoryKey)) { return BEAN_FACTORY_BY_LAYER.get(factoryKey); } BeanFactoryLocator bfl = SingletonBeanFactoryLocator.getInstance(); BeanFactoryReference bfReference = bfl.useBeanFactory(factoryKey.name()); ListableBeanFactory topmostBeanFactory = (ListableBeanFactory) bfReference.getFactory(); BEAN_FACTORY_BY_LAYER.put(factoryKey, topmostBeanFactory); return topmostBeanFactory; } private static final Map<Layers, ListableBeanFactory> BEAN_FACTORY_BY_LAYER = new EnumMap<Layers, ListableBeanFactory>(Layers.class); }
Now let’s look into the DomainContext
which will subclass LayerContext
to return us the Domain specific factory.
DomainContext:
package com.javarticles.business; import org.springframework.beans.BeansException; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.ListableBeanFactory; import com.javarticles.Layers; public class DomainContext extends LayerContext { public static boolean containsBean(String beanName) { return getBeanFactory().containsBean(beanName); } public static Object getBean(String beanName) { BeanFactory beanFactory = getBeanFactory(); Object retBean = null; try { retBean = beanFactory.getBean(beanName); } catch (BeansException e) { System.out.println("Exception while getting the bean " + beanName + e); throw e; } return retBean; } private static synchronized BeanFactory getBeanFactory() { if (beanFactory == null) { beanFactory = createBeanFactory(Layers.DOMAIN); } return beanFactory; } private static ListableBeanFactory beanFactory; }
Retrieve the domain specific beans
Let’s now retrieve the domain specific beans DomainObject1
and
DomainObject2
.
SpringBeanFactoryLocatorExample:
package com.javarticles.business; public class SpringBeanFactoryLocatorExample { public static void main(String[] args) { DomainObject1 d1 = (DomainObject1) DomainContext.getBean("domain1"); System.out.println(d1.getS()); DomainObject2 d2 = (DomainObject2) DomainContext.getBean("domain2"); System.out.println(d2.getS()); } }
Output:
D1 D2
Download the source code
This was an example about BeanFactoryLocator
. We looked into the the first aspect of layering to get the layer specific bean factory. You can download the source code here: springLayeringExample.zip