의존 자동 주입

컴공생의 코딩 일기·2023년 1월 4일
0

스프링

목록 보기
5/16
post-thumbnail

의존 자동 주입

의존 대상을 설정 코드에서 직접 주입하지 않고 스프링이 자동으로 의존하는 빈 객체를 주입 해주는 기능을 자동 주입이라고 한다.

  • 의존 주입을 하지 않을 경우
// 빈 설정
@Configuration
public class Config(){
    @Bean
   public MemberDao memberDao(){
       return new MemberDao();
   }
    @Bean
    public RegisterService registerService(){
        return new RegisterService(memberDao()); // 의존 주입
    }
}
// 의존 주입
public Class RegisterService(){
    MemberDao memberDao;
    
    public RegisterService(MemberDao memberDao){
        this.memberDao = memberDao; //의존 주입
    }
}
    
  • 의존 주입을 사용했을 경우
// 빈 설정
@Configuration
public class Config(){
    @Bean
   public MemberDao memberDao(){
       return new MemberDao();
   }
    @Bean
    public RegisterService registerService(){
        return new RegisterService(); 
    }
}
// 의존 주입
public Class RegisterService(){
    @Autowired// 필드에 사용할 경우
    MemberDao memberDao; 
    
    //--------------------------------------------------
    @Autowired // 메서드에 사용할 경우
    MemberDao memberDao; 
    public RegisterService(MemberDao memberDao){
        this.memberDao = memberDao;
    }
}

@Autowired 어노테이션

  • @Autowired 어노테이션을 주입하려는 필드나 메서드에 사용하면 스프링이 자동으로 Context에 등록된 빈을 찾아서 할당해 준다.

  • @Autowired 어노테이션 주의할 점

    • 일치하는 빈이 없거나 주입 대상에 빈이 두 개 이상일 경우 오류가 발생한다.
    // 빈 설정
    @Configuration
    public class Config(){
        @Bean
       public MemberDao memberDao(){
           return new MemberDao();
       }
        @Bean
        public RegisterService registerService1(){
            return new RegisterService(); 
        }
         @Bean
        public RegisterService registerService2(){
            return new RegisterService(); 
        }
        // 오류 발생
    }

@Qualifier 어노테이션을 이용한 의존 객체 선택

자동 주입 가능한 빈이 두 개 이상이면 자동 주입할 빈을 지정할 수 있는 방법이 필요하다. 이때 @Qualifier 어노테이션을 사용하면 자동 주입 대상 빈을 한정할 수 있다.

@Bean
@Qualifier("register")
public RegisterService registerService1(){
    return new RegisterService(); 
}
@Bean
public RegisterService registerService2(){
    return new RegisterService(); 
}
  • 위에 코드 처럼 같은 값을 가진 빈이 두 개 이상을 경우 빈에 등록하려는 빈 어노테이션에 @Qualifier 어노테이션을 지정하면 스프링에서 자동으로 @Qualifier 어노테이션을 빈으로 등록한다.
  • 기본적으로 빈으로 등록한 빈의 이름은 해당 클래스의 이름으로 등록된다. 막약 @Qualifier 어노테이션을 사용해 빈을 등록한 경우라면 @Qualifier에서 지정한 이름이 빈 이름으로 등록된다.

상위/하위 타입 관계와 자동 주입

public class MemberSummaryPrinter extends MemberPrinter {
    @Override
    public void print(Member member) {
        System.out.printf(
                "회원 정보: 이메일=%s, 이름=%s\n", 
                member.getEmail(), member.getName());
    }
}
@Configuration
public class AppCtx {
    // ... 생략
    @Bean
    public MemberPrinter memberPrinter1() {
        return new MemberPrinter();
    }
    @Bean
    public MemberSummaryPrinter memberPrinter2() {
        return new MemberSummaryPrinter();
    }
}

위에 코드를 살펴보면 MemberSummaryPrinter 메서드는 MemberPrinter를 상속 받고 있다. AppCtx에서 memberPrinter1, memberPrinter2가 각각MemberPrinter, MemberSummaryPrinter를 빈에 등록하면 익셉션이 발생한다. 왜냐하면 MemberSummaryPrinter는 MemberPrinter 클래스를 상속 받고 있기 때문에 MemberSummaryPrinter는 MemberPrinter에도 할당 받을 수 있기 때문에 스프링에서는 어떤 클래스를 빈에 등록할지 모르기 때문이다.

