Building a Simple Spring-like Dependency Injection Framework in Java | by daleef rahman | Apr, 2022

Mix reflections and annotations to construct highly effective libraries and frameworks

Photograph by Our Life in Pixels on Unsplash

Frameworks like Spring, Hibernate, and so on. make the lifetime of java programmers straightforward. These frameworks give us quite a lot of superpowers. For e.g., you may magically inject objects in spring by annotating with simply an Autowired annotation.

Utilizing hibernate you may merely persist objects in relational databases, which is a completely totally different paradigm.

What’s behind this sorcery?

Properly, all that is made potential by java reflections and annotations.

IDEs like Intellij Thought and Eclipse use reflection behind the scenes to supply us with all of the intelligence. Reflections and annotations are additionally utilized by frameworks like Spring, Hibernate, and Lombok to hurry up and simplify Java programming.

On this article, we’re going to take out our magic wands and discover ways to solid a spell to construct objects out of skinny air — we are going to create a mini model of a dependency injection framework like Spring with the assistance of reflection and annotations.

The goal is to get you accustomed to reflections and annotations and likewise to offer you adequate sensible understanding to construct these sorts of frameworks your self. And sprinkle some magic in your subsequent tasks.

Reflections:

Java reflection API, permits us to examine and alter runtime attributes of courses, interfaces, fields, and strategies. These are bundled within the java.lang.reflect package deal.

The spine of reflection is java.lang.Class. All courses and interfaces in a working Java software have a corresponding occasion of this class. Class cases are constructed routinely as courses are loaded in JVM.

There are a number of methods of making a Class occasion.

We are able to use forName() static technique of the Class.

Class<?> clazz = Class.forName("java.lang.String")

One other method is by invoking getClass() technique on an object.

Class<?> clazz = object.getClass()

A category object gives a wide range of strategies for accessing the runtime info of the category. Listed here are some examples.

We received’t be capable to cowl all of the options on this weblog. You’ll be able to refer right here to be taught extra: https://www.baeldung.com/java-reflection.

Annotations:

Annotations are used to outline further metadata info for courses, strategies, fields, and so on., and may be learn at runtime through reflection. By themselves, annotations wouldn’t have any impact on the way in which a program executes.

There are just a few built-in annotations in Java. @Override, @Decprecated, SuppressWarningsand so on are some examples. We are able to additionally create customized annotations. Within the subsequent part, we are going to see how customized annotations together with reflections can be utilized to write down highly effective code.

Right here is a straightforward instance to get us began. We now have an OrderService class that gives an API to get order particulars utilizing an order id. OrderService internally makes use of OrderRepository class to fetch the small print from a knowledge retailer.

Let’s take a look at if the code is working.

Up to now, so good! We bought the anticipated output.

Id: 1, description: Particular Order, value: 10.0

In a while, you got here throughout dependency injection sample and discovered its advantages. Now you need to introduce DI on this venture and construct a light-weight framework round it.

So how will we go about it? We must always take away all direct instantiations of dependencies and as an alternative let the framework create and inject the dependencies.

First, let’s take away the instantiation of OrderRepository in OrderService and annotate the sphere with a SimplyAutoWired annotation. We count on the framework to inject OrderRepository for us.

Now declare the customized annotation SimplyAutoWired . Annotations are declared utilizing the @interface key phrase.

Did you discover the two further annotations on the highest of it? These built-in annotations utilized to different annotations are referred to as meta-annotations.

The primary annotation declares that we will apply SimplyAutoWired solely to Fields and the second annotation specifies that it has runtime visibility. The annotation doesn’t have any strategies, it serves as a easy marker.

Now that we’ve our annotation prepared, let’s create a category ApplicationContext that may care for instantiating and assembling objects.

We want to obtain one thing like this.

ApplicationContext applicationContext = new ApplicationContext();
orderService = applicationContext.getInstance(OrderService.class);

We’re getting the general public default constructor of the category utilizing its getConstructor technique. By utilizing the constructor, we’re creating an occasion.

Now we are going to implement injectAnnotatedFields technique, that may instantiate and inject fields within the class which can be annotated with our SimplyAutoWired annotation.

Right here we will are studying the customized annotation by utilizing Java’s Reflection API. Utilizing isAnnotationPresent() technique within the Discipline class, we’re checking if the sphere is annotated with SimplyAutoWired annotation.

Fields is perhaps personal, so we set the accessible flag to be true, earlier than setting the sphere with its occasion. Now, we are going to recursively name injectAnnotatedFields .

Lastly, we will use applicationContext to get an occasion OrderService .

Up to now, we’ve seen tips on how to create customized annotations. We additionally noticed tips on how to use them together with reflections APIs to facilitate the framework to create cases and inject dependencies into them.

Now we are going to put some bells and whistles.

As per Spring documentation Spring beans are singletons by default.

Nonetheless, we’re creating new cases each time getInstance technique is known as, which is inefficient.

Additional, the DI framework also needs to have full management over the lifecycle of objects when it comes to creation, orchestration, and destruction.

So we are going to make the next enhancements.

  1. Create objects as soon as and reuse them.
  2. Management the creation of objects by finding and registering all of the parts when the appliance begins.

However first, we have to inform the framework, which parts it ought to register, and by which packages it ought to search.

To point out which courses to register, we are going to create a brand new annotation @SimpleComponent .

@SimpleComponent
class OrderService
...

For storing the values of packages to scan, we are going to add one other annotation @ComponentScan. Beforehand, we’ve solely seen annotations with out strategies. However like another interface, annotations can even have strategies. Furthermore, all attributes of annotations are outlined as strategies.

This annotation declares one String attribute with the title “worth”.

This time, we’re utilizing a brand new meta annotation known as @Repeatable, which specifies that the annotation may be utilized greater than as soon as to the identical declaration.

To make use of Repeatable, we have to have a container annotation to retailer the array of values. On this case, we’ll use ComponentScans.

Now let’s put every part collectively. First, we are going to create a configuration file annotated with ComponentScan and packages to be scanned as its worth.

Then, it will likely be handed to ApplicationContext.

ApplicationContext applicationContext = new ApplicationContext(ApplicationConfig.class);

In ApplicationContext we are going to now use a map to retailer and reuse the created cases.

Upon startup of the appliance, we’ll create objects. First, get the packages to be scanned from the ComponentScan annotation. Then get all of the courses within the packages. After that, iterate by means of every class and verify whether it is annotated with Element. Whether it is annotated, create an occasion and retailer the item within the map.

For the sake of readability, I’m not including code for findClasses right here. You’ll be able to learn the code here.

Now refactor the remaining strategies to get objects from the map.

Our easy dependency injection framework is prepared!

The supply code of this text is on the market on the GitHub project.

We now have created a mini dependency injection framework like Spring and discovered the fundamentals of reflection and annotations processing alongside the way in which.

The precise implementation of DI frameworks is way more advanced than this.

The goal of this weblog was simply to offer you a way of how one can mix reflections and annotations to construct highly effective libraries and frameworks like these.

For those who loved studying this, you may also discover the beneath article value your time:

More Posts