In this article, I’ll tell you about inversion of control. This is a very useful thing if you are going to develop a dynamic application in compliance with the modern approach to software development.
Spring framework gives you flexibility by providing dynamic bean wiring, but for a deeper understanding, let’s consider the principle of Inversion of Control beyond Spring. Look at this code:
public class Calculator {
private final DbWriter dbWriter;
public Calculator() {
// Class Calculator can't be used in DbWriter absence.
this.dbWriter = new DbWriter();
}
// Some methods in Calculator class
public final Integer opAdd(Integer a, Integer b) {
return a + b;
}
public final boolean saveResult(Integer number){
return dbWriter.saveInDb(number);
}
}
public class DbWriter {
public DbWriter(){
// code for database initialization
}
public boolean saveInDb(Integer value ){
// save value to db
// if save operation is successful return true
return true;
}
}This is an example of poor program design because the program loses flexibility. The Calculator class can’t be used independently of the DBSaver class. So, what will you do if you need to change the code quickly?
Thus, we have to separate classes into two (or many more in real projects) independent logical blocks called beans. Let’s create a new interface that contains methods concerned with both classes.
In such a way, we destroy dependencies between two classes and obtain the possibility to choose the right way to save data to the database in our case.
public interface IResultSaver {
public boolean saveResult(Integer result);
}
public class Calculator {
private IResultSaver saver;
public Calculator() {
}
// Some methods in Calculator class
public final Integer opAdd(Integer a, Integer b) {
return a + b;
}
//Here you can put any instance of classes implement IResultSaver
public final void setSaver(IResultSaver saver) {
// Choose saver
this.saver = saver;
}
public final boolean saveResult(Integer resultToSave){
rerurn saver.saveResult(resultToSave)
}
}
// DB saver.
public class DbWriter implements IResultSaver {
public DbWriter() {
// code for database initialization
initializeDbConnection();
}
@Override
public boolean saveResult(Integer result) {
return saveDbOperation(result);
}
}
public class FileWriter implements IResultSaver {
public FileWriter() {
// code for database initialization
initializeFile();
}
@Override
public boolean saveResult(Integer result) {
return saveFileOperation(result);
}
}The calculator class can use any classes via an interface, so we can use them in an elegant manner. So everything is clear, and we can apply this principle for calculation operations, like in the last article. This is a nice example of inversion of control (IoC). Spring framework provides this trick using containers for creating some class instances and dependencies between them in an XML file.
Take a look at this code:
public interface IOperation {
public Integer operation(final Integer a, final Integer b);
}
public interface IResultSaver {
public boolean saveResult(Integer result);
}
public class Calculator {
private IResultSaver saver;
private IOperation operation;
// Spring container automaticaly sets
// beans corespond to XML conficuraion file via setters
public void setSaver(IResultSaver saver) {
this.saver = saver;
}
public void setOperation(AddBean operation) {
this.operation = operation;
}
public boolean saveResult(final Integer result) {
return saver.saveResult(result);
}
public final Integer calculate(final Integer a, final Integer b) {
return operation.operation(a, b);
}
}
// DB saver.
public class DbWriter implements IResultSaver {
public DbWriter(final String dataSourceName) {
// code for database initialization
System.out.println("Here you can initialize you db connection!"
+ dataSourceName);
}
public boolean saveResult(final Integer result) {
// code for saving result
System.out.println("This is Db saver!");
return true;
}
}
// File saver
public class FileWriter implements IResultSaver {
public FileWriter(final String filename) {
// code for database initialization
System.out.println("Initializing file writer with parameter:file name" + filename);
}
public boolean saveResult(final Integer result) {
//code for saving result
System.out.println("This is file saver!");
return true;
}
}
// Bean for add
public class AddBean implements IOperation{
public Integer operation(final Integer a, final Integer b) {
return a + b;
}
}
// Bean for multiply
public class MultiplyBean implements IOperation {
public Integer operation(final Integer a,final Integer b) {
return (Integer) a * b;
}
}
public class main {
public static void main(String args[]) {
BeanFactory factory = new XmlBeanFactory(new ClassPathResource("spring-config.xml"));
Calculator calculator = (Calculator) factory.getBean("calculator");
calculator.saveResult(calculator.calculate(2, 2));
}
}This is a great example of Inversion of Control using the Spring framework. The Spring container hides the creation of corresponding bean instances, giving you a dynamic application. The container’s configuration information is stored in an XML file, which you can change even during application execution.
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="https://www.springframework.org/schema/beans"
xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://springframework.org/schema/beans
https://springframework.org/schema/beans/spring-beans.xsd">
<!--IOperation beans-->
<bean name="add" class="InversioOfControl.AddBean"/>
<bean name="mul" class="InversioOfControl.MultiplyBean"/>
<!--IResultSaver beans-->
<bean name="db" class="InversioOfControl.DbWriter">
<constructor-arg value="yourDataSource"/>
</bean>
<bean name="file" class="InversioOfControl.FileWriter">
<constructor-arg value="fileForSave.txt"/>
</bean>
<!-- Here we describe calculator bean contains two props -->
<bean name="calculator" class="InversioOfControl.Calculator">
<property name="saver" ref="db"/>
<property name="operation" ref="add"/>
</bean>
</beans>So the user can choose beans dynamically, during program execution. The user has to change the names of beans in the property fields.
