fork(4) download
import java.util.*;
import java.lang.*;
import java.io.*;
import java.lang.annotation.*;
import java.lang.reflect.*;

// created for https://p...content-available-to-author-only...e.com/questions/232229/understanding-dependency-injection/232239#232239
// Copyright 2014 by "amon"
// Licensed under CC-BY-SA
// https://c...content-available-to-author-only...s.org/licenses/by-sa/4.0/

class Main {
	
	public static void main (String[] args){
		try {
			// set up some dependencies
			Dizzy.DependencyManager dm = new Dizzy.DependencyManager();
			dm.set(Foo.class, Foo.class)
			  .set(Bar.class, Bar.Mocking.class)
			  .set(Qux.class, Qux.Mocking.class);
			
			// create a Foo instance with all dependencies resolved
			dm.make(Foo.class).run();
			
			// change the configuration
			dm.set(Bar.class, Bar.Production.class)
			  .set(Qux.class, Qux.Production.class);
			
			// make another instance with different implementations
			dm.make(Foo.class).run();
		}
		catch(Exception e) {
			e.printStackTrace(System.out);
		}
	}
	
	interface Bar {
		public void bar();
		
		class Production implements Bar {
			public void bar() {
				System.out.println("Bar.Production.bar called");
			}
		}
		
		class Mocking implements Bar {
			public void bar() {
				System.out.println("Bar.Mocking.bar called");
			}
		}
	}
	
	interface Qux {
		public void qux();
		
		class Production implements Qux {
			public void qux() {
				System.out.println("Qux.Production.qux called");
			}
		}
		
		class Mocking implements Qux {
			public void qux() {
				System.out.println("Qux.Mocking.qux called");
			}
		}
	}
	
	static class SuperFoo {
		@Dizzy.Dependency(Bar.class)
		protected Bar bar;
	}
	
	static class Foo extends SuperFoo {
		@Dizzy.Dependency(Bar.class)
		final private Bar bar = null; // final fields work if we init them to bogus
		
		@Dizzy.Dependency(Qux.class)
		private Qux qux;
		
		public void run() {
			bar.bar();
			qux.qux();
			super.bar.bar();
		}
	}
	
	/**
	 * Dizzy is a small dependency injection framework.
	 */
	static class Dizzy {
		/**
		 * Marks instance fields which should be injected
		 */
		@Target(value=ElementType.FIELD)
		@Retention(value=RetentionPolicy.RUNTIME)
		public @interface Dependency {
			Class value();
		}
		
		static class DependencyManager {
			private Map<Class<?>, Class<?>> dependencies = new HashMap<>();
			
			/**
			 * Sets a dependency
			 */
			public <T> DependencyManager set(
					Class<T>            dependency,
					Class<? extends T>  implementation)
			{
				dependencies.put(dependency, implementation);
				return this;
			}
			
			/**
			 * Fetches the currently set dependency for some instance
			 */
			public <T> Class<T> get(Class<T> dependency) throws Dizzy.Exception {
				Class<T> implementation = (Class<T>) dependencies.get(dependency);
				if (implementation == null) {
					throw new Dizzy.Exception(new NullPointerException());
				}
				return implementation;
			}
			
			/**
			 * Creates a new instance of the required type and automatically initializes any fields marked as a "@Dizzy.Dependency"
			 */
			public <T> T make(Class<T> dependency) throws Dizzy.Exception {
				Class<T> implementation = this.get(dependency);
				try {
					Constructor<T> ctor = implementation.getDeclaredConstructor();
					T instance = ctor.newInstance();
					this.initializeFields(implementation, instance);
					return instance;
				}
				catch (	IllegalAccessException
					|	NoSuchMethodException
					|	InstantiationException
					|	InvocationTargetException
					|	SecurityException e)
				{
					throw new Dizzy.Exception(e);
				}
			}
			
			private <T> void initializeFields(
					Class<T>    clazz,
					T           instance)
					throws Dizzy.Exception
			{
				for (Class<? super T> c = clazz; c != null; c = c.getSuperclass()) {
					for (Field f : c.getDeclaredFields()) {
						if (!f.isAnnotationPresent(Dizzy.Dependency.class)) {
							continue;
						}
						try {
							Dizzy.Dependency dep = f.getAnnotation(Dizzy.Dependency.class);
							f.setAccessible(true);
							f.set(instance, this.make(dep.value()));
						}
						catch (	IllegalAccessException
							 |	Exception e)
						{
							throw new Dizzy.Exception(e);
						}
					}
				}
			}
		}
		
		/**
		 * A wrapper exception type
		 */
		static class Exception extends java.lang.Exception {
			public Exception(java.lang.Exception e) {
				super(e);
			}
		}
	}
	
}
Success #stdin #stdout 0.1s 380352KB
stdin
Standard input is empty
stdout
Bar.Mocking.bar called
Qux.Mocking.qux called
Bar.Mocking.bar called
Bar.Production.bar called
Qux.Production.qux called
Bar.Production.bar called