@Autowired의 원리와 스프링 팀에서 @Autowired의 사용을 지양하라고 하는 이유? (추가 수정 필요..)

김성혁·2022년 9월 28일
0

스프링의 철학은 POJO(Plain Old Java Object)로 스프링 프레임워크를 나중에 걷어내더라도 코드가 정상적으로 동작할 수 있도록 프렘웍에 의존적인 코드를 작성하지 않는 것인데 그런 의미에서 @Autowired 보다는 다른 방법을 이용하는 것이 좋다고 공부했었다. 그럼 왜 @Autowired 어노테이션을 이용한 DI를 지양해야 하는지 @Autowired의 원리에 대해서 알아보자.

  • @Autowired 애노테이션을 이용하면 설정자 메서드를 이용하지 않고도 스프링 프레임워크가 설정 파일을 통해 설정자 메서드 대신 속성을 주입해 준다. (스프링 설정 파일을 보고 자동으로 속성의 설정자 메서드에 해당하는 역할을 해주겠다는 의미)

  • @Autowired는 type 기준으로 매칭을 하기 때문에 같은 타입을 구현한 클래스가 여러 개 있다면 그때 bean 태그의 id로 구분해서 매칭한다. (id 매칭 보다 type 매칭이 우선이다)
    - @Resource의 경우 type과 id 가운데 매칭 우선순위는 id가 높다. id로 매칭할 빈을 찾지 못한 경우 type으로 매칭할 빈을 찾게 된다.

자바 표준인 @Resource를 쓰는 것이 유리(내가 공부했던 책의 필자의 생각)

@Autowired는 type을 기준으로 매칭하기 때문에 같은 타입을 구현한 클래스가 여러 개 있다면 단일 빈을 매칭하기에 부적절합니다.

How to solve such problem:

  • 두 개의 구현 클래스가 존재하고, @Autowired 어노테이션을 사용하는 경우 userService를 UserService Impl01과 같이 특정한 구현 클래스의 이름으로 변경할 수 있습니다.
  • 또한, @Qualifier(value = “userService Impl01”) 과 같이 추가하여 주입하려는 구현 클래스를 지정할 수 있습니다.
  • @Resource 어노테이션을 사용하여 구현 클래스의 이름을 수동으로 지정

슈퍼 클래스가 같은 두 개 이상의 서브 클래스들을 구별하기 위해서 스프링 컨텍스트가 서브 클래스를 찾을 수 있는 특정한 이름을 지정해야 합니다.

언제 주입이 되는가? (@Autowired 원리)

객체가 생성되면, 객체 변수에 할당될 때 주입됩니다.

  • InstantiationAwareBeanPostProcessor
    • 인스턴스화 전후에 객체를 관리하는 기능
  • BeanPostProcessor
    • 초기화 전후에 객체를 관리하는 기능
  • BeanFactoryAware
    - BeanFactory를 언제든지 가져오는 기능

컨테이너가 초기화되면, post processor의 초기화가 나머지 사용자 정의 빈(사용자 정의 서비스, 컨트롤러 등등..)의 초기화들보다 먼저 일어난다. 사용자 정의 빈 초기화는 AbstractApplicationContext 의 finishBeanFactoryInitialization 메서드에 의해 실행된다.


FinishBeanFactory Initialization (beanFactory)

– > beanFactory. preInstantiate Singletons () 

– > getBean (beanName) 

– > doGetBean (beanName) > to line 317 of AbstractBeanFactory (beanName, mbd, args) to create bean instances 

– > to line 503 of AbstractBeanFactory CreateBean (beanTombuse, args) 

– > and then to line 533 of AbstractBeanFactory AutowireableB In line 543 of eanFactory, instance Wrapper = createBeanInstance (beanName, mbd, args) has created the bean instance, except that instance Wrapper is a wrapped bean whose properties have not yet been assigned the actual value 

– > and then comes to line 555, applyMergedBeanDefinitionPostProcessors (mbd, beanType, beanName).

