Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Todo.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ Parameter-agnostic methods


- DI framework
Autowiring
Autowiring (done)
Maybe explicit mapping/configuration

- error handling
Expand Down
49 changes: 43 additions & 6 deletions src/main/java/com/norwood/core/AnnotationProcessor.java
Original file line number Diff line number Diff line change
Expand Up @@ -40,16 +40,12 @@ public void processAnnotations(List<Class<?>> classDefinitions, Router router) {

private void inject(Field field) {
try {
Class<?> fieldType = field.getType();
Object owner = container().get(field.getDeclaringClass());
Object dependency = fieldType.getDeclaredConstructor().newInstance();
Object dependency = resolveDependency(field.getType());

field.setAccessible(true);
field.set(owner, dependency);

System.out.println(owner);
System.out.println(dependency);
} catch (InstantiationException |
} catch (InstantiationException |
IllegalAccessException | IllegalArgumentException |
InvocationTargetException | NoSuchMethodException e)
{
Expand All @@ -58,6 +54,47 @@ private void inject(Field field) {
}
}

private Object resolveDependency(Class<?> fieldType)
throws InstantiationException, IllegalAccessException,
IllegalArgumentException, InvocationTargetException,
NoSuchMethodException {
Object existing = container().get(fieldType);
if (existing != null && fieldType.isAnnotationPresent(Singleton.class)) {
return existing;
}

Object instance = instantiate(fieldType);

if (fieldType.isAnnotationPresent(Singleton.class)) {
container().set(fieldType, instance);
}

return instance;
}

private Object instantiate(Class<?> clazz)
throws InstantiationException, IllegalAccessException,
IllegalArgumentException, InvocationTargetException,
NoSuchMethodException {
// Prefer zero argument constructor
try {
var ctor = clazz.getDeclaredConstructor();
ctor.setAccessible(true);
return ctor.newInstance();
} catch (NoSuchMethodException e) {
// Fall back to first constructor and resolve parameters
}

var ctor = clazz.getDeclaredConstructors()[0];
ctor.setAccessible(true);
Class<?>[] paramTypes = ctor.getParameterTypes();
Object[] params = new Object[paramTypes.length];
for (int i = 0; i < paramTypes.length; i++) {
params[i] = resolveDependency(paramTypes[i]);
}
return ctor.newInstance(params);
}

private void routePost(Post a, Router router, Method method) {
String path = a.path();
if (router.hasRouteWithPath(path)) {
Expand Down
5 changes: 5 additions & 0 deletions src/main/java/com/norwood/core/KatanaContainer.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,13 @@ public <T> T get(Class<T> beanClass) {
@Override
public <T> void set(Class<T> beanClass, T bean) {
if (beans.get(beanClass) != null) {
// Ignore subsequent registrations for singletons
if (beanClass.isAnnotationPresent(Singleton.class)) {
return;
}
throw new BeanAlreadyDefinedException("Bean already defined.");
}

beans.put(beanClass, bean);
}

Expand Down
42 changes: 42 additions & 0 deletions src/test/java/com/norwood/AutowireTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package com.norwood;

import junit.framework.TestCase;

import com.norwood.core.AnnotationProcessor;
import com.norwood.core.KatanaContainer;
import com.norwood.core.Singleton;
import com.norwood.core.annotations.Inject;
import com.norwood.routing.Router;

public class AutowireTest extends TestCase {
@Singleton
public static class SingletonService {}

public static class NoDefaultConstructor {
final SingletonService service;
public NoDefaultConstructor(SingletonService service) {
this.service = service;
}
}

public static class ClientBean {
@Inject SingletonService singleton;
@Inject NoDefaultConstructor nodefault;
}

public void testAutowiring() {
KatanaContainer container = new KatanaContainer();
container.set(ClientBean.class, new ClientBean());
container.set(SingletonService.class, new SingletonService());

AnnotationProcessor processor = new AnnotationProcessor();
processor.processAnnotations(container.classDefinitions(), new Router());

ClientBean bean = container.get(ClientBean.class);
SingletonService svc = container.get(SingletonService.class);

assertSame(svc, bean.singleton);
assertNotNull(bean.nodefault);
assertSame(svc, bean.nodefault.service);
}
}