스프링 #3 스프링 컨테이너, 스프링 빈

함형주·2022년 4월 4일
1

spring

목록 보기
3/12

질문, 피드백 등 모든 댓글 환영합니다.

스프링에서 처음 마주하게 되는 스프링 컨테이너와 빈에 대해서 정리해보고자 합니다.
스프링을 처음 공부하며 느낀 것을 정리한거라 내용이 부실하고 틀린 부분이 있을 수 있습니다.
앞으로 공부하며 추가하겠습니다.


스프링 컨테이너란?

스프링 컨테이너는 스프링 빈의 형태로 객체를 생성하여 생명주기를 관리하고 이에 추가적인 기능을 제공하는 컨테이너 입니다. IoC(제어의 역전)와 DI의 개념을 사용하여 객체지향적인 개발을 편리하게 도와주는 역할을 합니다.


스프링 빈이란?

스프링 컨테이너에 등록된 객체를 스프링 빈(Bean)이라고 합니다. 스프링 빈은 빈의 이름과 객체가 마치 Map의 형태로 함께 저장됩니다. 스프링 빈은 기본적으로 @Bean이 붙은 메서드 명이 빈의 이름으로 등록됩니다. 또는 @Bean(name="빈 이름")으로 이름을 직접 지정할 수 있습니다. 각각의 스프링 빈은 반드시 다른 이름으로 지정해야 합니다. 등록된 스프링 빈은 스프링 컨테이너를 통해 조회하고 관리할 수 있습니다.


스프링 빈 등록

스프링 빈을 등록하는 방식은 크게 수동 방식과 자동 방식이 있습니다.

스프링 빈 수동 등록

  1. 애플리케이션의 설정정보를 담당하는 클래스에 @Configuraion 사용
  2. 객체 생성 메서드에 @Bean 등록
@Configuration
public class AppConfig(){

	@Bean
	public Service service() {
		return new XxxService();
	}
}

스프링 빈을 수동으로 등록하는 방식은 등록하는 객체가 많아질 수록 코드 분량도 늘어나고 누락 될 위험도 존재하므로 특정한 상황이 아니라면 자동으로 등록하는 것이 좋습니다.

스프링 빈 자동 등록

  1. 애플리케이션의 설정정보를 담당하는 클래스에 @Configuraion과 @ComponentScan 사용
@Configuraion
@ComponentScan
public class Appconfig() {
}
  1. 스프링 빈으로 등록할 객체에 @Component 사용
  2. 생성자에 @Autowired 사용
@Component
public class XxxService() {

	@Autowired
    public XxxService() {
    	...
    }
}

@ComponentScan은 @Component가 붙은 클래스를 자동으로 스프링 빈에 등록시켜 줍니다.
( @Component뿐만 아니라 @Configuration, @Controller, @Service, @Repository를 모두 스프링 빈에 등록합니다. 해당 annotation들은 @Component를 포함합니다.)
@Autowired는 스프링 컨테이너에서 같은 타입의 스프링 빈을 찾아 의존성을 주입시켜 줍니다.
@Autowired는 생성자에 여러 파라미터가 존재하더라도 스프링 빈에 등록된 객체라면 한 번에 여러 의존성을 주입할 수 있습니다.

@ComponentScan 탐색 범위

@ComponentScan은 해당 클래스를 포함한 패키지와 그 하위 패키지를 탐색하며 @Component를 조회합니다. @ComponentScan( basePakage = "aaa.bbb" ) 로 탐색 위치를 변경할 수 있으나 일반적으로 @ComponentScan를 포함한 설정 정보 클래스를 프로젝트 최상단에 두어 사용합니다.
(스프링 부트에서도 이를 권장합니다.)


스프링 컨테이너의 종류

스피링 컨테이너는 BeanFactory와 ApplictaionContext가 있습니다.

BeanFactory

BeanFactory는 인터페이스로 AnnotationConfigApplicationContext 등의 구현클래스를 가집니다. BeanFactory는 스프링 빈으로 등록된 객체를 관리하는 기능을 가지고 있습니다.

	BeanFactory bf = new AnnotationConfigApplicationContext(설정 정보 클래스.class);
    클래스 class = bf.getBean(클래스.class);

이와 같이 스프링 빈으로 등록된 객체를 BeanFacotory의 getBean()을 통해 조회하고 사용할 수 있습니다.

ApplicationContext

