[스프링] 스프링5 프로그래밍 입문 - 4장 의존 자동 주입

June·2021년 5월 24일
0

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

ChangePasswordService

public class ChangePasswordService {
    
    @Autowired
    private MemberDao memberDao;

    public void changePassword(String email, String oldPwd, String newPwd) {
        Member member = memberDao.selectByEmail(email);
        if (member == null) {
            throw new MemberNotFoundException();
        }

        member.changePassword(oldPwd, newPwd);
        memberDao.update(member);
    }

    public void setMemberDao(MemberDao memberDao) {
        this.memberDao = memberDao;
    }
}

@Autowired 애노테이션을 붙이면 설정 클래스에서 의존을 주입하지 않아도 된다.
스프링이 해당 타입의 빈 객체를 찾아서 필드에 할당한다.

@Autowired 애노테이션을 memberDao 필드에 붙였으므로 AppCtx 클래스의 @Bean 설정 메서드에서 의존을 주입하는 코드를 삭제하면 된다.

@Autowired 애노테이션은 메서드에도 붙일 수 있다.

MemberInfoPrinter

public class MemberInfoPrinter {

    private MemberDao memDao;
    private MemberPrinter printer;

    public void printMemberInfo(String email) {
        Member member = memDao.selectByEmail(email);
        if (member == null) {
            System.out.println("데이터 없음\n");
            return;
        }
        printer.print(member);
        System.out.println();
    }

    @Autowired
    public void setMemberDao(MemberDao memberDao) {
        this.memDao = memberDao;
    }

    @Autowired
    public void setPrinter(MemberPrinter printer) {
        this.printer = printer;
    }
}

AppCtx

@Configuration
public class AppCtx {

	...

    @Bean
    public MemberInfoPrinter infoPrinter() {
        MemberInfoPrinter infoPrinter = new MemberInfoPrinter();
        return infoPrinter;
    }
}

빈 객체의 메서드에 @Autowired 애노테이션을 붙이면 스프링은 해당 메서드를 호출한다. 이때 메서드 파라미터 타입에 해당하는 빈 객체를 찾아 인자로 주입한다.

MemberRegisterService

public class MemberRegisterService {

    @Autowired
    private MemberDao memberDao;
    
    public MemberRegisterService() {
        
    }

    public MemberRegisterService(MemberDao memberDao) {
        this.memberDao = memberDao;
    }

	...
}

MemberListPrinter

public class MemberListPrinter {

    private MemberDao memberDao;
    private MemberPrinter printer;

    public MemberListPrinter() {

    }

    ...
    
    @Autowired
    public void setMemberDao(MemberDao memberDao) {
        this.memberDao = memberDao;
    }

    @Autowired
    public void setMemberPrinter(MemberPrinter printer) {
        this.printer = printer;
    }
}

AppCtx

@Configuration
public class AppCtx {

    @Bean
    public MemberDao memberDao() {
        return new MemberDao();
    }

    @Bean
    public MemberRegisterService memberRegSvc() {
        return new MemberRegisterService(memberDao());
    }

    @Bean
    public ChangePasswordService changePwdSvc() {
        return new ChangePasswordService();
    }

    @Bean
    public MemberPrinter memberPrinter() {
        return new MemberPrinter();
    }

    @Bean
    public MemberListPrinter listPrinter() {
        return new MemberListPrinter();
    }

    @Bean
    public MemberInfoPrinter infoPrinter() {
        return new MemberInfoPrinter();
    }
}

일치하는 빈이 없는 경우
@Autowired 애노테이션을 적용한 대상에 일치하는 빈이 없으면 에러가 난다.

@Autowired 애노테이션을 붙인 주입 대상에 일치하는 빈이 두 개 이상이어도 에러가 난다.

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

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

@Configuration
public class AppCtx {

    @Bean
    @Qualifier("printer")
    public MemberDao memberPrinter1() {
        return new MemberPrinter();
    }

    @Bean
    @Qualifier("printer")
    public MemberDao memberPrinter2() {
        return new MemberPrinter();
    }
}

