[Spring] 네번째. 스프링 빈과 의존관계
스프링 입문 - 코드로 배우는 스프링 부트, 웹 MVC, DB 접근 기술을 들으며 정리하는 POST입니다.
전체적인 흐름
- Spring Project 생성
- Spring boot로 웹 서버 실행
- 회원 도메인 개발
- 웹 MVC 개발
- DB 연동 - JDBC, JPA, Spring data JPA
- 테스트 케이스 작성
컴포넌트 스캔과 자동 의존관계 설정
이전 시간까지 생성한 Member가 서비스를 이용하기 위해서는 Service, Controller 간 의존성이 필요하다.
MemberController 생성
controller/MemberController
import org.springframework.stereotype.Controller;
@Controller
public class MemberController {
}
- 이렇게
@Controller를 사용해 아무 코드가 없는 controller를 생성하게 되면, 스프링 컨테이너에MemberController라는 객체가 생성되어 관리된다.
= Spring Bean이 관리된다고 표현, 이곳에서 스프링 컨테이너의 구조를 확인할 수 있다.
private final MemberService memberService = new MemberService();
- Controller에서,
new를 이용하여 객체를 생성해 사용할 수도 있지만, 스프링이 관리하기 때문에 컨테이너에 등록하고 이로부터 받아서 사용하도록 바꿔야 한다.- 다른 code에서도 동일 서비스 객체를 여러 번 생성하여 사용하는 것은 불필요하기 때문이다.
@Autowired
public MemberController(MemberService memberService) {
this.memberService = memberService;
}
- 생성자를 이용하여 스프링 컨테이너에 연결한다.
이때,@Autowired를 이용하는데, 이는 호출된 생성자의 파라미터와 스프링 컨테이너를 연결시켜준다. 위의 예제에서는 스프링 컨테이너에 있는MemberService와 연결을 수행한다.
실행을 하게 되면
Parameter 0 of constructor in hello.hellospring.controller.MemberController required a bean of type 'hello.hellospring.service.MemberService' that could not be found.
위와 같이 MemberService 를 찾을 수 없다는 에러가 발생한다.
이러한 이유는,
이전에 작성한 MemberService 는 순수한 Java 코드이므로, 스프링이 이를 인식할 방법이 없다.
@Service
public class MemberService
따라서 스프링 컨테이너가 해당 Service를 인식하고 등록할 수 있도록 @Service annotation을 사용한다. 또한,
전체적으로 보았을때,
Controller class - 외부 요청을 받음 - @Controller,
Test class - @SpringBootTest,
Repository class - Data 저장 - @Repository,
Service class - 비즈니스 로직 생성 - @Service annotation이 사용하여 스프링이 동작할 때, 컨테이너에 등록하는 것을 확인할 수 있다.
의존 관계 주입 (Dependency Injection)
Controller 생성자에 @Autowired 를 사용해 연결을 수행
- controller가 생성될 때, 스프링 빈에 있는 객체를 가져다 연결에 사용
= Dependency Injection(의존성 주입)
MemberService.java
@Autowired
public MemberService(MemberRepository memberRepository) {
this.memberRepository = memberRepository;
}
MemberService또한MemberRepository를 사용하는@Service이므로,@Autowiredannotation을 이용한다.- 이를 통해 스프링 컨테이너로부터
MemberRepository를 주입받는다.
컴포넌트 스캔인 이유
@Component 를 이용하여 의존 관계를 명시하고 등록하는 방식이다.
하지만 우리가 작성한 class들의 Annotation을 보면, class에 대해 @Service , @Controller , @Repository 등을 사용한다.
그럼 왜 @Service 와 같이 @Component 라고 명시하지 않아도 사용이 가능할까 ?
@Service에command + click해보면, 아래와 같이@Component가 명시되어 있는 것을 확인할 수 있다.
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Service {
/**
* The value may indicate a suggestion for a logical component name,
* to be turned into a Spring bean in case of an autodetected component.
* @return the suggested component name, if any (or empty String otherwise)
*/
@AliasFor(annotation = Component.class)
String value() default "";
}
아래는 위에서 설명한 Controller, Service, Repository 를 포함하는 스프링 컨테이너의 구조를 간략하게 표현했다.

