이 글은 김영한 님의 스프링 입문 강좌 수강 후에 정리한 글입니다.
(https://www.inflearn.com/course/스프링-입문-스프링부트/dashboard)
스프링 빈과 의존관계
회원 컨트롤러가 회원 서비스와 회원 리포지토리를 사용할 수 있게 의존관계를 추가하여야 한다.
package hello.hellospring.controller;
import hello.hellospring.service.MemberService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
@Controller
public class MemberController {
private final MemberService memberService;
@Autowired
public MemberController(MemberService memberService) {
this.memberService = memberService;
}
}
MemberController에 의존관계를 추가한 것이다.
생성자에 @Autowired가 있으면 스프링이 연관된 객체를 스프링 컨테이너에서 찾아서 넣어주는데 이것을 의존성 주입, 즉 DI이라고 한다.
저번 회원 서비스 테스트 시에도 DI를 했었는데 그때는 개발자가 직접 주입한 형식이었고, 지금은 @Autowired를 사용하여 스프링이 직접 주입해준다.
Consider defining a bean of type 'hello.hellospring.service.MemberService' in your configuration.
하지만 실행 시에 다음과 같은 오류가 생긴다. 이는 memberService가 스프링 빈으로 등록되지 않았기 때문에 일어난 오류이다.
스프링 빈을 등록하는 2가지 방법이 있다. 첫 번째 방법은 컴포넌트 스캔과 자동 의존관계 설정이고, 두 번째 방법은 자바 코드로 직접 스프링 빈을 등록하는 방법이다.
● 컴포넌트 스캔과 자동 의존관계 설정
1. 컴포넌트 스캔 원리
@Component가 있으면 스프링 빈으로 자동 등록된다. 그리고 @Component를 포함하는 @Controller, @Service, @Repository도 스프링 빈으로 자동 등록된다.
2. 회원 서비스 스프링 빈 등록
@Service
public class MemberService {
private final MemberRepository memberRepository;
@Autowired
public MemberService(MemberRepository memberRepository) {
this.memberRepository = memberRepository;
}
}
MemberService 클래스에 @Service를 사용하여 스프링 빈으로 자동 등록한다.
MemberService 생성자에 @Autowired를 사용하여 객체 생성 시점에 스프링 컨테이너에서 해당 스프링 빈을 찾아서 주입한다.
생성자가 하나인 경우에는 @Autowired를 생략할 수 있다.
3. 회원 리포지토리 스프링 빈 등록
@Repository
public class MemoryMemberRepository implements MemberRepository {}
위와 마찬가지로 MemoryMemberRepository 클래스에 @Repository를 사용하여 스프링 빈으로 자동 등록한다.
4. 스프링 빈 등록 이미지
1~3의 과정을 통해 memberService와 memberRepository도 memberController와 마찬가지로 스프링 컨테이너에 스프링 빈으로 등록이 완료되었다.
참고로 스프링 빈이 등록될 때 기본적으로 싱글톤(유일하게 하나만 등록해서 공유)으로 등록한다. 그래서 같은 스프링 빈이면 모두 같은 인스턴스다.
● 자바 코드로 직접 스프링 빈을 등록하기
우선 memberService와 memoryMemberRepository의 @Service, @Repository, @Autowired를 제거하고 진행한다.
package hello.hellospring;
import hello.hellospring.repository.MemberRepository;
import hello.hellospring.repository.MemoryMemberRepository;
import hello.hellospring.service.MemberService;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class SpringConfig {
@Bean
public MemberService memberService() {
return new MemberService(memberRepository());
}
@Bean
public MemberRepository memberRepository() {
return new MemoryMemberRepository();
}
}
현재 진행 중인 프로젝트는 DB를 어떤 것으로 할지 정해져 있지 않으므로 향후에 MemoryRepository를 다른 Repository로 대체할 예정이다. 그래서 컴포넌트 스캔 대신에 자바 코드로 스프링 빈을 설정하여 나중에 간단하게 수정하는 방법이 더 적당하다.
1. @Configuration
설정 파일을 만들거나, 스프링 빈을 등록할 때 사용하는 어노테이션이다.
2. @Bean
스프링 컨테이너에 스프링 빈을 등록할 때 사용하는 어노테이션이다.
특히 @Bean은 개발자가 직접 제어가 불가능하거나 정형화 되어있지 않거나, 상황에 따라 구현 클래스를 변경해야 할 때사용한다.
반면에 정형화된 Controller, Service, Repository 같은 코드는 컴포넌트 스캔을 사용한다.
위에서는 DI를 할 때 생성자 주입을 사용했는데 그 외에도 필드 주입과 setter 주입이 존재한다.
//필드 주입
@Autowired private MemberService memberService;
//setter 주입
@Autowired
public void setMemberService(MemberService memberService){
this.memberService = memberService;
}
//생성자 주입
@Autowired
public MemberController(MemberService memberService){
this.memberService = memberService;
}
필드 주입은 외부에서의 변경이 불가능해지므로 테스트 케이스 등을 작성할 때 객체 수정이 불가능하기 때문에 어려움을 겪을 수 있다.
setter 주입은 setter가 public으로 공개되기 때문에 잘못 수정되어 문제가 발생할 가능성이 있다.
생성자 주입은 생성자의 호출 시점에 1회 호출이 보장되고, 주입받은 객체는 변하지 않고, DI를 강제할 수 있다는 장점이 있다.
(출처: https://velog.io/@zihs0822/DI의존성-주입-방식별-장단점)
마지막으로 @Autowired 어노테이션을 통한 DI는 helloConroller , memberService 등과 같이 스프링이 관리하는
객체에서만 동작한다. 스프링 빈으로 등록하지 않고 내가 직접 생성한 객체에서는 동작하지 않는다.
'개발 > 스프링 입문' 카테고리의 다른 글
(스프링 입문) 6. 스프링 DB 접근 기술 (0) | 2022.09.24 |
---|---|
(스프링 입문) 5. 회원 관리 예제 (웹 MVC 개발) (0) | 2022.09.24 |
(스프링 입문) 3. 회원 관리 예제 (0) | 2022.09.24 |
(스프링 입문) 2. 스프링 웹 개발 기초 (0) | 2022.09.24 |
(스프링 입문) 1. 프로젝트 환경 설정 (0) | 2022.09.06 |