ApplicationContext applicationContext =
new AnnotationConfigApplicationContext(AppConfig.class);
AppConfig 로도 만들 수 있다.BeanFactory 와 ApplicationContext 로 구분한다.BeanFactory 를 직접 사용하는 경우는 거의 없다.ApplicationContext 를 스프링 컨테이너라한다.@Configuration이 붙은 AppConfig를 설정(구성)정보로 사용한다.@Bean이 적힌 메서드를 모두 호출해서 반환된 객체를 스프링 컨테이너에 등록한다.
root-applicationContextservlet-applicationContext
ApplicationContext applicationContext =
new AnnotationConfigApplicationContext(AppConfig.class);
AppConfig.class 를 구성정보로 지정했다.
@Bean 어노테이션을 참조하여 스프링 빈을 등록한다.@Bean(name="원하는이름")


AppConfig는 요청을 할 때마다 객체를 새로 생성한다.private 생성자를 이용해서 외부에서 임의로 new 키워드를 사용하지 못하도록 막아야 한다.public class SingletonService {
private static final SingletonService instance = new SingletonService();
public static SingletonService getInstance(){
return instance;
}
public void logic(){
System.out.println("싱글톤 객체 로직 호출");
}
}
getInstance() 메서드를 통해서만 조회 가능 private 로 생성자를 막아, 외부 객체가 생성되는 것을 방지@Test
@DisplayName("싱글톤 패턴을 적용한 객체 사용")
public void singletonServiceTest() throws Exception{
SingletonService singletonService1 = SingletonService.getInstance();
SingletonService singletonService2 = SingletonService.getInstance();
//같은 객체 인스턴스를 출력한다.
System.out.println(singletonService1);
System.out.println(singletonService2);
}
private때문에 자식 클래스 만들기가 어렵다.
@Test
@DisplayName("스프링 컨테이너와 싱글톤")
public void springContainer() throws Exception{
// AppConfig appConfig = new AppConfig();
ApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);
MemberService memberService1= ac.getBean("memberService", MemberService.class);
MemberService memberService2 = ac.getBean("memberService", MemberService.class);
System.out.println("MemberService1 = "+memberService1);
System.out.println("MemberService2 = "+memberService2);
}
스프링의 기본 빈 등록 방식은 싱글톤이지만, 싱글톤 방식만 지원하는 것은 아니다.
⇒ 요청할 때 마다 새로운 객체를 생성하여 반환하는 기능 또한 있다.
지역변수 , 파라미터 , ThreadLocal 등을 사용해야한다.public class StatefulService {
private int price;
public void order(String name, int price){
System.out.println("name= "+ name+" price = "+ price);
this.price = price;
}
public int getPrice(){
return price;
}
}
@Test
public void statefulServiceSingleton() throws Exception{
ApplicationContext ac = new AnnotationConfigApplicationContext(TestConfig.class);
StatefulService statefulService1 = ac.getBean(StatefulService.class);
StatefulService statefulService2 = ac.getBean(StatefulService.class);
**//A가 10000원 주문하고 있는 와중에 B가 20000으로 주문한다면 어떻게 될까??
//스레드 1: A 사용자 10000원 주문
statefulService1.order("A", 10000);
//스레드 2: B 사용자 20000원 주문
statefulService2.order("B", 20000);
int price = statefulService1.getPrice();
//A의 주문 결과 확인 : 원래는 10000원을 주문해야하지만 20000원으로 갱신되어 있다.
System.out.println(price);
}**
static class TestConfig{
@Bean
public StatefulService statefulService(){
return new StatefulService();
}
}
public class StatefulService {
// private int price;
public int order(String name, int price){
System.out.println("name= "+ name+" price = "+ price);
// this.price = price;
**return price;**
}
}
@Test
public void statefulServiceSingleton() throws Exception{
ApplicationContext ac = new AnnotationConfigApplicationContext(TestConfig.class);
StatefulService statefulService1 = ac.getBean(StatefulService.class);
StatefulService statefulService2 = ac.getBean(StatefulService.class);
//A가 10000원 주문하고 있는 와중에 B가 20000으로 주문한다면 어떻게 될까??
**//스레드 1: A 사용자 10000원 주문
int priceA = statefulService1.order("A", 10000);
//스레드 2: B 사용자 20000원 주문
int priceB = statefulService2.order("B", 20000);
//정상적으로 10000원 출력됨
System.out.println(priceA);**
}
static class TestConfig{
@Bean
public StatefulService statefulService(){
return new StatefulService();
}
}
@Configuration은 싱글톤을 보장한다.@Test
public void configurationDeep() throws Exception{
ApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);
AppConfig bean = ac.getBean(AppConfig.class);
System.out.println(bean.getClass());
}
class hello.core.AppConfigclass hello.core.AppConfig$$EnhancerBySpring**CGLIB**$$713455cd 를 출력CGLIB 라는 바이트 조작 라이브러리를 사용AppConfig클래스를 상속받은 임의의 다른 클래스를 만든다.AppConfig@CGLIB 예상코드@Beanpublic MemberRepository memberRepository() {
if (memoryMemberRepository가 이미 스프링 컨테이너에 등록되어 있으면?) {
return 스프링 컨테이너에서 찾아서 반환;
} else{
1. 스프링 컨테이너에 없으면기존 로직을 호출
2. MemoryMemberRepository를 생성
3. 스프링 컨테이너에 등록
return 반환
}
}
@Bean 이 붙은 메서드마다이미 스프링 빈이 존재하면 존재하는 빈을 반환
스프링 빈이 없으면 생성해서 스프링 빈으로 등록하고 반환하는 코드가 동적으로 만들어짐
⇒ 싱글톤이 보장된다.
AppConfig@CGLIB는AppConfig의 자식 타입이므로,AppConfig타입으로 조회할 수 있다.
@Configuration 을 적용하지 않고, @Bean 만 적용하면 어떻게 될까??@Bean
public MemberService memberService(){
System.out.println("call AppConfig.memberService");
return new MemberServiceImpl(memberRepository());
}
@Bean
public MemberRepository memberRepository() {
System.out.println("call AppConfig.memberRepository");
return new MemoryMemberRepository();
}
@Bean
public OrderService orderService(){
System.out.println("call AppConfig.orderService");
return new OrderServiceImpl(memberRepository(), discountPolicy());
}
@Test
public void configurationTest() throws Exception{
ApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);
**//@Bean 코드 내부에서 생성한다 -> 얘만 스프링 컨테이너가 관리한다.**
MemberRepository original = ac.getBean("memberRepository", MemberRepository.class);
**//나머지는 자바 코드 흐름에 따라 생성되기 때문에 스프링 컨테이너가 관리하지 않는다.**
MemberServiceImpl memberService = ac.getBean("memberService", MemberServiceImpl.class);
OrderServiceImpl orderService = ac.getBean("orderService", OrderServiceImpl.class);
MemberRepository memberRepository = memberService.getMemberRepository();
MemberRepository memberRepository1 = orderService.getMemberRepository();
System.out.println("memberService -> memberRepository"+ memberRepository);
System.out.println("orderService -> memberRepository"+memberRepository1);
System.out.println(original);
}

@Bean 만 사용해도 스프링 빈으로 등록되지만, 싱글톤을 보장하지 않는다.new MemberServiceImpl(memberRepository());처럼 의존관계 주입이 필요해서 직접 호출할 때 싱글톤 보장 x@Configuration 을 사용하자