Context에서 @Autowired한 Bean이 null일 때 주입된 경우

eora21·2023년 12월 17일
0

문제를 어떻게 발견했나요?

토비의 스프링 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);
    }
    ...
}

mailSenderProductionAppContextLocalAppContext에 기입되어 있습니다.

@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를 생성하는 타이밍보다 늦기 때문임을 확인했습니다.

즉, 현재 코드를 기반으로는 userLevelUpgradePolicymailSender를 필요로 한다는 조건을 보일 수 없기 때문입니다.

대처법은 무엇인가요?

userLevelUpgradePolicymailSender를 필요로 한다는 것을 IoC 컨테이너에게 알리면 됩니다.

@Bean
public UserLevelUpgradePolicy userLevelUpgradePolicy(UserDao userDao, MailSender mailSender) {
    return new NormalLevelUpgradePolicy(userDao, mailSender);
}

Bean 생성 메서드의 파라미터에 필요한 Bean을 선언하여 '주입받아야 할 Bean'을 알리면 IoC 컨테이너가 이에 맞게 Bean을 생성하고 주입해 줍니다.

profile
나누며 타오르는 프로그래머, 타프입니다.

0개의 댓글