woniper

prototype bean은 정말 항상 새로운 객체를 반환할까? 본문

Spring

prototype bean은 정말 항상 새로운 객체를 반환할까?

woniper1 2017. 2. 12. 20:19

  Bean Scope를 좀 살펴보다가 아래와 같은 테스트를 돌려보았다.

@Configuration
public class BeanScopeConfig {
    @Bean
    public AccountService accountService() {
        AccountService accountService = new AccountService();
        accountService.setAccountRepository(accountRepository());
        return accountService;
    }

    @Bean
    @Scope(scopeName = "prototype")
    public AccountRepository accountRepository() {
        return new AccountRepository();
    }
}

public class AccountService {

    private AccountRepository accountRepository;

    public AccountRepository getAccountRepository() {
        return accountRepository;
    }

    public AccountService setAccountRepository(AccountRepository accountRepository) {
        this.accountRepository = accountRepository;
        return this;
    }
}

  AccountService는 AccountRepository를 주입받으며, AccountRepository의 scope는 prototype이다.

DI 된 prototype bean 테스트

@Test
public void test() throws Exception {
    ApplicationContext applicationContext = new AnnotationConfigApplicationContext(BeanScopeConfig.class);
    AccountService accountService1 = applicationContext.getBean(AccountService.class);
    AccountService accountService2 = applicationContext.getBean(AccountService.class);

    assertThat(accountService1.getAccountRepository()).isNotSameAs(accountService2.getAccountRepository());
}

  그럼 이 테스트 코드는 통과 할까? 정답부터 말하면 통과하지 않는다.
  나는 singleton bean에 주입되는 prototype bean은 항상 다른 객체를 주입받는다고 생각했다. 그런데 그렇지 않다. BeanScopeConfig 클래스에 AccountService @Bean 설정을 보면 accountRepository 메소드를 통해 주입받는다. 이건 같은 객체를 주입받는다는 뜻이다.

ApplicationContext에서 가져온 prototype bean 테스트

@Test
public void test1() throws Exception {
    ApplicationContext applicationContext = new AnnotationConfigApplicationContext(BeanScopeConfig.class);
    AccountRepository accountRepository1 = applicationContext.getBean(AccountRepository.class);
    AccountRepository accountRepository2 = applicationContext.getBean(AccountRepository.class);

    assertThat(accountRepository1).isNotSameAs(accountRepository2);
}

  위 테스트는 통과한다. Spring Container인 ApplicationContext에서 getBean 메소드를 통해 가져오는 prototype bean은 항상 다른 객체를 반환한다. 그렇다면 내가 의도한대로 singleton bean에 prototype bean 주입 시 항상 다른 객체가 반환되게 하는 방법은 없을까?

Bean Lifecycle

Bean Lifecycle 관련된 인터페이스는 많다. 다 그렇진 않지만 Aware가 suffix로 붙은 인터페이스가 대부분 Lifecycle 관련된 인터페이스다.

BeanFactoryAware

public interface BeanFactoryAware extends Aware {
    void setBeanFactory(BeanFactory beanFactory) throws BeansException;
}

ApplicationContextAware

public interface ApplicationContextAware extends Aware {
    void setApplicationContext(ApplicationContext applicationContext) throws BeansException;
}

이번 글에서는 BeanFactoryAware로 예제를 설명해보겠다.

public class AccountService implements BeanFactoryAware {

    private BeanFactory beanFactory;
    
    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        this.beanFactory = beanFactory;
    }

    public AccountRepository getAccountRepository() {
        return beanFactory.getBean(AccountRepository.class);
    }

    public AccountService setAccountRepository(AccountRepository accountRepository) {
        this.accountRepository = accountRepository;
        return this;
    }
}

  BeanFactoryAware를 통해 getAccountRepository method 호출 시 getBean 메소드를 통해 항상 다른 객체를 반환하게 만들었다. 다시 test를 돌려보자. 정상적으로 돌아간다.

Comments