1.3 빈 개요 by ks

Spring IoC 컨테이너는 bean들을 관리한다. 이 bean들은 컨테이너에 들어간 설정(configuration) 메타데이터와 함께 만들어진다. (예를 들어서 <bean/> 에 설정된 것들이다.)

컨테이너 안에는 이 bean의 정의가 다음의 메타데이터를 포함하는 BeanDefinition 오브젝트로서 표현이 된다.

  1. 패키지 자격의 클래스 이름 : 전형적으로 bean이 정의되어 있는 실제 구현 클래스

  2. Bean 행위 설정(congifuration) 요소. (범위(scope), 라이프사이클 콜백, 기타 등등 컨테이너에서 어떻게 bean이 동작하는지 상태를 보여준다.)

  3. 동작하기 위해 필요한 bean들의 레퍼런스. 이 레퍼런스를 collaborators 또는 dependencies라고 부른다.

  4. 새롭게 생성된 오브젝트에 세팅할 다른 configuration 설정. 예를 들어서 pool의 사이즈 제한과 커넥션 풀을 관리하는 bean에 사용되는 커넥션 수.

이 메타데이터는 각각 bean 을 정의하는 properties의 셋으로 변환한다. 아래 테이블은 이 properties를 설명한다.

Table 1. The bean definition

Property

Explained in…​

Class

Name

Scope

Constructor arguments

Properties

Autowiring mode

Lazy initialization mode

Initialization method

Destruction method

구체적인 bean을 어떻게 생성하는 지에 대한 정보를 포함하는 bean 정의 이외에도 ApplicationContext 구현제 또한 존재하는 오브젝트(사용자에 의해 컨테이너 밖에서 생성된) 등록을 허가한다. 이는 ApplicationContext의 BeanFactory 에서 BeanFactory DefaultListableBeanFactory 구현체가 반환한 getBeanFactory 메소드를 통해 접근해서 수행된다. DefaultListableBeanFactory는 registerSingletone(..)과 registerBeanDefinition(..) 메소드를 통해 이 등록을 지원한다. 하지만 전형적인 apllication들은 단독으로 일반적인 bean 정의 메타데이터를 통해 정의된 bean으로 동작한다.

! Bean 메타데이터와 메뉴얼리하게 지원하는 singleton 인스턴스는 가능한 한 빨리 등록해야한다. 다른 내부 단계와 autowiring하는 동안 적절하게 동작하기 위해서는. 기존 메타데이터와 싱글톤 인스턴스를 재정의 하는것이 어느정도 지원되지만, runtime에 (비동기적으로 factory에 접근하는) 새로운 bean 의 등록은 공식적으로 지원되진 않고 비동기 접근 예외, 비일관적인 상태(inconsistent state)가 발생할지도 모른다.

1.3.1 Naming Beans

모든 bean(이하 빈)은 하나 또는 그 이상의 식별자를 가지고 있다. 이러한 식별자들은 빈을 호스트하는 컨테이너 안에서 유니크해야한다. 빈은 보통 단 하나의 식별자를 가진다. 하지만 만약 그 이상을 요구하면 다른것들에 별명( aliases)가 고려될 수 있다.

XML을 기본으로 하는 설정(configuration) 메타데이터에는 id, name attribute를 각각 또는 둘다 빈 식별자를 구체화하기 위해 사용한다. id 는 정확하게 하나의 id를 구체화한다. 전통적으로 이러한 이름들은 영문/숫자이자만('myBean', 'someService'같이) 특수문자를 포함할 수 있다. 만약 빈에 다른 별명(aliases) 을 붙이고 싶으면 name attribute 콤마(,)나 세미콜론(;), 화이트 스페이스( ) 으로 분리해서 구체화하면 된다. 스프링 3.1 이전 버전에는 id attribute는 xsd:ID로 정의됐다. xsd:string 타입으로 정의됐다. id 빈 유일성은 여전히 컨테이너에 의해 사용되었지만 XML parser에 의해 더이상 쓰지 않다. (Note that bean id uniqueness is strill enforced by the container, though no longer by XML parsers.)

당신은 name또는 id를 쓰는 것을 원하지 않아서 만약 name 또는 id를 명시적으로 쓰지 않는다면 컨테이너는 그 빈을 유니크한 이름으로 생성한다. 하지만 ref element나 Service Locator style lookup을 통해서 그 빈을 이름으로 언급하고 싶으면 이름을 정의해야만 한다. 이름을 제공하지 않는 것은 이너빈즈(inner beans)와 autowiring collaborators를 사용하는것에 관련있다.

빈 이름 컨벤션(conventions)

컨벤션은 빈에 이름을 지정해줄때 인스턴스 필드명을 위해 자바 컨텐션 기준을 사용한다. 즉, 빈 이름은 소문자로 시작해서 카멜형식을 사용한다. 예를 들어서 accountManager, accountService, userDao, loginController와 같다.

