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.
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?
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.
Why creating helper class wrapper? why not using factory method pattern instead?
very nice sir. Thanks. Other tutorials on internet are not capable to explain this concept properly. Thanks
Example could have been better.
If i want call jsonoutput how do i call in main funtion
so for JsonOutputGenerator we create another bean of outpuhelper and name it outputhelper!!, is that correct?
Excellent explanation. Good example and pinpoint information. Really helpful.
thank you so much
it is very easy to understand for beginer.
Thank you so much
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();
}
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*.
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.Good concept of loose coupling and DI
Amazing Example Helps Alot…….Thanks
Excelent !
good example
pefect
gereat
Great Example! Thanks Mr.Mkyong for clear explanation and showing the differences between three approaches. Great work! Keep it up.
Or simplier, without Helper and no coupling :
ApplicationContext context =
new ClassPathXmlApplicationContext(new String[] {“SpringBeans.xml”});
IOutputGenerator output = (IOutputGenerator)context.getBean(“JsonOutputGenerator”);
output.generateOutput();
And you have to change source code, when you need to use the CsvOutputGenerator. This is tight coupling.
p.s. Sorry my bad english 🙂
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…
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.
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.
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 “.
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.
Nice explanation . thanks 🙂
hjkhjk
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
Excelent clarification…
cool Example
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…..
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 ?
And what should we do if we want to attach booth CsvOutputHelpere and JsonOutputHelper ?
Really useful….keep posting like these…
Nice, precise and clear information. Thanks.
Hi mkyong,
Great help to the java community, best regards.
can anyone tell me what is the difference between ApplicationContext and BeanFactory
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