[Java Spring] @Autowired

khj·2025년 3월 23일

Java

목록 보기
3/11
post-thumbnail

1. @Autowired란?

@Autowired는 Spring의 의존성 주입(DI)을 자동으로 수행하는 어노테이션이다.
스프링 컨테이너가 관리하는 빈(Bean) 중에서 타입(Type)이나 이름을 기준으로 적절한 객체를 찾아 자동으로 주입해 준다.

@Service
public class MyService {
    private final UserRepository userRepository;

    @Autowired
    public MyService(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    public void execute() {
        userRepository.findAll();
    }
}

@Autowired를 사용하면 개발자가 직접 객체를 생성하지 않아도 Spring이 UserRepository 객체를 찾아서 자동으로 주입해 준다.


2. @Autowired의 동작 방식

의존성 주입을 위한 Bean 검색 방법

스프링 컨테이너는 @Autowired가 붙은 필드나 생성자를 만나면, 아래 순서로 적절한 빈을 찾아서 주입한다.

(1) 타입(Type) 기반 검색

  • 먼저 동일한 타입의 빈을 찾아서 주입

(2) 빈 이름 기반 검색 (@Qualifier 사용 시)

  • 동일한 타입의 빈이 여러 개 있을 경우, 빈 이름을 기준으로 선택

(3) @Primary가 설정된 빈이 우선 적용

  • @Primary 어노테이션이 붙어 있으면 해당 빈을 우선적으로 주입

3. @Autowired 사용 방법

Spring에서 @Autowired를 사용할 때는 3가지 방법이 있다.

(1) 필드 주입 (Field Injection)

@Service
public class MyService {
    @Autowired
    private UserRepository userRepository; 

    public void execute() {
        userRepository.findAll();
    }
}

❌ 비추천하는 방식

  • Spring이 없는 환경에서 테스트하기 어려움
  • final을 사용할 수 없어 불변성을 보장할 수 없음
  • 순환 참조 문제 발생 가능

(2) 생성자 주입 (Constructor Injection) → ✅ 가장 권장됨

@Service
public class MyService {
    private final UserRepository userRepository;

    @Autowired
    public MyService(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    public void execute() {
        userRepository.findAll();
    }
}

✅ 추천하는 방식

  • 객체가 반드시 필요함을 보장할 수 있음 (final 사용 가능)
  • 테스트가 용이 (Spring 없이도 객체를 주입 가능)
  • 순환 참조가 발생하면 애플리케이션 실행 시점에 오류를 발견할 수 있음

📌 Spring 4.3 이상에서는 생성자가 하나뿐이라면 @Autowired 생략 가능

@Service
public class MyService {
    private final UserRepository userRepository;

    public MyService(UserRepository userRepository) { // @Autowired 생략 가능
        this.userRepository = userRepository;
    }
}

(3) 세터 주입 (Setter Injection)

@Service
public class MyService {
    private UserRepository userRepository;

    @Autowired
    public void setUserRepository(UserRepository userRepository) {
        this.userRepository = userRepository;
    }

    public void execute() {
        userRepository.findAll();
    }
}

✅ 장점

  • 선택적으로 의존성을 주입할 수 있음 (필수 의존성이 아닐 경우)

❌ 단점

  • 객체를 생성한 후에도 변경 가능하여 불변성이 보장되지 않음
  • 필수 의존성이 빠질 가능성이 있음

4. @Autowired와 @Qualifier, @Primary

(1) 같은 타입의 빈이 여러 개 있을 때 (@Qualifier)

@Component("userRepository1")
public class UserRepository1 implements UserRepository { }

@Component("userRepository2")
public class UserRepository2 implements UserRepository { }

@Service
public class MyService {
    private final UserRepository userRepository;

    @Autowired
    public MyService(@Qualifier("userRepository1") UserRepository userRepository) {
        this.userRepository = userRepository;
    }
}
  • @Qualifier("userRepository1")을 사용하면 특정 빈을 명시적으로 주입할 수 있다.

(2) 기본적으로 사용할 빈을 지정하기 (@Primary)

@Component
@Primary
public class UserRepository1 implements UserRepository { }

@Component
public class UserRepository2 implements UserRepository { }

@Service
public class MyService {
    private final UserRepository userRepository;

    @Autowired
    public MyService(UserRepository userRepository) {
        this.userRepository = userRepository;
    }
}
  • @Primary가 붙은 UserRepository1이 기본적으로 주입된다.

5. @Autowired(required = false)로 선택적 주입

@Service
public class MyService {
    private final UserRepository userRepository;

    @Autowired(required = false)
    public MyService(UserRepository userRepository) {
        this.userRepository = userRepository;
    }
}
  • 해당 타입의 빈이 없을 경우에도 오류 없이 실행된다.
  • 단, userRepository가 null일 수도 있기 때문에 Optional을 사용하면 더 안전하다.
@Autowired
private Optional<UserRepository> userRepository;

6. @Autowired와 순환 참조 문제

순환 참조 발생 예시

@Service
public class ServiceA {
    private final ServiceB serviceB;

    @Autowired
    public ServiceA(ServiceB serviceB) {
        this.serviceB = serviceB;
    }
}

@Service
public class ServiceB {
    private final ServiceA serviceA;

    @Autowired
    public ServiceB(ServiceA serviceA) {
        this.serviceA = serviceA;
    }
}
  • ServiceA → ServiceB → ServiceA 형태로 무한 루프가 발생하여 애플리케이션이 실행되지 않는다.
  • 생성자 주입을 사용할 경우 순환 참조 오류를 앱 실행 시점에 바로 감지할 수 있다.
  • 해결 방법:
    • Setter 주입 또는 @Lazy 사용
    • 서비스 구조를 변경하여 순환 참조 제거
profile
Spring, Django 개발 블로그

0개의 댓글