ApplicationContext 또한 BeanFactory와 마찬가지로 AnnotationConfigApplicationContext 등의 구현 클래스가 존재하고 스프링 빈으로 등록된 객체를 관리하는 기능을 가집니다. BeanFactory에서 스프링 빈을 관리하는 기능을 상속받았기 때문에 동일한 기능을 수행할 수 있습니다. 위의 코드에서 BeanFactory 대신 ApplicationContext을 사용해도 동일한 결과가 나옵니다.


BeanFactory와 ApplicationContext의 차이점

BeanFactory와 ApplicationContext는 둘 다 스프링 빈을 관리하는 기능을 가지지만 완전히 같은 기능을 하진 않습니다.
BeanFactory는 클라이언트가 getBean()을 호출하는 시점에 스프링 빈이 등록되고 스프링 빈을 사용하는 시점에 로딩되어 사용할 수 있습니다.
ApplicationContext는 애플리케이션이 실행되는 시점에 스프링 빈을 등록하고 등록 된 모든 스프링 빈을 미리 로딩해두어 언제든지 바로 사용할 수 있습니다. 때문에 BeanFactory를 경량 컨테이너, ApplicationContext를 중량 컨테이너로 나누기도 합니다.

또한 ApplicationContext는 이 외에 많은 부가 기능을 제공합니다.
메시지 소스를 사용한 국제화 기능, 이벤트 관리, AOP처리 등 여러 편리한 기능을 제공합니다.

ApplicationContext이 pre-loading 컨테이너로써 지연 없이 빈을 사용할 수 있고 여러 편리한 기능을 제공하기에 대부분 ApplicationContext을 사용하고 이를 주로 스프링 컨테이너로 지칭합니다.


싱글톤 컨테이너

스프링 컨테이너는 기본적으로 스프링 빈을 싱글톤으로 관리합니다.

싱글톤

싱글톤은 생성자가 여러 차례 호출되더라도 실제로 생성되는 객체는 하나이고 최초 생성 이후에 호출된 생성자는 최초의 생성자가 생성한 객체를 리턴한다. 이와 같은 디자인 유형을 싱글턴 패턴이라고 합니다.#
즉 객체를 한 번 생성하면 두 번 다시 생성하지 않고 처음에 생성한 객체를 재사용하는 것이 싱글톤입니다.

Spring이 싱글톤으로 객체를 관리하는 이유는 간단합니다. 과도한 리소스 사용을 방지하기 위함 입니다.
만약 싱글톤으로 객체를 관리하지 않는다면 클라이언트가 요청을 할 때 마다 객체가 새로 생성될 것이고 그 리소스를 감당하기 어려울 것입니다. 스프링 빈으로 등록해서 사용하는 인스턴스의 대부분은 싱글톤 패턴으로 사용해도 문제가 발생하지 않기 때문에 기본적으로 싱글톤으로 사용하고 필요에 따라 빈 스코프를 달리 사용하면 됩니다. 빈 스코프는 다음에 정리하겠습니다.

자바 언어로 싱글톤 패턴 구현하기(스프링 X)

싱글톤 패턴을 구현하기 위해선 클래스의 인스턴스가 반드시 하나만 생성되도록 막아야합니다.
-> private 생성자로 구현

public class Service {
	private static final Service instance = new XxxService(); // static 영역에 생성
    
    private Service(){} // private 생성자로 외부에서 객체를 생성할 수 없게 막음
    
	public static SingletonService getInstance() {return instance;} // 이 메서드를 통해서만 객체를 참조할 수 있게 설정
}

클라이언트가 이 클래스를 사용할 땐 아래와 같이 사용합니다.

XxxService service1 = XxxService.getInstance(); 

싱글톤 패턴을 사용하면 리소스를 효율적으로 관리할 수 있지만 단점 또한 명확히 존재합니다

  1. 구현 코드가 늘어남 (귀찮음)
  2. DIP를 위반 (구현 클래스에 의존)
  3. OCP 위반 가능성 높음 (구현 클래스에 의존)
  4. 테스트 작성이 어려움 (초기화 불가)
  5. 상속이 어려움 (private 상속자)

-> 결론적으로 유연한 개발이 불가능합니다.

하지만 스프링 컨테이너는 싱글톤 패턴을 유지하기 위한 로직을 대신 해주므로 이런 단점을 해소하면서 객체를 싱글톤으로 관리해줍니다.

또한 싱글톤 패턴은 하나의 인스턴스를 여러 클라이언트가 공유하기 때문에 스프링 빈에 등록할 객체는 반드시 무상태(stateless)로 설계해야 합니다.

profile
평범한 대학생의 공부 일기?

0개의 댓글