Main Tutorials

Spring loosely coupled example

The concept of object-oriented is a good design to break your system into a group of reusable objects. However, when system grows larger, especially in Java project, the huge object dependencies will always tightly coupled causing objects very hard to manage or modify. In this scenario, you can use Spring framework to act as a central module to manage all the object dependencies easily and efficiently.

Output Generator Example

Let’s see an example, assume your project has a function to output the content to Csv or Json format. Your code may look like the following example:

File : IOutputGenerator.java – An interface for output generator


package com.mkyong.output;

public interface IOutputGenerator
{
	public void generateOutput();
}

File : CsvOutputGenerator.java – A Csv output generator to implement the IOutputGenerator interface.


package com.mkyong.output.impl;

import com.mkyong.output.IOutputGenerator;

public class CsvOutputGenerator implements IOutputGenerator
{
	public void generateOutput(){
		System.out.println("Csv Output Generator");
	}
}

File : JsonOutputGenerator.java – A Json output generator to implement the IOutputGenerator interface.


package com.mkyong.output.impl;

import com.mkyong.output.IOutputGenerator;

public class JsonOutputGenerator implements IOutputGenerator
{
	public void generateOutput(){
		System.out.println("Json Output Generator");
	}
}

There are couple of ways to call the IOutputGenerator, and how to use Spring to avoid objects to coupled tightly with each other.

1. Method 1 – Call it directly

Normal way, call it directly.


package com.mkyong.common;

import com.mkyong.output.IOutputGenerator;
import com.mkyong.output.impl.CsvOutputGenerator;

public class App 
{
    public static void main( String[] args )
    {
    	IOutputGenerator output = new CsvOutputGenerator();
    	output.generateOutput();
    }
}

Problem
In this way, the problem is the “output” is coupled tightly to CsvOutputGenerator, every change of output generator may involve code change. If this code is scattered all over of your project, every change of the output generator will make you suffer seriously.

Method 2 – Call it with helper class

You may think of creating a helper class to move all the output implementation inside.


package com.mkyong.output;

import com.mkyong.output.IOutputGenerator;
import com.mkyong.output.impl.CsvOutputGenerator;

public class OutputHelper
{
	IOutputGenerator outputGenerator;
	
	public OutputHelper(){
		outputGenerator = new CsvOutputGenerator();
	}
	
	public void generateOutput(){
		outputGenerator.generateOutput();
	}
	
}

Call it via helper class.


package com.mkyong.common;

import com.mkyong.output.OutputHelper;

public class App 
{
    public static void main( String[] args )
    {
    	OutputHelper output = new OutputHelper();
    	output.generateOutput(); 
    }
}

Problem
This looks more elegant, and you only need to manage a single helper class, however the helper class is still tightly coupled to CsvOutputGenerator, every change of output generator still involves minor code change.

Method 3 – Spring

In this scenario, Spring Dependency Injection (DI) is a good choice. Spring can make your output generator loosely coupled to the output generator.

Minor change in OutputHelper class.


package com.mkyong.output;

import com.mkyong.output.IOutputGenerator;

public class OutputHelper
{
	IOutputGenerator outputGenerator;
	
	public void generateOutput(){
		outputGenerator.generateOutput();
	}
	
	public void setOutputGenerator(IOutputGenerator outputGenerator){
		this.outputGenerator = outputGenerator;
	}
}

Create a Spring bean configuration file and declare all your Java object dependencies here.


<!-- Spring-Common.xml -->
<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-2.5.xsd">

	<bean id="OutputHelper" class="com.mkyong.output.OutputHelper">
		<property name="outputGenerator" ref="CsvOutputGenerator" />
	</bean>
	
	<bean id="CsvOutputGenerator" class="com.mkyong.output.impl.CsvOutputGenerator" />
	<bean id="JsonOutputGenerator" class="com.mkyong.output.impl.JsonOutputGenerator" />
		
</beans>

Call it via Spring


package com.mkyong.common;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.mkyong.output.OutputHelper;

public class App 
{
    public static void main( String[] args )
    {
    	ApplicationContext context = 
    	   new ClassPathXmlApplicationContext(new String[] {"Spring-Common.xml"});

    	OutputHelper output = (OutputHelper)context.getBean("OutputHelper");
    	output.generateOutput();
    	  
    }
}

Now, you just need to change the Spring XML file for a different output generator. When output changed, you need to modify the Spring XML file only, no code changed, means less error.

Conclusion

With Spring framework – Dependency Injection (DI) is a useful feature for object dependencies management, it is just elegant, highly flexible and facilitates maintainability, especially in large Java project.

About Author

author image
Founder of Mkyong.com, love Java and open source stuff. Follow him on Twitter. If you like my tutorials, consider make a donation to these charities.

Comments

Subscribe
Notify of
79 Comments
Most Voted
Newest Oldest
Inline Feedbacks
View all comments
Somapala
10 years ago

With JavaConfig no XML based bean configuration is required, so everytime you have to change annotations in the java classes when you need to change the output method, so how we can achieve this loose coupling with annotations? Is this specific to xml based bean injection?

Ankit
5 years ago

In method 1 that you explained, “every change of output generator may involve code change” can you give an example. Its difficult for me to understand.

La VloZ
6 years ago

Why creating helper class wrapper? why not using factory method pattern instead?

Arun Singh
6 years ago

very nice sir. Thanks. Other tutorials on internet are not capable to explain this concept properly. Thanks

Prem Pratick Kumar
9 years ago

Example could have been better.

Vairamuthu
9 months ago

If i want call jsonoutput how do i call in main funtion

