In this small article I will try to explain why I created this library ? and how it's implemented ?
why I created this library ?
I worked with Java EE framework on many project and in most of them there was no limitation on the available resources to run the application but in some rare cases we had a limited resources especially the memory to deploy the application on a deployment service, so when the application exceed the limit the deployment service will slow down the application at first then if it continue the service will shut it down. We were using legacy spring framework without even spring boot, we tried to use different library but the difference was minimal and worthless, and this is where the idea ,to create a lightweight spring alternative focusing on reducing the memory consumption as much as possible, started.
there was only two goals in my mind when i started designing the library :
1 - reducing the Memory consumption as much as possible
2 - try to use existing Java EE API as much as possible to make the transition to the library easy from learning point of view and from the complexity of the transition in itself.
and with these 2 goals I managed to reduce the memory footprint of one of my application around 40% and make the transition easy and fast since it's similar to the existing solutions.
How I created this library ?
Here's GitHub repository so you can check the code while reading.
The library as a whole has many parts (screenshot below), most of the implementation in the main module which is also divided in three kind of independent modules : Core, JPA, Web. The App modules is mainly for integration, the Plugin module is a maven plugin to help create the necessary files inside the package archive for the library to work properly
Core Module
So we will start with the core module which as the name suggest contains the core functionality of the library which is the dependency injection or the inversion of control.
In order to achieve this fonctionality, first it start by scanning the class path of the application during the compile phase for all the annotated class the library should manage to create a component definitions for annotated classes.
The component definition contain basically all the information that we will need to instantiate an object from this class later, like the constructor information, if there are some setters with inject annotation (only constructor and setter injection are supported) if the class has some interface or extend some other class, we will have all the information that we need to create an object from this class (screenshot below).
And then, after we scan all the class path annotation and we create all the component definition that we need we will store them in the class path as JSON file.
The second and main functionality of the core module is the dependency injection and inversion of control which based on factory design pattern, so we have ApplicationContext interface which in it self extend the ComponentFactory interface and the main method of this interface is getComponent method which will return the object from the name of the component.
As you can see in the screenshot above first, we try to check if the component exist in the already initialised Singleton components, if it's not then we start by getting the component definition from the JSON file then we start the while loop to get all the dependencies of the component before passing the component definition and its dependencies to the component assembler to get a full object ready to be injected.
JPA module
The implementation of JPA module is very similar to spring data JPA but very minimal, the reason it's very similar because I used spring data in many project and I found it easy to use and as I said before I wanted the transition to the library to be smooth and require less work as much as possible so implementing my own mini spring data version was the best choice.
The implementation is around the JpaRepository interface which contain most common operation for database like save, delete and findAll... and in order to use the JPA module you need to extend this interface and provide the entity that this interface should manage and it's id, then after you extending the interface and annotate with Repository annotation you can define your methods and annotate them with Query annotation and provide the JPQL query, and then the library at the compile phase will create a fully functional class that implement this interface.
The library will also manage the transactional part of the application, so all repository interface and any class annotated with transactional will be managed by the library from transaction point of view. so for any transactional component the library will create proxy to manage the transaction based on the Transactional annotation and will also manage the entity manager life-cycle.
Web module
The web model is responsible for managing all the web part of the application and by design it's an independent module which mean it can be used independently from the rest of the modules in the library, as usual it's very similar in usage to some familiar Java EE library like Spring web or Jax-rs.
The implementation is based on annotation, you have classes annotated with Controller annotation and inside this controller you will find method annotated with the PathMapping and these methods will handle specific path or specific request based on some criteria like the type of the request, the content type...
From the outside it will look very similar to the other library but from the inside it's a different because the library will change these Controller classes at run time to make all of them extend the BaseHttpServlet, which also extends the HttpServlet and they will work as a regular servlet.
As you can see in the screenshot above, first we initialize the component in the init method to inject all the dependency using the WebApplicationContext, and then we will handle all the request coming to this Controller using the handleRequest method, with this approach we will use the existing servlet api to manage the controllers, this will help keep the memory footprint low and also reduce the overhead since the library act as a plugin to complement the servlet API work.
First, we try to map the request to the right method and after that, we try to inject all the requested information inside the method by getting the information either from the context or the HttpServletRequest like the request parameter or headers or path variable or the body of the request...
We convert all of these informations and then inject them as a parameter to the method when it's requested and then we execute the method and convert the outcome or the result based on PathMapping produces or content type (by default it's application/Json) and then we write the content to the HttpServletResponse.
Last if something goes wrong in the process and an error got thrown, we catch this error or the exception and we try to handle it based on the type of the exception, we have exception handler to handle different type of exception and the user can also provide more handler to handle any exception the way he want.
Maven plugin
The last and important part is the maven plugin that will create all the necessary files for the application to work properly and also to build the jar or war package.
First the plugin will scan the class path and the dependencies to search component-definitions-json files, from this files it will generate:
lazy-application.json: contains all the component and their dependencies for the application
lazy-application.properties: contains the list of controllers and entities so we don't need to scan the class path at run time.
and last if the packaging is jar we will get the main class.
and last we build the package archive file that will contains application code with its dependencies and the files we generated in the step before.
I tried not to dive in details to keep the articles short and not so complicated to understand, of course the code is available on GitHub so you can play with it as well.If you have question drop them below and I will try to answer them.
Top comments (0)