MemberListPrinter

public class MemberListPrinter {

    private MemberDao memberDao;
    private MemberPrinter printer;

    ...

    @Autowired
    @Qualifier("printer")
    public void setMemberPrinter(MemberPrinter printer) {
        this.printer = printer;
    }
}

setMemberPrinter() 메서드에 @Autowired 애노테이션을 붙였으므로 MemberPrinter탕비의 빈을 자동 주입한다. 이때 @Qualifier 값이 "printer"이므로 한정 값이 "printer"인 빈을 의존 주입 후보로 사용한다.

빈 이름과 기본 한정자

빈 설정에 @Qualifier 애노테이션이 없으면 빈의 이름을 한정자로 지정한다.

``` java
@Configuration
public class AppCtx {

    @Bean
    public MemberPrinter printer() {
        return new MemberPrinter();
    }

    @Bean
    @Qualifier("mprinter")
    public MemberPrinter printer12() {
        return new MemberPrinter();
    }

    @Bean
    public MemberInfoPrinter2 infoPrinter() {
        MemberInfoPrinter2 infoPrinter = new MemberInfoPrinter2();
        return infoPrinter;
    }
}

printer()메서드로 정의한 빈의 한정자는 빈 이름인 "printer"가 된다. printer2 빈은 @Qualifier 애노테이션 값인 "mprinter"가 한정자가 된다.

public class MemberInfoPrinter2 {
    @Autowired
    private MemberPrinter printer;

@Autowired 애노테이션도 @Qualifier 애노테이션이 없으면 필드나 파라미터 이름을 한정자로 사용한다. 예를 들어 위의 코드는 pinter 필드에 일치하는 빈이 두 개 이상 존재하면 한정자로 필드 이름인 "printer"를 사용한다.

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

자동 주입할 대상이 필수가 아닌 경우네는 @Autowired 애노테이션의 required 속성을 다음과 같이 false로 지정하면 된다. @Autowired 애노테이션의 required 속성을 false로 지정하면 매칭되는 빈이 없어도 익셉션이 발생하지 않으며 자동 주입을 수행하지 않는다. 스프링 5 버전부터는 @Autowired 애노테이션의 required 속성을 false로 하는 대신 의존 주입 대상에 자바 8의 Optional을 사용해도 된다.

@Autowired(required = false)

public class MemberPrinter {

    private DateTimeFormatter dateTimeFormatter;

    ...

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

Optional

Optional 이용하기

public class MemberPrinter {

    private DateTimeFormatter dateTimeFormatter;

    ...
    public void setDateFormatter(Optional<DateTimeFormatter> dateTimeFormatter) {
        if (dateTimeFormatter.isPresent()) {
            this.dateTimeFormatter = dateTimeFormatter.get();
        } else{
            this.dateTimeFormatter = null;
        }
    }
}

자동 주입 대상 타입이 Optional인 경우, 일치하는 빈이 존재하지 않으면 값이 없는 Optional을 인자로 전달하고, 일치하는 빈이 존재하면 해당 빈을 값으로 갖는 Optional을 인자로 전달한다.

@Nullable

public class MemberPrinter {

    private DateTimeFormatter dateTimeFormatter;

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

@Autowired 애노테이션을 붙인 세터 메서드에서 @Nullable 애노테이션을 의존 주입 대상 파라미터에 붙이면, 스프링 컨테이너는 세터 메서드를 호출할 때 자동 중비할 빈이 존재하면 해당 빈을 인자로 전달하고, 존재하지 않으면 인자로 null을 전달한다.

자동 주입과 명시적 의존 주입 간의 관계

설정 클래스에서 세터 메서드를 통해 의존을 중비해도 해당 세터 메서드에 @Autowired 애노테이션이 붙어 있으면 자동 주입을 통해 일치하는 빈을 주입한다. 따라서 @Autowired 애노테이션을 사용했다면 설정 클래스에서 객체를 주입하기보다는 스프링이 제공하는 자동 주입 기능을 사용하는 편이 낫다.

0개의 댓글