[Spring] Component Scan, DI autowired

2022. 11. 30. 17:40·JVM/Spring

 

When to use component scan?

코드레벨에서 설정 정보가 없어도 자동으로 스프링 빈을 자동 등록하려 할 때

 

 

How to use?

class 어노테이션으로 '@Component' 혹은 그 자식 어노테이션을 붙여준다.

이때, 생성자에는 '@Autowired' 등으로 자동으로 DI가 일어날 수 있도록 매핑해주는 것이 좋다.

 

@Component
public class RateDiscountPolicy implements DiscountPolicy {

    private final int discountPercent = 10;

	// .. 생략
}

 

스프링 빈의 기본 이름은 클래스명을 사용하되 맨 앞글자만 소문자를 사용한다. (PascalCase -> camelCase) 

빈 이름 직접 지정시 @Component(새로운_빈_이름) 이런 식으로 지정해줄 수 있다.

 

 

 

프로젝트 최상단에
설정정보 클래스(@SpringBootApplication)
를 두는 것이 국룰이되었다.

 

package milkcoke.core;
// 스프링 부트의 초기화시 자동으로 생성되는 클래스가 
// 프로젝트 최상단에 위치한다.

// @SpringBootApplication 은 Component 스캔을 포함한다.
@SpringBootApplication
public class CoreApplication {

	public static void main(String[] args) {
		SpringApplication.run(CoreApplication.class, args);
	}

}

 

 

동작 과정

Component Scan

@Component 어노테이션이 붙은 모든 클래스를 뒤져서 Spring Container 에 등록

이 때, camelCase 로 자동으로 변경하여 등록

 

AutoWired

모든 빈을 '타입'으로 조회하여 자동 의존관계 주입, 동일 타입이 여러개 있을 경우 충돌이 발생할 수 있음.

부모타입 지정시 당연히 예하의 자식 타입은 모두 스캔됨.

생성자 파라미터 개수와 상관없이 알아서 찾아서 자동 주입.

 

 

 

다양한 의존관계 주입 방법

  1. 생성자 주입
  2. setter 주입
  3. 필드 주입
  4. 일반 메소드 주입

1. 생성자로 주입

생성자로 딱 1회 호출되는 것이 보장된다.

When to use?

불변/필수 의존관계에 사용

거의 항상.

 

생성자가 유일한 경우 @AutoWired 어노테이션 생략이 가능하다.

bean 을 등록하며 의존관계 주입도 동시에 일어난다. (어차피 Spring Container 가 스프링 빈 객체를 싱글톤으로 생성하여 등록해야하므로)

@Service
public class OrderServiceImpl implements OrderService{
    private final MemberRepository memberRepository;
    private final DiscountPolicy discountPolicy;
    
    // 생성자 의존관계 주입
    @Autowired
    public OrderServiceImpl(MemberRepository memberRepository, DiscountPolicy discountPolicy) {
        this.memberRepository = memberRepository;
        this.discountPolicy = discountPolicy;
    }
}

 

 

2. setter  주입

Spring container 가

(1) bean 을 우선 등록하고

(2) 의존관계를 찾아 알아서 주입해준다. (`@Autowired` 를 보고)

 

When to use?

선택/변경 가능성이 있는 의존관계에 사용.

memberRepository, discountPolicy 가 존재하지 않을때도 등록하도록 할 때

 

@Autowired 의 주입 대상이 존재하지 않아도 동작하게 끔 하는 옵션
(required = false)
@Service
public class OrderServiceImpl implements OrderService{
 	private MemberRepository memberRepository;
    private DiscountPolicy discountPolicy;

	// 필수가 아닌 선택, 변경값으로 지정 가능.
	@Autowired(required = false)
    public void setMemberRepository(MemberRepository memberRepository) {
        this.memberRepository = memberRepository;
    }
    
	@Autowired(required = false)
    public void setDiscountPolicy(DiscountPolicy discountPolicy) {
        this.discountPolicy = discountPolicy;
    }
    
}

 

 

3. 필드 주입

간결하지만, 권장하지 않는 방식이다.

Spring Container 도움 없이 순수 자바/코틀린 기반으로 dummy 데이터로 값을 대체해서 커스텀할 수 있는 여지가 없어진다. (별도로 setter 를 만들어야만 한다.)

 

 

When to use?

DI 프레임워크 내에서만 동작하는 코드

애플리케이션과 실제 관계 없는 스프링에 종속적인 코드 테스트

필드 주입은 쓰지마라.
public class OrderServiceImpl implements OrderService{
    @Autowired private MemberRepository memberRepository;
    @Autowired private DiscountPolicy discountPolicy;
    
    // .. 생략
}

테스트 코드 레벨에서 memberRepository, discountPolicy 를 임의지정 하는게 불가능해진다.

=> 안티패턴이므로 사용을 지양할 것

 

 

4. 일반 메소드 주입

생성자 주입과 setter 주입 방법이 제공하는 이점이 더 크기 때문에 거의 사용하지 않는다.

@Service
public class OrderServiceImpl implements OrderService{
    private MemberRepository memberRepository;
    private DiscountPolicy discountPolicy;

	// 일반 메소드에 주입하는 방식
    // Spring Bean 등록 => DI 주입 by normal method by the `@Autowired` annotation.
    @Autowired
    public void init(MemberRepository memberRepository, DiscountPolicy discountPolicy) {
        this.memberRepository = memberRepository;
        this.discountPolicy = discountPolicy;
    }
}

 

 

Spring Container 가 관리하는 Bean 만
의존관계 주입이 발생한다.

