Spring | DI

DoItDev·2021년 9월 18일
1
post-thumbnail

DI (Dependency Injection)

Note:

  • DI 이란 Dependcy Injection 의 약자로 직역하면 의존성 주입 이라고 한다.
  • IOC 의 한 패턴으로 사용이된다.
  • 각 Class 사이에 필요로 하는 의존관계가 있다면 이를 컨테이너가 자동적으로 연결시켜 주는 것으로 각 Class 사이의 의존관계를 Bean 설정 정보를 바탕으로 컨테이너가 자동적으로 연결해 주는 것이다.
public class Store {

    private Item item;

    public Store(Item item) {
        this.item = item;
    }
   
}

생성자 기반의 의존성 주입

설정할 의존성을 나타내는 인수를 각각 생성자를 호출한다.
스프링에서는 기본적으로 유형에 따라 각 인수를 해결하고 그 뒤에 속성 이름과 명확성을 색인이 온다.

@Configuration
public class ExConfiguration {

    @Bean
    public Item getItem() {
        return new Item("사과 나무");
    }

    @Bean
    public Store getStore() {
        return new Store(getItem());
    }
   
}

생성자 기반의 의존성 테스트를 위하여 아래와 같이 테스트 케이스를 작성하였다.
ExConfiguration class 를 Autowired 로 주입해주고 케이스를 만들었다.

@SpringBootTest
class CoreApplicationTests {
    @Autowired
    ExConfiguration exConfiguration;

    @Test
    @DisplayName("DI Example1")
    void loadByExConfiguration() {
        Store store = exConfiguration.getStore();
        String itemTitle = store.getItem().getTitle();
        Assertions.assertEquals(itemTitle, "사과나무");
        return;
    }   
}

테스트 케이스가 성공했다. 이렇게 보면 우리가 주입 시킨 아이템이라는 객체에 사과나무라는 변수 title 이 정상적으로 같은 객체를 보고 있다고 할 수 있다.


Setter 기반의 종속성 주입

Setter 기반 DI의 경우 컨테이너는 인수가 없는 생성자 또는 인수가 없는 정적 팩터리 메소드를 호출하여 빈을 인스턴스화한 후 클랙스의 setter 메소드를 호출하여서 사용한다.

@Bean
public Store getStore1() {
    Store store = new Store();
    store.setItem(getItem());
    return store;
}

필드 기반의 종속성 주입

필드 기반의 종속성 주입의 경우 @Autowired 주석으로 종속성 주입이 가능하며

@RequiredArgsConstructor 요즘 들어서 이어노테이션을 많이 사용을 한다.

@RequiredArgsConstructor 의 경우 final 키워드를 붙여주어야 한다.


@Service
@RequiredArgsConstructor
public class UserService {

@Autowired
ExConfiguration exConfiguration;

private final UserRepository repository;

}

Spring Bean Example

Bean 주입 방식 중 어노테이션 주입 방식으로 spring 에서 configration class 를 만들어서 테스트를 해보려고 한다.

Bean 이 싱글톤 생성이 맞는지 그리고 싱글톤 생성이 맞다면 진짜로 맞는지 체크를 하기위해 예제 코드를 만들어 보았다.

예제를 이해하기 위해서는 application context 라는 것이 무엇인지 알아야 한다.

Application Context:

  • Bean Factory 를 주입 받은 spring 에서 제공하는 bean factory라고 할 수 있다.
  • 별도의 설정 정보를 참고 하여 IOC 를 적용하고 빈의 생성, 관계설정 등의 제어 작업을 총괄한다.
@Configuration
public class CoreConfiguration {

    @Bean
    public MemberVO memberVO() {
        MemberVO memberVO = new MemberVO();
        memberVO.setName("릴보이");
        memberVO.setNickName("작은거인");
        return memberVO;
    }

    @Bean(name = "hello")
    public MemberVO memberVO1() {
        return new MemberVO("페노메코", "페노메코1");
    }

    @Bean
    public PrintA printA() {
        return new PrintA();
    }

    @Bean
    public PrintB printB() {
        return new PrintB();
    }

}

configuration class 에서 memberVO 라는 Bean 그리고 hello라는 bean 과 함께 print 인터 페이스에서 적용하기 위한 printA, printB를 주입 해주었다.

이렇게 주입된 bean들은 application context에서 가지고 올 수 있으며 또한 @Autowired 어노테이션을 사용하여서 사용이 가능하다. (이론적으로)


// print interface
public interface Print {
    String sout();
}

// print을 상속한 printA class
public class PrintA implements Print {
    @Override
    public String sout() {
        return "I am Print A";
    }
}

// print을 상속한 printB class
public class PrintB implements Print {
    @Override
    public String sout() {
        return "I am Print B";
    }
}

// 테스트를 위한 MeberVO
@Getter
@NoArgsConstructor
@AllArgsConstructor
public class MemberVO {

    @Setter
    private String name;

    @Setter
    private String nickName;

    @Autowired
    @Qualifier("printA")
    private Print print;

    public String getPrint() {
        return print.sout();
    }

    public MemberVO(String name, String nickName) {
        this.name = name;
        this.nickName = nickName;
    }

}

위의 코드처럼 데이터 class를 만들 었다.

MeberVO에서 @Qualifier 어노테이션을 사용을 하면 빈을 강제로 주입이 가능하다

이렇게 데이터 class 를 구성을 하였다.

@Slf4j
@RestController
@RequiredArgsConstructor
@RequestMapping("/start")
public class StartResource {

    private final ApplicationContext context;

    private final MemberVO hello;

    private final MemberVO memberVO;

    @GetMapping("")
    public String showByWebStart() {
        return "SUCCESS";
    }

    @GetMapping("/test1")
    public MemberVO showByMember() {
        return context.getBean("memberVO", MemberVO.class);
    }

    @GetMapping("/test2")
    public MemberVO showByPrint() {
        return hello;
    }

    @GetMapping("/test3")
    public boolean checkByBean() {
        MemberVO beanMemberVO = context.getBean("memberVO", MemberVO.class);
        return beanMemberVO.equals(memberVO);
    }

}

컨트롤러의 경우 위처럼 작성을 했는데 필드에서 @Autowired 를 사용을 안하는 이유는 스프링 부트와 AWS로 혼자 구현하는 웹 서비스 라는 책에서 @RequiredArgsConstructor 를 선호해서 사용하라는 말을해서 습관처럼 사용을 한다.

필드에서 ApplicationContext 를 선언을하면 Bean을 가지고 올 수 있다.

그리고 Bean Name 으로 각각의 MemberVO 을 선언 해준다. java configuartion naming을 선언이 가능하는데 Bean 어노테이션에서 설정을 안해주면 메소드 명으로 bean naming으로 가지고 온다.

비교하는 api를 통해서 보았을 때 같은 객체이고 싱글톤 패턴을 사용을 한다는 것을 알 수 있다.

객체를 디버깅 하여서 보았는데 객체의 참조 주소가 같아서 같은 객체이다 라고 할 수 있다.

profile
Back-End Engineer

0개의 댓글