일관되게 빈 이름을 짓는 것은 설정 (configuration)의 가독성을 좋게 한다. 또한 만약 Spring AOP를 사용한다면 advice(이름과 연관된 빈들에)를 적용할때 더 좋다.

! 클래스패스에서 컴포넌트스캐닝에서 스프링은 이름없는 컴포턴트들에 빈 이름을 생성한다. 다음 룰들은 더 이르게 서술된다. 필수적으로 간단한 클래스 이름을 가져와서 첫 문자를 소문자로 바꾼다. (예를 들어서 클래스명이 MicroService면 microService란 이름으로 빈을 생성해준다는 말이야!) 하지만 특별한 경우에는 하나 이상의 문자가 있을때 그리고 첫 글자와 두번째 글자가 대문자면 보통 원래의 대소문자를 사용합니다. (EPGService면 EPGService 그대로 빈을 생성한다는 것같아). 같은 룰들은 java.beans.Introspector.decapitalize에 의해 정의된다.

빈 정의 밖에 빈의 별명을 짓는 것

빈을 정의하면 id attribute로 구체화한 한 이름과 name attribute에 다른 이름들을 조합해 빈에 하나 이상의 이름을 줄 수 있다. 이 이름들은 같은 빈에 동일한 이름을 줄 수 있고 몇 유용한 상황이 될 수 있다. 가령 어플리케이션에 각 컴포넌트를 구체화한 빈 이름을 사용해서 일반적인 의존을 참조하게 한다.

빈이 정의된 모든 별명들을 구체화하는 것은 항상 적절한 건 아니다. 하지만 때때로 다른곳에 정의된 빈의 별명을 소개하는게 바람직하다. 이건 보통 자신의 각 오브젝트 정의를 가지는 서브시스템을 분리하는 설정(configuration)이 있는 큰 시스템의 상황이다. XML을 베이스로 하는 configuration 메타데이터에서는 <alias/> element를 사용할 수 있다. 아래 예제는 어떻게 사용하는 지 알려준다.

<alias name="fromName" alias="toName"/>

이 경우엔 같은 컨테이너 안에서 이 빈은 fromName이란 이름이고 alias 정의후엔 toName으로서 참조된다.

예를 들어서 서브시스템 A의 configuration 메타데이터는 subsystemA-dataSource에 의해 DataSource를 참조할 것이다. 서브시스템 B configuration 메타데이터는 subsystemB-dataSource를 참조할 것이다. 이 서브시스템들을 사용하는 메인 어플리케이션을 구성할 때, 메인 어플리케이션은 myApp-dataSoucr로 참조한다. 같은 오브젝트를 참조하는 세 이름을 가지기 위해 아래의 alias를 더할 수 있다.

<alias name = "myApp-dataSource" alias = "subsystemA-dataSource"/>

<alias name = "myApp-dataSource" alias = "subsystemB-dataSource"/>

이제 각 컴포넌트와 메인 어플리케이션은 유니크하고 다른 정의때문에 충돌하지않는 이름으로 dataSource를 참조할 수 있지만 실제로는 동일한 빈을 참조한다.

! Java-congirutaion. 만약 JavaConfiguration을 사용한다면 @Bean 어노테이션으로 alias를 사용할 수 있다.

1.3.2 Bean 인스턴스화

빈의 정의는 오브젝트를 생성하는 필수적이다. 컨테이너는 요청될 때 명명된 빈의 레시피를 보고 캡슐화된 설정 (configuration) 메타데이터를 사용하여 실제 오브젝트를 생성(또는 획득)한다.

만약 XML 베이스 설정(configuration) 메타데이터를 사용하면 <bean>의 class attribute에서 인스턴스화된 오브젝트의 타입을 구체화한다. class attribute(BeanDefinition 인스턴스의 class property) 는 보통 필수적이다. (예외적인 상황은, Instantiation by Using an Instance Factory Method and Bean Definition Inheritance를 참조)

class property는 아래 두 가지 방법 중에서 선택해 사용할 수 있다.

  1. 전형적으로

생성자로 인스턴스화하기

생성자로 빈을 생성하고자 하면 웬만한 클래스들은 스프링에 적합한것으로 사용한다. 즉, 개발된 클래스는 어떤 구체적인 인터페이스를 구체화할 필요가 벗거나 구체화할 필요도 없다. 간단하게 구체화한 빈 클래스면 충분하다. 하지만 무슨 IoC타입에 의존하느냐에 따라 기본 빈 생성자가 필요할수도 있다.

스프링 IoC 컨테이너는 실질적으로 어떤 클래스던 관리할 수 있다. 진짜 자바 빈즈를 관리하는것에는 제한이 없다. 대부분 스프링 유저들은 사실적인 자바빈즈를 기본 생성자와 세터게터와 함께 사용하는것을 선호한다. 또한 더 색다르게 non-bean-style-클래스를 사용할 수도 있다. 예를들어서 만약 절대적으로 자바빈에 고착하지 않은 레거시한 커넥션 풀을 사용한다면, 스프링은 잘 관리할 수 있다.

XML 베이스 설정(configuratoin) 메타데이터로 아래와같이 빈을 구체화할 수 있다.

<bean id="exampleBean" class="example.ExampleBean"/>

<bean name="anotherExample" class="examples.ExampleBeanTwo"/>

생성자에 인자를 더한 메커니즘이나 오브젝트를 생성한 후에 프로퍼티를 세팅하는 경우는 Injecting Dependencies를 봐라.

Static Factory method로 인스턴스화

static factory method로 생성한 빈을 정의할 때 static factory method를 포함하는 클래스를 구체화하기 위해 class 어트리뷰트를 사용하고 factory method의 이름을 구체화하기 위해 factory-method로 명명된 어트리뷰트를 사용한다. 이 메소드(나중에 설명할 선택 옵션)과 이 메소드를 부를 수 있어야하고 그 후에 생성자를 통해 생성되어 다뤄지는 살아있는 오브젝트(사게안된건가봐!) 도 사용할 수 있어야한다. 빈을 정의하는 한가지 방법은 레거시 코드에서 정적(static) 팩토리를 부르는 것이다.

아래의 빈 정의는 팩토리 메소드를 불러서 생성하는 것이다. 이 정의는 리턴 오브젝트를 구체화하지않았다. 이 예제는 createInstance() 메소드는 정적인 메소드여야한다는 것이다. 아래 예제는 어떻게 팩토리 메소드를 구체화하는지보여준다.

<bean id="clientService"
    class="examples.ClientService"
    factory-method="createInstance"/>

아래 예제는 선행하는 빈으로 동작하는 클래스를 보여준다.

public class ClientService {
    private static ClientService clientService = new ClientService();
    private ClientService() {}

    public static ClientService createInstance() {
        return clientService;
    }
}

팩토리 메소드에 선택 인자를 사용하는 매커니즘과 오브젝트가 팩토리로부터 반환되고 난 후에 프로퍼티를 세팅하는 방법에 대해서는 Dependencies and Configuration in Detail을 확인하자.

팩토리 메소드 인스턴스를 사용해서 인스턴스화

정적 팩토리 메소드를 사용하여 인스턴스화하는 것과 비슷하게 인스턴스 팩토리 메소드와 인스턴스화는 새로운 빈을 생성하기 위해 컨테이너로부터 존재하는 빈의 동적 메소드를 부른다. 이 매커니즘을 사용하기 위해 factory-bean 어트리뷰터안에 class 어트리뷰트를 비웠고, 오브젝트를 생성하기 위해 불러진 인스턴스 메소드를 포함하는 현재 컨테이너 안에 빈의 이름을 구체화 했다. factory-method 어트리뷰트와 팩토리메소드의 이름을 세팅해라. 아래 예제는 어떻게 하는지를 보여준다.

<!-- the factory bean, which contains a method called createInstance() -->
<bean id="serviceLocator" class="examples.DefaultServiceLocator">
    <!-- inject any dependencies required by this locator bean -->
</bean>

<!-- the bean to be created via the factory bean -->
<bean id="clientService"
    factory-bean="serviceLocator"
    factory-method="createClientServiceInstance"/>
    

그리고 아래는 자바 클래스이다.

public class DefaultServiceLocator {

    private static ClientService clientService = new ClientServiceImpl();

    public ClientService createClientServiceInstance() {
        return clientService;
    }
}

한 팩토리 클래스는 한 팩토리 메소드보다 더 많은것을 가질 수 있다.

<bean id="serviceLocator" class="examples.DefaultServiceLocator">
    <!-- inject any dependencies required by this locator bean -->
</bean>

<bean id="clientService"
    factory-bean="serviceLocator"
    factory-method="createClientServiceInstance"/>

<bean id="accountService"
    factory-bean="serviceLocator"
    factory-method="createAccountServiceInstance"/>
    
    

아래는 자바 클래스이다.

public class DefaultServiceLocator {

    private static ClientService clientService = new ClientServiceImpl();

    private static AccountService accountService = new AccountServiceImpl();

    public ClientService createClientServiceInstance() {
        return clientService;
    }

    public AccountService createAccountServiceInstance() {
        return accountService;
    }
}

이 시도는 팩토리 빈이 관리할 수 있고 주입으로 설정될 수 있는 것을 보여준다. Depencies and Configuration in Detail에서 확인할 수 있다.

! 스프링 문서에는 "팩토리 빈"은 스프링 컨테이너에서 설정된, 그리고 정적 팩토리 메소드 또는 인스턴스를 통해 생성된 오브젝트 빈을 참조한다. 반면에 FactoryBean은 스프링이 구체화한 FactoryBean을 참조한다.

Last updated