-
Notifications
You must be signed in to change notification settings - Fork 6
Bridje IoC
Inversion of control(IoC) is a method of programming that happens due to the need to give solution to the problems to try to obtain: the dependence between our components. In IoC the interaction is expressed of imperative form doing calls to procedures or functions. The utilization of interfaces and the frameworks's appearing have yielded popularity to this term. Bridje IoC is the Inversion of Control and Dependency Injection API of Bridje Framework.
In object-oriented programming, there are several basic techniques to implement **IoC**:
1. Using a factory pattern
2. Using a service locator pattern
3. Using a dependency injection of any given below type:
-a constructor injection
-a setter injection
-an interface injection
Bridje IoC use a service locator pattern to implement IoC. The same can be used with maven from central repository:
<dependencies>
....
<dependency>
<groupId>org.bridje</groupId>
<artifactId>bridje-ioc</artifactId>
<version>${bridje.version}</version>
</dependency>
....
</dependencies>
Bridje IoC manages the concept of components, a component is a java annotated class (with the @Component annotation), Bridje IoC will instantiate this class for you when it is required. A component class may be declared as public, but if you desire to hide the class from other package it may be declared as package private too. A Bridje IoC component looks like this:
import org.bridje.ioc.Component;
@Component
class MyComponent
{
}
The Components can depend on other components, when this is the case Bridje IoC will inject the dependencies so you don't have to worry about it. For this purpouse we use the @Inject annotation like this:
import org.bridje.ioc.Component;
import org.bridje.ioc.Inject;
@Component
class OtherComponent
{
@Inject
private MyComponent myComp;
}
The components you declare will never be created until it is needed. So from any program to do something with components at least one component must be obtained through the Ioc.context().find() method like this:
import org.bridje.ioc.Ioc;
public class Main
{
public static void main(String[] args)
{
Ioc.context().find(MyComponent.class);
}
}
When a component is created sometimes you'll need to do some work with it upon initialization, this can be done with the PostConstruct java standard annotation.
import org.bridje.ioc.Component;
import org.bridje.ioc.Inject;
import javax.annotation.PostConstruct;
@Component
class OtherComponent
{
@Inject
private MyComponent myComp;
@PostConstruct
public void init()
{
myComp.doSomething();
}
}
You should use the PostConstruct annotation for components initialization because without the constructor the dependencies are not already injected.
import org.bridje.ioc.Component;
import org.bridje.ioc.Inject;
@Component
class OtherComponent
{
@Inject
private MyComponent myComp;
public OtherComponent()
{
// myComp will be null here, as the object is being created.
}
}
All components must have a default constructor otherwise the component cannot be created. The framework will try any component without a default constructor as if it does not exists.
import org.bridje.ioc.Component;
import org.bridje.ioc.Inject;
@Component
class OtherComponent
{
public OtherComponent(String str)
{
// This component cannot be created as it does not have a default constructor, a call to
// Ioc.context().find(OtherComponent.class) will result in null
// And injection will not work either @Inject OtherComponent comp; will result in null as well.
}
}
A service in Bridje IoC is nothing else than a class or generic type used to inject components that extends from or implement it. If a component extends from a class or implements an interface, it is said that the component provides that service.
public interface MyService
{
void doSomething();
}
@Component
class MyComponent implements MyService
{
@Override
public void doSomething()
{
System.out.println("doing something");
}
}
In this case the MyComponent component provides the MyService interface, what this means is that you can inject MyService in any other component without need to know that MyComponent class even exist.
@Component
class OtherComponent
{
@Inject
private MyService serv;
@PostConstruct
public void init()
{
serv.doSomething();
}
}
The purpose of Bridje IoC is to provide this kind of behavior of loose coupling. So you can write components depending on the specifications and not the implementation.
The services can be generic types and Bridje IoC will take notice of this, so you can have something like this:
public interface MyService<T>
{
void doSomething(T data);
}
@Component
class MyComponent implements MyService<String>
{
@Override
public void doSomething(String data)
{
System.out.println("doing something with " + data);
}
}
@Component
class OtherComponent
{
@Inject
private MyService<String> serv;
@PostConstruct
public void init()
{
serv.doSomething("Bridje IoC Framework");
}
}
The generic type may be as complex as you like. Here are some examples of what type of service you may declare.
MyService with String array
class PrintAllStringComponent implements MyService<String[]>
....
@Inject
private MyService<String[]> serv;
MyService with String list
class PrintStringListComponent implements MyService<List<String>>
....
@Inject
private MyService<List<String>> serv;
MyService with some interface called Repository of User class
class UserRepositoryServicesComponent implements MyService<Repository<User>>
....
@Inject
private MyService<Repository<User>> serv;
Multiple components may implement the same service, you can then inject all of then like this:
public interface MyService
@Component
class MyComponent1 implements MyService
@Component
class MyComponent2 implements MyService
@Inject
private MyService[] serv;
// or with list
@Inject
private List<MyService> serv;
// or with set
@Inject
private Set<MyService> serv;
In this case the framework will inject MyComponent1 and MyComponent2 on the serv field; you can then use it as you like: iterate, add, remove, etc. The order will not be defined in this example, but if the components needs to be in a specific order that must be specified in it's priorities, like this:
@Component
@Priority(1)
class MyComponent1 implements MyService
@Component
@Priority(2)
class MyComponent2 implements MyService
A lower priority number means a higher priority component, in this example MyComponent1 will be injected before the MyComponent2, if priority annotation is not present then the default priority is Integer.MAX_VALUE, which means that components with no priority will be the last to be injected. If you have several components implementing the same service and you like to inject only one, then only the higher priority component will be injected, if several components have the same priority the injected component is not determined, this means that if you must specify the priority of the component to a lower number than the other components providing the same service so this component will be the default one for that service.
All the work of the frameworks happends in a context, a context is the container in wich components are created and mapped to each other. the default context is the APPLICATION context it´s represented by the org.bridje.ioc.Application class and it can be obtained with the Ioc.context() method.
IocContext<Application> appContext = Ioc.context();
The context may be injected also on any component like this:
@Component
class MyComponent
{
@Inject
private IocContext<Application> appContext;
}
And scope is a class that determines which components are handled in a context. The scopes need to implement the org.bridje.ioc.Scope interface like this:
public class MyScopeObject implements Scope
{
@Override
public void preCreateComponent(Class<Object> clazz)
{
//Called by the context before a component instantiation.
}
@Override
public void preInitComponent(Class<Object> clazz, Object instance)
{
//Called by the context after the dependencies has being injected.
}
@Override
public void postInitComponent(Class<Object> clazz, Object instance)
{
//Called by the context after the PostConstruct methods has being called.
}
}
All context have a scope, the default context is the Application scoped context, only one Application context may exists as there is no way to create another. But you may create as many scopes as you want, and as many context as you want for tree scopes.
The @Component annotation can specify on with scope this component must be created.
@Component(scope = MyScopeObject.class)
class SomeComponent
This component will not be available from the IocContext context, which means that a child context must be created with the MyScopeObject scope, as this.
IocContext<MyScopeObject> childContext = Ioc.context().createChildContext(new MyScopeObject());
// or
@Inject
private IocContext<Application> appContext;
....
IocContext<MyScopeObject> childContext = appContext.createChildContext(new MyScopeObject());
Then you can obtain the component from the context in which it lives.
....
SomeComponent someComp = childContext.find(SomeComponent.class);
The InjectNext annotation was created to allow the chain of responsability pattern into the components.
When to use the Chain of Responsability Pattern:
- More than one objects may handle a request, and the handler isn’t known a priori. The handler should be ascertained automatically. -You want to issue a request to one of several objects without specifying the receiver explicitly.
- The set of objects that can handle a request should be specified dynamically.
In this pattern exist three important elements:
- Handler: -Defines an interface for handling requests.
- (Optionally) Implements the successor link.
-
ConcreteHandler: -Handles requests it is responsible for. -Can access its successor. -If the ConcreteHandler can handle the request, it does so; otherwise it forwards the request to its successor.
-
Client:
- Initiates the request to a ConcreteHandler object on the chain.
When a client issues a request, the request propagates along the chain until a ConcreteHandler object takes responsibility for handling it.
Then to use the InjectNext annotation you must create some common interface to all the components in the chain, like this.
public interface MyChainHandler<T> //This interface is the **Handler**.
{
T execute(T prev);
}
Then you can create as many components as you whant for the chain. Every implementation of components represent the ConcreteHandler element.
@Component
@Priority(1)
public class ChainHandlerFirst implements MyChainHandler<String>
{
@InjectNext
private MyChainHandler<String> next;
@Override
public String execute(String prev)
{
if(someCOndition)
{
return next.execute(c);
}
return "somedata";
}
}
.....
@Component
@Priority(2)
public class ChainHandlerSecond implements MyChainHandler<String>
{
@InjectNext
private MyChainHandler<String> next;
@Override
public String execute(String prev)
{
.....
}
}
@Component
@Priority(3)
public class ChainHandlerThird implements MyChainHandler<String>
{
@InjectNext
private MyChainHandler<String> next;
@Override
public String execute(String prev)
{
.....
}
}
Notice that all the components in the chain inject the MyChainHandler next; component, with the @InjectNext annotation, the framework will inject the next available component in the chain, this is a component that provides the same service and that has a lesser priority (a higher number) than the current component. By using this pattern you can inject components one inside the others creating a chain.
ChainHandlerFirst -> ChainHandlerSecond -> ChainHandlerThird
Then you can use this chain by injectig the highest priority component in the chain into any component you whant.
@Component
public class SomeComponent
{
@Inject
private MyChainHandler<String> first;
public String execute()
{
return first.execute(null);
}
}
In Java there is a class called ThreadLocal which provides another way of thread-safety apart from synchronization. Usually when we have multiple threads sharing an object we need to synchronize the critical section of the code in order to make it thread safe.
ThreadLocal class provides thread-local variables where each thread that accesses one (via its get or set method) has its own, independently initialized copy of the variable. Since each and every thread has its own copy of the object so explicit synchronization is not needed to provide thread safety.
An important characteristic about ThreadLocal variable is the global access. Any ThreadLocal variable is global to the thread. It can be accessed anywhere from the thread. If, from a thread several methods residing in different classes are called, ThreadLocal variable will be visible to all those methods. There is no need to pass the ThreadLocal variable as a parameter.
At the same time any Threadlocal variable is local to a thread. If there are 10 threads spawned all the 10 threads will have their own ThreadLocal variable. One thread can not access/modify other thread's ThreadLocal variables.
private static final ThreadLocal<String> threadLocalVar = new ThreadLocal<String>();
Here I have created a ThreadLocal variable called threadLocalVar which will store a String value.
##Setting and accessing the value:
Once a ThreadLocal instance is created, its set method can be used to set a value.
threadLocalVar.set("This is a thread local variable");
get method is used to read the value, note that get method returns the value in the current thread's copy of this ThreadLocal variable.
Bridje Framework improve Threadlocal implementation at a so-called class ThreadLocalStorage, here it is define ThreadLocal variable Of the following way:
package org.bridje.ioc.thls.impl;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
class ThreadLocalStorage
{
private final ThreadLocal<Map<Class,List<Object>>> threadLocalStorage;
public ThreadLocalStorage()
{
threadLocalStorage = new ThreadLocal<>();
}
public <T> T get(Class<T> cls)
{
Map<Class, List<Object>> map = threadLocalStorage.get();
if(map != null)
{
List<Object> objects = map.get(cls);
if(objects != null && !objects.isEmpty())
{
return (T)objects.get(objects.size()-1);
}
}
return null;
}
public <T> void pop(Class<T> cls)
{
Map<Class, List<Object>> map = threadLocalStorage.get();
if(map != null)
{
List<Object> objects = map.get(cls);
if(objects != null && !objects.isEmpty())
{
objects.remove(objects.size()-1);
}
}
}
public <T> void put(Class<T> cls, T obj)
{
Map<Class, List<Object>> map = threadLocalStorage.get();
if(map == null)
{
map = new HashMap<>();
threadLocalStorage.set(map);
}
List<Object> objects = map.get(cls);
if(objects == null)
{
objects = new ArrayList<>();
map.put(cls, objects);
}
objects.add(obj);
}
}
The threadLocalStorage variable will store a group of key/value pairs (also known as entries), in this case the keys are class, the values are a object list of the class that represents key. the key identifies an entry, a map cannot contain duplicate keys.
If we want to make use of ThreadLocal variable , then we used Thls class, this class have two methods very important, get and doAs.
public class Thls
{
private static ThlsService thlsServ;
/**
* Get the last object of the cls Class that was put in the thread local
* storage.
* <p>
* @param <T> The type of the object to look for.
* @param cls The class of the object to look for.
* <p>
* @return The last object of the specified class that was put in the thread
* local storage, or null if none can be found.
*/
public static <T> T get(Class<T> cls)
{
....
}
/**
* This method puts all the data objects on the internal thread
* local storage an executes the {@link ThlsAction}.
* <p>
* @param <T> The type of the resulting object for the action.
* @param <D> The type of the data to be put in the thread.
* @param action The action to be executed.
* @param cls The class of the data to be put in the thread.
* @param data The data that must be available for the action.
* <p>
* @return The object returned by the {@link ThlsAction#execute()} method.
* <p>
* @throws Exception If {@link ThlsAction#execute()} throw an exception.
*/
public static <T, D> T doAs(ThlsAction<T> action, Class<D> cls, D data) throws Exception
{
....
}
}
Let's suppose to have a method that accesses a connection of database. The database connection must not be used from varied thread at the same time, the best is that each thread have your own connection in order to avoid problems. Let's look at the following code where we applied ThreadLocal variables:
This code is without ThreadLocal variable
Connection cn = createconnection(dsn); //Create database connection
Record user = finduserfromkey('1',cn); //Search one user with primary key "1"
Let's see the code applying ThreadLocal variables:
Connection cn = createconnection(dsn); //Create database connection
Thls.doAs(()->{ //Save database connection for the thread, and execute **FindUserFromKey** method
return finduserfromkey('1',cn)
}, Connection.class, cn);
If sometime this thread I need the connection, then we get it from the following way:
Connection cn = Thls.get(Connection.class); //return database connection for the thread