Building a simple IoC Container for Java using AspectJ

Having classes,fields, and methods in your code does not mean that you are applying object oriented principles. Classes, fields, and methods are just structures that we use to achieve some level of abstraction and cohesion.

 class Adder{
 
  void add(int x,int y){
   int sum = x + y;
   DatabaseWriter writer = new DatabaseWriter();
   writer.writeToDB(sum);
 }
}

Despite the fact that it is just an adder, this code does not work if the database is not available, simply because it is dependant on some database features. So basically, this code is rigid, untestable, tightly coupled, and it is just awful.

Cool,lets hack it and make it better, more abstract, and less dependant.

interface Writer{
 void write(Object object);
}
DatabaseWriter implements Writer{
 void write(Object object){
  ...
 }
} 

And now, instead of using DatabaseWriter directly in the Adder, we can inject it and use the Writer object which is more abstract.

 class Adder{
  private Writer writer;
  
  void add(int x,int y){
   int sum = x + y;
   writer.write(sum);
 }
}

So far so good, the Adder looks alright now, but we still missing something, how we gonna tell Adder to use DatabaseWriter or any Writer implementation. The answer is: Dependency Injection.
Dependency Injection is a pattern that implements inversion of control. A common way for doing DI in java is to use Spring DI which is brilliant. But what stops from building our own inversion of control container, it is fun ,isnt it? Our IoC container is called simpleDI and it is on GitHub, Go and fork it

Well, Let’s get started.

public interface IFoo {
    String fn();
}

public class Bar implements IFoo{

    @Override
    public String fn() {
        return "Bar";
    }
}
@Service
public class SimpleService {

    @Injectable
    private IFoo foo;
}

SimpleService is a service that has a dependency on some implementation of IFoo. SimpleService is annotated by @Service and IFoo by @Injectable and this how we can tell simpleDI that SimpleService is a service that has an injectable dependency on IFoo.Alright, now we need to tell simpleDI that Bar is the concrete implementation of IFoo.

DIBinding fooDIBinding = new DIBinding.BindingBuilder(IFoo.class).to(Bar.class).build();
DIContext.INSTANCE.addBinding(fooDIBinding);
DIContext.INSTANCE.injectDependencies();

Whenever an instance of a type that is annotated by @Service get instantiated, our DI has to inject its dependency based on a binding we specify.Hmmm, in plain English, we need to hijack constructor calls.
AspectJ is a very powerful tool for Aspect Oriented Programming in Java,and here we go

@Aspect
public class InjectionAspect{

    @Pointcut("execution ( (@simpleDI.annotations.Service *).new(..))")
    public void serviceInitPointcut(){}

    @Before("serviceInitPointcut()")
    public void serviceInitAdvice(JoinPoint joinPoint) throws InstantiationException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        Object target =  joinPoint.getTarget();
        DIContext.INSTANCE.initService(target);
    }

}

Our Aspect contains a simple advice that catches service’s constructor calls and its dependencies get injected accordingly.
Now we can iterate over declared fields in the service and instantiate them.

   public Object initService(Object service) throws NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException {
        if(!service.getClass().isAnnotationPresent(Service.class)){
            throw new IllegalArgumentException("The passed object is not a service, use "+Service.class.getName() +" annotation");
        }
        for(Field field: service.getClass().getDeclaredFields()){
            if(field.isAnnotationPresent(Injectable.class)){
                recursiveFieldBinding(service, field);
            }
        }

        return service;
    }

    private void recursiveFieldBinding(Object root, Field field) throws InvocationTargetException, NoSuchMethodException, InstantiationException, IllegalAccessException {
        Class fieldType = field.getType();
        field.setAccessible(true);
        DIBinding binding = DIContext.INSTANCE.getBindingForType(fieldType);
        InstantiationMethod method = binding.getInstantiationMethod();
        Object instance = method.getInstance(binding.getBindingType());

        field.set(root,instance);

        for(Field innerField: fieldType.getDeclaredFields()){
            if(innerField.isAnnotationPresent(Injectable.class)){
                this.recursiveFieldBinding(instance, innerField);
            }
        }


    }

Fields are types and types might be injectables, and therefore, so we have to inject their dependencies, this why we had to bind fields recursively.Well, our recursive binding is a bit vulnerable, we will definitely get a stack overflow if the service contains cyclic dependencies. So, Let’s check for cycles then.

public void checkForCycles() throws CyclicDependencyException {
        for(DIBinding binding: bindingSet){
            checkForCycles(binding.getBindingType(), new HashSet<Class>());
        }
    }

    void checkForCycles(Class clazz, HashSet<Class> visited) throws CyclicDependencyException {
        if(visited.contains(clazz)){
            throw new CyclicDependencyException(String.format("Cyclic Dependency Detected: %s", clazz));
        }
        visited.add(clazz);
        for(Field innerField: clazz.getDeclaredFields()){
            if(innerField.isAnnotationPresent(Injectable.class)){
                this.checkForCycles(innerField.getType(), visited);
            }
        }
    }

Brilliant, our tiny framework is ready now.So let’s see how flexible our simpleDI is.

  • We can bind abstract types to concrete ones
               DIBinding fooDIBinding = new DIBinding.BindingBuilder(IFoo.class).to(Bar.class).build();
               
  • We can bind concrete types to themselves
               DIBinding barDIBinding = new DIBinding.BindingBuilder(Bar.class).toSelf().build();
               
  • Be default,void constructors are used to instantiate injectables, but we can easily change this behavior and make it an object factory instead

And here we go, we’ve implemented our very basic IoC container. Feel free to fork it on GitHub and hack it if you want.

Advertisements

Leave a Reply

Name and email address are required. Your email address will not be published.

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

w

Connecting to %s

You may use these HTML tags and attributes:

<a href="" title="" rel=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <pre> <q cite=""> <s> <strike> <strong>