We tag our method with @Test
and it becomes JUnit or TestNg unit test. We tag our class @Controller
and it becomes a controller class in Spring. In this article, we will try to understand the background of these kinds of features in java.
Inversion of control
To get started with our discussion let’s talk about the inversion of control principle. It is commonly used by frameworks. The framework is a set of defined patterns to effectively complete a task. The framework has generic components with their roles. For example, let’s take frameworks for web applications, what kind of generic components or mechanisms they should have. Might be a controller where we can define endpoints and corresponding methods, a service where we can connect to the database and do some business logic. Now if we think almost every web project should have these mechanisms (controllers, services). But of course, the type of controllers and services and what they do will depend on the project.
That’s exactly what the Inversion Of Control principle is. It should give us a common structure and flow, but the logic we will write by ourselves.
Now, let’s take as our example the Spring framework. How will Spring know if your class is a controller class? Right, we tag it with annotation @Controller
and after that, it will be part of the controllers and take specific features. The same thing with services in spring, we tag with @Service
annotation and regular class will become a service with transactions and so on. Spring utilized inversion of control widely.
So how does it work? We know from the annotation article, annotation is used to give metadata to a class. The annotation itself is not enough, however, in combination with reflection, it will suit this job perfectly. The annotation will tag a regular class let’s say as controller and the reflections will scan classes and whatever class will have controller annotation will receive controller features.
So far I think you understand the main idea of using annotation and reflection together for the Inversion Of Control principle.
Let’s create something simple
Let’s create our own small framework and utilize Inversion Of Control with annotations and reflection. This is not a production version or complete framework. The main idea is to understand how does it work.
Github Repo: https://github.com/Suranchiyev/rest-client
The purpose of the framework is to create the rest clients to do requests and test responses (or actually just print them). It’s a Maven project with two dependencies(for the actual HTTP request part). Let’s see the structure of the project and pom.xml
First I have created test.java.restClient
a package. It’s under test because I think it will be more useful for API testing.
Then I have test.java.test
where I will have my test scripts
Ok, let’s start with our annotations
test.java.restClient.ResstClient.java
We will tag with this annotation our classes and we can provide the base URL this class will use to request/test APIs.
test.java.restClient.ClientRequest.java
With this annotation, we will tag our methods. We will provide a URL(which will be concatenated to the base URL) and all other details for this specific request in the method.
test.java.restClient.Response.java
With this annotation, we will tag our method argument and once we do request, we will put a response inside this argument, or better to say we will run this method with this response from the request as a method argument.
I decided to keep it super simple, but we could kind of create a specific response type and load the status code, response, and all other details of the response. Our response is just a String that represents the actual response body.
We have additional enum for request types
package restClient;
public enum RequestMethod {
GET, POST, PUT, DELETE
}
Ok, it’s time for the best part. If we think the annotations don’t give us much, we can tag the element and it provides extra information for it. But with reflection combination, we can do really cool stuff. Most of the(almost all) reverse of control frameworks will provide their running point because they need to scan the classes and provide them with features based on their annotations.
This is our reflection part and our framework will always get started from here
test.java.restClient.RequestRunner.java
When we run this class it looks at all classes under test.java.test
package for the classes with RestClient
annotation. Then it looks at the methods with ClientRequest
annotation. Once found it gets all the needed information from annotation to do the request. In the last step, it calls this method with one argument which is a response.
The user can write now a bunch of RestClient classes with ClientRequest methods and do rest calls. They will get a response as a method argument and they can test the response and so on(we just printing it).
test.java.test.DummyRestApiTesting.java
and one more
test.java.test.DummyRestApiTesting.java
The output I got is here:
Summary
Inversion of control when the framework has ready flow and pattern but it lets user specify the business logic. The annotation and reflection can be used to build frameworks with an inversion of the control principle.
Please take my Java Course for video lectures.This article is part of the series of articles to learn Java programming language from Tech Lead Academy:Introduction to programming
OS, File, and File System
Working with terminal
Welcome to Java Programming Language
Variables and Primitives in Java
Convert String to numeric data type
Input from the terminal in Java
Methods with Java
Java Math Operators and special operators
Conditional branching in Java
Switch statement in Java
Ternary operator in Java
Enum in Java
String class and its methods in Java
Loops in Java
Access modifiers in Java
Static keyword in Java
The final keyword in Java
Class and Object in Java
Object-Oriented Programming in Java
OOP: Encapsulation in Java
OOP: Inheritance in Java
OOP: Abstraction in Java
OOP: Polymorphism in Java
The method Overriding vs Overloading in Java
Array in Java
Data Structures with Java
Collection framework in Java
ArrayList in Java
Set in Java
Map in Java
Date and Time in Java
Exception in Java
How to work with files in Java
Design Patterns
Generics in Java
Multithreading in java
Annotations in Java
Reflection in Java
Reflection & Annotations - The Powerful Combination
Run terminal commands from Java
Lambda in Java
Unit Testing in Java
Big O Notation for coding interviews
Top Java coding interview questions for SDET