Spring Loose coupling + Basic Terminlogies

·

4 min read

In my previous article, Tight Coupling and Loose Coupling in Java, we had implemented loose coupling using an interface. If you are new to the concept of implementing loose coupling then I would recommend you to read the above mentioned article before you proceed with this. With that sorted, let's start with Loose coupling in Spring.

Inversion of Control

The problem with the approach I had used to implement loose coupling in Tight Coupling and Loose Coupling in Java is that it is not efficient for enterprise applications. An enterprise application might have throusands of classes and objects and manually creating and managing objects for them is very tedious. So, we give this responsibility to spring. Sping creates and manages the instances of the classes on our behalf. This giving of control to spring to manage classes and objects in called Inversion of Control.

Annotations and Terminologies

To allow Spring to take control of classes there are 2 annotations we need to be familiar with. Annotations give meaning to a piece of code. It allows Spring to better understand our goal. The 2 annotations and what message they convey to Spring are as follows:

  1. @Component: Hey spring, this is a component and you need to manage its lifecycle.
  2. @Autowired: Hey spring, there is already a component of this type, so go and find it. Once you find it, autowire it to the current object. Now, we can tell spring to manage a component lifecycle using @Component and allow Spring to identify and create an object of a component using @Autowired.

But there should be a place where the objects should be managed right? Yes, there is an interface called ApplicationContext which provides the functionalities to manage the object lifecycle. This Application Context represents the IoC Container in Spring. IoC Container stores and manages the instances of the classes and the instances created are called Beans.

To demonstrate all of the things we learned so far, let's see a real life example. Consider our smartphone, we have a camera in our smartphone, that same camera is used by various apps like Instagram, Facebook, etc. Now, let's to implement the scenario where Instagram will be using our camera.

Loose coupling Implementation in Spring

// consider this the base camera architecture giving features like 12MP with OIS, etc.
public interface Camera {  
    public void click();  
    public void record();  
}

// We use the camera architecture to create our camera software for our phone
@Component  
public class MyPhoneCamera implements Camera {  
    @Override  
    public void click() {  
        System.out.println("Picture clicked!");  
    }  
    @Override  
    public void record() {  
        System.out.println("Recording video...");  
    }  
}

// Instagram will interact with the Mobile OS like Android, iOS, etc. to use the camera and provide its own features like filters, stickers, etc. but here we will see only the basics
@Component  
public class Instagram {  
    @Autowired  
    private Camera camera;  

    public void run() {  
        int choice = 0;  
        Scanner sc = new Scanner(System.in);  
        outerloop:  
        while (true) {  
            System.out.println("1. Click picture\n2. Record Video\n3. Exit Instagram\nEnter choice: ");  
            choice = sc.nextInt();  
            switch (choice) {  
                case 1: camera.click();break;  
                case 2: camera.record();break;  
                case 3: System.out.println("Instagram closed"); break outerloop;  
                default: System.out.println("Please enter correct choice!");  
            }  
        }  
    }  
}

// finally our smartphone from where we run the instagram app
@SpringBootApplication  
public class Smartphone {  
   public static void main(String[] args) {  
      ConfigurableApplicationContext applicationContext = SpringApplication.run(Smartphone.class, args);  
      Instagram instagramBean = applicationContext.getBean(Instagram.class);  
      instagramBean.run();  
   }  
}

So, some observations we can see from the above implementation:

  1. In our Smartphone class, we didn't create any object manually. Instead, we used getBean() to get an instance of the Instagram class.
  2. We also see than when we are calling the instagramBean.run() method, the private variable camera is able to access click() and record() methods without being instantiated. Since, we have used @Autowired, spring automatically finds the class MyPhoneCamera using the Camera type and autowires an object of it to the private Camera camera variable. This method of injecting an object to another object is called Dependency Injection in Spring.

Now, you may have a question what does all this demonstrate? Why are we using the annotations? The answer is to create Loosely-coupled systems. We can change MyPhoneCamera implementation and the application will run fine(till the architecture is unchanged i.e. the methods of the interface remains same). Other applications can also use our phone camera i.e. MyPhoneCamera and create its own features.

Summary

We learned:

  1. Annotations:
    1. @Component
    2. @Autowired
  2. Terminologies:
    1. Beans
    2. IoC Container
    3. Application Context
    4. Dependency Injection
    5. Loose coupling with implementation