Ahsan Siddiqui
2 years ago

so for JsonOutputGenerator we create another bean of outpuhelper and name it outputhelper!!, is that correct?

Abhishek G
3 years ago

Excellent explanation. Good example and pinpoint information. Really helpful.

ninh xuân hu?n
4 years ago

thank you so much
it is very easy to understand for beginer.

ninh xuân hu?n
4 years ago

Thank you so much

vijay
4 years ago

Hi,

Why cant you declare in helper class in following code like in the second approach then there is no tight coupling will occur

public class OutputHelper {

IOutputGenerator outputGenerator;

public void generateOutput() {
outputGenerator.generateOutput();
}

public void setGenarator(IOutputGenerator outputGenerator) {
this.outputGenerator = outputGenerator;
}

}
and app class like

public static void main(String[] args) {

OutputHelper oh = new OutputHelper();
IOutputGenerator output = new CsvOutputGenerator();
oh.setGenarator(output);
oh.generateOutput();
}

Arindam
2 years ago
Reply to  vijay

We can’t use this way because everywhere we have decided to change the generator, we have to change the code.
And the loosely coupled means that *less or no code change*.

frank
5 years ago

it seems that method 2 and method 3 are the same, they only need to change the implementation in one place, just one is changed on code, another is changed on xml file, right? Just like if we use @Autowired("csvGenerator") in code, the implementation changed, we also need to modify the code.

Parag
6 years ago

Good concept of loose coupling and DI

Anuj Dubey
9 years ago

Amazing Example Helps Alot…….Thanks

Karimoune
9 years ago

Excelent !

Raghvendra kumar
9 years ago

good example

jack willam
9 years ago

pefect

wholet
10 years ago

gereat

jeff
10 years ago

Great Example! Thanks Mr.Mkyong for clear explanation and showing the differences between three approaches. Great work! Keep it up.

Bogdan
10 years ago

Or simplier, without Helper and no coupling :

ApplicationContext context =
new ClassPathXmlApplicationContext(new String[] {“SpringBeans.xml”});

IOutputGenerator output = (IOutputGenerator)context.getBean(“JsonOutputGenerator”);
output.generateOutput();

BelianskyAS
10 years ago
Reply to  Bogdan

And you have to change source code, when you need to use the CsvOutputGenerator. This is tight coupling.

p.s. Sorry my bad english 🙂

Steve J
10 years ago
Reply to  BelianskyAS

I am not sure why people say changing the spring config is not a code change. The bad part about changing the spring config file is that it is not under unit test so there is no easy to tell if you have broken anything…

ramandeep89
10 years ago
Reply to  Steve J

Hey Steve, spring config is actually not a code change. Consider the scenario below:
You have an application deployed on your server, and you need to change the implementation. If you have to change it in a java class, you need to compile the class first, build it and redeploy it. For spring config, change xml on the fly and you are set, no compilation, no build 🙂
PS: I assume you have the other implemetation ready on the server already.

Abhilash
6 years ago
Reply to  ramandeep89

Still you have to restart the application for it to pickup latest changes right ? Wont that be a serious flaw ? Why cant we get a particular bean dynamically based on some parameter ?
Let me know if I have understood it wrong.

Umer
10 years ago
Reply to  Steve J

Steve,

I have used my actual spring configuration file under my unit test case, all of my test code use actual spring configuration file, so If we have make incorrect changes in spring configuration , my test case start getting failed, but I also agree spring configration change is a ” CHANGE “.

Alex
10 years ago
Reply to  Steve J

Assuming you define (needs to be the same, because product owner said so) and call the output.generateOutput in more than one class, if you need to change to Csv you will have to modify 2 or more classes, instead of 1 xml file. Low Coupling is also defined by extensibility, not only by what your source code looks like at the moment.

mohiadeen
10 years ago

Nice explanation . thanks 🙂

manisg
11 years ago

hjkhjk

Ayaz
11 years ago

Its good tutorial but my question is it looks same just the difference is you placed bean.xml what if i want that both can be accessed by xml like csv and xml both. sory if my question is wrong because iam new in spring. but regular reader of your articles. Thanks

Abdul Gafoor
11 years ago

Excelent clarification…

Selvakumar
11 years ago

cool Example

Rajpal Singh
11 years ago

Hello sir,

You said that when output changed, you need to modify the Spring XML file only.

Can you explain with example what type of code we change in the output.

Help me…..

Chandra Sekhar
10 years ago
Reply to  Rajpal Singh

As per the below configuration,

we are attaching a CsvOutputGenerator to the OutputHelper. If we want to attach JsonOutputGenerator, then we change the spring configuration as below:

Hope this clarifies ?

Victor
8 years ago
Reply to  Chandra Sekhar

And what should we do if we want to attach booth CsvOutputHelpere and JsonOutputHelper ?

Srikanth
11 years ago

Really useful….keep posting like these…

Rajesh Kumar
11 years ago

Nice, precise and clear information. Thanks.

Trivi
11 years ago

Hi mkyong,

Great help to the java community, best regards.

Suyog
11 years ago

can anyone tell me what is the difference between ApplicationContext and BeanFactory

 ApplicationContext context = new ClassPathXmlApplicationContext(new String[]{"context.xml"}); 
 BeanFactory context = new ClassPathXmlApplicationContext(context.xml); 
ironhide
8 years ago
Reply to  Suyog

ApplicationContext container includes all functionality of the BeanFactory container, so it is generally recommended over the BeanFactory.

BeanFactory can still be used for light weight applications like mobile devices or applet based applications where data volume and speed is significant