스프링 의존성 검색

HOSEON YOO·2024년 2월 16일
0

싱글톤 빈과 프로토타입 빈이 함께 사용하여 발생하는 문제

아래의 코드를 보면 SingletonBeangetPrototypeBean() 을 실행할 때마다 새로운 PrototypeBean 을 반환해야 한다. 하지만 항상 같은 PrototypeBean 을 반환하여 개발자가 의도한대로 동작하지 않는 문제가 발생한다. 이 문제를 해결하기 위해서는 의존성을 주입(DI)받는 것이 아니라 의존성을 검색(DL)해서 주입해야한다.

@Scope("prototype")
class PrototypeBean {}

class SingletonBean {
	@Autowired
    private PrototypeBean prototypeBean;
    
	public PrototypeBean getPrototypeBean() {
		return prototypeBean;
	}
}

class SingletonWithPrototypeTest {
	@Test
    void singletonWithPrototype() {
		AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(SingletonBean.class, PrototypeBean.class);
        
    	SingletonBean singletonBean1 = ac.getBean(SingletonBean.class);
        SingletonBean singletonBean2 = ac.getBean(SingletonBean.class);
        
		PrototypeBean prototypeBean1 = singletonBean1.getPrototypeBean();
		PrototypeBean prototypeBean2 = singletonBean1.getPrototypeBean();
       
		assertThat(prototypeBean1).isSameAs(prototypeBean2);
    }
}

DL(Dependency Lookup, 의존성 검색)

  • 의존성을 외부에서 주입(DI) 받는 게 아니라 직접 필요한 의존성을 검색(DL)한다.
  • DL을 사용하면 싱글톤 빈과 프로토타입 빈을 함께 사용 시에 발생하는 문제를 해결할 수 있다.
  • 프로토타입 빈을 의존성 주입 시점에만 새로 생성하는 게 아니라, 사용할 때마다 새로 생성해서 사용할 수 있다.
  • DL의 방법으로는 IoC 컨테이너 , ObjectProvider(ObjectFactory) , JSR-330 Provider 등이 있다.

IoC 컨테이너

  • IoC 컨테이너에서 직접 찾는 방법이다.
@Scope("prototype")
class PrototypeBean {}

class SingletonBean {
	@Autowired
    private ApplicationContext ac;
    
	public PrototypeBean getPrototypeBean() {
		return ac.getBean(PrototypeBean.class);
	}
}

class SingletonWithPrototypeTest {
	@Test
    void singletonWithPrototype() {
		AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(SingletonBean.class, PrototypeBean.class);
        
    	SingletonBean singletonBean1 = ac.getBean(SingletonBean.class);
        SingletonBean singletonBean2 = ac.getBean(SingletonBean.class);
        
		PrototypeBean prototypeBean1 = singletonBean1.getPrototypeBean();
		PrototypeBean prototypeBean2 = singletonBean1.getPrototypeBean();
       
		assertThat(prototypeBean1).isSameAs(prototypeBean2);
    }
}

IoC 컨테이너 전체를 주입받는 방법보다는 지정한 빈을 IoC 컨테이너에서 대신 찾아주는 방법을 사용하자.

ObjectProvider(ObjectFactory)

  • 스프링에서 제공해준다.
  • IoC 컨테이너에서 지정한 빈을 대신 찾아주는 방법이다.
  • 과거에는 ObjectFactory 가 있었는데, 여기에 편의 기능을 추가해서 ObjectProvider 가 만들어졌다.
@Scope("prototype")
class PrototypeBean {}

class SingletonBean {
	@Autowired
    private ObjectProvider<PrototypeBean> prototypeBeanProvider;
    
	public PrototypeBean getPrototypeBean() {
		return prototypeBeanProvider.getObject();
	}
}

class SingletonWithPrototypeTest {
	@Test
    void singletonWithPrototype() {
		AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(SingletonBean.class, PrototypeBean.class);
        
    	SingletonBean singletonBean1 = ac.getBean(SingletonBean.class);
        SingletonBean singletonBean2 = ac.getBean(SingletonBean.class);
        
		PrototypeBean prototypeBean1 = singletonBean1.getPrototypeBean();
		PrototypeBean prototypeBean2 = singletonBean1.getPrototypeBean();
       
		assertThat(prototypeBean1).isSameAs(prototypeBean2);
    }
}

JSR-330 Provider

  • 자바 표준이다.
  • IoC 컨테이너에서 지정한 빈을 대신 찾아주는 방법이다.
  • 스프링 부트 3.0 미만은 Javax Inject
  • 스프링 부트 3.0 이상은 Jakarta Dependency Injection
@Scope("prototype")
class PrototypeBean {}

class SingletonBean {
	@Autowired
    private Provider<PrototypeBean> provider
    
	public PrototypeBean getPrototypeBean() {
		return provider.get()
	}
}

class SingletonWithPrototypeTest {
	@Test
    void singletonWithPrototype() {
		AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(SingletonBean.class, PrototypeBean.class);
        
    	SingletonBean singletonBean1 = ac.getBean(SingletonBean.class);
        SingletonBean singletonBean2 = ac.getBean(SingletonBean.class);
        
		PrototypeBean prototypeBean1 = singletonBean1.getPrototypeBean();
		PrototypeBean prototypeBean2 = singletonBean1.getPrototypeBean();
       
		assertThat(prototypeBean1).isSameAs(prototypeBean2);
    }
}

그렇다면 어느 상황에 ObjectProvider(ObjectFactory)와 JSR-330 Provider를 사용해야 될까?

  • 스프링의 ObjectProvider(ObjectFactory) 는 DL을 위한 편의 기능을 많이 제공해주고 스프링 외에 별도의 의존성 추가가 필요 없어서 편리하다.
  • 만약 코드를 스프링이 아닌 다른 컨테이너에서도 사용해야 한다면 JSR-330 Provider 를 사용해야 한다.
  • 대부분 스프링이 더 다양하고 편리한 기능을 제공해주기 때문에, 특별히 다른 컨테이너를 사용할 일이 없다면, 스프링이 제공하는 기능을 사용하면 된다.

참고자료

profile
안녕하세요~ 👋, 대한민국 개발자 유호선입니다.

0개의 댓글

관련 채용 정보