@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
@Component
public class AppRunner implements ApplicationRunner {
@Autowired
Single single;
@Autowired
Proto proto;
@Override
public void run(ApplicationArguments args) throws Exception {
// AppRunner가 불러오는 proto
System.out.println(proto);
// Single이 불러오는 proto
System.out.println(single.getProto());
}
}
@Component
public class Single {
@Autowired
Proto proto;
public Proto getProto() {
return proto;
}
}
@Component
public class AppRunner implements ApplicationRunner {
@Autowired
ApplicationContext ctx;
@Override
public void run(ApplicationArguments args) throws Exception {
System.out.println("proto");
System.out.println(ctx.getBean(Proto.class));
System.out.println(ctx.getBean(Proto.class));
System.out.println(ctx.getBean(Proto.class));
System.out.println("\nsingle");
System.out.println(ctx.getBean(Single.class));
System.out.println(ctx.getBean(Single.class));
System.out.println(ctx.getBean(Single.class));
}
}
proto
me.whiteship.beanscope.Proto@632aa1a3
me.whiteship.beanscope.Proto@20765ed5
me.whiteship.beanscope.Proto@3b582111
single
me.whiteship.beanscope.Single@2899a8db
me.whiteship.beanscope.Single@2899a8db
me.whiteship.beanscope.Single@2899a8db
여러 번을 시도해도 역시 같은 결과를 가져온다.
프로토타입 빈이 싱글턴 빈을 참조하는 경우
@Component
@Scope("prototype")
public class Proto {
@Autowired
Single single;
}
프로토타입 빈은 항상 새롭지만 안에서 참조하는 싱글턴 빈은 항상 동일하므로 아무 문제가 없다.
싱글턴 빈이 프로토타입 빈을 참조하는 경우
싱글턴 빈은 한 번만 만들어지므로 이미 프로토타입의 빈이 이미 세팅된 상태다.
따라서 프로토타입 빈이 변경되지 않는다.
@Component
public class AppRunner implements ApplicationRunner {
@Autowired
ApplicationContext ctx;
@Override
public void run(ApplicationArguments args) throws Exception {
System.out.println("\nproto by single");
System.out.println(ctx.getBean(Single.class).getProto());
System.out.println(ctx.getBean(Single.class).getProto());
System.out.println(ctx.getBean(Single.class).getProto());
}
}
proto by single
me.whiteship.beanscope.Proto@1162410a
me.whiteship.beanscope.Proto@1162410a
me.whiteship.beanscope.Proto@1162410a
코드로 테스트 해보니 역시 프로토타입임에도 같은 주소값을 출력한다.
해결법
프록시 모드를 설정하면 된다. 기본은 DEFAULT(프록시 모드 사용 안 함)으로 되어있다.
@Component
@Scope(value = "prototype", proxyMode = ScopedProxyMode.TARGET_CLASS)
public class Proto {
@Autowired
Single single;
}
proto by single
me.whiteship.beanscope.Proto@4a951911
me.whiteship.beanscope.Proto@55b62629
me.whiteship.beanscope.Proto@a53bb6f
매번 새로운 인스턴스가 만들어짐을 확인할 수 있다.
TARGET_CLASS
클래스 기반의 프록시로 빈(Proto)를 감싸라고 설정하는 것
프록시를 쓰지 않으면 직접 참조하므로 프로토타입을 생성할 때마다 새로 바꿔 줄 여지가 없다.
프록시를 거쳐서 참조하도록 하면 매번 바꿔서 참조할 수 있다.
원래 자바 안에 있는 다이내믹 프록시는 인터페이스의 프록시만 만들 수 있다.
CG LIB(Code Generator Library라는 서드 파티 라이브러가 TARGET_CLASS 설정을 보고 클래스도 만들 수 있도록 한다.
만약 인터페이스였다면 INTERFACES로 설정해서 인터페이스 기반의 프록시를 썼을 것이다.
프록시 빈도 프로토타입의 빈을 상속해서 만들었기 때문에 해당 빈과 타입(여기서는 Proto 타입)은 같다.