DI, Bean, Proxy

Leo·2023년 1월 27일
1

DI (Dependency Injection)

DI란?

의존 주입을 이해하려면 '의존'이 무엇인지 알아야 한다.
의존은 객체 간의 의존을 의미한다.

public calss MemberRegisterService{

	private MemberDao memberDao = new MemeberDao();
    
    public void regist(RegisterRequest req){
    	
        //중복 검사로직 생략
        
        Member newMember = new Member(
        	req.getEmail(), req.getPassword(), req.getName(), LocalDateTime.now());
            
        memberDao.insert(newMember);        

위 코드처럼 한 클래스가 다른 클래스의 메서드를 실행할 때 이를 '의존'한다고 표현한다.
MemberRegisterService 클래스가 MemberDao 클래스에 의존한다고 표현할 수 있다.
(MemberDaoinsert 메서드명 변경시 MemberRegisterService에도 영향 전파 -> 의존)

MemberRegisterService 클래스에서 의존하는 MemberDao 객체를 직접 생성하기 때문에 MemberRegisterServce 객체를 생성하는 순간 MemberDao 객체도 함께 생성된다.

클래스 내부에서 직접 의존 객체를 생성하는 것이 쉽지만 유지보수 관점에서 문제점을 유발할 수 있다.
의존 객체를 변경해야 한다면 의존하고 있는 모든 클래스를 수정해야 하기 때문이다.

따라서 의존하는 객체를 직접 생성하는 대신 전달받는 방식인 DI를 사용한다.

main 메서드에서 의존 대상 객체를 생성하고 생성자 or setter로 주입해도 되지만 객체를 생성하고 의존 객체를 주입하는 클래스를 따로 작성하는 방법이 좀 더 나은 방법이다.(main에서는 assembler클래스에서 get을 통해 객체를 가져와 사용)

이렇게 따로 작성된 클래를 '클래스 조립기'(Assembler)로 부른다.

클래스 조립기를 통해 객체 변경이 발생해도 변경할 코드가 한 곳에서 관리 할 수 있다.

스프링의 DI

스프링은 Assembler 클래스처럼 필요한 객체를 생성하고 의존 주입을 해준다.
객체의 라이프 사이클을 관리하는 스프링 컨테이너에 객체를 등록한다.
이렇게 등록된 객체를 스프링 빈이라 한다.

수동 빈 등록

어떤 객체를 생성하고 주입할지 정의한 설정 정보가 필요하다.

@Configuration - 스프링 설정 클래스 애노테이션
@Bean - 해당 메서드가 생성한 객체를 스프링 빈으로 설정 애노테이션(메서드명을 빈 이름으로 사용)

위 애노테이션으로 클래스, 스프링 컨테이너를 생성한다.

자동 빈 등록

@Component 애노테이션으로 스캔을 대상을 지정하고 빈을 자동 등록 할 수 있다.(클래스명을 소문자로 변환 후 빈 이름으로 사용)

@Component("직접이름등록") 으로 빈 이름을 직접 지정할 수 있다.

의존관계 자동 주입

@Autowired 애노테이션을 사용해 타입이 같은 빈을 주입한다.

생성자, 수정자(setter), 필드 를 통해 주입이 가능하다.

생성자 주입을 사용하면 final 키워드를 사용해서 값이 설정되지 않는 오류를 컴파일 시점에 막을 수 있고, 불변객체로 설계할 수 있기 때문에 생성자 주입을 권장한다.

상속 관계

의존 주입, 빈 조회시 하위 타입빈도 함께 조회가 되기 때문에 에러가 발생할 수 있기 때문에 @Qualifiy를 사용해 해결해야 한다.

Proxy

@Configuration    
public class AppConfig {

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

memberDao가 2번 객체생성되는 자바코드가 실행되지만, 동일한 객체 반환이 보장된다.

@Bean 을 사용하여 Bean객체 생성을 선언하고, @Configuration을 통해 바이트조작 라이브러리(CGLIB)를 사용하여 Bean 생성하고, 이미 존재한는 Bean인 경우, 기존 객체를 리턴하며 singleton을 유지한다.

CGLIB 확인해 보기
System.out.println(ac.getBean(AppConfig.class).getClass()); 을 실행했을 때,
@Configuration 제거 -> class spring.core.AppConfig
@Configuration 설정 -> class spring.core.AppConfigEnhancerBySpringCGLIBEnhancerBySpringCGLIB11083357

CGLIB를 통해 생성된 Bean은 AppConfig를 상속받아 생성되었기 때문에, getBean(AppConfig.class)로 조회가 가능하다

@Configuration 없이 Bean을 등록하고 사용한다면 스프링 컨테이너가 관리하는 스프링빈을 사용한다고 할 수 없다.

0개의 댓글