Spring 어노테이션으로 DI 구현

Bean의 이용은 Bean 설정 파일을 사용하지 않고, 클래스에 어노테이션(@annotation)을 작성하여 수행 할 수 있다. 이 방법을 기본으로 기억하고 더 나아가서는 Bean을 “구성 요소(component)“를 사용할 수 있게 해보자.

어노테이션에 의한 Bean 제어란?

이전에 Bean 설정 파일을 사용하여 Bean의 생성에 대해 간략하게 설명했다. 이 방법은 매우 편리하긴 하지만, 최근에는 그다지 많이 사용되지 않는 방법이다. 이유는 개발 스타일이 ‘설정 파일’에서 ‘어노테이션’으로 바뀌고 있기 때문이다.

어노테이션은 클래스나 메소드, 필드 등의 선언문에 쓴 “@ 기호로 시작하는 텍스트"이다. 예를 들어, 클래스의 서브 클래스를 만들 때 메소드의 재정의를 명시하기 위해 “@Override"라는 어노테이션을 쓴 경험은 있을 것이다.

어노테이션은 클래스에는 영향을 주지 않는다. 다만, 수정하면 다시 빌드 등은 필요하지만 메소드나 클래스에 간단한 단어를 쓰는 것만으로 필요한 작업을 수행 할 수 있으므로 알기 쉬워서 많은 프레임워크가 설정 파일에 사용하는 것에서 어노테이션 방식으로 전환되고 있다.

Spring Framework에도 설정 파일 방식과 어노테이션 방식 중의 어느 방식이라도 지원하고 있다. 지난번 Bean 설정 파일을 사용한 기본 설명 했기에 이번에는 어노테이션 방식에 대해 설명하겠다.

Bean 설정 클래스 생성

어노테이션 방식은 Bean 설정 파일을 사용하지 않고, Java의 클래스로 모두를 작성하는 방식이다. 결국은 Bean 설정 파일에 해당하는 클래스를 제공해야 한다.

그럼 Bean 설정 클래스를 만들어 보자. 지난번 이용한 MySpringApp 프로젝트를 그대로 이용한다. 이번에는 com.devkuma.spring 패키지에 “SampleBeanConfig"라는 클래스를 만들어 보자. 아래에 소스 코드를 올려 두었으므로, 그것을 참고로 기술를 하길 바란다.

package com.devkuma.spring;
 
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
 
@Configuration
public class SampleBeanConfig {
     
    @Bean
    public SampleBeanInterface sampleBean() {
        return new SampleBean("설정 클래스에서 만든 인스턴스입니다.");
    }
 
}

Bean 설정 클래스도 보면 알 수 있듯이 간단한 POJO 클래스로 정의된다. 다만, 2 개의 어노테이션이 기술되어 있다.

@Configuration
이것은 클래스 선언 앞에 기술한다. 이 어노테이션은 해당 클래스가 Bean의 설정을 할 것이라는 것을 나타낸다. Bean 설정 클래스는 항상 이것을 붙인다.

@Bean
Bean을 작성하는 메소드 앞에 기술한다. 이것을 기술하면 그 메소드를 Bean 인스턴스 생성을 위한 것으로 인식한다. 이것을 붙인 메소드는 반드시 Bean 인스턴스를 반환 값으로 지정해야 한다.

이번에는 SampleBeanInterface 인스턴스를 Bean으로 생성하기 위한 설정 클래스를 정의하고 있다는 의미가 된다.

App 클래스 수정

이제 생성한 Bean 설정 클래스를 이용하여 Bean을 얻을 수 있도록 응용 프로그램 클래스를 수정하자. App.java를 아래와 같이 소스 코드를 작성한다.

package com.devkuma.spring;
 
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
 
public class App {
 
    public static void main(String[] args) {
        ApplicationContext app = new
            AnnotationConfigApplicationContext(SampleBeanConfig.class);
        SampleBeanInterface bean1 = 
            (SampleBeanInterface)app.getBean(SampleBeanInterface.class);
        System.out.println(bean1);
    }
}

실행 해보면 “SampleBean [message=“설정 클래스에서 만든 인스턴스입니다.]“라는 텍스트가 출력된다. SampleBeanConfig 클래스의 @Bean 지정된 메서드 (sampleBean)로 new를 이용해서 인스턴스를 얻을 수 있고, 이를 이용되고 있는 것을 알 수 있을 것이다.

이번에는 bean.xml를 이용한 경우와는 조금 다른 방식으로 하고 있다. 간단하게 정리해 보겠다.

1. ApplicationContext를 얻기

ApplicationContext app = new AnnotationConfigApplicationContext(SampleBeanConfig.class);

먼저, ApplicationContext 인스턴스를 만든다. 이것은 동일하지만, 잘 보면 사용하고 있는 클래스가 다르다. 이번에는 “AnnotationConfigApplicationContext"라는 클래스의 인스턴스를 생성하고 있다.

이것은 Bean 설정 클래스를 이용하여 ApplicationConfig을 생성하기 위한 것이다. 인수에는 설정 클래스의 Class 값을 지정한다. 이것으로 그 설정 클래스에서 Bean을 관리하는 ApplicationContext가 만들어진다.

2. Bean을 얻기

SampleBeanInterface bean1 = (SampleBeanInterface) app.getBean(SampleBeanInterface.class);

Bean을 얻는 것은 지금까지와 동일하다. “getBean"메소드를 사용하여 인수 꺼내 Bean의 Class를 지정하기만 하면 된다. 이것으로 해당 Bean의 인스턴스를 얻을 수 있다.

