전전차시에서 봤던 스프링 구조를 살펴보자.
스프링 컨테이너에 helloController가 등록되어 있는 것을 볼 수 있다.
이를 스프링에서 "스프링 빈"에 등록되었다 라고 하며, 스프링 빈 내에서 의존관계를 가지며 동작하게 된다.
초록색 동그라미가 땅콩(bean)같다고 하여 빈이라고 표현하는 것 같다.
무슨 소리인가 싶겠지만, 천천히 알아보자.
이전차시에서 Member에 관련된 서비스, 리포지토리, 객체를 만들었다.
이들을 유기적으로 연결하여 멤버 컨트롤러가 서비스를 통해서 기능하고, 서비스는 리포지토리를 통해 데이터를 조회할 수 있어야한다. (의존 관계가 있다.)
다음과 같이 의존관계를 표현할 수 있고, 이를 스프링 컨테이너에 스프링 빈이 관리된다고 표현한다.
컨트롤러 외부 요청 -> 서비스 비즈니스 로직 동작 -> 리포지토리 데이터 저장 ... 정형적인 패턴으로 기억해두면 된다.
컨트롤러가 서비스에 의존하고,
서비스가 리포지토리에 의존하고... 를 이해하면 아래에서 DI를 받는 것이 이해될 것이다.
hello/hellospring/controller/MemberController.java
package hello.hellospring.controller;
import hello.hellospring.service.MemberService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
// @Controller를 명시함으로써 스프링 컨테이너가
// 스프링이 처음에 실행될 때 멤버컨트롤러 객체를 생성하여 스프링 빈에 등록한다.
@Controller
public class MemberController {
private final MemberService memberService;
/* @Autowired란? 스프링이 연관된 객체를 스프링 컨테이너에서 찾아서 넣어줌.
이렇게 객체 의존관계를 외부에서 넣어주는 것을 DI(Dependency Injection, 의존성 주입)이라 함.
이전 테스트에서는 개발자가 직접 주입했고, 여기서는 @Autowired에 의해 스프링이 주입함
*/
@Autowired
public MemberController(MemberService memberService) {
this.memberService = memberService;
}
}
MemberService 클래스에도 @Service를 붙여준다.
MemoryMemberRepository 클래스에도 @Repository를 붙여준다.
스프링 빈을 등록하는 2가지 방법
1. 컴포넌트 스캔과 자동 의존관계 설정 (위에서 한 방법)
2. 자바 코드로 직접 스프링 빈 등록하기
컴포넌트 스캔 원리
- @Component - 스프링 빈으로 자동 등록
- @Component를 포함하는 다음 애노테이션도 스프링 빈으로 자동 등록(애노테이션 클래스에 들어가보면 @Component가 애노테이션 되어있음)
- @Controller
- @Service
- @Repository
참고
- 생성자가 1개만 있으면 @Autowired는 생략할 수 있음
- 스프링은 스프링 컨테이너에 스프링 빈을 등록할 때, 기본으로 싱글톤으로 등록함(유일하게 하나만 등록해서 공유). 따라서 같은 스프링 빈이면 모두 같은 인스턴스임. 설정으로 싱글톤이 아니게 설정할 수 있지만 특별한 경우를 제외하면 대부분 싱글톤 사용
- HelloSpringApplication.java 가 속해있는 hello.hellospring 이외의 패키지에서의 애노테이션은 스프링에서 스캔하지 않으므로 적용되지 않음
자바 코드로 직접 스프링 빈 등록하기
MemberService, MemoryMemberRepository에서 먼저 애노테이션을 지우고 진행한다.
hello/hellospring/service/SpringConfig.java
package hello.hellospring.service;
import hello.hellospring.repository.MemberRepository;
import hello.hellospring.repository.MemoryMemberRepository;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class SpringConfig {
// MemberController은 따로 등록 못해줘서 Autowired해줘야함
@Bean
public MemberService memberService() {
return new MemberService(memberRepository());
}
@Bean
public MemberRepository memberRepository() {
return new MemoryMemberRepository();
}
}
향후 메모리 리포지토리를 다른 리포지토리로 변경할 예정이므로, 컴포넌트 스캔 방식 대신에 자바 코드로 스프링 빈을 설정함
참고
- 과거에는 XML로 설정했지만 요즘엔 잘 사용하지 않음
- 실무에서는 자바코드로 많이 사용
- DI에는 필드 주입, setter 주입, 생성자 주입 3가지 방법이 있다. 의존관계가 실행중에 동적으로 변하는 경우는 거의 없으므로 생성자 주입을 권장함
- 필드 주입 : @Autowired private MemberService memberService;
- setter 주입 : 나중에 호출이 돼서 멤버서비스에 들어옴. public으로 열려있어야 된다는 단점이 있음. 중간에 바꿀일이 없는데도 ㅠ. final로도 선언못해서 바뀌게 되는거
@Autowired
public void setMemberController(MemberService memberService) {
this.memberService = memberService;
}
- 실무에서는 주로 정형화된 컨트롤러, 서비스, 리포지토리 같은 코드는 컴포넌트 스캔을 사용한다.
- 정형화 되지 않거나, 상황에 따라 구현 클래스를 변경해야 하면(이전에 메모리멤버리포지토리를 쓰다가 다른 DB로 변경해야 하는 경우)설정을 통해 스프링 빈으로 등록한다(지금 코드 그대로 멤버리포지토리만 바꿔치기 할 수 있음. 설정만 변경하면 되니깐.. 컴포넌트 스캔을 사용하면 모든 코드를 손대야함)
- @Autowired를 통한 DI는 helloController, memberService등과 같이 스프링이 관리하는 객체에서만 동작하며, 스프링 빈으로 동작하지 않고 내가 직접 생성한 객체에서는 동작하지 않는다.
사진, 코드 출처 - https://inf.run/hivx6
'스프링' 카테고리의 다른 글
스프링 입문 (7) - DB 접근 기술 (0) | 2024.03.18 |
---|---|
스프링 입문 (6) - 회원 관리 예제 : 웹 MVC 개발 (0) | 2024.03.18 |
스프링 입문 (4) - 회원 관리 예제 - 백엔드 개발 (0) | 2024.03.14 |
스프링 입문 (3) - 웹 개발 기초 (3) | 2024.03.12 |
스프링 입문 (2) - View 환경 설정, 빌드해보기 (0) | 2024.03.12 |