В прошлый раз обещал, что выложу самописный IoC контейнер. Видимо пора бы это сделать уже - столько времени прошло.
Итак исходники качаем тут.
Начнем как всегда с тестов.
Именно так я и написал код, когда я начал разработку этого примера. Никаких классов контейнера естественно не было, и тест не компилился. Но я создал пустые классы с помощью IDE (Ctrl1-1 в Eclipse или Alt-Enter в Idea).
Вот он код пустой
Так теперь код хоть компилится! :) После того как была создана интерфейсная часть, я взялся за реализацию с помощью TDD.
Вот те тестовые сценарии, которые я один за другим написал и реализовал. Кода очень много :) потому что должен был проверить всевозможные случаи инъекции с разными полями, с одинаковыми полями, с наследниками, с реализациями, с примитивами и так далее. Если хочешь скипнуть - жми сюда.
Текста на самом деле много. А еще я все классы сделал Inner классами хотя в реальной версии они должны быть отдельными public классами иначе ничего не получится. Работа с иннераклассами у меня у туду.
Постепенно появлялась реализация. После несокльких подходов к коду я родил вот эти реализации
Классы достаточно сложновастые, но рефлексия все же. Библиотеку Fest Reflection я не хотел подключать из за лишней jar-dependency. В следующий раз попробую сделаю с ней - за одно попиарю этот классный инструмент для работы с рефлексией.
В будущих планах есть сделать то же но со Spring IoC. Ждите, продолжение следует..
Итак исходники качаем тут.
Начнем как всегда с тестов.
package container;
import static org.junit.Assert.*;
import org.junit.Before;
import org.junit.Test;
import container.ioc.*;
import container.flashlight.*;
import container.battery.*;
public class TestBaterry {
private Container container;
@Before
public void initContainer() {
// обрати внимание перед каждым тестом проинитится контейнер, которому мы сообщили, что
// заместь батарейки используй ChinaBattery а вместо фонарика SomeFlashlight
container = new ContainerImpl(
Binder.use(ChinaBattery.class).as(Battery.class),
Binder.use(SomeFlashlight.class).as(Flashlight.class));
}
@Test
public void testDischargeNewBattery() {
// таким нехитрым способом по интерфейсу мы получаем реализацию
// контейнер пройдется по всем полям новосозданного объекта и
// если там найдет знакомые типы то вставит в них реализации,
// в соответствии с настройками, которые мы указали
Flashlight flashlight = container.get(Flashlight.class);
assertFalse(flashlight.isShines());
flashlight.swithOn();
assertTrue(flashlight.isShines());
for (int count = 0; count < 1000; count ++) {
flashlight.swithOff();
flashlight.swithOn();
}
flashlight.swithOn();
assertFalse(flashlight.isShines());
}
@Test
public void testBadBattery() {
Battery battery = new Battery(){
@Override
public boolean getVoltage() {
return false;
}
};
// а вот так мы вдруг можем передумать и переопределить настройки контейнера
container.update(Binder.use(battery).as(Battery.class));
Flashlight flashlight = container.get(Flashlight.class);
assertFalse(flashlight.isShines());
flashlight.swithOn();
assertFalse(flashlight.isShines());
}
@Test
public void testNoGetPowerIfDoubleSwithOn() {
Flashlight flashlight = container.get(Flashlight.class);
assertFalse(flashlight.isShines());
for (int count = 0; count < 1000; count ++) {
flashlight.swithOn();
}
assertTrue(flashlight.isShines());
}
@Test
public void testNoBatteryNoLight() {
container.remove(Battery.class);
Flashlight flashlight = container.get(Flashlight.class);
assertFalse(flashlight.isShines());
flashlight.swithOn();
assertFalse(flashlight.isShines());
}
@Test
public void integrationTestGetPowerFormNewChinaBattery() {
Flashlight flashlight = container.get(Flashlight.class);
assertFalse(flashlight.isShines());
flashlight.swithOn();
assertTrue(flashlight.isShines());
}
}Именно так я и написал код, когда я начал разработку этого примера. Никаких классов контейнера естественно не было, и тест не компилился. Но я создал пустые классы с помощью IDE (Ctrl1-1 в Eclipse или Alt-Enter в Idea).
Вот он код пустой
package container.ioc;
public class Binder {
public static Binder use(Class<?> classToCreate) {
return null;
}
public static Binder use(Object object) {
return null;
}
public Binder as(Class<?> interfaceClass) {
return null;
}
}package container.ioc;
public interface Container {
<T> T get(Class<T> interfaceClass);
void remove(Class<?> clazz);
void update(Binder binder);
}package container.ioc;
public class ContainerImpl implements Container {
public ContainerImpl(Binder...binders) {
}
@Override
public <T> T get(Class<T> interfaceClass) {
return null;
}
@Override
public void remove(Class<?> clazz) {
}
@Override
public void update(Binder binder) {
}
}Так теперь код хоть компилится! :) После того как была создана интерфейсная часть, я взялся за реализацию с помощью TDD.
Вот те тестовые сценарии, которые я один за другим написал и реализовал. Кода очень много :) потому что должен был проверить всевозможные случаи инъекции с разными полями, с одинаковыми полями, с наследниками, с реализациями, с примитивами и так далее. Если хочешь скипнуть - жми сюда.
Текста на самом деле много. А еще я все классы сделал Inner классами хотя в реальной версии они должны быть отдельными public классами иначе ничего не получится. Работа с иннераклассами у меня у туду.
package container2.ioc.test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import org.junit.Test;
import container.ioc.Binder;
import container.ioc.ContainerImpl;
public class IoCTest {
interface Marker1 {
}
class Marker1Impl implements Marker1{
}
interface Marker2 {
}
class Marker2Impl implements Marker2 {
}
interface Marker3 {
}
class Marker3Impl implements Marker3 {
}
interface MainMarker {
}
private <T> T getImpl(Class<T> mainClass) {
return (T)new ContainerImpl(
Binder.use(Marker1Impl.class).as(Marker1.class),
Binder.use(Marker2Impl.class).as(Marker2.class),
Binder.use(Marker3Impl.class).as(Marker3.class),
Binder.use(mainClass).as(MainMarker.class)).get(MainMarker.class);
}
class ClassWithDefaultConstructor implements MainMarker {
Marker1 marker1;
public ClassWithDefaultConstructor() {
}
}
@Test
public void testWithDefaultConstructor() {
assertEquals(Marker1Impl.class,
getImpl(ClassWithDefaultConstructor.class).marker1.getClass());
}
class ClassWithoutAnyConstructors implements MainMarker {
Marker1 marker1;
private ClassWithoutAnyConstructors() {
}
}
@Test
public void testWithoutAnyConstructors() {
assertEquals(Marker1Impl.class,
getImpl(ClassWithoutAnyConstructors.class).marker1.getClass());
}
class ClassWithPrivateField implements MainMarker {
private Marker1 marker1;
public ClassWithPrivateField() {}
public Object getMarker1() {
return marker1;
}
}
@Test
public void testWithPrivateField() {
assertEquals(Marker1Impl.class,
getImpl(ClassWithPrivateField.class).getMarker1().getClass());
}
class ClassWithInjectorConstructor implements MainMarker {
private Marker1 marker1;
public ClassWithInjectorConstructor(Marker1 marker1) {
this.marker1 = marker1;
}
public Marker1 getMarker1() {
return marker1;
}
}
@Test
public void testWithInjectorConstructor() {
assertEquals(Marker1Impl.class,
getImpl(ClassWithInjectorConstructor.class).getMarker1().getClass());
}
class СlassWithInjectorConstructorAndTwoFields implements MainMarker {
private Marker1 marker1;
private Marker2 marker2;
public СlassWithInjectorConstructorAndTwoFields(Marker2 marker2, Marker1 marker1) {
this.marker1 = marker1;
this.marker2 = marker2;
}
public Marker1 getMarker1() {
return marker1;
}
public Marker2 getMarker2() {
return marker2;
}
}
@Test
public void testWithInjectorConstructorAndTwoFieldsCheckField1() {
assertEquals(Marker1Impl.class,
getImpl(СlassWithInjectorConstructorAndTwoFields.class).getMarker1().getClass());
}
@Test
public void testWithInjectorConstructorAndTwoFieldsCheckField2() {
assertEquals(Marker2Impl.class,
getImpl(СlassWithInjectorConstructorAndTwoFields.class).getMarker2().getClass());
}
class СlassWithPrivateInjectorConstructorAndTwoFields implements MainMarker {
private Marker1 marker1;
private Marker2 marker2;
private СlassWithPrivateInjectorConstructorAndTwoFields(Marker2 marker2, Marker1 marker1) {
this.marker1 = marker1;
this.marker2 = marker2;
}
public Marker1 getMarker1() {
return marker1;
}
public Marker2 getMarker2() {
return marker2;
}
}
@Test
public void testWithPrivateInjectorConstructorAndTwoFieldsCheckField1() {
assertEquals(Marker1Impl.class,
getImpl(СlassWithPrivateInjectorConstructorAndTwoFields.class).getMarker1().getClass());
}
@Test
public void testWithPrivateInjectorConstructorAndTwoFieldsCheckField2() {
assertEquals(Marker2Impl.class,
getImpl(СlassWithPrivateInjectorConstructorAndTwoFields.class).getMarker2().getClass());
}
class ClassWithTwoConstructors implements MainMarker {
private Marker1 marker1;
public ClassWithTwoConstructors() {
}
private ClassWithTwoConstructors(Marker1 marker) {
this.marker1 = marker;
}
public Object getMarker1() {
return marker1;
}
}
@Test
public void testWithTwoConstructors() {
assertEquals(Marker1Impl.class,
getImpl(ClassWithTwoConstructors.class).getMarker1().getClass());
}
class ClassWithTwoConstructorsWitnSameParametersCount implements MainMarker {
private Marker1 marker1;
private ClassWithTwoConstructorsWitnSameParametersCount(Marker1 marker) {
this.marker1 = marker;
}
private ClassWithTwoConstructorsWitnSameParametersCount(String string) {
}
public Object getMarker1() {
return marker1;
}
}
@Test
public void testWithTwoConstructorsWithSameParametersCount() {
assertEquals(Marker1Impl.class,
getImpl(ClassWithTwoConstructorsWitnSameParametersCount.class).getMarker1().getClass());
}
class ClassWithTwoFieldsAndOnlyOneInjectedViaConstructor implements MainMarker {
private Marker1 marker1;
private Marker2 marker2;
private boolean costructorCall = false;
private ClassWithTwoFieldsAndOnlyOneInjectedViaConstructor(Marker1 marker) {
this.costructorCall = true;
this.marker1 = marker;
}
public Object getMarker1() {
return marker1;
}
public Object getMarker2() {
return marker2;
}
public boolean isCostructorCall() {
return costructorCall;
}
}
@Test
public void testWithTwoFieldsAndOnlyOneInjectedViaConstructor1() {
assertEquals(Marker1Impl.class,
getImpl(ClassWithTwoFieldsAndOnlyOneInjectedViaConstructor.class).getMarker1().getClass());
}
@Test
public void testWithTwoFieldsAndOnlyOneInjectedViaConstructor2() {
assertEquals(Marker2Impl.class,
getImpl(ClassWithTwoFieldsAndOnlyOneInjectedViaConstructor.class).getMarker2().getClass());
}
@Test
public void testWithTwoFieldsAndOnlyOneInjectedViaConstructor_constructorUsed() {
assertTrue(getImpl(ClassWithTwoFieldsAndOnlyOneInjectedViaConstructor.class).isCostructorCall());
}
class ClassWithTwoFieldsAndOtherOneInjectedViaConstructor implements MainMarker {
private Marker1 marker1;
private Marker2 marker2;
private boolean costructorCall = false;
private ClassWithTwoFieldsAndOtherOneInjectedViaConstructor(Marker2 marker) {
this.costructorCall = true;
this.marker2 = marker;
}
public Object getMarker1() {
return marker1;
}
public Object getMarker2() {
return marker2;
}
public boolean isCostructorCall() {
return costructorCall;
}
}
@Test
public void testWithTwoFieldsAndOtherOneInjectedViaConstructor_CheckField1() {
assertEquals(Marker1Impl.class,
getImpl(ClassWithTwoFieldsAndOtherOneInjectedViaConstructor.class).getMarker1().getClass());
}
@Test
public void testWithTwoFieldsAndOtherOneInjectedViaConstructor_CheckField2() {
assertEquals(Marker2Impl.class,
getImpl(ClassWithTwoFieldsAndOtherOneInjectedViaConstructor.class).getMarker2().getClass());
}
@Test
public void testWithTwoFieldsAndOtherOneInjectedViaConstructor_constructorUsed() {
assertTrue(getImpl(ClassWithTwoFieldsAndOtherOneInjectedViaConstructor.class).isCostructorCall());
}
class ClassWithThreeFieldsAndOnlyOneInjectedViaConstructor implements MainMarker {
private Marker1 marker1;
private Marker2 marker2;
private boolean costructorCall = false;
private Marker3 marker3;
private ClassWithThreeFieldsAndOnlyOneInjectedViaConstructor(Marker3 marker) {
this.costructorCall = true;
this.marker3 = marker;
}
public Object getMarker1() {
return marker1;
}
public Object getMarker2() {
return marker2;
}
public boolean isCostructorCall() {
return costructorCall;
}
public Object getMarker3() {
return marker3;
}
}
@Test
public void testWithThreeFieldsAndOtherOneInjectedViaConstructor_CheckField1() {
assertEquals(Marker1Impl.class,
getImpl(ClassWithThreeFieldsAndOnlyOneInjectedViaConstructor.class).getMarker1().getClass());
}
@Test
public void testWithThreeFieldsAndOtherOneInjectedViaConstructor_CheckField2() {
assertEquals(Marker2Impl.class,
getImpl(ClassWithThreeFieldsAndOnlyOneInjectedViaConstructor.class).getMarker2().getClass());
}
@Test
public void testWithThreeFieldsAndOtherOneInjectedViaConstructor_CheckField3() {
assertEquals(Marker3Impl.class,
getImpl(ClassWithThreeFieldsAndOnlyOneInjectedViaConstructor.class).getMarker3().getClass());
}
@Test
public void testWithThreeFieldsAndOtherOneInjectedViaConstructor_constructorUsed() {
assertTrue(getImpl(ClassWithThreeFieldsAndOnlyOneInjectedViaConstructor.class).isCostructorCall());
}
class Marker1Fasade implements Marker1 {
private Marker1 marker;
public Marker1Fasade(Marker1 marker) {
this.marker = marker;
}
public Object getMarker() {
return marker;
}
}
class ClassWithTwoFieldsAndOnlyOneInjectedViaConstructorWithFasade implements MainMarker {
private Marker1 marker1;
private Marker2 marker2;
private ClassWithTwoFieldsAndOnlyOneInjectedViaConstructorWithFasade(Marker1 marker) {
this.marker1 = new Marker1Fasade(marker);
}
public Object getMarker1() {
return marker1;
}
public Object getMarker2() {
return marker2;
}
}
@Test
public void testWithTwoFieldsAndOnlyOneInjectedViaConstructorWithFasade_checkThatNoReflectionInjectionIfConstructorInjected() {
ClassWithTwoFieldsAndOnlyOneInjectedViaConstructorWithFasade impl =
getImpl(ClassWithTwoFieldsAndOnlyOneInjectedViaConstructorWithFasade.class);
assertEquals(Marker1Fasade.class, impl.getMarker1().getClass());
assertEquals(Marker1Impl.class, ((Marker1Fasade)impl.getMarker1()).getMarker().getClass());
}
class ClassWithThreeFieldsAndTwoInjectedViaConstructor implements MainMarker {
private Marker3 marker3;
private boolean costructorCall = false;
private Marker1 marker1;
private Marker2 marker2;
private ClassWithThreeFieldsAndTwoInjectedViaConstructor(Marker3 marker3, Marker2 marker2) {
this.costructorCall = true;
this.marker3 = marker3;
this.marker2 = marker2;
}
public Object getMarker1() {
return marker1;
}
public Object getMarker2() {
return marker2;
}
public boolean isCostructorCall() {
return costructorCall;
}
public Object getMarker3() {
return marker3;
}
}
@Test
public void testWithThreeFieldsAndTwoInjectedViaConstructor_CheckField1() {
assertEquals(Marker1Impl.class,
getImpl(ClassWithThreeFieldsAndTwoInjectedViaConstructor.class).getMarker1().getClass());
}
@Test
public void testWithThreeFieldsAndTwoInjectedViaConstructor_CheckField2() {
assertEquals(Marker2Impl.class,
getImpl(ClassWithThreeFieldsAndTwoInjectedViaConstructor.class).getMarker2().getClass());
}
@Test
public void testWithThreeFieldsAndTwoInjectedViaConstructor_CheckField3() {
assertEquals(Marker3Impl.class,
getImpl(ClassWithThreeFieldsAndTwoInjectedViaConstructor.class).getMarker3().getClass());
}
@Test
public void testWithThreeFieldsAndTwoInjectedViaConstructor_constructorUsed() {
assertTrue(getImpl(ClassWithThreeFieldsAndTwoInjectedViaConstructor.class).isCostructorCall());
}
class Marker3Fasade implements Marker3 {
private Marker3 marker;
public Marker3Fasade(Marker3 marker) {
this.marker = marker;
}
public Object getMarker() {
return marker;
}
}
class ClassWithThreeFieldsAndTwoConstructors implements MainMarker {
private Marker3 marker3;
private boolean isCommonCostructorCall = false;
private Marker1 marker1;
private Marker2 marker2;
private ClassWithThreeFieldsAndTwoConstructors(Marker1 marker1) {
this.marker1 = marker1;
}
private ClassWithThreeFieldsAndTwoConstructors(Marker3 marker3, Marker2 marker2) {
this.isCommonCostructorCall = true;
this.marker3 = new Marker3Fasade(marker3);
this.marker2 = marker2;
}
public Object getMarker1() {
return marker1;
}
public Object getMarker2() {
return marker2;
}
public boolean isCommonCostructorCall() {
return isCommonCostructorCall;
}
public Object getMarker3() {
return marker3;
}
}
@Test
public void testWithThreeFieldsAndTwoConstructors_CheckField1() {
assertEquals(Marker1Impl.class,
getImpl(ClassWithThreeFieldsAndTwoConstructors.class).getMarker1().getClass());
}
@Test
public void testWithThreeFieldsAndTwoConstructors_CheckField2() {
assertEquals(Marker2Impl.class,
getImpl(ClassWithThreeFieldsAndTwoConstructors.class).getMarker2().getClass());
}
@Test
public void testWithThreeFieldsAndTwoConstructors_CheckField3() {
ClassWithThreeFieldsAndTwoConstructors impl =
getImpl(ClassWithThreeFieldsAndTwoConstructors.class);
assertEquals(Marker3Fasade.class, impl.getMarker3().getClass());
assertEquals(Marker3Impl.class, ((Marker3Fasade)impl.getMarker3()).getMarker().getClass());
}
@Test
public void testWithThreeFieldsAndTwoConstructors_constructorUsed() {
assertTrue(getImpl(ClassWithThreeFieldsAndTwoConstructors.class).isCommonCostructorCall());
}
class ClassWithThreeFieldsAndThreeConstructorsOneIsNotUsed implements MainMarker {
private Marker3 marker3;
private boolean isCommonCostructorCall = false;
private Marker1 marker1;
private Marker2 marker2;
private ClassWithThreeFieldsAndThreeConstructorsOneIsNotUsed(Marker1 marker1) {
this.marker1 = marker1;
}
private ClassWithThreeFieldsAndThreeConstructorsOneIsNotUsed(Marker3 marker3, Marker2 marker2) {
this.isCommonCostructorCall = true;
this.marker3 = new Marker3Fasade(marker3);
this.marker2 = marker2;
}
private ClassWithThreeFieldsAndThreeConstructorsOneIsNotUsed(Marker3 marker3, Marker2 marker2, String bla) {
this.marker3 = marker3;
this.marker2 = marker2;
}
public Object getMarker1() {
return marker1;
}
public Object getMarker2() {
return marker2;
}
public boolean isCommonCostructorCall() {
return isCommonCostructorCall;
}
public Object getMarker3() {
return marker3;
}
}
@Test
public void testWithThreeFieldsAndThreeConstructorsOneIsNotUsed_CheckField1() {
assertEquals(Marker1Impl.class,
getImpl(ClassWithThreeFieldsAndThreeConstructorsOneIsNotUsed.class).getMarker1().getClass());
}
@Test
public void testWithThreeFieldsAndThreeConstructorsOneIsNotUsed_CheckField2() {
assertEquals(Marker2Impl.class,
getImpl(ClassWithThreeFieldsAndThreeConstructorsOneIsNotUsed.class).getMarker2().getClass());
}
@Test
public void testWithThreeFieldsAndThreeConstructorsOneIsNotUsed_CheckField3() {
ClassWithThreeFieldsAndThreeConstructorsOneIsNotUsed impl =
getImpl(ClassWithThreeFieldsAndThreeConstructorsOneIsNotUsed.class);
assertEquals(Marker3Fasade.class, impl.getMarker3().getClass());
assertEquals(Marker3Impl.class, ((Marker3Fasade)impl.getMarker3()).getMarker().getClass());
}
@Test
public void testWithThreeFieldsAndThreeConstructorsOneIsNotUsed_constructorUsed() {
assertTrue(getImpl(ClassWithThreeFieldsAndThreeConstructorsOneIsNotUsed.class).isCommonCostructorCall());
}
}Постепенно появлялась реализация. После несокльких подходов к коду я родил вот эти реализации
package container.ioc;
public class Binder {
Class<?> interfaceClass;
Class<?> classToCreate;
Object object;
public Binder(Class<?> classToCreate) {
this.classToCreate = classToCreate;
}
public Binder(Object object) {
this.object = object;
}
public static Binder use(Class<?> classToCreate) {
return new Binder(classToCreate);
}
public static Binder use(Object object) {
return new Binder(object);
}
public Binder as(Class<?> interfaceClass) {
this.interfaceClass = interfaceClass;
return this;
}
}package container.ioc;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.LinkedList;
import java.util.List;
public class ContainerImpl implements Container {
private Collection<Binder> binders;
public ContainerImpl(Binder...binders) {
this.binders = new LinkedList<Binder>(Arrays.asList(binders));
}
@Override
public <T> T get(Class<T> interfaceClass) {
Binder binder = find(interfaceClass);
if (binder.object != null) {
return (T) binder.object;
}
Class<?> classToCreate = binder.classToCreate;
List<Constructor<?>> constructors = getCostructors(classToCreate);
List<Object> dependencies = getObjectsFor(onlyInterfaceTypes(getTypes(getFields(classToCreate))));
return (T) foundCommonConstructor(constructors, dependencies).newInstanceFor(dependencies);
}
private InstanceMaker foundCommonConstructor(
List<Constructor<?>> constructors, List<Object> dependencies)
{
sortByParameterCount(constructors);
for (Constructor<?> constructor : constructors) {
if (sufficiently(constructor.getParameterTypes(), dependencies)) {
return new InstanceMaker(constructor);
}
}
throw new RuntimeException("Constructor not found");
}
private void sortByParameterCount(List<Constructor<?>> constructors) {
Collections.sort(constructors, new Comparator<Constructor<?>>() {
@Override
public int compare(Constructor<?> constructor1, Constructor<?> constructor2) {
return constructor2.getParameterTypes().length -
constructor1.getParameterTypes().length;
}
});
}
private boolean sufficiently(Class<?>[] parameterTypes, List<Object> dependencies) {
for (Class<?> clazz : parameterTypes) {
if (notIn(clazz, dependencies)) {
return false;
}
}
return true;
}
private boolean notIn(Class<?> clazz, List<Object> dependencies) {
for (Object dependency : dependencies) {
if (clazz.isAssignableFrom(dependency.getClass())) {
return false;
}
}
return true;
}
private List<Class<?>> getTypes(Collection<Field> fields) {
List<Class<?>> result = new LinkedList<Class<?>>();
for (Field field : fields) {
result.add(field.getType());
}
return result;
}
private List<Class<?>> onlyInterfaceTypes(List<Class<?>> classes) {
List<Class<?>> result = new LinkedList<Class<?>>();
for (Class<?> clazz : classes) {
try {
find(clazz);
result.add(clazz);
} catch (RuntimeException e) {
continue;
}
}
return result;
}
private Binder find(Class<?> interfaceClass) {
for (Binder binder : binders) {
if (binder.interfaceClass.equals(interfaceClass)) {
return binder;
}
}
throw new RuntimeException("Dependency class not found for interface" + interfaceClass.getName());
}
private List<Object> getObjectsFor(List<Class<?>> parameterTypes) {
List<Object> result = new LinkedList<Object>();
for (Class<?> clazz : parameterTypes) {
result.add(get(clazz));
}
return result;
}
private List<Constructor<?>> getCostructors(Class<?> classToCreate) {
return Arrays.asList(classToCreate.getDeclaredConstructors());
}
private Collection<Field> getFields(Class<?> classToCreate) {
return Arrays.asList(classToCreate.getDeclaredFields());
}
@Override
public void remove(Class<?> clazz) {
for (Binder binder : binders) {
if (binder.interfaceClass.equals(clazz)) {
binders.remove(binder);
return;
}
}
}
@Override
public void update(Binder binder) {
remove(binder.interfaceClass);
binders.add(binder);
}
}package container.ioc;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.LinkedList;
import java.util.List;
public class InstanceMaker {
private Constructor<?> constructor;
public InstanceMaker(Constructor<?> constructor) {
this.constructor = constructor;
}
public Object newInstanceFor(List<Object> dependencies) {
List<Object> foundDependencies = getDependenciesFor(constructor, dependencies);
Object object = getObject(constructor, foundDependencies);
dependencies.removeAll(foundDependencies);
return injectToField(object, dependencies);
}
private List<Object> getDependenciesFor(Constructor<?> constructor, List<Object> dependencies) {
List<Object> result = new LinkedList<Object> ();
for (Class<?> parameterType : constructor.getParameterTypes()) {
for (Object dependency : dependencies) {
if (parameterType.isAssignableFrom(dependency.getClass())) {
result.add(dependency);
break;
}
}
}
return result;
}
private Object getObject(Constructor<?> constructor, List<Object> dependencies) {
try {
constructor.setAccessible(true);
Object newInstance = constructor.newInstance(dependencies.toArray(new Object[0]));
constructor.setAccessible(false);
return newInstance;
} catch (IllegalArgumentException e) {
throw new RuntimeException(e);
} catch (InstantiationException e) {
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
} catch (InvocationTargetException e) {
throw new RuntimeException(e);
}
}
private <T> T injectToField(T object, List<Object> dependencies) {
for (Object dependency : dependencies) {
Injector.injectAll(object, dependency);
}
return object;
}
}Классы достаточно сложновастые, но рефлексия все же. Библиотеку Fest Reflection я не хотел подключать из за лишней jar-dependency. В следующий раз попробую сделаю с ней - за одно попиарю этот классный инструмент для работы с рефлексией.
В будущих планах есть сделать то же но со Spring IoC. Ждите, продолжение следует..

Комментариев нет:
Отправить комментарий