토비의 스프링 3.1에서 기존의 Context들을 역할 기반으로 쪼개고, @Import와 @Autowired를 사용해 다른 Bean에 주입하던 중 Bean이 아직 생성되지 않아 null이 주입되는 경우가 생겼습니다.
@Configuration
@EnableTransactionManagement
@ComponentScan(basePackages = "user")
@Import({SqlServiceContext.class, ProductionAppContext.class, LocalAppContext.class})
public class AppContext {
@Autowired
UserDao userDao;
@Autowired
MailSender mailSender;
@Bean
public UserLevelUpgradePolicy userLevelUpgradePolicy() {
return new NormalLevelUpgradePolicy(userDao, mailSender);
}
...
}
mailSender
는 ProductionAppContext
와 LocalAppContext
에 기입되어 있습니다.
@Configuration
@Profile("local")
public class LocalAppContext {
@Bean
public MailSender mailSender() {
JavaMailSenderImpl mailSender = new JavaMailSenderImpl();
mailSender.setHost("localhost");
return mailSender;
}
}
그러나 테스트에 @ActiveProfiles("local")
을 적용해도 mailSender
가 null이 주입되어 테스트가 실패했습니다.
java.lang.NullPointerException: Cannot invoke "org.springframework.mail.MailSender.send(org.springframework.mail.SimpleMailMessage)" because "this.mailSender" is null
처음에는 Context의 분리가 문제라고 생각했습니다. 그러나 Bean의 위치를 옮겨보는 등 많은 테스트를 해보며 결론적으로는 @Autowired로 mailSender
를 생성하는 타이밍이 userLevelUpgradePolicy
를 생성하는 타이밍보다 늦기 때문임을 확인했습니다.
즉, 현재 코드를 기반으로는 userLevelUpgradePolicy
가 mailSender
를 필요로 한다는 조건을 보일 수 없기 때문입니다.
userLevelUpgradePolicy
가 mailSender
를 필요로 한다는 것을 IoC 컨테이너에게 알리면 됩니다.
@Bean
public UserLevelUpgradePolicy userLevelUpgradePolicy(UserDao userDao, MailSender mailSender) {
return new NormalLevelUpgradePolicy(userDao, mailSender);
}
Bean 생성 메서드의 파라미터에 필요한 Bean을 선언하여 '주입받아야 할 Bean'을 알리면 IoC 컨테이너가 이에 맞게 Bean을 생성하고 주입해 줍니다.