Looking into the Magic of Dependency Injection Using Annotations - Part 3
Join the DZone community and get the full member experience.
Join For FreeThis is the third in a short series of blogs that looks into
implementing dependency injection using annotations.If you’re familiar
with Spring or EJB3, you’ll know the sort of thing I’m talking about:
you write you class; add a few annotations, for example @Autowired or @Ejb; deploy it to your web-server and then et volia it’s all glued together and works, as if by magic...
This blog isn’t going to write a fully formed DI factory using
annotations, but it will give you a few hints in to the kinds of
techniques used by the Guys at Spring and the EJB3 Team.
In order to write a DI factory like this, you need to complete a few
specific tasks. These include figuring out which classes on your
class-path are annotated, instantiating those classes and gluing them
all together. My last two blogs demonstrated how to find all the .class
files located either directly on the file system or in JAR files. In
today’s scenario, and using the code from my previous blogs, we’ve
already located some classes and are ready to start checking them for
annotations. In order to demonstrate this I’ve written a couple of
suitable annotations. The first one is called @MyComponent,
which is a type level annotation that’s only applied to whole classes.
This annotation tells us which classes we’ll be instantiating as part of
our application:
@Documented @Retention(RetentionPolicy.RUNTIME) @Target({ ElementType.TYPE, ElementType.ANNOTATION_TYPE }) public @interface MyComponent { }The second annotation that’s needed is one that’ll take care of the dependency injection. This is called @MyAutoWire, which as the name suggests is a method and field level annotation that tells us which classes to inject into what methods or fields:
@Documented @Retention(RetentionPolicy.RUNTIME) @Target({ ElementType.METHOD, ElementType.FIELD }) public @interface MyAutoWire { }
Before getting down to the nitty-gritty we’ll also need a class that’s marked with these annotations that we can use for testing:
@MyComponent public class ClassWithAttributes { private String str1; private int value1; @MyAutoWire private InjectedClass injectedClass; @MyAutoWire public void setValue1(int value1) { this.value1 = value1; } }
Now we can get down to business. The code below is an annotation checker that contains three straight forward methods all of which use reflection.
public class AnnotationChecker { private final Class<?> instance; public AnnotationChecker(Class<?> instance) { this.instance = instance; } public boolean isMyComponent() { return instance.isAnnotationPresent(MyComponent.class); } private boolean isNotNull(Object obj) { return obj != null; } public List<Method> getAutoWireMethods() { List<Method> methodList = new ArrayList<Method>(); Method[] methods = instance.getDeclaredMethods(); for (Method method : methods) { if (isNotNull(method.getAnnotation(MyAutoWire.class))) { methodList.add(method); } } return methodList; } public List<Field> getAutoWireFields() { List<Field> fieldList = new ArrayList<Field>(); Field[] fields = instance.getDeclaredFields(); for (Field field : fields) { if (isNotNull(field.getAnnotation(MyAutoWire.class))) { fieldList.add(field); } } return fieldList; } }
public class AnnotationCheckerTest { @Test public void testIsAnnotatedWithMyComponent() { final AnnotationChecker instance = new AnnotationChecker( ClassWithAttributes.class); assertTrue(instance.isMyComponent()); } @Test public void testIsNotAnnotatedWithMyComponent() { assertFalse(new AnnotationChecker(String.class).isMyComponent()); } @Test public void testGetAutoWireMethodsWithAnnotationPresent() { final AnnotationChecker instance = new AnnotationChecker( ClassWithAttributes.class); List<Method> methods = instance.getAutoWireMethods(); assertEquals(1, methods.size()); // Test that its the right method Method method = methods.get(0); String name = method.getName(); assertEquals("setValue1", name); // Check that it is actually the correct annotation type MyAutoWire myComponent = method.getAnnotation(MyAutoWire.class); assertNotNull(myComponent); } @Test public void testGetAutoWireMethodsWithNoMethodsAnnotated() { final AnnotationChecker instance = new AnnotationChecker(String.class); List<Method> methods = instance.getAutoWireMethods(); // return empty array rather than risk a NullPointerException assertEquals(0, methods.size()); } @Test public void testGetAutoWireFieldsWithAFieldAnnotated() { final AnnotationChecker instance = new AnnotationChecker( ClassWithAttributes.class); List<Field> fields = instance.getAutoWireFields(); assertEquals(1, fields.size()); // Test that its the right method Field field = fields.get(0); String name = field.getName(); assertEquals("injectedClass", name); // Check that it is actually the correct annotation type MyAutoWire myComponent = field.getAnnotation(MyAutoWire.class); assertNotNull(myComponent); } @Test public void testGetAutoWireFieldsWithNofieldsWired() { final AnnotationChecker instance = new AnnotationChecker(String.class); List<Field> fields = instance.getAutoWireFields(); assertEquals(0, fields.size()); } }
All that’s left to do is to use this class with the code from the previous two blogs together with a little jiggery-pokery to wire the whole lot together. More on that later...
From http://www.captaindebug.com/2011/10/looking-into-magic-of-dependency.html
Opinions expressed by DZone contributors are their own.
Comments