1.3 프로토타입과 스코프
- 싱글톤이 아닌 빈은 두 가지로 나눌 수 있다 : 프로토토압 빈과 스코프 빈
1.3.1 프로토타입 스코프
- 프로토타입 빈은 컨테이너에 빈을 요청할 때마다 새로운 오브젝트를 만든다.
- getBean() 메소드를 이용해 명시적으로 호출할 때와 @Autowired나 <property>같은 DI선언도 각각 독립적인 빈 요청에 해당된다.
프로토타입 빈의 생명주기와 종속성
- 프로토타입 스코프를 갖는 빈은 요청이 있을 때마다 컨테이너가 생성하고 초기화하고 DI까지 해주기도 하지만, 일단 빈을 제공하고 나면 컨테이너는 더 이상 빈 오브젝트를 관리하지 않는다.
- 프로토타입 빈 오브젝트는 한번 DL이나 DI 통해 컨테이너 밖으로 전달된 후에는 이 오브젝트는 더 이상 스프링이 관리하는 빈이 아니다.
- 그래서 프로토타입 빈은 이 빈을 주입받은 오브젝트에 종속적일 수 밖에 없다.
DI와 DL
📌 DI (Dependency Injection, 의존성 주입)
✔️ 의미: 필요한 객체를 외부에서 주입받는 방식
✔️ 장점: 낮은 결합도, 테스트 용이, 유연한 구조
✔️ 예시: 생성자 주입, Setter 주입
✔️ 스프링 핵심 철학으로 적극 권장됨
📌 DL (Dependency Lookup, 의존성 검색)
✔️ 의미: 필요한 객체를 직접 찾아서 사용하는 방식
✔️ 단점: 높은 결합도, 테스트 어려움
✔️ 예시: ApplicationContext.getBean(), JNDI 조회
✔️ 스프링에서는 지양하는 방식
- 이 코드를 테스트해보면 정상적으로 동작하는 것처럼 보이지만 운영 시스템에 적용하면 매우 심각한 문제가 발생한다.
- 웹 컨트롤러도 싱글톤이기 때문에 단 한 번만 만들어지는데, DI 작업은 빈 오브젝트가 처음 만들어질 때 한 번만 진행되기 때문이다.
- DI는 프로토타입 빈을 사용하기에 적합한 방법이 아니다. 코드 내에 필요할 때마다 컨테이너에게 요청해 새로운 오브젝트를 받는 DL 방식을 사용해야 한다.
- DI 받는 빈마다 다른 오브젝트를 사용해야 하는 특별한 이유가 있다면 그때는 프로토타입 빈을 DI로 사용할 수 있다.
프로토타입 빈의 DL 전략
ApplicationContext, BeanFactory
- @Autowired나 @Resource 를 이용해 ApplicationContext 또는 BeanFactory를 DI 받은 뒤에 getBean() 메소드 직접 호출해 빈을 가져오면 된다.
- 사용하지는 간단하지만 코드에 스프링 API가 등장한다는 단점이 있다.
ObjectFactory, ObjectFactoryCreatingFactoryBean
- 직접 애플리케이션 컨텍스트를 사용하지 않으려면 중간에 컨텍스트에 getBean()을 호출해주는 역할을 맡은 오브젝트를 두면 된다
- ApplicationContext를 DI 받아서 getBean()를 호출해 원하는 프로토타입 빈을 가져오는 방식으로 동작하는 팩토리를 하나 만들어서 빈으로 등록해두고, 이 팩토리 역할을 하는 빈을 DI 받아서 필요할 대 getObject()와 같은 메소드를 호출해 빈을 가져올 수 있도록 만든다.
- ObjectFactory의 구현 클래스는 스프링이 제공해주고 있다.
- 클래스의 이름은 ObjectFactoryCreatingFactoryBean이다.
ServiceLocatorFactoryBean
- ObjectFactory처럼 스프링이 미리 정의해둔 인터페이스를 사용하지 않아도 된다.
- 범용적으로 사용하는 ObjectFactory와 달리 ServiceRequest 전용으로 만든 인터페이스가 이 빈의 타입이 되기 때문에 @Autowired를 이용해 가져올 수 있고, 이름으로 접근할 필요가 없을때는 id를 생략할 수 았다.
- 타임 파라미터 사용해야 하는 ObjectFactory 보다 코드가 깔끔하다.
메소드 주입
- 스프링 API에 의존적인 코드 가지는 단점과, 빈을 새로 추가해야 하는 두 가지 단점 모두 극복하는 바업이다.
- 메소드 주입은 메소드를 통한 주입이 아니라 메소드 코드 자체를 주입하는 것을 말한다.
- 메소드 주입은 일정한 규칙 따르는 추상 메소드 작성해두면 ApplicationContext와 getBean()메소드를 사용해서 새로운 프로토타입 빈을 가져오는 기능을 담당하는 메소드를 런타임 시에 추가해주는 기술이다.
- 메소드 주입 방식은 그 자체로 스프링 API에 의존적이 아니므로 스프링 외의 환경에 가져다 사용할 수도 있고, 컨테이너 도움 없이 단위 테스트 할 수도 있다.
- (단점) 클래스 자체가 추상 클래스이므로 테스트에서 사용할 때 상속을 통해 추상 메소드를 오버라이드 한 뒤에 사용해야 한다는 번거로움이 있다.
Provider<T>
- Provicer는 ObjectFactory와 거의 유사하게 <T>타입 파라미터와 get()이라는 팩토리 메소드를 가진 인터페이스다.
- 기본 개념은 ObjectFactory와 거의 유사하지만 ObjectFactoryCreatingFactoryBean을 이용해 등록해주지 않아도 되기 때문에 사용이 편리하다.
- Provider 인터페이스를 @Inject, @Autowired, @Resource 중의 하나를 이용해 DI 되도록 지정해주기만 하면 스프링이 자동으로 Provider를 구현한 오브젝트를 생성해서 주입해주기 때문이다.
1.3.2 스코프
- 스프링은 싱글톤, 프로토타입 외에 요청, 세션, 글로벌세션, 애플리케이션이라는 네 가지 스코프를 기본적으로 제공한다.
- 이 네가지 스코프 중에서 애플리케이션을 제외한 나머지 세 가지 스코프는 싱글톤과 다르게 독립적인 상태를 저장해두고 사용하는데 필요하다.
요청 스코프
- 요청 스코프 빈은 하나의 웹 요청 안에서 만들어지고 해당 요청이 끝날 때 제거된다.
- 동시에 웹 요청이 달라지면 별도의 요청 스코프 빈이 만들어지기 때문에 동시에 여러 사용자가 많은 요청을 보내도 안전하다.
- 주요 용도는 애플리케이션 코드에서 생성한 정보를 프레임워크 레벨의 서비스나 인터센터 등에 전달하는 것이다.
- 또는 애플리케이션 코드가 호출되기 전에 프레임워크나 인터셉터 등에서 생성한 정보를 애플리케이션 코드에서 이용할 때 유용하다.
세션 스코프, 글로벌세션 스코프
- HTTP 세션과 같은 존재 범위를 갖는 빈으로 만들어주는 스코프다.
- 웹 페이지가 바뀌고 여러 요청을 거치는 동안에도 세션 스코프 빈은 유지된다.
- 글로벌세션 소크프는 포틀릿에만 존재하는 글로벌 세션에 저장되는 빈이다.
애플리케이션 스코프
- 애플리케이션 스코프는 서블릿 컨텍스트에 저장되는 빈 오브젝트다.
- 애플리케이션 스코프는 컨텍스트가 존재하는 동안 유지되는 싱글톤 스코프와 비스산 존재 범위를 갖는다.
- 싱글톤 스코프를 사용하면 될 텐데 애플리케이션 스코프가 따로 존재하는 이유는 무엇일까 ? 드물지만 웹 애플리케이션 밖에서 더 오랫동안 존재하는 컨텍스트도 있고 더 짧은 동안 존재하기도 하는 서블릿 레벨의 컨텍스트도 있기 때문이다.
1.4 기타 빈 설정 메타정보
1.4.1 빈 이름
XML 설정에서의 빈 식별자와 별칭
- 빈 아이디id와 빈 이름name은 특정 빈을 구분해서 가리키기 위해 사용되는 빈 식별자identifier를 말한다.
- 빈은 하나 또는 그 이상의 식별자를 가질 수 있으며, 빈의 식별자는 빈이 정의된 애플리케이션 내에서 고유해야 한다.
id
- id를 사용해 빈의 식별자를 지정해야 하는 경우에는 XML의 ID타입의 기준을 지켜야 한다.
- id는 문서 전체에서 고유해야 하며 규칙이 있다.
- id는 생략도 가능하다.
- 빈 선언에 어떠한 식별자도 부여하지 않는다면 스프링 컨테이너가 자동으로 빈의 아이디를 부여해준다.
- id를 지정하지 않았더라도 빈의 타입을 이용해 DI하는 건 가능하다.
name
- XML의 DI타입의 형식을 따라야 하는 id와 달리 name에는 특별한 제약이 없다.
- 또한 id와 달리 한 번에 여러 개의 이름을 지정할 수 있다.
애노테이션에서의 빈 이름
- 클래스에 @Component와 같은 스테레오타입의 애노테이션을 부여해주고 빈 스캐너에 의해 자동인식되도록 만든 경우에는 보통 클래스 이름을 그대로 빈 이름으로 사용하는 방법을 선호한다.
- 클래스 이름 첫 글자만 소문자로 바꾸면 빈 이름이 된다는 관례를 이용한다.
- 직접 빈 이름을 지정할 수도 있다.
- 1.4.2 빈 생명주기 메소드
초기화 메소드
- 초기화 메소드는 빈 오브젝트가 생성되고 DI작업까지 마친 다음에 실행되는 메소드를 말한다.
- 오브젝트의 기본적인 초기화 작업은 생성자에서 진행하면 되지만, DI를 통해 모든 프로퍼티가 주입된 후에 가능한 초기화 작업 시 사용한다.
초기화 콜백 인터페이스
- InitializingBean 인터페이스를 구현해서 빈을 작성한다.
- InitializingBean의 afterPropertiesSet() 메소드는 이름 그대로 프로퍼티 설정까지 마친 뒤에 호출한다.
init-method 지정
- XML 이용해 빈 등록한다면 <bean> 태그에 init-method 애트리뷰트 넣어서 초기화 작업 수행할 메소드 이름을 지정할 수 있다.
- 스프링 API가 노출되지 않는다는 깔끔하다는 장점이 있지만, 코드만 보고는 초기화 메소드가 호출될지 알 수 없기 때문에 코드를 이해하는데 불편할 수 있다.
@PostConstruct
- 스프링 콜백 인터페이스 사용하는 것보다 상대적으로 부담이 적으면서, 코드에서 초기화 메소드가 존재한다는 사실을 파악할 수 있으므로 직관적이다.
- 가장 사용이 권장되는 방식이다.
@Bean(init-method)
제거 메소드
- 컨테이너가 종료될 때 호출돼서 빈이 사용한 리소스 반환하거나 종료 전에 처리해야 할 작업을 수행한다.
제거 콜백 인터페이스
- DisposableBean 인터페이스 구현해서 destroy() 를 구현하는 방법이다.
- 스프링 API에 종속되는 코드를 만드는 단점이 있다.
destroy-method
- <bean>태그에 destroy-method를 넣어서 제거 메소드를 지정할 수 있다.
@PreDestroy
- 컨테이너가 종료될 때 실행될 메소드에 @PreDestroy를 붙여주면 된다.
@Bean(destroyMethod)
- @Bean 애노테이션의 destroyMethod 엘리멑으 이용해서 제거 메소드를 지정할 수 있다.
1.4.3 팩토리 빈과 팩토리 메소드
- 생성자 대신 오브젝트를 생성해주는 코드의 도움을 받아서 빈 오브젝트를 생성하는 것을 팩토리 빈이라고 한다
- 팩코리 빈 자신은 빈 오브젝트로 사용되지 않는다. 대신 빈 오브젝트를 만들어주는 기능만 제공해줄 뿐이다.
📌 FactoryBean (팩토리 빈)
✔️ 스프링 빈을 생성하는 스프링 빈 객체
✔️ 개발자가 직접 구현 (org.springframework.beans.factory.FactoryBean<T> 인터페이스)
✔️ getObject() 메서드를 통해 실제 생성될 객체를 반환
✔️ 예: SqlSessionFactoryBean, LocalContainerEntityManagerFactoryBean
💡 빈을 만드는 공장 역할을 하는 스프링 빈
📌 BeanFactory (빈 팩토리)
✔️ 스프링 컨테이너의 가장 기본적인 인터페이스
✔️ 객체를 생성하고 관리하는 기능 제공
✔️ ApplicationContext의 상위 개념
✔️ 지금은 거의 사용되지 않고, 테스트나 경량 환경에 사용
💡 스프링 컨테이너 자체, 빈을 담고 관리하는 저수준 인터페이스
FactoryBean 인터페이스
- 가장 단순하고 자주 사용되는 방법이다.
- 보통 팩토리 빈은 기술 서비스 빈이나 기반 서비스 빈을 활용할 때 주로 사용된다.
- 따라서 스프링 인터페이스를 구현하는 것이 불편하지 않다면 사용하기 적당하다.
스태틱 팩토리 메소드
- 클래스의 스태틱 메소드 호출해서 인스턴스를 생성하는 방식이다.
- 전통적인 싱글톤 클래스는 생성자를 직접 호출해서 오브젝트를 만들 수 없다.
- 물론 스프링의 빈으로 선언해버리면 private 생성자도 호출해서 오브젝트를 만들어버리긴 하지만, 오브젝트 생성과 함께 초기화 작업이 필요한 경우라면 스태틱 팩토리 메소드를 이용해야 한다.
인스턴스 팩토리 메소드
- 임의의 오브젝트의 메소드를 호출해서 빈을 생성해야 한다면, factory-bean과 factory-method를 함께 사용한다.
- 이때는 팩토리 기능을 제공할 빈을 따로 등록해야 한다.
@Bean 메소드
- 자바 코드에 의한 빈 등록 방식에서 @Bean 메소드도 일종의 팩토리 메소드다.
- 스프링 컨테이너가 @Bean 메소드를 실행해 빈 오브젝트를 가져오기 때문이다.
1.5 스프링 3.1의 IoC 컨테이너와 DI
1.5.1 빈의 역할과 구분
빈의 종류
1) 애플리케이션 로직 빈
- 스프링에서 말하는 빈은 스프링 IoC/DI 컨테이너에 의해 생성되고 관리되는 오브젝트이다.
- 일반적으로 애플리케이션 로직을 담고 있는 주요 클래스의 오브젝트가 빈으로 지정된다.
- 데이터 로직을 다루는 DAO, 비즈니스 로직과 기반 서시브를 다루는 서비스 오브젝트 그리고 컨트롤러 오브젝트 등이 대표적이다.
2) 애플리케이션 인프라 빈
- 애플리케이션이 동작하는데 참여하므로 애플리케이션 빈의 일종이지만 개발자가 직접 작성하는 로직을 담고 있는 것이 아니므로 애플리케이션 로직 빈과 분리해서 애플리케이션 기반 빈 또는 애플리케이션 인프라스트럭쳐 빈이라고 부르면 된다.
- DAO가 사용되는 DataSource 오브젝트, 트랜잭션 추상화에 사용되는 DataSourceTransactionManager이 있다.
3) 컨테이너 인프라 빈
- 스프링 컨테이너의 기능을 확장해서 빈의 등록과 생성, 관계설정, 초기화 등의 작업에 참여하는 빈을 컨테이너 인프라스트럭쳐 빈, 줄여서 컨테이너 인프라 빈이라고 부른다.
- DefaultAutoProxyCreator, BeanPostProcessor나 BeanFactoryPostProcessor 등이 있다.
컨테이너 인프라 빈과 전용 태그
- 컨테이너 인프라 빈은 스프링 컨테이너의 기본 기능을 확장하는 데 사용되고 주로 전용 태그를 통해 간접적으로 등록된다.
- 컨테이너 인프라 빈은 일반 애플리케이션 개발자가 직접 개발해서 추가할 일이 거의 없고, 일정한 설정 패턴이 있기 때문에 전용 태그로 등록하고 애트리뷰트를 통해 필요한 속성만 부여하도록 하는 것이 일반적이다.
- 특별히 스프링 내부구조나 동작원리에 관심 있는 게 아니라면 스프링 전용 태그에 의해 등록되는 빈이 어떤 것인지 알 필요는 없다.
- <context:annotaion-config> 태그를 추가해야 스프링 컨테이너의 기능을 사용할 수 있다.
- context 네임스페이스의 태그를 처리하는 핸들러를 통해 특정 빈이 등록되게 해줄 뿐이다. 이 과정에서 등록되는 빈이 스프링 컨테이너를 확장해서 빈의 등록과 관계 설정, 후처리 등에 새로운 기능을 부여하는 컨테이너 인프라 빈이다.
1.5.2 컨테이너 인프라 빈을 위한 자바 코드 메타정보
자바 코드를 이용한 컨테이너 인프라 빈 등록
1) @ComponentScan
- @ComponentSan의 기본 앨리컨트 값은 빈을 스캔할 기반 패키지다.
- 일부 틀래스를 스캔 대상에서 제외하고 싶을 때는 excludes 앨리먼트를 사용한다.
2) @Import
- 다른 @Configuration 클래스를 빈 메타정보에 추가할 때 사용한다.
- 히나의 애플리케이션 컨텍스트가 사용할 설정 클래스가 여러 개라면 모든 클래스를 컨텍스트에 직접 등록하는 대신 기준이 되는 @Configuration 클래스 파일 하나만 컨텍스트에 등록하고, 이 클래스에서 다른 부가적인 빈 설정 담은 @Configuartion클래스를 @Import 하는 방법을 사용할 수 있다.
3) @ImportResource
- XML이 꼭 필요한 빈 설정만 별도의 파일로 자겅한 뒤에 @COnfiguration클래스에서 @ImportResource를 이용해 XML 파일의 빈 설정을 가져올 수 있다.
4) @EnableTransactionManagement
- XML의 <tx:annotation-driven/> 태그와 동일한 기능 수행한다.
- 이 외에도 @EnableAspectJAutoProxy, @EnableAsync, @EnableCaching, @EnableLoadTimeWeaving, @EnableScheduling, @EnableSpringConfigured, @EnableWebMvc 등이 있다.
'스터디 > [토비의 스프링 3.1 Vol 1] (2025.03)' 카테고리의 다른 글
[토비의 스프링 3.1 Vol 2] 1장. 오브젝트와 의존관계 (~1.2.2) (0) | 2025.06.22 |
---|---|
[토비의 스프링 3.1 Vol 2] 1장. 오브젝트와 의존관계 (~1.2.2) (3) | 2025.06.14 |
[토비의 스프링 3.1 Vol 1] 8장. 스프링이란 무엇인가? (2) | 2025.06.08 |
[토비의 스프링 3.1 Vol 1] 7장. 스프링 핵심 기술의 응용 (~7.7) (0) | 2025.05.31 |
[토비의 스프링 3.1 Vol 1] 7장. 스프링 핵심 기술의 응용 (~7.5 DI를 이용해 다양한 구현 방법 적용하기) (0) | 2025.05.25 |