1. 구성 클래스에서 정의된 두 빈의 관계 구현
1) @Bean 애너테이션을 사용한 메서드 간의 직접 호출
2) @Bean 메서드의 매개변수를 통한 빈 와이어링
구성 클래스:
- @Configuration 애너테이션을 사용해 빈을 정의하는 클래스입니다.
- 이 클래스는 @Bean 메서드를 통해 객체를 생성하고, 스프링 컨텍스트에 빈으로 등록합니다.
의존성 주입:
- 두 객체 간의 관계를 정의하고, 스프링 컨텍스트가 자동으로 의존성을 관리해 줍니다.
- 예를 들어, Car가 Engine을 의존하는 경우, Car 빈에 Engine 빈을 주입할 수 있습니다.
1) @Bean 애너테이션을 사용한 메서드 간의 직접 호출
@Bean 애너테이션을 사용하여 스프링 구성 클래스에서 두 빈 간의 관계를 설정하는 방법은 스프링의 의존성 주입(DI)을 활용하는 좋은 예입니다. 이 방법을 사용하면 스프링 컨텍스트 내에서 빈을 생성하고 서로 연결할 수 있습니다. 이러한 관계 설정은 보통 객체 간의 협력(의존성)을 관리하는 데 사용됩니다.
- 구성클래스 정의 및 직접 메서드 호출을 통한 빈 주입
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class VehicleConfig {
@Bean
public Engine engine() {
Engine engine = new Engine();
engine.setType("V8 Engine");
return engine;
}
@Bean
public Car car() {
Car car = new Car();
car.setModel("Sports Car");
// engine() 메서드를 직접 호출하여 Engine 빈 주입
car.setEngine(engine());
return car;
}
}
- 클래스 정의
public class Engine {
private String type;
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
}
public class Car {
private String model;
private Engine engine;
public String getModel() {
return model;
}
public void setModel(String model) {
this.model = model;
}
public Engine getEngine() {
return engine;
}
public void setEngine(Engine engine) {
this.engine = engine;
}
public void drive() {
System.out.println("Driving a " + model + " with " + engine.getType());
}
}
- VehicleConfig 클래스:
- VehicleConfig 클래스는 스프링의 빈 설정 클래스입니다. @Configuration을 통해 스프링 컨텍스트에서 이 클래스에 정의된 @Bean 메서드들이 스프링이 관리하는 빈으로 등록됩니다.
- @Bean 메서드 정의:
- engine() 메서드는 Engine 객체를 생성하고, 그 타입을 "V8 Engine"으로 설정한 후 스프링 컨텍스트에 빈으로 등록합니다.
- car() 메서드는 Car 객체를 생성하고, 그 모델을 "Sports Car"로 설정합니다. 여기서 engine() 메서드를 호출하여 Engine 객체를 생성 및 주입한 후, Car 빈으로 등록합니다.
- 직접 메서드 호출을 통한 빈 주입:
- car() 메서드 안에서 engine() 메서드를 직접 호출하여 Car 객체에 Engine 객체를 주입합니다.
- engine() 메서드가 Car 메서드 안에서 여러 번 호출되더라도 스프링이 이 메서드를 프록시로 관리하기 때문에 하나의 Engine 빈만 생성됩니다.
- 스프링의 빈 관리:
- 스프링은 @Configuration 클래스 내의 @Bean 메서드들을 자동으로 프록시 객체로 감쌉니다. 그래서 engine() 메서드가 여러 번 호출되더라도 매번 새 객체가 만들어지지 않고, 첫 번째 호출 시 생성된 Engine 객체를 반환하게 됩니다. 즉, 하나의 Engine 객체만 생성되고 재사용됩니다.
- 실행
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class MainApp {
public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext(VehicleConfig.class);
Car car = context.getBean(Car.class);
car.drive(); // "Driving a Sports Car with V8 Engine" 출력
}
}
- 프록시 패턴으로 빈 관리:
- 스프링이 @Configuration 클래스의 @Bean 메서드들을 프록시로 감싸기 때문에, 동일한 메서드를 여러 번 호출하더라도 새로운 객체를 생성하지 않고, 기존에 생성된 빈을 반환합니다.
- 빈 간 의존성 주입:
- car() 메서드가 engine() 메서드를 호출해 Car 객체에 Engine 객체를 주입하는 방식은, 스프링의 DI(의존성 주입) 개념을 활용한 것입니다. 스프링 컨텍스트에서 관리되는 빈들은 서로 필요에 따라 주입될 수 있습니다.
- 빈 재사용:
- 동일한 타입의 빈이 여러 번 필요할 때도, 스프링 컨텍스트는 처음 생성된 빈을 계속 재사용하여 메모리 효율성을 높입니다.
2) @Bean 메서드의 매개변수를 통한 빈 와이어링
@Bean 메서드의 매개변수로 다른 빈을 주입받아 사용합니다. 스프링은 필요한 빈을 자동으로 생성한 후, 해당 빈을 @Bean 메서드의 매개변수로 전달하여 의존성을 주입합니다. 이 방식은 @Autowired 애너테이션 없이도 명시적으로 의존성을 설정할 수 있어 가독성이 높아집니다.
- 구성 클래스 정의
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class VehicleConfig {
@Bean
public Engine engine() {
// Engine 빈 생성
Engine engine = new Engine();
engine.setType("V8 Engine");
return engine;
}
// Engine 빈을 매개변수로 주입받아 Car 빈 생성
@Bean
public Car car(Engine engine) {
Car car = new Car();
car.setModel("Sports Car");
car.setEngine(engine); // 매개변수로 받은 Engine 빈 주입
return car;
}
}
- 클래스 정의
public class Engine {
private String type;
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
}
public class Car {
private String model;
private Engine engine;
public String getModel() {
return model;
}
public void setModel(String model) {
this.model = model;
}
public Engine getEngine() {
return engine;
}
public void setEngine(Engine engine) {
this.engine = engine;
}
public void drive() {
System.out.println("Driving a " + model + " with " + engine.getType());
}
}
- 실행
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class MainApp {
public static void main(String[] args) {
// 스프링 컨텍스트 초기화 및 빈 가져오기
ApplicationContext context = new AnnotationConfigApplicationContext(VehicleConfig.class);
// Car 빈 가져오기
Car car = context.getBean(Car.class);
// Car의 기능 실행
car.drive();
// 출력: Driving a Sports Car with V8 Engine
}
}
- @Bean 메서드에서 매개변수로 빈 주입
- VehicleConfig 클래스는 @Configuration을 사용해 스프링 구성 클래스로 설정되며, 스프링 컨텍스트가 해당 클래스 내의 @Bean 메서드를 호출하여 빈을 생성하고 관리합니다.
- car() 메서드는 Engine 빈을 매개변수로 주입받아 Car 빈을 생성하고, 주입받은 Engine 빈을 Car 객체에 설정합니다.
- 스프링의 빈 관리
- 스프링 컨텍스트는 Car 빈을 생성하기 전에 Engine 빈을 먼저 생성합니다. 그리고 car() 메서드가 호출될 때 Engine 빈을 매개변수로 전달하여 Car 빈에 주입합니다.
- 이 과정을 통해 @Autowired 애너테이션 없이도 스프링 컨텍스트에서 자동으로 의존성을 주입할 수 있습니다.
- 프록시 패턴을 통한 빈 관리
- 스프링은 @Configuration 클래스를 프록시 객체로 감싸 관리하기 때문에, 동일한 @Bean 메서드가 여러 번 호출되더라도 동일한 빈을 재사용합니다.
- Engine 빈은 car() 메서드에서 여러 번 호출되더라도 하나의 인스턴스만 생성되고, 이를 재사용하게 됩니다.
매개변수를 통한 빈 와이어링의 장점
- 명확한 의존성 표현:
- @Bean 메서드의 매개변수로 빈을 주입받으면, 해당 빈이 다른 빈에 의존하고 있음을 명확하게 표현할 수 있습니다. 코드만 봐도 어떤 빈이 필요하고, 어떻게 주입되는지 직관적으로 이해할 수 있습니다.
- 자동 관리:
- 스프링은 자동으로 빈을 관리하며, 의존성 주입 순서도 자동으로 처리해 줍니다. 개발자가 직접 메서드를 호출하거나 객체를 생성하는 번거로움이 없습니다.
- 중복 생성 방지:
- 스프링이 프록시 패턴을 사용해 동일한 빈을 여러 번 호출하더라도 중복 생성하지 않고, 항상 같은 빈을 사용하므로 메모리 효율성이 높습니다.
2. @Autowired 애너테이션을 사용한 빈 주입
1) 필드 주입
2) Setter 메서드를 통한 주입
3) 생성자 주입
@Autowired 애너테이션
- @Autowired 애너테이션은 스프링이 자동으로 의존성을 주입할 수 있도록 도와줍니다.
- 스프링 컨텍스트에서 관리하는 빈 중에서 해당 클래스에서 필요로 하는 빈을 찾아서 자동으로 주입합니다.
- @Autowired는 생성자, 필드, setter 메서드에 적용할 수 있으며, 각각의 방식에 따라 의존성 주입이 이뤄집니다.
1) 필드 주입
- 필드에 직접 @Autowired를 적용하여 주입할 수 있습니다.
- 필드 주입 방식은 간단하지만 테스트나 유지보수에서 단점이 있을 수 있어 권장되지 않는 경우가 많습니다.
public class Car {
@Autowired
private Engine engine;
private String model = "Sports Car";
public void drive() {
System.out.println("Driving a " + model + " with " + engine.getType());
}
}
2) Setter 메서드를 통한 주입
- @Autowired 애너테이션을 setter 메서드에 적용하여 의존성을 주입할 수도 있습니다. 이 방식은 객체 생성 후에도 의존성을 주입할 수 있는 유연성을 제공합니다.
public class Car {
private Engine engine;
private String model = "Sports Car";
@Autowired
public void setEngine(Engine engine) {
this.engine = engine;
}
public void drive() {
System.out.println("Driving a " + model + " with " + engine.getType());
}
}
3) 생성자 주입
- 생성자 주입 방식은 권장되는 방식입니다. 주입받을 의존성을 명확하게 알 수 있으며, 테스트하기도 쉽습니다.
- 생성자를 통해 필수적인 의존성을 설정할 수 있고, 객체의 불변성을 보장하는 데 도움을 줍니다.
public class Engine {
private String type;
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
}
public class Car {
private String model = "Sports Car";
private Engine engine;
// 생성자 주입: 생성자에서 Engine 객체를 받아 주입
public Car(Engine engine) {
this.engine = engine;
}
public void drive() {
System.out.println("Driving a " + model + " with " + engine.getType());
}
}
@Autowired의 장점
- 자동 의존성 해결:
- @Autowired 애너테이션은 스프링 컨텍스트에서 관리하는 빈을 자동으로 주입해 주므로, 개발자는 객체 간의 관계를 쉽게 설정할 수 있습니다.
- 유연한 사용 방식:
- 생성자, 필드, setter 메서드 등 다양한 방식으로 사용할 수 있어 상황에 맞게 의존성을 설정할 수 있습니다.
- 가독성 향상:
- 의존성이 명시적으로 주입되므로, 코드의 가독성이 높아집니다. 특히 생성자 주입 방식을 사용할 때, 어떤 빈이 필요한지 명확히 알 수 있습니다.
3. 동일한 타입의 빈 의 여러개 일때
스프링에서 매개변수 또는 클래스 필드에 값을 주입해야 할 때, 동일한 타입의 빈이 여러 개 있을 경우 어떤 빈을 선택해서 주입할지를 명확하게 지정해야 할 때가 있습니다. 이때 사용할 수 있는 주요 기법이 @Qualifier 애너테이션입니다. 스프링은 기본적으로 동일한 타입의 여러 빈이 존재할 때 어느 빈을 주입해야 할지 혼란을 겪을 수 있으므로, 명확한 빈 선택을 위해 이 애너테이션을 사용하게 됩니다.
1) @Qualifier 애너테이션을 사용하여 빈 선택하기
2) 필드 주입 시 @Qualifier 사용
3) @Primary 애너테이션 사용
4) @Primary와 @Qualifier 혼합 사용
- @Qualifier: 동일한 타입의 빈이 여러 개 있을 때, 특정 빈을 선택해 주입할 수 있는 애너테이션입니다. 생성자, 필드, setter 모두에 사용할 수 있습니다.
- @Primary: 빈 중에서 기본적으로 주입할 빈을 지정하는 애너테이션입니다. @Qualifier로 지정한 빈이 우선하지만, 지정하지 않으면 @Primary가 지정된 빈이 주입됩니다.
- 필드 주입, 생성자 주입: @Qualifier와 @Primary는 필드 주입, 생성자 주입 등 모든 의존성 주입 방식에서 사용할 수 있습니다.
[기본 문제 상황]
- 동일한 타입의 빈이 여러 개 정의되어 있는 상황에서, 스프링은 어느 빈을 주입해야 할지 결정할 수 없습니다.
- 예를 들어, Car라는 타입의 빈이 여러 개 존재할 때, 스프링이 @Autowired를 통해 빈을 주입할 때 어떤 빈을 사용할지 혼란을 겪습니다.
@Bean
public Car carA() {
return new Car("Model A");
}
@Bean
public Car carB() {
return new Car("Model B");
}
위 예시처럼 Car 타입의 빈이 두 개 정의되어 있을 경우, 스프링은 어느 빈을 주입할지 모호해지므로 명시적으로 빈을 지정해야 합니다.
1) @Qualifier 애너테이션을 사용하여 빈 선택하기
@Qualifier 애너테이션을 사용하여 어떤 빈을 선택할지 명시할 수 있습니다. 아래 예시는 Car 빈 두 개(carA와 carB)를 정의하고, @Qualifier 애너테이션을 사용해 특정 빈을 주입받는 방법을 보여줍니다.
구성 클래스
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class VehicleConfig {
@Bean
public Car carA() {
return new Car("Model A");
}
@Bean
public Car carB() {
return new Car("Model B");
}
}
클래스 정의
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;
@Component
public class CarService {
private final Car car;
// @Qualifier로 주입할 빈을 명시적으로 지정
@Autowired
public CarService(@Qualifier("carA") Car car) {
this.car = car;
}
public void drive() {
System.out.println("Driving " + car.getModel());
}
}
실행 클래스
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class MainApp {
public static void main(String[] args) {
// 스프링 컨텍스트 초기화
ApplicationContext context = new AnnotationConfigApplicationContext(VehicleConfig.class);
// CarService 빈 가져오기
CarService carService = context.getBean(CarService.class);
// 주입된 Car 빈 사용
carService.drive();
// 출력: Driving Model A
}
}
- VehicleConfig 클래스에서 두 개의 Car 빈(carA와 carB)을 정의했습니다. 이 두 빈은 동일한 Car 타입이지만 서로 다른 모델을 가지고 있습니다.
- CarService 클래스에서 Car 타입의 빈을 주입받을 때, @Qualifier("carA") 애너테이션을 사용하여 어떤 빈을 주입받을지 명시했습니다. 이 경우 carA 빈이 주입됩니다.
- 실행 시 CarService 클래스에서는 carA 빈을 주입받아 "Driving Model A"가 출력됩니다.
2) 필드 주입 시 @Qualifier 사용
@Qualifier는 생성자뿐만 아니라 필드 주입(Field Injection)에도 사용할 수 있습니다. 필드에 직접 주입할 때도 동일한 방식으로 사용하여 특정 빈을 선택할 수 있습니다.
@Component
public class CarService {
@Autowired
@Qualifier("carB") // carB 빈을 주입
private Car car;
public void drive() {
System.out.println("Driving " + car.getModel());
}
}
이 경우 carB 빈이 주입되어 "Driving Model B"가 출력됩니다.
3) @Primary 애너테이션 사용
스프링은 또 다른 방법으로 @Primary 애너테이션을 통해 기본 빈을 지정할 수 있습니다. 동일한 타입의 빈이 여러 개 있을 때, @Primary 애너테이션을 적용한 빈이 우선적으로 주입됩니다. @Primary를 사용하면 명시적으로 @Qualifier를 지정하지 않더라도 기본으로 사용할 빈을 지정할 수 있습니다.
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class VehicleConfig {
@Bean
@Primary // 기본으로 주입될 빈 지정
public Car carA() {
return new Car("Model A");
}
@Bean
public Car carB() {
return new Car("Model B");
}
}
@Component
public class CarService {
@Autowired
private Car car; // @Qualifier 없이 자동으로 @Primary가 붙은 carA 빈이 주입됨
public void drive() {
System.out.println("Driving " + car.getModel());
}
}
- 위 예제에서는 carA에 @Primary 애너테이션이 붙어 있기 때문에, @Qualifier를 사용하지 않더라도 기본적으로 carA가 주입됩니다.
- 주입된 Car 빈을 사용하여 carA가 선택되며, "Driving Model A"가 출력됩니다.
4) @Primary와 @Qualifier 혼합 사용
@Primary와 @Qualifier를 혼합해서 사용할 수 있습니다. @Primary로 기본 빈을 지정하더라도, @Qualifier를 사용해 특정 빈을 선택하면 해당 빈이 우선적으로 주입됩니다.
@Configuration
public class VehicleConfig {
@Bean
@Primary
public Car carA() {
return new Car("Model A");
}
@Bean
public Car carB() {
return new Car("Model B");
}
}
@Component
public class CarService {
@Autowired
@Qualifier("carB") // carB를 명시적으로 주입받음
private Car car;
public void drive() {
System.out.println("Driving " + car.getModel());
}
}
- carA에 @Primary가 지정되어 있지만, @Qualifier("carB")로 명시적으로 carB를 선택했기 때문에 carB 빈이 주입됩니다.
- 결과적으로 "Driving Model B"가 출력됩니다.
'IT개발및프로그래밍 > 웹서버프로젝트' 카테고리의 다른 글
스프링 순환 의존성이 발생하는 이유 (0) | 2024.10.22 |
---|---|
스프링 컨텍스트에 빈 추가하는 3가지 방법: 예제와 함께 배우기 (1) | 2024.10.21 |
Maven 프로젝트의 기초 개념과 구조 설명 및 실습을 통한 의존성 추가 (2) | 2024.10.19 |
IntelliJ IDEA 설치부터 첫 프로젝트 실행까지, 단계별 가이드 (0) | 2024.10.19 |
스프링 생태계: 쉽게 이해하는 스프링 프레임워크 (3) | 2024.10.19 |