우와한 개발자

[Spring] Bean 등록과 의존성 주입(DI) 설정 - 어노테이션(Annotation) 방식

by 우와한개발자

1. Bean 등록

  • 클래스에 어노테이션을 붙여 자동으로 Bean을 등록하는 방식.
  • 단, 어노테이션만으로는 동작하지 않고 어느 패키지를 스캔할지 알려주는 컴포넌트 스캔(Component Scan)설정이 반드시 필요함.
  • XML을 쓰면 <context:component-scan>, Java Config를 쓰면 @ComponentScan로 설정함
  • 코드 중심의 설정으로 가독성이 좋고 실무에서 가장 많이 사용함.
  • 스프링부트 기반 프로젝트에서 Java Config 방식과 어노테이션 방식이 많이 사용됨

1) 컴포넌트 스캔 설정

(1) XML로 컴포넌트 스캔 설정 방식

  • namespace에 context 추가
  • < context:component-scan base-package=””> 태그 추가
<?xml version="1.0" encoding="UTF-8"?>
<!-- namespace context 추가 -->
<beans  ...
	xmlns:context="http://www.springframework.org/schema/context"
	...
	xsi:schemaLocation=" ...
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd">
		
<!-- applicationContext.xml -->
<context:component-scan base-package="com.example.myapp"/>

 

(2) Java Config로 컴포넌트 스캔 설정 방식

@Configuration
@ComponentScan("com.example.myapp")
public class AppConfig {
    // @Bean 메서드 없이 @ComponentScan으로만 Bean 자동 등록되어 안써도됨
}

 

2) Bean 등록 어노테이션

  • Bean의 이름은 해당 어노테이션이 붙은 클래스 이름에서 첫문자만 소문자로 해서 생성됨
어노테이션 용도 패키지
@Component 일반 Bean 등록 org.springframework.stereotype.Component
@Service 비즈니스 로직 계층 org.springframework.stereotype.Service
@Repository 데이터 접근 계층 org.springframework.stereotype.Repository
@Controller 웹 요청 처리 계층 org.springframework.stereotype.Controller
@Service
public class MemberService {
    // 자동으로 Bean 등록됨
}

 

2. 의존성 주입 DI 설정

1) 의존성 주입 DI 어노테이션

어노테이션 용도 패키지
@Autowired 타입 기준으로 Bean 자동 주입 org.springframework.beans.factory.annotation.Autowired
@Qualifier 같은 타입의 Bean이 여러 개일 때 이름으로 구분 org.springframework.beans.factory.annotation.Qualifier
@Resource 이름 기준으로 Bean 주입 javax.annotation.Resource 또는 jakarta.annotation.Resource
@Inject @Autowired와 유사한 의존성 주입 javax.inject.Inject 또는 jakarta.inject.Inject

 

(1) @Autowired와 @Inject

  • 타입을 기준으로 의존성 주입
@Service
public class MemberService {

    @Autowired
    private MemberRepository memberRepository;
    
    @Inject
    private OrderRepository orderRepository;
}

 

(2) @Qualifier

  • 같은 타입의 Bean이 2개 이상인 경우 변수의 이름과 같은 Bean을 찾아 연결
@Component("mainRepository")
public class MemberRepositoryImpl implements MemberRepository {
}
@Service
public class MemberService {

    @Autowired
    public MemberService(@Qualifier("mainRepository") MemberRepository memberRepository) {
        this.memberRepository = memberRepository;
    }
}

 

(3) @Resource

  • name 속성을 기준으로 주입
@Service
public class MemberService {

    @Resource(name = "mainRepository")
    private MemberRepository memberRepository;
}

 

2) 생성자 주입 방식

  • 생성자가 하나뿐이면 @Autowired 생략 가능
  • 스프링이 Bean을 생성할 때 @Autowired가 붙은 생성자를 찾아서 딱 하나만 실행
  • 생성자가 여러 개인 경우 스프링이 어떤 생성자로 주입할지 알 수 없으므로, 사용할 생성자 하나에 @Autowired를 붙여서 명시해야 함
@Service
public class OrderService {
    private final MemberService memberService;
    private final String name;

    @Autowired // 이 생성자로 주입하라고 명시
    public OrderService(MemberService memberService) {
        this.memberService = memberService;
    }
	
    public OrderService(MemberService memberService, String name) {
        this.memberService = memberService;
        this.name = name;
    }
}
구분 위치 방식
생성자 하나 생략 가능 생성자 주입
생성자 여러 개 원하는 생성자에 붙임 생성자 주입
필드에 붙임 필드에 붙임 필드 주입 (별개)

 

3) setter 주입 방식

  • setter 메서드에 @Autowired를 붙이면 스프링이 자동으로 찾아서 주입.
@Service
public class OrderService {
    private MemberService memberService;

    @Autowired
    public void setMemberService(MemberService memberService) {
        this.memberService = memberService;
    }
}

 

4) 필드 주입 방식 (비권장)

  • 주입받을 각 필드마다 @Autowired를 붙여서 주입받는 방식
  • 객체를 먼저 생성한 다음 리플렉션(Reflection)으로 @Autowired가 붙은 필드에 직접 값을 주입하는 방식
  • 코드가 간결하지만 테스트가 어렵고 순환 참조 감지가 안 돼 권장하지 않음

 

❓리플렉션(Reflection)
자바의 접근 제한(private, protected)을 무시하고 강제로 접근할 수 있게 해주는 기능

@Service
public class OrderService {

    @Autowired
    private MemberService memberService;
}
스프링이 OrderService 객체를 기본 생성자로 생성
        ↓
@Autowired 붙은 필드를 찾음
        ↓
리플렉션으로 private 필드에 직접 값을 주입

블로그의 정보

우와한개발자 님의 블로그

우와한개발자

활동하기