2023. 2. 7. 11:56ㆍSpring
◎ 스프링 컨테이너
-> 스프링에서 자바 객체를 관리하는 공간이다.
-> 빈은 스프링에서 관리되는 객체다.
-> 스프링 컨테이너는 서로 다른 빈을 연결하여 애플리케이션의 빈을 연결하는 역할을 한다.
->
※ 스프링 컨테이너는 왜 쓸까?
- 외부 객체를 사용하기 위해서 이전까지 new를 사용해 객체를 생성했다. new를 통해 객체를 생성하는 방식으로 객체를 사용하게 되면 객체간 참조의 정도가 높아져 하나의 객체를 수정하면 연관된 다른 코드까지 모두 수정해야 하는 문제가 있다. 따라서 객체간 의존도를 낮춰 하나의 코드가 다른 코드에 주는 영향을 줄여야 하는데 이 경우 객체를 관리하는 스프링 컨테이너를 사용해 객체 간 의존도를 낮출 수 있다.
* 스프링 컨테이너의 생성과정
- 스프링 컨테이너는 객체를 인스턴스화, 구성하는 것에 대한 정보인 Configuration Metadata를 사용한다.
- 스프링 컨테이너는 파라미터로 넘어온 설정 클래스 정보를 사용해 스프링 빈을 등록한다.
- new AnnotationConfigApplicationContext(구성정보.class)로 스프링에 있는 @Bean의 메서드를 등록한다.
(참고 : https://docs.spring.io/spring-framework/docs/3.0.0.M4/reference/html/ch03s02.html)
@Configuration // 스프링 설정 정보임을 알린다.
public class AppConfig { // 의존관계는 여기에서 관리한다.
/*
스프링 컨테이너는 @Configuration이 붙은 AppConfig를 스프링 설정 정보로 사용한다.
@Bean이라 적인 메서드를 모두 호출하여 반환된 객체를 스프링 컨테이너에 등록한다. 스프링 컨테이너에 등록된 객체를 스프링 빈이라 한다.
스프링 빈은 @Bean이 붙은 메서드의 이름을 스프링 빈의 이름으로 사용한다.
*/
@Bean // 스프링 컨테이너에 등록됨
public MemberService memberService(){
return new MemberServiceImpl(memberRepository());
}
@Bean
public MemoryMemberRepository memberRepository() {
return new MemoryMemberRepository();
}
@Bean
public OrderService orderService(){
return new OrderServiceImpl(memberRepository(), discountPolicy());
}
@Bean
public DiscountPolicy discountPolicy(){
// return new FixDiscountPolicy();
return new RateDiscountPolicy();
}
}
◎ 스프링 컨테이너 종류
-> 파라미터로 넘어온 설정 클래스 정보를 이용해 빈의 생성, 관계 설정 등 전반적인 제어 작업을 하는 컨테이너다.
-> BeanFactory
- 스프링 컨테이너의 최상위 인터페이스.
- getBean()을 통해 빈을 인스턴스화할 수 있다.
- @Bean이 붙은 메서드의 이름을 스프링 빈의 이름으로 하여 빈 등록한다.
-> ApplicationContext
- BeanFactory의 기능을 상속받아 제공한다.
- 빈을 관리하고 검색하는 기능은 BeanFactory로부터 제공받고 추가적인 기능을 제공한다.
- MessageSource(메시지 다국화 인터페이스), ResourceLoader(파일, 클래스 패스, 등 리소스 조회) 등의 기능을 추가적으로 제공한다.
◎ 컨테이너 인스턴스화
-> ApplicationContext생성자에 제공된 경로는 컨테이너가 로컬 파일 시스템, Java CLASSPATH와 같은 외부 리소스로부터 configuration metadata를 로드할 수 있는 리소스 문자열이다.
ApplicationContext ac = new AnnotationConfigApplicationContext(DependencyConfig.class);
◎ 빈 (Bean)
-> 스프링 컨테이너가 관리하는 자바 객체로 스프링 컨테이너에 등록된 객체를 스프링 빈이라고 한다.
-> 빈은 인스턴스화된 객체를 의미한다.
-> @Bean애너테이션이 붙은 메서드들을 모두 호출하여 반환된 객체를 스프링 컨테이너에 등록한다.
-> 빈은 클래스의 등록정보, getter/setter메서드를 포함한다.
-> 빈은 컨테이너에 사용되는 *설정 메타데이터로 생성된다.
*설정 메타데이터 : XML or 자바 애너테이션, 자바 코드로 표현한다. 컨테이너의 명령, 인스턴스화, 설정, 조립할 객체를 정의한다.
-> ApplicationContext로 bean의 정의를 읽고 액세스할 수 있다.
ApplicationContext context = new ClassPathXmlApplicationContext("services.xml", "daos.xml");
MemberService service = context.getBean("memberRepository", memberRepository.class);
-> getBean()을 사용하여 bean의 인스턴스를 가져올 수 있다.
※ 실제 응용 프로그램에서 getBean()로 호출하여 사용하지 않는다.
<why?>
- getBean()으로 스프링 컨테이너에 있는 객체 정보를 가져오려면 위 코드 처럼 객체의 이름을 알아야 한다. 객체 클래스를 직접 입력을 해야하는데 이는 객체 사용자가 객체의 이름과 객체를 불러오는 시점을 알아야 한다는 것이다. 즉, 객체 간의 결합이 강해지게 된다.
◎ BeanDefinition
-> 스프링은 BeanDefinition으로 스프링의 다양한 설정 형식을 지원한다.
-> 속성에 따라 컨테이너가 bean을 어떻게 생성하고 관리할지 결정한다.
-> 스프링이 다양한 형태의 설정 정보를 BeanDefinition으로 추상화하여 사용하는 것이다.
-> BeanDefinition이라는 추상화로 스프링 컨테이너는 자바코드인지 xml인지 알 필요 없이 BeanDefinition만 알면 된다.
◎ Bean Scope
-> 빈이 사용되는 범위
-> 스프링에서는 기본적으로 Singleton으로 설정되어있다.
(bean scope : https://docs.spring.io/spring-framework/docs/3.2.x/spring-framework-reference/html/beans.html#beans-factory-scopes )
◎ 싱글톤 스코프
->클래스의 인스턴스가 1개만 생성되는 것을 보장하는 디자인 패턴
-> 스프링 컨테이너의 시작과 함께 생성되어 스프링 컨테이너가 종룓될 때 까지 유지 된다.
- >싱글톤 빈의 하나의 공유 인스턴스만 관리한다. -> private 생성자를 사용하여 외부에서 임의로 new를 사용하지 못하게 방지한다.
-> 해당 beanDefinition과 일치하는 id나 id를 가진 빈에 대한 모든 요청은 스프링 컨테이너에서 해당 특정 빈 인스턴스를 반환한다.
-> 스프링 컨테이너 종료시 소멸 메서드가 자동으로 실행된다.
-> 단일 인스턴스는 싱글톤 빈의 캐시에 저장된다.
-> 이름이 정해진 빈에 대한 모든 요청, 참조는 캐시된 개체를 반환한다. -> 싱글톤 스코프의 스프링 빈은 여러번 호출해도 항상 같은 인스턴스 참조 주소값을 가진다.
-> 싱글톤을 적용하지 않은 코드
-> 위 코드에서 같은 TeamService를 사용하지만 주소 값이 서로 다르다.
-> 객체를 많이 생성하면 해당 방식은 매번 새로운 객체를 생성하여 메모리 낭비가 발생한다.
-> 이 문제를 싱글톤을 통해 해결할 수 있다.
-> 싱글톤 패턴을 적용하여 getInstance()를 통해서만 객체 인스턴스를 조회할 수 있다.
-> getInstance()를 호출하면 항상 같은 static instance를 호출한다.
-> 외부에서 생성자가 new로 생성되는 것을 막기위해 private 생성자로 생성한다.
◎ 싱글톤 패턴의 단점
-> 싱글톤 패턴을 구현하는 코드가 많다.
-> 의존 관계상 클라이언트가 구체 클래스에 의존한다.(위 코드에서는 SingletonService 클래스에 의존)
-> private 생성자를 사용하여 자식 클래스기 어려워 유연성이 떨어진다.
-> 1개의 인스턴스에서 속성 값을 공유하기 때문에 멀티 스레드 환경에서 싱글톤 객체의 속성이 여러 스레드에 의해 변경될 수 있다.
-> 싱글톤 빈은 애플리케이션 실행 시 생성되기 때문에 싱글톤 빈이 많을 수록 실행 시간이 증가한다.
-> 해당 문제들은 싱글톤 컨테이너가 해결한다.
◎ 싱글톤 컨테이너
-> 객체 인스턴스를 싱글톤으로 관리한다.
-> 스프링 컨테이너는 싱글톤 컨테이너 역할을 한다.
-> 싱글톤 객체로 생성하고 관리하는 기능을 '싱글톤 레지스트리'라고한다.
// 의존성 주입을 관리하는 클래스
@Configuration
public class DependencyConfig {
@Bean
public TeamService teamService(){
return new TeamService(teamRepository());
}
@Bean
public TeamRepository teamRepository(){
return new TeamRepository();
}
@Bean
public ChampionsShipsService championsShipsService(){
return new ChampionsShipsService(championShipRepository());
}
@Bean
public ChampionShipRepository championShipRepository(){
return new ChampionShipRepository();
}
}
-> 기존 의존성 주입을 관리하는 DependencyConfig클래스에 @Configuration, 클래스 내 각 메서드 위에 @Bean을 붙여준다.
-> @Bean을 통해 스프링 컨테이너에 등록된다.
-> 싱글톤을 사용할 때는 여러 클라이언트가 하나의 객체 인스턴스를 공유하기 때문에 싱글톤 객체를 무상태로 설계해야한다. 즉, 스프링 빈으로 등록되는 객체는 특정 클라이언트가 객체의 필드값을 변경할 수 없게 만들어야 한다. 객체가 상태를 유지하면 여러 클라이언트가 해당 객체를 요청할 때마다 값이 변경되기 때문이다.