이렇게 @Component annotation이 사용된 경우, 스프링이 동작할 때
이들을 모두 객체 로 생성하여 스프링 컨테이너에 자동 등록된다. (=스프링 빈으로 자동 등록)
- 위 예시에서는
helloController,memberService,memberRepository가 객체로 생성되어 등록된다. - 그리고,
@Autowiredannotation은 등록된 객체간의 연관관계를 의미한다.
그럼 아무 class에서 Annotation을 명시하면 스프링 컨테이너에 자동 등록될까 ?
정답은 우리가 실행하는HelloSpringApplication의package의 위치에 따라 결정된다.
- 해당 package와 동일하거나 하위 package에 포함되어 있는 경우, 스프링이 모두 찾아 스프링 빈으로 컴포넌트 스캔을 수행한다.
참고
스프링은 스프링 컨테이너에 스프링 빈을 등록할 때, 싱글톤으로 등록한다. (default)
- 싱글톤: 유일하게 하나만 등록하여 이를 공유한다. 이렇게 되면, 같은 스프링 빈인 경우 모두 같은 instance를 사용하는 것이므로, 메모리를 절약할 수 있다.
자바 코드로 직접 스프링 빈 등록하기
먼저, 직접 코드로 등록하기 위해 위에서 사용했던 컴포넌트 스캔 코드를 삭제한다.
(이때 Controller 는 삭제하지 않고 그대로 둔다.)
Controller는 어차피 스프링이 관리하기 때문에 그냥 컴포넌트 스캔 방식을 그대로 사용한다.
이후 실행을 하게 되면, 스프링 빈에 등록된 객체가 없으므로, 에러가 발생한다.
이제 직접 등록을 위해 HelloSpringApplication 과 같은 위치에 SpringConfig class를 생성한다.

@Configuration
public class SpringConfig {
@Bean
public MemberService memberService() {
return new MemberService();
}
}
@Beanannotation은 스프링 빈을 등록한다는 것을 명시한다.- 스프링이 동작할 때,
@Configuration을 읽으면@Bean을 찾아 해당 로직을 호출하여 이를 스프링 빈으로 등록한다.
- 위의 코드는 에러가 뜨는데, 해결하기 위해서 생성자에서의 추가가 필요하다.
@Bean
public MemberService memberService() {
return new MemberService(memberRepository());
}
@Bean
public MemberRepository memberRepository() {
return new MemoryMemberRepository();
}
MemberService는MemberRepository가 필요하다.- 먼저,
MemberService를 스프링 빈에 등록한 것과 동일하게MemberRepository또한 등록한다. - 그리고 등록된
MemberRepository를MemberService에 전달한다.
실행했을 때 문제 없이 잘 동작한다.
DI(Dependency Injection)에는 3가지 방법이 있다.
- Field Injection : 고정적인 방법, 수정이 불가능
@Autowired private MemberService memberService;
- Setter Injection : Setter를 통해 주입, 생성 따로 주입 따로, setter가 public하게 노출되어 위험할 수 있다.
private MemberService memberService; // final 제거 @Autowired public void setMemberService(MemberService memberService) { this.memberService = memberService; }
- Constructor Injection : 가장 권장, 의존관계가 실행 중에 동적으로 변경되는 경우는 거의 없기 때문이다.
public MemberController(MemberService memberService) { this.memberService = memberService; }
⇢ 해당 예제에서는 데이터 저장소가 선정되지 않아 MemoryMemberRepository 를 사용하기 때문에, 설정(SpringConfig)을 통해 스프링 빈으로 등록해야 한다.
📌 중요한 개념
의존성 주입
자료 참고
스프링 입문-코드로 배우는 스프링 부트, 웹 MVC, DB 접근 기술
Author And Source
이 문제에 관하여([Spring] 네번째. 스프링 빈과 의존관계), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://velog.io/@bsu1209/Spring-네번째.-스프링-빈과-의존관계저자 귀속: 원작자 정보가 원작자 URL에 포함되어 있으며 저작권은 원작자 소유입니다.
우수한 개발자 콘텐츠 발견에 전념
(Collection and Share based on the CC Protocol.)