DI 생성해보기

고재석·2021년 12월 5일
0

Java는 기본!

목록 보기
12/12

DI?

Spring에서 지원하는 주요 개념인 DI(Dependency Injection)는 의존성 주입의 약어로 인스턴스를 만들지 않더라도 스프링 컨텍스트에 등록된 빈의 인스턴스를 생성해주는 것으로 잘 알려져있다.

실제 DI가 어떤 식으로 구현되어 있고 동작하는지 백기선님의 Java강의를 참고하여 DI를 만들어보며 이해해보자.

DI 동작 순서

우선 Java의 리플렉션 API를 활용하여 개발을 해보고자 한다. 우선 특정 클래스의 인스턴스를 만들고자 한다. 이 인스턴스를 생성할 때 해당 클래스의 필드 중 특정 어노테이션이 달려 있는 경우에 해당 필드의 인스턴스까지 함께 생성하고자 한다.

Annotation 생성

스프링에서는 @Autowired 어노테이션으로 필드에 의존성을 주입한다. 나는 @Inject라는 어노테이션을 정의하여 이 어노테이션으로 의존성을 주입하고자 한다. 우선 Inject 어노테이션 파일을 만든다.

package study.jaeseok;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

@Retention(RetentionPolicy.RUNTIME)
public @interface Inject {
}

getObject 메소드 생성

컨테이너 서비스에서는 인스턴스를 생성해서 반환하는 createInstance() 메소드를 가진다. 이 때 리플렉션 api를 활용하여 기본 생성자를 활용하여 생성한다.

그리고 생성한 인스턴스에 Inject 어노테이션이 붙어있는 필드가 있는지 검사해서 해당 필드의 인스턴스도 생성하는 로직을 구성한다.

package study.jaeseok;

import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationTargetException;
import java.util.Arrays;

public class ContainerService {

    public static <T> T getObject(Class<T> classType) {
        T instance = createInstance(classType);

        Arrays.stream(classType.getDeclaredFields())
                .forEach(field -> {
                    Annotation annotation = field.getAnnotation(Inject.class);
                    if (annotation != null) {
                        try {
                            field.setAccessible(true);
                            field.set(instance, createInstance(field.getType()));
                        } catch (IllegalAccessException e) {
                            e.printStackTrace();
                        }
                    }
                });

        return instance;
    }

    private static <T> T createInstance(Class<T> classType) {
        try {
            return classType.getConstructor(null).newInstance();
        } catch (InstantiationException | IllegalAccessException | 
                InvocationTargetException | NoSuchMethodException e) {
            e.printStackTrace();
        }
        return null;
    }
}

테스트

그렇다면 의존성 주입이 제대로 이루어졌는지 확인하는지 테스트 코드로 확인해보자. 아래와 같이 코드를 작성하고 BookService에 BookRepository 타입의 필드에 @Inject 어노테이션을 달아놓은 상태이다.


package study.jaeseok;

import org.junit.Assert;
import org.junit.Test;

public class ContainerServiceTest {

    @Test
    public void getBookService() {
        BookService bookService = ContainerService.getObject(BookService.class);
        Assert.assertNotNull(bookService);
        Assert.assertNotNull(bookService.bookRepository);
    }
    
    @Test
    public void getBookRepository() {
        BookRepository bookRepository = ContainerService.getObject(BookRepository.class);
        Assert.assertNotNull(bookRepository);
    }
}

테스트 코드 실행 결과 아래와 같이 Tests passed 결과를 확인할 수 있다.

profile
명확하게 말하고, 꼼꼼하게 개발하자

0개의 댓글