Dependency Injection with Spring


본 카테고리는 스프링 프레임워크를 다룬다.

좀 더 자세한 내용은 아래의 공식 사이트를 참고하자.

참고 스프링 프레임워크 공식 사이트

의존 주입에 관하여

한글로 의존 주입이라는 단어는 영어 Dependency Injection으로 보통 DI라고 부른다.

여기서 의존 즉, Dependency란 무엇일까?

스프링에서의 Dependency는 객체 간의 의존 을 의미한다.

다시 한 번 정리하면 Dependency는 객체의 변경에 의한 영향을 받는 관계를 뜻하며,

이 관계안에서 변경에 따른 영향이 전파되는 관계를 Dependency 라 하는 것이다.

1
2
3
4
5
6
7
8
public class DependencyTest {

private ObjectDao mObjectDao = new ObjectDao();

public DependencyTest(ObjectDao objectDao) {
this.mObjectDao = objectDao;
}
}

의존하고 있는 객체가 있을 때, 의존 대상 객체를 구하는 가장 빠른 방법은

위의 코드처럼 해당 객체를 직접 생성하는 것이다.

다만 직접 객체를 생성하는 경우 유지보수 관점에서 그렇게 권장되진 않는다.

따라서, 스프링에서 제공되는 DIService Locator 를 통해 의존 대상 객체를 구하는 것이 권장된다.

DI를 통한 Dependency 처리

DI는 의존하는 객체를 직접 생성하는 대신 의존 객체를 전달 받는 방식으로 구현된다.

1
2
3
4
5
6
7
8
public class DependencyTest {

private ObjectDao mObjectDao;

public DependencyTest(ObjectDao objectDao) {
this.mObjectDao = objectDao;
}
}

위의 코드는 ObjectDao라는 객체에 의존하는 DependencyTest가 파라미터를 통해 ObjectDao라는 의존 객체를 주입(injection)받는 코드이다.

이러한 코드를 DI Pattern이라고 한다.

DI를 통한 의존 객체 유지보수 향상

만약 아래와 같은 상황의 코드가 있다고 하자.

1
2
3
4
5
6
7
8
9
// DependencyA.java
public class DependencyA {
private ObjectDao mObjectDao = new ObjectDao();
}

// DependencyA.java
public class DependencyB {
private ObjectDao mObjectDao = new ObjectDao();
}

DependencyADependencyBObjectDao에 의존하고 있으며, ObjectDao의 변경에 따라 두 개의 클래스가 모두 바뀌어야 한다.

만약 ObjectDao를 상속받는 객체가 있고 의존성을 받게 된다면 3개의 클래스 모두 수정하는 작업이 진행되어야 한다.

이는 상술했듯 유지보수 관점에서의 성능 저하를 유발한다.

만약 DI Pattern을 이용해 작성했다면 수정할 코드가 현격히 줄어들었을 것이다.

스프링의 DI 설정

스프링은 그 자체로 DI를 지원하는 일종의 조립기다.

즉, 스프링은 필요한 객체를 생성하고 생성한 객체에 DI를 제공해주며, 생성한 객체를 제공해주기도 한다.

아래의 코드를 보자.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Configuration
public class ApplicationContext {

@Bean
public void methodA() {
///
}

@Bean
public void methodB() {
///
}

}

위의 @Configuration은 스프링의 설정 클래스를 의미하는 어노테이션이다.

이 어노테이션을 적용해야만 설정 클래스로 사용할 수 있다.

@Bean 어노테이션은 해당 메소드마다 한 개의 스프링 빈(Spring Bean) 객체를 생성하여 스프링 컨테이너에 등록시켜준다.

이 빈 객체를 이용하면 객체의 생성없이도 메소드를 통해 생성한 객체를 주입받아 사용할 수 있게 된다.

설정 클래스를 선언했다면 아래와 같이 해당 클래스 안에서 생성된 빈 객체를 등록할 스프링 컨테이너를 생성해준다.

1
2
3
///
ApplicationContext applicationContext = new AnnocationConfigApplicationContext(ApplicationContext.class);
///

위와 같이 컨테이너를 생성했다면 getBean() 메소드를 통해 객체를 받아올 수 있게 된다.

아래는 사용 예시이다.

1
DependencyTestService service = applicationContext.getBean(/* Parameters */);

DI - 생성자 방식

1
2
3
4
5
6
7
8
9
10
11
public class DependencyTest {
private ObjectDao mObjectDao;

public DependencyTest(ObjectDao objectDao) {
this.mObjectDao = objectDao;
}

public void methodA() {
Object object = mObjectDao.METHOD_NAME();
}
}

생성자 방식의 DI를 사용하면 생성자가 호출될 당시에 주입받은 객체를 활용하게 된다.

DI - Setter 메소드 방식

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class DependencyTest {
private ObjectDao mObjectDao;

public DependencyTest() {
//
}

public void setObjectDao(ObjectDao objectDao) {
this.mObjectDao = objectDao;
}

public void methodA() {
Object object = mObjectDao.METHOD_NAME();
}
}

Setter 메소드 방식의 DI를 사용하면 생성자를 통해 객체를 생성한 후 setter 메소드를 통해 의존 객체를 주입한다.

@Autowired를 통한 DI

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class DependencyTest {

@Autowired
private ObjectADao mObjectADao;

@Autowired
private ObjectBDao mObjectBDao;

public DependencyTest() {
//
}

///
}

@Autowired 어노테이션을 이용하면 DI 대상이 되는 객체에 @Bean 어노테이션을 가진 메소드 없이도 DI를 자동으로 처리해준다.

@Autowired 어노테이션은 스프링 Bean에 의존하는 다른 Bean을 자동으로 주입하고 싶을 때 사용하며, @Bean 어노테이션을 통한 메소드 작성을 처리하지 않아도 된다.

스프링 컨테이너가 자동으로 의존 대상 객체에 맞는 타입으로 자동으로 주입시켜 준다.