logo ilegra laranja
banner

Inversion of Control in Java

escrito por Augusto Klaic

5 minutos de leitura

null

Inversion of control (IoC) is a must nowadays in software engineering, it provides lots of benefits for software development.

Let the framework do the job for you!

Hollywood Principle: Don’t call us, we’ll call you! — Some Hollywood Agent

Inversion of control (IoC) is a must nowadays in software engineering, it provides lots of benefits for software development. This pattern almost always comes together with Dependency Injection (DI) pattern when they are used in frameworks like Spring to manage the lifecycle of an object (creation, destruction, invoke…). The name “Inversion of Control” means that instead of you, the framework will be behind the steering wheel of the execution flow of your program, and that helps in decoupling the execution from the implementation, facilitating changes of different implemented code, modularization of the program and for last but not least the ease of testing the software by isolating or mocking dependencies.

That being said, how does it work? Different patterns and frameworks have their own way of handling IoC but generalizing it hooks a listener to the framework and it will react to a specific event with a callback. So you don’t call it anymore, the framework will.

Boom! Hollywood Principle!

I pretend not to overextend here in how IoC works because my objective here is to show you, readers of this amazing text, the different ways to use IoC. So let’s move on! There are some different ways to achieve IoC such as Strategy design pattern, Service Locator pattern, Factory pattern, and Dependency Injection (DI). Here we will focus on DI. So let’s start trying to implement dependency injection without anything, just you and your IDE:

The example below is a very simple one, just to show how it can be done without any framework. The concept of DI, like I said before is to decouple the logic of the program from its dependencies, so to achieve that we need to create an interface that can be implemented by others classes, doing that we no longer be limited by the class because for example:

The classes of product, store, and the interface

Calling all things together

Explaining a little, we have a store that sells products but if we create a class product we will need to create a different constructor and add a new or change the type of the attribute product on Store class. Creating an interface Product and implementing it on different product classes will let our Store class untouched, and that is a big advantage because it reduces refactors and their complexity.

The concept of IoC is so important that some authors state that to deserve the title of Framework it needs to apply IoC. So let’s take a look at some popular frameworks today and their IoC implementation:

Spring Framework

In Spring exists something called IoC container that is composed of 2 basic packages the Beans, the Context, and others. This container is represented by the interface ApplicationContext, this guy does the work of instantiating, configuring, assembling, and managing the lifecycle of objects known as beans. The ApplicationContext interface has different implementations making it possible to use it with XML-configuration (but between us, nobody uses XML config anymore) or with Annotation-based configuration. And the Beans are the backbone objects of your application.
So let’s start doing some IoC things, firstly using Annotation-based. For that are necessary 2 annotations the @Bean and @Configuration, these annotations will be used in a configuration class where the beans are declared. Here an example of a configuration class:

Creating beans

In this configuration class, I tried to test different bean types. Because I have created a lot of similar beans with the same return types, I had to give the @Bean methods different names. To use the beans created, instantiate an object of the desired type and instead of using new Object(), use the ApplicationContext.getBean(). The getBean() method can receive different parameters like the class of the object, or the name of the bean… In this file has 2 types of annotation-based configuration, the Constructor-based Dependency Injection, and the Setter-based Dependency Injection.

Creating objects using pre-defined beans

Using the getBean(); will call the interface ApplicationContext of Spring that receives the information “create this Store object plz”, and then the interface will look upon the config file for the most applicable method to construct the object. See that none object dependencies were informed to ApplicationContext, they are all defined in the beans.
There is one more type of annotation-based configuration, the Field-based Dependency Injection. For this case is needed the annotation @Autowired to be above the object dependency that will be injected by Spring. Just to point out, this annotation can be used above of constructor methods and also above of setter methods of a class. The autowired annotation has to be used inside of a class marked with some of the Spring Stereotype annotations (@Component, @Service, @Controller, and @Repository), it will auto inject the dependency on the target class. In the example below I created 2 POJO’s classes with a stereotype:

Creating stereotypes classes

In this example, the class Product will be injected in the class Store. To achieve this, by the way, it is necessary to do some configuration too. Pretty similar to the AppConfig class I showed earlier in this text, but with the @ComponentScan annotation that will point the package that the AppConfig has to look for the Stereotypes classes. It will look like this:

Configuration of package scan

And after you have done that, just use a similar Main class that was used before in the previous example.

Instantiating a class using autowired

Autowired can save a lot of handwork code by freeing you of writing and instantiating beans, but it is not a win-win trade to use it. The autowired is based on reflection that is costlier than it’s counterparts in terms of performance. I think for now it is enough of Spring, let’s see another framework.

Google-Guice

Reading about the Guice I could see that it is pretty similar to Spring. It has annotations like Spring but with different names, it has a configuration class as well and it is possible to create beans too, in a different way.

So going directly to the point, Guice can do IoC with a very simple class called Injector. This class can create any injector of any class just needing to call the static method Guice.createInjector(); this will result in an Injector untyped, that can be used for any class desired. To use the injector just use the injector.getInstance(any.class);, this will inject the object you want. See the example below:

Simple Injection

That is the simplest way of using Guice and it creates an object with all attributes set to null, but for more advanced things we need to use a configuration class like the AppConfig we have created in Spring. The configuration class in Guice has a different name, it is called module and have to extend the AbstractModule class of Guice. In this class, you can create your beans. Guice uses a different annotation that is @Provides, and when you call the injector.getInstance(any.class); the Guice will use your provider that you defined, like this:

But something that bothered me, was that you cannot create more than one @Provider for the same type and here in Guice we cannot attribute names to our providers to differentiate them. This part took me several hours and a lot of readings (some discussions with the compiler and the IDE too) to understand how to define different bindings/providers to the same class, because it is not even a bit intuitive, thanks to Google… The solution for this is known in Guice by named annotations. To achieve this we need 3 things: @Named annotation, @Inject annotation, and the bind() method from Guice. The inject annotation will tell Guice what you are trying to inject (constructor method, parameters, setter methods), the named annotation will be used to give a name to what Guice will inject and the bind method will connect the name you gave with the class that has to be injected. Like this:

Classes that will suffer injection

Configuring module

Like I said before, the inject annotation here is signalizing to Guice that I want it to inject the constructor method of Store class. The named annotation is giving a name to Product dependency in Store class and in the Guice Module I am binding the Product class to the parameter that I named in Store class, and at last saying to Guice that, when it encounters the named dependency product instantiate a new Product. In the Store2 class (sorry for this trashy name, my creativity wasn’t working when I created it) is basically the same thing but the injection occurs in the parameter field and not in the constructor. On the Guice Module I configurated two different injections of the same type, they are chosen by their name productConstructor and product Field.

Conclusion

Inversion of Control is a very useful pattern to decouple your program, and the frameworks facilitate its application on real programs with annotations and configurations. The IoC pattern can be achieved without any framework help and it is nice to see how it works “over the hoods”. These two frameworks here bring different ways of doing IoC in Java, particularly, I am more used to Spring but I liked how Guice handled it too. What I can say about this two frameworks is that Spring is much easier to code and to get how the flow of things work, like creating different beans and injecting them, but in Guice, I felt that the configuration is more practical because I used fewer annotations to do the same thing I’ve done on Spring.

My initial objective was to test 3 frameworks, Spring, Guice, and Java EE CDI but I have so many problems trying to use the CDI that I just gave up trying, it has so many problems of conflicting libs, XML config files, and problems scanning beans that my only advice to you is: RUN AWAY FROM JAVA EE THINGS! It is degrading for your brain. Just kidding, the Java EE framework has its advantages, but I will test it in another dev text like this one.

Compartilhe esse post: