Skip to main content

빈 훑어보기

SpringAbout 2 min

원문 : https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#beans-definitionopen in new window

스프링 IoC 컨테이너는 한개 이상의 빈을 관리합니다.
이 빈들은 컨테이너에 적용한 구성 메타데이터로 생성되어집니다. (예로들어, XML의 <bean/> 정의형식)

컨테이너 자체에서 이러한 빈 정의들은 (다른 정보 중에서) 아래의 메타데이터를 가지고 있는 BeanDefinition 객체로 표현됩니다.

  • 패키지포함 클래스 이름 : 일반적으로, 정의된 빈의 실제 구현 클래스
  • 빈의 행위적 구성 요소 : 컨테이너에서 빈이 어떻게 동작하는지에 대한 상태 (범위, 라이프사이클 콜백 등등)
  • 빈의 동작을 위한 참조되는 다른 빈 : 이 참조를 협력자 또는 의존성으로 부름
  • 새로 생성되는 객체에 설정되는 다른 구성 설정 : 예로, 커넥션 풀을 관리하기 위해 빈에서 사용되는 풀의 크기제한 또는 커넥션 숫자

이러한 메타데이터는 각각 빈 정의를 구성하는 속성의 집합으로 해석됩니다.
아래 표는 이러한 속성을 보여주고 있습니다.

속성설명
클래스빈의 인스턴스화
이름빈의 이름
범위빈의 범위
생성자 인자의존성 주입
속성의존성 주입
자동연결 모드자동연결 협력자
지연 초기화 모드지연초기화 빈
초기화 함수초기화 콜백
소멸자 함수소멸자 콜백

특정 빈이 어떻게 생성되는지에 대한 정보를 포함한 빈 정의에 추가로, ApplicationContext 구현체는 또한 컨테이너 밖(사용자에의해)에서 생성된 기존 객체에 대한 등록도 허용합니다.
이것은 BeanFactory의 DefaultListableBeanFactory 구현체를 반환하는 getBeanFactory() 함수를 통해 ApplicationContext의 BeanFactory에 접근해서 할 수 있습니다.
DefaultListableBeanFactoryregisterSingleton(..) 이나 registerBeanDefinition(..) 함수를 통해 등록기능을 지원합니다.
그러나 일반적인 어플리케이션은 일반적인 빈 정의 메타데이터를 통해 정의된 빈들로만 작동합니다.

Tips

빈 메타데이터와 수동으로 제공되는 싱글턴 인스턴스는 가급적 빨리 등록되어야 컨테이너가 자동연결 및 기타 자체검사 단계에서 이에 대해 적절하게 추론할 수 있습니다.
기존 메타데이터와 싱글턴 인스턴스에 대하여 재정의하는 것은 어느정도 지원되지만, 런타임중에 새로운 빈을 등록하는것(동시에 Factory를 실시간으로 접근)에 은 공식적으로 지원하지 않습니다.
이것은 동시접근에 대한 예외나 빈 컨테이너의 잘못된 상태 또는 둘다를 유발하게 됩니다.

1.3.1 빈의 명명

모든 빈은 하나 이상의 식별자를 가지고 있습니다.
이 식별자들은 빈이 호스팅되는 컨테이너 내에서는 유일해야 합니다.
빈은 일반적으로 한개의 식별자만 가지고 있습니다.
그러나, 하나 이상이 필요할 경우 별칭으로 간주하여 추가할 수 있습니다.

XML 기반 구성 메타데이터에서 id 속성과 name 속성 또는 둘다를 빈의 식별자로 지정하는데 사용할 수 있습니다.
id 속성은 정확히 딱 한개의 id만 지정할 수 있습니다.
전통적으로 이 이름은 영숫자를 사용합니다. (예, myBean, someService 등)
그러나 특수문자를 포함해도 상관은 없습니다.
빈에 대하여 다른 별칭으로 소개하고 싶으면 name 속성에 쉼표(,), 세미콜론(;) 또는 공백으로 구분하여 별칭을 지정할 수 있습니다.
연혁에 따르면 스프링 3.1 버전 이전의 id 속성은 xsd:ID 유형으로 정의되어 제한된 가능한 문자로만 될 수 있습니다.
3.1 부터는 xsd:string 유형으로 정의되며 빈 id 의 유일성은 XML 파서가 아닌 컨테이너에 의하여 강제됩니다.

빈의 name 이나 id 를 정의하는 것은 필수가 아닙니다.
만약 name 이나 id 가 명시적으로 정의되어있지 않으면 컨테이너가 빈에대한 유일한 이름을 생성합니다.
그러나, ref 속성을 사용하거나 Service Locator 스타일 탐색을 통한 이름으로 빈을 참조하고자 하는 경우에는 name 을 제공해야 합니다.
이름을 제공하지 않는 경우는 내부 빈자동연결 협력자를 사용하는 것에 관련되어있습니다.

빈의 명명 규칙

규칙은 빈의 이름을 지정할 때 인스턴스 필드 이름에 대한 표준 자바 규칙을 사용하는 것입니다.
즉, 빈의 이름은 소문자로 시작하고 카멜케이스를 따릅니다.
이러한 이름은 예로 accountManager, accountService, userDao, loginController 등과 같습니다.

일관된 빈의 명명은 구성을 쉽게 읽고 이해할 수 있게 만듭니다.
또한 스프링 AOP를 사용하면 advice를 적용할 때 이름 관련으로 빈의 집합을 적용할 수 있어 도움이 됩니다.

Tips

클래스패스에서 컴포넌트 스캐닝을 하면 스프링은 앞에서 설명한 규칙에 따라 이름없는 컴포넌트에 대한 빈이름을 생성합니다.
본질적으로, 단순 클래스명을 가져와서 가장 앞글자를 소문자로 바꿉니다.
그러나, 한글자 이상이고 첫번째 두번째 글자 모두 대문자인 (비정상적) 특수한 경우에는 원래 대소문자가 유지됩니다.
동일한 규칙이 java.beans.Introspector.decapitalize 에도 정의되어있습니다. (스프링도 이것을 사용)

빈 정의 밖에서 빈에대한 별칭지정

빈 정의 자체에서 id 속성에 정의된 한개의 이름과 name 속성의 여러개 다른 이름을 조합하여 빈에 대한 한개 이상의 이름을 적용할 수 있습니다.
이름들은 동일한 빈으로 동등한 별칭이 되며 어플리케이션의 컴포넌트 각각이 컴포넌트 자체에 정의된 빈 이름을 사용하여 공통 의존성을 참조하는 것과 같은 몇몇 상황에서 유용합니다.

그러나, 실제 정의된 빈에 대하여 모든 별칭을 정의하는 것이 항상 적절하지는 않습니다.
때떄로 다른 곳에서 정의된 빈에 대한 별칭을 도입하는 것이 적절합니다.
각각의 서브시스템이 자체적 객체 정의를 가지며 서브시스템 별로 구성이 분리된 큰 시스템의 경우가 일반적입니다.
XML기반 구성 메타데이터에서 <alias/> 요소를 사용하여 이것을 이룰 수 있습니다.
다음 예제는 어떻게 하는지 보여줍니다.

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

이 경우, 빈(동일 컨테이너)이 fromName 으로 불리며 이 별칭정의를 사용한 후 toName 으로 참조될 수 있습니다.

예로 들어, 서브시스템 A의 구성 메타데이터에서 subsystemA-dataSource 의 이름으로 데이터소스를 참조할 수 있습니다.
서브시스템 B의 구성 메타데이터에서 subsystemB-dataSource 의 이름으로 데이터소스를 참조할 수 있습니다.
이 서브시스템을 사용하는 메인 어플리케이션을 작성할 때, 메인 어플리케이션은 myApp-dataSource 의 이름으로 데이터소스를 참조합니다.
세가지 이름일 동일한 객체를 참조하게 만드려면 구성 메타데이터에 다음 별칭 정의를 추가하면 됩니다.

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

이제 각각 컴포넌트와 메인 어플리케이션은 유일하고 다른 정의에 충돌되지 않음(효과적으로 네임스페이스 생성)을 보장하면서 동일한 빈을 참조하는 이름으로 데이터소스를 참조할 수 있습니다.

자바-구성

자바 구성을 사용한다면 @Bean 어노테이션이 별칭을 제공하는데 사용됩니다.
상세한 정보는 @Bean 어노테이션 사용하기 에서 볼 수 있습니다.

1.3.2 빈 인스턴스화

빈 정의는 본질적으로 한개 이상의 객체를 생성하기 위한 레시피입니다.
컨테이너는 요청될 때 명명된 빈의 레시피를 살펴보고 빈 정의에 의해 캡슐화된 구성 메타데이터를 실제 객체를 생성하기 위해 사용합니다.

XML기반 구성 메타데이터를 사용하면 <bean/> 요소의 class 속성에서 초기화되어지는 객체의 유형(또는 클래스)를 정의합니다.
class 속성(내부적으로 BeanDefinition 인스턴스의 class 속성)은 항상 필수적입니다.
(예외의 경우, 인스턴스 팩토리 함수를 사용하여 인스턴스화빈 정의 상속을 참고하세요)
class 속성을 두가지중 한가지 방법으로 사용할 수 있습니다.

중첩 클래스 이름

중첩 클래스를 위한 빈 정의를 구성하고 싶을 때 중첩 클래스의 바이너리 이름이나 소스 이름을 사용할 수 있습니다.
예로 들어, com.example 패키지 안에 SomeThing 라는 클래스를 가지고 있을 때, SomeThing 클래스에 static 으로 OtherThing 이라는 중첩 클래스를 가지고 있습니다.
그럼 이것을 달러()나마침표(.)으로구분할수있습니다.빈정의의class속성의값은com.example.SomeThing)나 마침표(.)으로 구분할 수 있습니다. 빈 정의의 ```class``` 속성의 값은 ```com.example.SomeThingOtherThing또는com.example.SomeThing.OtherThing``` 이 될 수 있습니다.

생성자로 인스턴스화

빈을 생성자로 접근해서 생성할 때 모든 일반적인 클래스를 사용할 수 있고 스프링과 호환됩니다.
즉, 개발된 클래스를 어느 특정 인터페이스를 구현하거나 특정 방식으로 코딩할 필요가 없습니다.
단순히 빈 클래스를 지정하는 것으로 충분합니다.
그러나, 특정 빈에 사용할 IoC의 유형에 따라 기본 생성자가 필요할 수 있습니다.

스프링 IoC 컨테이너는 사실상 관리하고자 하는 모든 클래스를 관리할 수 있습니다.
진정한 자바빈 관리에만 제한되지 않습니다.
대부분 스프링 사용자는 컨테이너의 속성을 모델로한 기본 생성자와 적정한 setter와 getter를 가진 실제 자바빈을 선호합니다.
또한 컨테이너에 더 이국적인 빈스타일이 아닌 클래스를 가질수도 있습니다.
예로 들어, 물론 자바빈 사양을 준수하지 않는 레거시 접속풀을 사용해야할 경우라도 스프링은 적절하게 관리할 수 있습니다.

XML기반 구성 메타데이터에서 빈 클래스를 아래와 같이 정의할 수 있습니다.

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

생성자(필요하다면)에 매개변수를 지원하거나 객체가 생성된 뒤 객체 인스턴스의 속성을 설정하는 메커니즘에 관련된 자세한 정보는 의존성 주입을 참고하세요.

정적 팩토리 함수로 인스턴스화

스태틱 팩토리 함수를 통해 생성하는 빈을 정의할 때, static 팩토리 함수를 포함하고 있는 클래스를 class 속성에 정의하고 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;
    }
}

팩토리 함수에 매개변수를 제공하고(선택적) 팩토리에서 객체가 반환된 후 객체 인스턴스 속성을 설정한느 것에 대한 자세한 메커니즘은 세부적인 종속성과 구성을 참고하세요.

인스턴스 팩토리 함수를 사용하여 인스턴스화

정적 팩토리 함수를 통해 인스턴스화 하는것과 유사하게 인스턴스 팩토리 함수로 인스턴스화 하는 것은 새로운 빈을 생성하기 위해 컨테이너로부터 기존 빈의 정적이 아닌 함수를 실행하는 것 입니다.
이 메커니즘을 사용하려면 class 속성은 공백이어야 하고 factory-bean 속성에 객체를 생성하기 위해 실행되어지는 인스턴스 함수를 포함하고 있는 현재 컨테이너의 빈에 대한 이름을 정의합니다.
factory-method 속성에 팩토리함수 자체의 이름을 설정합니다. 아래 예제에서 이런 빈을 구성하는 방법을 보여줍니다.

<!-- createInstance() 함수를 포함하고 있는 팩토리 빈 -->
<bean id="serviceLocator" class="examples.DefaultServiceLocator">
    <!-- 이 설정자 빈 에 필요로하는 의존성 주입 -->
</bean>

<!-- 팩토리 빈에 의해 생성되는 빈-->
<bean id="clientService" factory-bean="serviceLocator" factory-method="createClientServiceInstance" />

아래 예제는 해당하는 클래스를 보여줍니다.

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

    public ClientService createClientServiceInstance() {
        return clientService;
    }
}

아래 예가 보여주듯, 한개의 팩토리 클래스가 한개 이상의 팩토리 함수를 가질 수 있습니다.

<bean id="serviceLocator" class="examples.DefaultServiceLocator">
    <!-- 이 설정자 빈 에 필요로하는 의존성 주입 -->
</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;
    }
}

이 접근 방식은 팩토리 빈 자체가 의존성 주입(DI)를 통해 관리 및 구성될 수 있음을 보여줍니다.
세부적인 종속성과 구성을 참고하세요.

Tips

스프링 문서에서 "팩토리 빈"은 스프링 컨테이너에서 구성되어지고 인스턴스정적 팩토리 함수를 통해 객체를 생성하는 빈을 가리킵니다.
대조적으로 FactoryBean(대소문자에 주의)는 스프링 특화 FactoryBean 구현 클래스를 가리킵니다.

빈의 런타임 유형 결정하기

특정 빈의 런타임 유형은 결정하기 쉽지 않습니다.
빈 메타데이터 정의의 특정 클래스는 선언된 팩토리 함수 또는 빈의 다른 런타임 유형으로 이어질 수 있는 FactoryBean 클래스, 인스턴스 수준의 팩토리 함수의 경우 전혀 설정되지 않은 것과 조합된 초기 클래스 참조입니다.
추가적으로 AOP 프록시는 대상 빈의 실제 타입의 제한된 노출을 가지는 인터페이스 기반 프록시(구현된 인터페이스)로 빈 인스턴스를 감쌉니다.

특정 빈의 실제 런타임 유형을 찾을 때 추천하는 방법은 BeanFactory.getType 을 특정 빈 이름으로 호출하는 것 입니다.
위의 모든 경우를 고려하여 동일 빈 이름으로 BeanFactory.getBean 에서 반환되는 객체의 유형을 반환합니다.