목차

스프링5; DI의 실제

🗓️

의존이란?

  • 서비스 객체가 있을때 비즈니스 로직을 처리하는 경우 저장소의 객체를 직접 들여다 볼 수 있다.
public class StudentRegisterService {
    private StudentRepositody studentRepository = new StudentRepository();
    //...
    public void register(Request request)
        //..
        Student student = studentRepository.findByName(studentName);
        if(student != null){
            //..
        }
}

위와 같은 코드가 있을때 서비스 객체를 생성한다면 자연스럽게 저장소 객체도 생성된다.

  • 이 경우를 두고 ‘서비스가 저장소에 의존된다’라고 말한다.
  • 의존경우가 있을 경우엔 어떤 일이 생길까?

위와같이 모든 서비스 코드에서 의존관계가 종속된다면 의존에 관련된 코드를 수정 할 때 마다 서비스코드도 전부 수정해야한다.

DI를 통한 의존관계 처리

  • 생성자를 통해 객체를 받아서 처리하면 의존관계를 해결할 수 있다.
public class StudentRegisterService {
    private StudentRepositody studentRepository;

    public class StudentRegisterService(StudentRepositody studentRepository){
        this.studentRepository = studentRepository; // << 이와같이 Constructor로 받으면 해결된다.
    }
    //...
    public void register(Request request)
        //..
        Student student = studentRepository.findByName(studentName);
        if(student != null){
            //..
        }
}
  • 스프링의 DI는 여러가지 방법이 있는데 크게 3가지만 아래 정리해보겠다.

Constructor 방식

  • 서비스코드는 위와 같고 bean 설정은 아래와 같다
//...
@Bean
public MemberDao memberDao() {
    return new MemberDao();
}

@Bean
public StudentRegisterService studentRegisterService() {
    return new StudentRegisterService(memberDao());
}
//...

객체를 생성하는 각각의 메소드를 만들어 주입하면 된다.

  • 생성자 방식의 특징
    1. 주입받은 객체를 완전한 상태로 사용할 수 있다. → 모든 인자를 반드시 받아야 하므로
    2. 대신 받아야할 객체가 많을 때는 일일이 필요한 객체를 확인해야하는 단점이 있다

Setter방식

  • 아래와 같이 서비스 코드에 setter메소드를 추가한다
//...
public void setMemberDao(MemberDao memberDao) {
    this.memberDao = memberDao;
}

public void setPrinter(MemberPrinter printer) {
    this.printer = printer;
}
//...
  • 그리고 아래와 같이 bean설정에 넣는다.
@Bean
public MemberDao memberDao() { 
    return new MemberDao();
}

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

    @Bean
    public MemberInformationPrinter memberInformationPrinter() {
        // setter 방식
        MemberInformationPrinter informationPrinter = new MemberInformationPrinter();
        informationPrinter.setMemberDao(memberDao()); // << 의존 주입
        informationPrinter.setPrinter(memberPrinter()); // << 의존 주입
        return informationPrinter;
    }

constructor방식과 다르게 객체를 먼저 생성한 다음 setter에 객체를 전달한다.

  • setter방식의 특징
    1. 코드를 따라 자연스럽게 필요한 객체를 모두 알 수 있다.
    2. 필요한 의존객체를 모두 전달하지 않아도 객체가 생성되기 때문에 NPE에 유의해야 한다.

일반 상수값 전달 방식

  • 꼭 객체가 아니더라도 일반 상수값을 전달 할 수도 있다.
@Bean
public VersionPrinter versionPrinter() {
    VersionPrinter versionPrinter = new VersionPrinter();
    versionPrinter.setMajorVersion(5); // <<
    versionPrinter.setMinorVersion(0); // <<
    return versionPrinter;
}

@Autowired

  • 스프링 빈에 의존하는 다른 빈을 자동으로 주입해준다. 아래 bean 설정의 선언부분에 어노테이션을 붙이면
public MemberDao memberDao() {
        return new MemberDao();
    }
// 이 코드를 아래와 같이 바꾸면 객체를 자동으로 @Bean어노테이션을 사용하는 메소드에 붙여준다.

@Autowired
private MemberDao memberDao;

// ...
// 아래와 같이 bean 생성 코드 안에서 객체를 선언할 필요가 없다
@Bean
public MemberListPrinter listPrinter() {
    return new MemberListPrinter(memberDao(), memberPrinter());
    // 이 코드 역시 아래와 같이 바꿀 수 있다.
    return new MemberListPrinter(memberDao, memberPrinter); //<<<
}

@Import

  • 두개 이상의 빈 설정 코드를 사용한다면 다음과 같이 최상위 빈 설정 코드에 선언하면 된다
@Configuration
@Import({AppConf1.class, AppConf2.class})
public class AppConfigurationMaster {
    //...
}