Autowired 옵션

자동 주입 대상을 옵션으로 처리하는 방법

  • Autowired(required = false)
    의존관계가 존재하지 않을 때 메소드 호출자체가 일어나지 않는다.
  • @Nullable annotation
    주입 대상이 존재하지 않을 때 null 값을 대입한다.
  • Optional<T> 
    주입 대상이 존재하지 않을 때 Optional.empty 값을 대입한다.
public class AutowiredTest {

    static class TestBean {

        // 의존관계가 존재하지 않을 때, 메소드 자체가 아예 호출되지 않는다.
        @Autowired(required = false)
        public void setNoBean1(Member member1) {
            System.out.println("member1 = " + member1);
        }

        @Autowired
        public void setNoBean2(@Nullable Member member2) {
            System.out.println("member2 = " + member2);
        }

        @Autowired
        public void setNoBean2(Optional<Member> member3) {
            System.out.println("member3 = " + member3);
        }
    }

    @Test
    void autowiredOptionTest() {
        // TestBean 클래스에 대한 BeanDefinition 메타 데이터를 생성하고 Spring Bean 을 Spring Container 에 직접 등록
        // 현재는 아무런 Bean 정보가 등록되있지 않으므로 깡통 빈이 등록됨.
        var ac = new AnnotationConfigApplicationContext(TestBean.class);

    }
}

 

 

Member.java

Member 클래스는 순수 자바 코드 도메인 클래스일 뿐이기 때문에 의존관계가 존재하지 않는다.

public class Member {
    private Long id;
    private String name;
    private Grade grade;

    public Member(Long id, String name, Grade grade) {
        this.id = id;
        this.name = name;
        this.grade = grade;
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Grade getGrade() {
        return grade;
    }

    public void setGrade(Grade grade) {
        this.grade = grade;
    }
}

 

 

 

왜 DI 중 생성자 주입이어야 하나요?

생성자 DI 를 해야하는 이유

  • 대부분의 애플리케이션에서 의존관계가 런타임시에 변경되야 하는 일은 없기 때문이다.
  • 수정자 주입 사용시 public 으로 setter 메소드를 열어둬야한다. (변경 위험 도래)
  • 객체 생성시 딱 1회만 호출되기 때문에 불변하게 설계할 수 있음.
  • field 에 final 키워드를 사용하여 개발자의 실수를 방지할 수 있다.
    생성자를 통한 모든 멤버를 강제 입력하게하여 의존성 주입 누락을 방지한다.

컴파일 전 단계에서 에러를 미리잡을 수 있어 개발자의 실수를 방지할 수 있는 DI 방식이다.

 

저작자표시 (새창열림)

'JVM > Spring' 카테고리의 다른 글

[SpringBoot] Naming methods in each layer  (0) 2023.02.09
[Spring] Library 살펴보기 및 스프링 환경설정  (0) 2022.12.12
[Spring] Bean Scope  (0) 2022.12.07
[Spring] 빈 생명주기 콜백  (0) 2022.12.06
[Spring] Autowired 사용시 주의 (Feat. Custom Annotation)  (0) 2022.12.01
'JVM/Spring' 카테고리의 다른 글
  • [Spring] Library 살펴보기 및 스프링 환경설정
  • [Spring] Bean Scope
  • [Spring] 빈 생명주기 콜백
  • [Spring] Autowired 사용시 주의 (Feat. Custom Annotation)
M_Falcon
M_Falcon
  • M_Falcon
    Falcon
    M_Falcon
  • 전체
    오늘
    어제
    • 분류 전체보기 (432)
      • Web (16)
        • Nodejs (14)
        • Javascript (23)
        • FrontEnd (4)
      • DataBase (39)
        • Fundamental (1)
        • Redis (4)
        • PostgreSQL (10)
        • NoSQL (4)
        • MySQL (9)
        • MSSQL (3)
        • Error (4)
      • Algorithm (79)
        • Algorithm (문제풀이) (56)
        • Algorithm (이론) (23)
      • JVM (65)
        • Spring (13)
        • JPA (5)
        • Kotlin (13)
        • Java (24)
        • Error (7)
      • 기타 (70)
        • Kafka (3)
        • Kubernetes (3)
        • Docker (13)
        • git (19)
        • 잡동사니 (27)
      • 재테크 (11)
        • 세무 (4)
        • 투자 (3)
        • 보험 (0)
      • BlockChain (2)
        • BitCoin (0)
      • C (32)
        • C (10)
        • C++ (17)
        • Error (3)
      • Low Level (8)
        • OS (3)
        • 시스템 보안 (5)
      • 네트워크 (3)
      • LINUX (30)
        • Linux (26)
        • Error (4)
      • 저작권과 스마트폰의 이해 (0)
      • 생각 뭉치 (6)
      • 궁금증 (2)
      • Private (4)
        • 이직 경험 (0)
        • 꿈을 찾아서 (1)
      • Android (21)
        • OS (4)
  • 블로그 메뉴

    • 홈
    • WEB
    • 알고리즘
    • DataBase
    • Linux
    • Mobile
    • C
    • 방명록
  • 링크

    • github
  • 공지사항

  • 인기 글

  • 태그

    docker
    android
    Bitcoin
    백준
    PostgreSQL
    알고리즘
    algorithm
    Programmers
    ubuntu
    Spring
    javascript
    linux
    Git
    database
    JPA
    java
    Kotlin
    프로그래머스
    kafka
    C++
  • hELLO· Designed By정상우.v4.10.3
M_Falcon
[Spring] Component Scan, DI autowired
상단으로

티스토리툴바