위의 흐름은 모든 뒤따라오는 빈들을 배치하는 것입니다. 프로세서는 그것을 꺼내 beanName 이라는 클래스의 변수를 InjectionMetadata의 주입된 Elements 세트에 캡슐화하고, 나중에 취득해, 인스턴스를 1개씩 작성하고, reflection을 통해 대응하는 클래스에 주입시킵니다.

남은 내용은 내가 생각했을 때 너무 DeepDive 하는 느낌이 들어 링크를 옮긴다. 나중에 궁금증이 더 생길 때 찾아보기로..

Spring Source Analysis: @Autowire Annotation Principle Analysis - Develop Paper

요약을 하자면 컨테이너가 시작되고 객체에 값을 할당할 때 @Autowired 어노테이션을 만나면 post-processor 매커니즘을 사용하여 애트리뷰트의 인스턴스를 생성한다. 그런다음 reflection 매커니즘을 사용하여 인스턴스화된 속성을 객체에 할당합니다.


의존성 주입의 유형

  • Contructor-based
    • 생성자에 @Autowired 어노테이션을 붙인다. (스프링 4.3 부터는 생략 가능)
    • 의존성 주입을 할 객체를 메서드 파라미터에 포함시킨다.
    • 의존성 주입을 시킬 빈이 IoC Container에 등록되었다는 것을 전제로 한다.
    • Contructor-based injection의 가장 큰 이점은 주입될 필드를 final로 선언할 수 있다는 것입니다. 이것은 필수 의존성에 편리합니다.
  • Setter-based
    • 세터 메서드에 @Autowired 어노테이션을 붙인다.
    • 빈의 의존성 주입을 위해 인수가 없는 생성자를 사용하거나 인수가 없는 정적 팩토리 메서드를 통해 빈이 인스턴스화되면 스프링 컨테이너는 세터 메서드를 호출
  • Field-based
    - 필드 / 프로퍼티에 @Autowired 어노테이션을 붙인다. 스프링 컨테이너는 해당 클래스가 인스턴스화되면 필드에 세팅한다.

@Autowired를 통해 DI 코드가 깔끔해 보이는데 Field Injection을 지양하라고 하는 이유는 뭘까?

  • 불변 필드 선언을 허락하지 않는다.
    • final / immutable 으로 선언된 필드에서는 동작하지 않는다.
    • 불변 의존성을 선언하는 유일한 방법은 Constructor-based
  • 단일 책임 원칙을 위반하기 쉽다.
    • 클래스에 @Autowired 필드 injection을 하는 것은 정말 쉬운 일이다. → 반면 생성자 기반 의존성 주입을 사용하면 의존성이 추가됨에 따라 코드가 냄새나 보이기 때문에 문제가 있다는 신호를 금방 알아챌 수 있다.
  • 의존성 주입 컨테이너와 긴밀하게 연결되었다.
    • 의존성 주입 설계 패턴은 클래스 의존성의 생성을 클래스 자체로부터 분리하고, 이 책임을 클래스 인젝터로 전송하고, 프로그램 설계를 느슨하게 결합하며, 단일 책임과 의존성 역전의 원칙(SOLID) 따를 수 있습니다
    • 필드를 autowired 시킴으로써 클래스 인젝터가 결합되어 스프링 컨테이너 외부에서 클래스를 쓸 수 없게 만듭니다.
      • 단위 테스트 시 애플리케이션 컨테이너 외부에서 클래스를 사용하려는 경우, 스프링 컨테이너를 사용하여 강제로(의도적으로) 클래스를 인스턴스화 해야합니다.
  • Hidden dependencies
    • 의존성 주입 패턴을 사용할 때 영향을 받는 클래스는 생성자에서 필수 의존성을 노출하거나 세터를 사용하여 선택적 의존성을 공개 인터페이스를 사용하여 의존성을 명확하게 노출해야 합니다.
    • 의존성을 외부 세계에 노출시켜야 하는데 필드 기반 의존성 주입은 이러한 의존성을 외부 세계로부터 숨깁니다.

<원문 참조>

Spring Source Analysis: @Autowire Annotation Principle Analysis - Develop Paper

[스프링 핵심기술] - @Autowired

Field injection is not recommended - Spring IOC - Marc Nuri

0개의 댓글