DI 처리 - 묵시적 DI

박준서·2024년 10월 16일
9

Web

목록 보기
8/16
post-thumbnail

명시적 DI

  • 이전 포스트에서 @Configuration과 @Bean을 이용해서 명시적인 Bean을 선언하는 방법을 봤다.
  • 명시적 DI의 경우 비즈니스 로직과 Bean 관리 로직이 잘 분리되어 관심사의 분리가 잘 되어 있다.
  • 하지만 이 명시적 DI는 Bean을 만들 때 마다 설정 파일에 명시적으로 Bean을 선언해주어야 한다.

묵시적 DI

  • 묵시적인 DI의 Bean 설정 방식은 Bean으로 사용할 클래스에 @Component라고 선언한다.
  • Bean에 대한 설정이 Bean 클래스 내부에 존재하기 때문에 관심사의 분리는 제대로 이루어지지 않는다.

📌 @Component 라고 선언된 클래스는 바로 Bean이 되는 것은 아니고, @ComponentScan을 통한 Scan 과정을 거쳐서 Bean으로 등록된다.

@Component

  • Bean으로 사용될 클래스에 선언하는 Annotation
@Component
public class LPhone{}

@Component

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Indexed
public @interface Component {
	String value() default ""; // 생성되는 Bean의 이름을 재정의 하려는 경우에 사용
}
  • 스프링은 Type 기반으로 Bean을 관리하는데 동일한 type의 Bean이 2개 이상인 경우 name이 중요한 구분자 역할을 한다.
  • @Component를 이용해 만들어진 Bean의 이름은 클래스 이름이 Pascal case인 경우 camel case로 변경해서 사용하고, 그렇지 않을 경우는 클래스 이름을 그대로 사용한다.
    pascal case : IronMan => ironMan
    pascal case 가 아닌 경우 : SPhone => SPhone
  • 이름 기반으로 Bean을 구별해야 한다면 자동으로 생성되는 이름에 의존하지 말고, value 속성을 이용해서 Bean에 적절한 이름을 명시적으로 주자.

@Autowired

  • 단순히 Bean 객체를 생성하는 것 뿐만 아니라, 의존성에 대한 주입 즉 DI 과정이 필요하다.
  • @Component는 단지 Bean 객체를 생성하는 것이다. 주입을 처리 해야 한다.
  • 이러한 주입을 처리할 때 사용되는 것이 @Autowired 이다.

@Autowired

@Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Autowired {

	/**
	 * Declares whether the annotated dependency is required.
	 * <p>Defaults to {@code true}.
	 */
	boolean required() default true;

}
  • @Autowired는 생성자, 필드, 메서드, 애너테이션에 선언할 수 있다.
  • 특징
    • Type 기반으로 Bean을 자동 주입하며 해당 타입의 Bean이 반드시 하나만 존재해야 한다.
      • 만약, Type 충돌이 발생할 경우에는 이름 기반의 사용을 위해서는 @Qualifier를 사용한다.
    • 생성자와 메서드에 사용 시 파라미터가 모두 Spring Bean이어야 한다. 이 때 여러 개의 파라미터에 대해 모두 주입이 이루어진다.
    • 한 클래스에 @Autowired가 적용되는 생성자는 최대 하나만 가능하며 메서드는 생성자와 달리 여러번 사용 가능하다.
      • 생성자가 1개의 경우 어차피 그 생성자가 호출되어야 객체가 생성되므로 @Autowired를 생략해도 된다.

@ComponentScan

  • Bean을 생성했다면 @Configuration에서 @ComponentScan을 통해 해당 Bean을 찾아주는 과정이 필요하다.
@Configuration
@ComponentScan({"com.hello.phone"})
public class PhoneConfig {
	
}

@Qualifier

  • 묵시적인 Bean 등록 방식에서 @Autowired는 type 기반으로 Bean을 자동 주입한다. 이름 기반으로 주입될 빈을 한정짓기 위해서는 @Qualifier 애너테이션이 필요하다

Qualifier

@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.TYPE, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Qualifier {

	String value() default "";

}
  • Qualifier는 Autowired과 함께 사용되며 value 속성에 주입할 Bean의 이름을 적어주면 된다.

📌 주의 ) Qualifier의 Target을 보면 Constructor가 없다. @Autowired가 생성자에 사용되기 때문에 같이 쓰는 실수를 할 때가 많은데, 생성자의 파라미터에 적용해주어야 한다.

적용코드

	@Autowired
	public Avengers(@Qualifier("ironMan") IronMan iman, Hulk hulk) {
		this.iman = iman;
		this.hulk = hulk;
	}

Stereotype Annotation

  • 스프링에서 @Component를 용도에 따라 미리 여러 타입으로 정형화 해 놓은 것
    • @Repository
    • @Service
    • @Controller
    • @Configuration
    • @Component

Bean 주입 방법의 비교

public class Avengers {
	@Autowired
	private IronMan iman; // filed 주입
	
	@Autowired
	public Avengers(@Qualifier("ironMan") IronMan iman, Hulk hulk) { // 생성자 주입
		this.iman = iman;
		this.hulk = hulk;
	}
	
	@Autowired
	public void setHulkBuster(HulkBuster hb) { // setter 주입
		this.hb = hb;
	}
}
  • 생성자 주입
    • 가장 권장되는 방식
    • 많은 경우 field를 Blank final로 선언하고 생성자에 주입하는데 lombok의 @RequiredArgsConstructor을 사용한다.
  • Setter 주의
    • 선택적인 의존성을 가진 Bean의 주입에 적합하다
  • 필드 주입
    • 가장 간단하지만 불변성을 보장하지 않으므로 권장하지 않는다.
profile
Back-End Developer

0개의 댓글

관련 채용 정보