해결 방법

  1. @Qualifier 어노테이션 사용
@Bean
@Qualifier("printer")
public MemberPrinter memberPrinter1() {
    return new MemberPrinter();
}

@Bean
@Qualifier("summaryPrinter")
public MemberSummaryPrinter memberPrinter2() {
    return new MemberSummaryPrinter();
}

각 메서드에 @Qualifier 어노테이션을 사용해 메서드를 지정할 경우 스프링에서 어떤 클래스를 빈으로 등록할지 명확하게 알 수 있기 때문에 익셉션 오류를 피할 수 있다.

  1. Memberlistprinter가 MemberSummaryPrinter를 사용하도록 수정
public class MemberListPrinter {
 private MemberDao memberDao;
 private MemberPrinter printer;
 // ...
@Autowired
public void setMemberPrinter(MemberSummaryPrinter printer) {
    this.printer = printer;
}

MemberListPrinter 메서드에 직접적으로 MemberSummaryPrinter를 주입 받도록 설정하면 된다. MemberSummaryPrinter는 단 하나만 존재하므로 스프링에서 빈으로 등록이 가능하다.

@Autowired 어노테이션의 필수 여부

public class MemberPrinter {
    private DateTimeFormatter dateTimeFormatter;
    public void print(Member member) {
        if (dateTimeFormatter == null) {
            System.out.printf(
                    "회원 정보: 아이디=%d, 이메일=%s, 이름=%s, 등록일=%tF\n", 
                    member.getId(), member.getEmail(),
                    member.getName(), member.getRegisterDateTime());
        } else {
            System.out.printf(
                    "회원 정보: 아이디=%d, 이메일=%s, 이름=%s, 등록일=%s\n", 
                    member.getId(), member.getEmail(),
                    member.getName(), 
                    dateTimeFormatter.format(member.getRegisterDateTime()));
        }
    }

     @Autowired
    public void setDateFormatter(DateTimeFormatter dateTimeFormatter) {
        this.dateTimeFormatter = dateTimeFormatter;
    }

 // ...

MemberPrinter 객체는 setDateFormatter 메서드가 null 인지에 여부에 따라 날짜 형식을 바꿔 출력하는 객체이다.

@Autowired 어노테이션는@Autowired 어노테이션이 붙인 타입에 해당하는 빈이 null일 경우(존재하지 않을 경우) 익셉션을 발생한다.

  1. required = false

    1. @Autowired가 필수로 주입될 필요가 없을 경우에는 @Autowired(required = false)로 설정하면 익셉션을 발생시키지 않고 자동 주입을 하지 않는다.
  2. Optional

    1. 스프링 5부터는 자바 8의 Optional을 사용할 수 있다. ( @Autowired도 null을 처리할 수 있다.)
    @Autowired
    public void setDateFormatter(Optional<DateTimeFormatter> formatterOpt) {
        if (formatterOpt.isPresent()) {
            this.dateTimeFormatter = formatterOpt.get();
        } else {
           this.dateTimeFormatter = null;
        }
    }
  3. @Nullable 어노테이션

    1. @Autowired 어노테이션을 붙인 메셔드에서 @Nullable 어노테이션을 의존 주입 대상 파라미터에 붙이면, 스프링 컨테이너는 세터 메서드를 호출할 때 자동 주입할 빈이 존재하면 해당 빈을 인자로 전달하고, 존재하지 않을 경우 인자로 null을 전달한다.
    @Autowired
    public void setDateFormatter(@Nullable DateTimeFormatter dateTimeFormatter) {
        this.dateTimeFormatter = dateTimeFormatter;
    }

required = false와 @Nullable 차이점

@Autowired 어노테이션의 required 속성을 false로 할 때와 차이점은 @Nullable 어노테이션을 사용하면 자동 주입할 빈이 존재하지 않아도 메서드가 호출 된다는 점이다. @Autowired 어노테이션의 경우 required 속성이 false인데 대상 빈이 존재하지 않을 경우 세터 메서드를 호출하지 않는다. 즉 null 값을 전달하지 않는다.

profile
더 좋은 개발자가 되기위한 과정

0개의 댓글