📙 목차
1. Spring Bean 등록
Spring에서 객체를 Bean으로 등록하는 방법은 크게 두 가지가 있다.
- 자동 등록 - @Component 기반
- 수동 등록 - @Configuration, @Bean 기반
* Spring에서는 기본적으로 자동 등록을 사용하며, 특정 상황에서만 수동 등록을 사용한다.
자동 등록 - @Component 기반
- Spring은 @ComponentScan을 통해 특정 패키지를 스캔한다.
- 해당 패키지 내의 클래스 중 @Component, @Service, @Repository, @Controller 등의 어노테이션이 붙은 클래스를 자동으로 Bean으로 등록한다.
- 등록된 클래스는 클래스 이름의 앞글자를 소문자로 바꿔 Bean 이름이 된다.
- @ComponentScan은 일반적으로 @SpringBootApplication에 포함되어 있어 별도로 명시하지 않아도 동작한다.
- 자동 등록 사용 시점
- 대부분의 일반적인 Bean 등록은 @Component, @Service, @Repository, @Controller 등으로 자동 등록
- Spring Boot는 기본적으로 @ComponentScan을 통해 자동 등록을 수행
- OCP, DIP를 만족하면서도 개발이 간편함
@Component // → Bean 이름: myService
public class MyService {
public void doSomething() {
System.out.println("Spring Bean으로 동작");
}
}
수동 등록 - @Configuration, @Bean 기반
- 직접 Bean을 등록하고자 할 때 사용하는 방식이다.
- 설정 클래스에 @Configuration을 붙이고, 해당 클래스 안에서 @Bean 메서드를 통해 원하는 객체를 반환하면 Bean으로 등록된다.
- 주의할 점
- @Configuration 없이 @Bean만 사용하면 싱글톤이 보장되지 않는다.
- 수동 등록 사용 시점
- 외부 라이브러리 객체를 Spring Bean으로 등록할 때
- DB 연결 등 기술 인프라용 객체 구성 시
- 같은 타입의 Bean 중 특정한 Bean을 명확하게 지정하고 싶을 때
@Configuration
public class AppConfig {
@Bean // → Bean 이름: testService
public TestService testService() {
return new TestServiceImpl();
}
}
ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
TestService service = context.getBean(TestService.class);
service.doSomething(); // "Test Service 메서드 호출"
Bean 충돌
- Spring에서 Bean은 각각 고유한 이름을 기준으로 등록된다.
- 같은 이름의 Bean이 중복 등록되면 충돌(ConflictingBeanDefinitionException) 이 발생하거나, 오버라이딩되어 예기치 않은 결과를 초래할 수 있다.
더보기
더보기
같은 이름의 Bean 등록: 자동 등록 vs 자동 등록
- ConflictingBeanDefinitionException 발생
@Component("service")
public class ConflictServiceV1 implements ConflictService {
public void test() { System.out.println("Conflict V1"); }
}
@Component("service")
public class ConflictServiceV2 implements ConflictService {
public void test() { System.out.println("Conflict V2"); }
}
@ComponentScan(basePackages = "com.example.springconcept.conflict")
public class ConflictApp {
public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext(ConflictApp.class);
ConflictService service = context.getBean(ConflictService.class);
service.test();
}
}
// 실행 시
org.springframework.beans.factory.support.BeanDefinitionOverrideException:
Invalid bean definition with name 'service'
같은 이름의 Bean 등록: 수동 등록 vs 자동 등록
- 수동 등록된 Bean이 우선권을 가지며, 자동 등록된 Bean은 덮어쓰기(overriding) 된다.
- 수동 등록이 자동 등록을 조용히 덮어쓰기 때문에, 실수로 잘못된 객체가 등록되어도 알아차리기 어렵다.
// 자동 등록
@Component // 이름: conflictService
public class ConflictService implements MyService {
public void doSomething() { System.out.println("자동 등록된 ConflictService 호출"); }
}
// 수동 등록
@Configuration
public class ConflictAppConfig {
@Bean(name = "conflictService")
public MyService myService() {
return new ConflictServiceV2(); // 수동 등록된 Bean
}
}
public class ConflictServiceV2 implements MyService {
public void doSomething() { System.out.println("수동 등록된 ConflictServiceV2 호출"); }
}
2. Spring Bean 등록 2
같은 타입의 Bean이 여러 개 존재할 때, 어떤 Bean을 주입할지 지정하는 방법
- @Autowired + 필드명
- @Qualifier
- @Primary
@Autowired + 필드명
- @Autowired는 타입으로 먼저 주입을 시도하고, 동일한 타입의 Bean이 여러 개 존재하면 필드명 또는 파라미터명으로 매칭하여 주입한다.
@Autowired
private MyService myServiceImplV2; // 필드명이 Bean 이름과 일치하면 주입됨
@Qualifier
- Bean 등록 시 @Qualifier("이름")을 지정하고, 주입 시 @Qualifier로 명시하여 사용한다.
- 생성자 주입, Setter 주입 모두 사용 가능하다.
@Component
@Qualifier("firstService")
public class MyServiceImplV1 implements MyService { }
@Component
@Qualifier("secondService")
public class MyServiceImplV2 implements MyService { }
@Autowired
public ConflictApp(@Qualifier("firstService") MyService myService) {
this.myService = myService;
}
@Primary
- 같은 타입의 Bean이 여러 개일 때, 기본으로 주입할 Bean을 @Primary로 지정할 수 있다.
- @Qualifier가 명시된 경우에는 @Primary보다 우선한다.
@Component
@Primary
public class MyServiceImplV2 implements MyService { }
@Autowired
public ConflictApp(MyService myService) {
this.myService = myService; // MyServiceImplV2가 주입됨
}
* 실전 예시
- DB 다중 구성 (MySQL + Oracle)
- 기본은 @Primary로 MySQL을 설정하고, 특정 상황에서는 @Qualifier("oracleService") 등으로 보조 DB 사용
3. 의존관계 주입
의존관계를 주입하는 방법 4가지
- 생성자 주입 (가장 권장되는 방식)
- Setter 주입
- 필드 주입
- 일반 메서드 주입
생성자 주입 (Constructor Injection)
- 객체 생성 시 의존성을 주입한다.
- 불변(immutable) 필드 주입이 가능하여 final 키워드를 사용할 수 있다.
- 테스트 코드 작성이 용이하다.
- 생성자가 1개일 경우 @Autowired를 생략할 수 있다.
- @Autowired: Spring이 의존 객체를 자동으로 주입해주는 어노테이션.
- 실무에서 가장 많이 사용하며, 스프링 외 환경에서도 적용이 가능하다.
@Component
public class OrderService {
private final MemberRepository memberRepository;
@Autowired
public OrderService(MemberRepository memberRepository) {
this.memberRepository = memberRepository;
}
}
// @RequiredArgsConstructor (Lombok) 사용 시
// final 필드를 모아서 생성자를 자동으로 만들어 준다.
@Component
@RequiredArgsConstructor
public class OrderService {
private final MemberRepository memberRepository;
}
Setter 주입
- 객체 생성 이후 의존성을 주입한다.
- 선택적 의존성에 적합하다.
- 주입 시점이 불분명하고, 필드가 변경될 수 있어 안정성이 떨어진다.
- 외부에서 setter를 호출하여 의존성을 바꿀 수 있기 때문에 주의가 필요하다.
@Component
public class OrderService {
private MemberRepository memberRepository;
@Autowired
public void setMemberRepository(MemberRepository memberRepository) {
this.memberRepository = memberRepository;
}
}
필드 주입
- 코드가 간단하고 짧다는 장점이 있다.
- 테스트 작성이 어렵고, 스프링 컨테이너 없이 사용할 수 없다.
- 주로 예제 코드나 설정 코드에서 사용한다.
- 테스트와 유지보수가 어렵기 때문에 실무에서는 지양한다.
@Component
public class OrderService {
@Autowired
private MemberRepository memberRepository;
}
일반 메서드 주입
- 일반 메서드를 통해 의존성을 주입한다.
- 잘 사용되지는 않지만, 여러 의존성을 한꺼번에 주입할 때 유용할 수 있다.
@Component
public class OrderService {
private MemberRepository memberRepository;
@Autowired
public void init(MemberRepository memberRepository) {
this.memberRepository = memberRepository;
}
}
'Spring > 강의' 카테고리의 다른 글
[📙 숙련 Spring] 2-1. 인증과 인가 (0) | 2025.05.16 |
---|---|
[📙 숙련 Spring] 1-3. Validation과 Bean Validation (4) | 2025.05.15 |
[📙 숙련 Spring] 1-1. 객체 지향과 Spring 핵심 개념 (0) | 2025.05.15 |
[📗 스프링 입문] 2. 스프링 웹 개발 기초 (1) | 2025.05.08 |
[📗 스프링 입문] 1. 프로젝트 환경 설정 (1) | 2025.05.07 |