인수에는 얻을 Bean 클래스의 Class 값을 지정한다. 이것으로 지정 클래스의 Bean을 얻을 수 있다.

컨포넌트(@Component) 생성 및 사용

어노테이션에 의한 Bean 이용은 여러가지 응용이 가능하다. 기본을 알았다면, 이어서 Bean을 사용하는 “컨포넌트"를 사용해 보자.

컨포넌트(@Component) 생성

Spring Framework에는 고급 기능을 가진 클래스를 컨포넌트로 정의하고 Bean과 같은 방식으로 사용할 수 있다. 간단한 예제로 “BeanHolder"라는 컨포넌트 클래스를 만들어 보자.

com.devkuma.spring 패키지에 BeanHolder 클래스를 만들고, 아래와 같이 소스 코드를 작성하자.

package com.devkuma.spring;
 
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
 
@Component
public class BeanHolder {
     
    @Autowired
    private SampleBeanInterface bean;
     
    public void showMessage() {
        System.out.println("*print by BeanHolder*");
        System.out.println(bean);
        System.out.println("*end*");
    }
 
}

이 BeanHolder은 아주 간단한 컨포넌트이다. 이 컨포넌트 안에는 이전의 Bean 설정 클래스으로 작성한 Bean을 필드에 저장하고 그것을 이용한 메소드가 작성되어 있다. 포인트를 정리해 보겠다.

1. “@Component” 아노테이션
클래스 선언 앞에는 @Component 어노테이션이 붙여져 있다. 이것은 해당 클래스가 컨포넌트임을 나타 내기 위한 것이다. 컨포넌트 클래스에는 반드시 이것을 붙여 둔다.

2. “@Autowired” 아노테이션
이 클래스에는 SampleBeanInterface를 필드에 존재하고 있다. 이 필드에는 @ Autowired 어노테이션이 붙여져 있다. 이것은 Bean 설정 클래스 (또는 파일)에 의해 자동 생성된 Bean 인스턴스를 자동으로 바인딩하기 위한 것이다.

이것을 붙이는 것으로, 생성된 Bean들 중에서 SampleBeanInterface 인스턴스 찾고 이 bean 필드에 자동으로 할당하는 것이다. 이 @Autowaired는 컨포넌트뿐만 아니라 다양한 Bean 이용하는 곳에서 사용된다.

SampleBeanConfig 클래스의 수정

이제 컨포넌트는 완성되었지만, 실은 더 해야 할 것이 하나가 더 있다. Bean 설정 클래스 (SampleBeanConfig)의 선언 앞에 다음 어노테이션을 추가해야 한다.

@ComponentScan

이미 @Configuration 어노테이션이 붙어 있기 때문에, 그 위나 아래에 추가하면 된다.

이 @ComponentScan는 컨포넌트를 검색하여 인스턴스를 생성하여 ApplicationContext에 등록하는 것이다. 컨포넌트(@Component를 붙인 클래스)를 이용할 경우에는 설정 클래스에 반드시 이 어노테이션을 꼭 추가해야 한다. 이렇게 하면 컨포넌트가 자동으로 인스턴스화되고 사용할 수 있게 된다.

컨포넌트(@Component) 사용

이제 컨포넌트를 실제로 이용해 보자. App.java을 열고 아래의 소스 코드 처럼 바꿔준다. 그리고 실행해 보자.

package com.devkuma.spring;
 
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
 
public class App {
 
    public static void main(String[] args) {
        ApplicationContext app = new AnnotationConfigApplicationContext(SampleBeanConfig.class);
        BeanHolder holder = app.getBean(BeanHolder.class);
        holder.showMessage();
    }
}

“* print by BeanHolder *“라고 표시되고 그 아래에 SampleBean의 출력이 된다. BeanHolder가 검색되어 그 중에 SampleBean를 사용하여 처리를 수행하고 있다는 것을 알 수 있다.

여기에서는 AnnotationConfigApplicationContext 인스턴스를 생성 한 후, getBean에서 “BeanHolder.class"를 인수로 지정해서 BeanHolder 인스턴스를 얻었다. BeanHolder는 Bean 설정 클래스 (SampleBeanConfig)에 기술되어 있지 않았다. 하지만 제대로 getBean에서 꺼낼 수 있다.

이것은 Bean 설정 파일 @ComponentScan로 부터 @Component로 지정된 클래스가 검색되어 그 인스턴스가 ApplicationContext에 등록되어 있기 때문이다. 또한 그 내부에서는 @Autowired로 인해 SampleBean이 자동으로 필드에 설정되어 있었으므로, 그 값이 showMessage에서 출력되어 있었다는 것이다.

이렇게 어노테이션을 사용하면 필요한 Bean과 컨포넌트들이 자동으로 생성이 되어 사용할 수 있게 된다. 이것으로 Spring Framework의 Bean 이용의 기본적인 구조를 많이 알게 되었다.

DI에 의하면 Bean의 사용은 Spring Framework의 핵심 기술이다. Spring Framework에는 다양한 라이브러리가 포함되어 있지만, 그 것들은 모두 이 Bean 기술을 사용하여 필요한 기능을 Bean을 만들어 사용하고 있다. Bean 이용을 모르면, Spring Framework의 기본적인 사용법을 이해할 수 없다. 이것은 “Spring Framework의 기본 중의 기본"으로 제대로 이해해 두도록 한다.




최종 수정 : 2017-12-22