목차
✨ 의존관계를 자동으로 주입할 객체가 Spring Bean으로 등록되어 있어야 `@Autowired`로 주입이 가능합니다.
생성자 주입
생성자를 통해 의존성을 주입하는 방법입니다. (가장 추천되는 방식)
특징
최초에 한번 생성된 후 값이 수정되지 못합니다.(불변, final)
예시 코드
더보기
올바른 코드
@Component
public class HelloController {
// 불변
private final HelloService helloService;
// 생성자 주입
@Autowired
public HelloController(HelloService helloService) {
this.helloService = helloService;
}
public void greet() {
helloService.sayHello();
}
}
@Service
public class HelloService {
public void sayHello() {
System.out.println("Hello, Spring!");
}
}
@SpringBootApplication
public class SpringConstructorInjectionExample {
public static void main(String[] args) {
SpringApplication.run(SpringConstructorInjectionExample.class, args);
}
}
- 생성자가 하나인 경우 `@Autowired` 생략이 가능합니다.
올바르지 못한 예시 코드
@Component
public class HelloController {
// 불변
private final HelloService helloService;
private final HelloRepository helloRepository;
public HelloController(HelloService helloService, HelloRepository helloRepository) {
this.helloService = helloService;
this.helloRepository = helloRepository;
}
public HelloController(HelloService helloService) {
this.helloService = helloService;
}
public void greet() {
helloService.sayHello();
}
}
- 생성자가 두 개 이상인 경우, `@Autowired`를 생략하면 둘 중 어떤 생성자를 사용해야 하는 지 Spring은 알지 못합니다.
- 따라서 두 개 이상의 생성자가 있을 경우, 어떤 생성자를 주입에 사용할지 명확히 지정하려면 `@Autowired`를 붙여야 합니다.
Setter 주입
Setter 메서드를 통해 의존성을 주입하는 방법입니다
특징
- 의존성을 선택적으로 주입할 수 있어, 필수가 아닌 의존성에 적합합니다.
- 생성자 주입과 달리, 생성 시점에 모든 필드가 초기화되지 않아 객체가 완전히 초기화되지 않은 상태로 존재할 수 있습니다.
- 어떤 필드가 필수이고 어떤 필드가 선택인지 명확하지 않을 수 있습니다.
예시 코드
더보기
public interface HelloService {
void sayHello();
}
@Primary
@Service("english")
public class EnglishHelloService implements HelloService {
@Override
public void sayHello() {
System.out.println("Hello!");
}
}
@Service("korean")
public class KoreanHelloService implements HelloService {
@Override
public void sayHello() {
System.out.println("안녕하세요!");
}
}
@Component
public class HelloController {
private final Map<String, HelloService> helloServiceMap;
private HelloService helloService;
public HelloController(Map<String, HelloService> helloServiceMap, HelloService helloService) {
// Spring이 모든 HelloService 구현체를 자동으로 주입하면서, Bean 이름을 키로 가지는 Map을 만들어줍니다.
this.helloServiceMap = helloServiceMap;
this.helloService = helloService; // 기본값
}
public void changeLanguage(String lang) {
if (helloServiceMap.containsKey(lang)) {
helloService = helloServiceMap.get(lang);
} else {
System.out.println("지원하지 않는 언어입니다.");
}
}
public void greet() {
helloService.sayHello();
}
}
@SpringBootApplication
public class RuntimeInjectionExample {
public static void main(String[] args) {
SpringApplication.run(RuntimeInjectionExample.class, args);
}
@Bean
CommandLineRunner run(HelloController controller) {
return args -> {
controller.greet(); // Hello!
controller.changeLanguage("korean");
controller.greet(); // 안녕하세요!
controller.changeLanguage("english");
controller.greet(); // Hello!
};
}
}
- 이렇게 런타임에도 의존성을 바꿀 수 있습니다.
- 기본적으로 의존성이 주입되는 것은 `@Primary`가 붙은 `EnglishHelloService`입니다.
- 시작될 때, HelloController의 helloService 변수에 `EnglishHelloService`가 주입됩니다.
- changeLanguage("korean")가 호출되어 `KoreanHelloService`가 주입됩니다.
- changeLanguage("english")가 호출되어 `EnglishHelloService`가 주입됩니다.
필드 주입
필드에 직접적으로 주입하는 방법입니다. (가장 추천되지 않음)
특징
- 별도의 생성자나 setter 메서드 없이 필드 위에 `@Autowired`만 붙이면 됩니다.
- private 필드에도 주입 가능합니다.
- Reflection을 사용해 접근 제한자 무시하고 주입할 수 있습니다.
- 의존성이 private 필드에 주입되므로, 단위 테스트에서 직접 필드 값을 바꾸기 어렵습니다. (테스트 시 번거로움)
- final 키워드를 사용할 수 없으므로, 의존성이 변경될 가능성이 있습니다.
- IoC 컨테이너 없이 객체 생성 시 의존성 주입이 되지 않습니다. (new)
예시 코드
더보기
@Component
public class MyService {
@Autowired
private SomeDependency someDependency;
public void doSomething() {
someDependency.action();
}
}
@Component
public class SomeDependency {
public void action() {
System.out.println("Dependency action!");
}
}
- MyService 클래스 안에 `@Autowired`로 직접 필드 주입합니다.
- `someDependency`는 private 필드인데도 스프링이 리플렉션으로 주입해 줍니다.
❓리플렉션(Reflection)이란?
간단히 말해서, 런타임에 프로그램이 자신(또는 다른 클래스)의 메타 정보(필드, 메섣, 생성자 등)를 조사하고 조작할 수 있는 기능입니다.
❓필요한 이유
일반적으로 자바는 컴파일 시점에 모든 타입과 접근이 정해져 있고, `private` 같은 접근 제한자가 있어서 직접 접근이 불가합니다. 하지만 리플렉션을 사용하면 private 필드에 접근해서 값을 바꿀 수 있습니다.
일반 메서드 주입
메서드를 통해 의존성을 주입하는 방식입니다.
❗ 생성자, setter 주입으로 대체가 가능하기 때문에 잘 사용하지 않습니다.
예시 코드
더보기
@Component
public class MyApp {
private MyService myService;
// 일반 메서드 주입
@Autowired
public void init(MyService myService) {
this.myService = myService;
}
public void run() {
myService.doSomething();
}
}
'Spring' 카테고리의 다른 글
[Spring] Spring MVC, RequestMappingHandlerAdapter의 요청 및 응답 처리 과정 (0) | 2025.06.05 |
---|---|
[Spring] Spring URI 매핑 방식의 진화: 초기 Bean 이름 매핑부터 어노테이션 기반까지 (0) | 2025.05.26 |
[Spring] Bean 등록 방법 (4) | 2025.05.23 |
[Spring] Spring Bean 어떻게 등록될까? (0) | 2025.05.23 |
[Spring] SOLID 원칙과 Spring의 등장 배경 (0) | 2025.05.15 |