전체적인 설명
의존관계는 2가지 방법이 있다
1. (자동) 컴포넌트 스캔과 자동 의존관계 설정
2. (수동) 자바 코드로 직접 스프링 빈 등록하기
컴포넌트 스캔은 어노테이션 박으면 저절로 스프링이 연결시켜준다
자바 코드로 직접 스프링 빈 등록하면
DB 교체할때 코드 하나도 안건드리고 교체할 수 있다
(우리가 DB 정해지지 않았다는 시나리오를 가정했다는 것을 기억하자)
맴버서비스 인스턴스를 여러개 생성할 필요가 없이
하나만 생성하고 공용으로 사용하면 됨 (싱글톤, 스프링은 싱글톤이 디폴트)
@Autowired를 쓰면 스프링 컨테이너에 있는 맴버서비스를 연결시켜줌 (DI, 의존관계 외부에서 주입)
맴버서비스를 스프링 컨테이너에 등록하려면 @Component를 써야되는데
컨트롤러, 서비스, 리포지토리 각각 @Contorller, @Service, @Repository를 씀
컴포넌트 스캔과 자동 의존관계 설정
■ 이번 시간에는 화면을 붙이고 싶음
ㅡ> 컨트롤러랑 뷰 탬플릿이 필요
■ 회원 가입하고, 회원 가입된 결과를 HTML로 뿌려주고 이런 것들을 하려고 함
ㅡ> 그럴려면 맴버 컨트롤러를 만들어야 함
■ 맴버 컨트롤러가 맴버 서비스를 통해서
회원 가입하고, 데이터를 조회할 수 있어야 함
ㅡ> 이런 것을 의존관계라고 함
ㅡ> 맴버 컨트롤러와 맴버 서비스의 의존관계
코딩
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;
}
}
- 생성자에 @Autowired 가 있으면 스프링이 연관된 객체를 스프링 컨테이너에서 찾아서 넣어준다. 이렇게 객체 의존관계를 외부에서 넣어주는 것을 DI (Dependency Injection), 의존성 주입이라 한다.
- 이전 테스트에서는 개발자가 직접 주입했고, 여기서는 @Autowired에 의해 스프링이 주입해준다.
Controller라는 Annotation을 보고 (@Controller)
스프링이 이 MemberController를 객체를 생성을 해서 스프링이 들고 있는다
ㅡ> 이것을 스프링 컨테이너에서 스프링 빈이 관리된다고 표현
HelloController로 돌어가서 예시
초록색
ㅡ> 스프링빈
ㅡ> 땅콩처럼 그려놨음
ㅡ> @Controller가 있으면, 컨테이너 안에 스프링 빈이 생성돼서 관리됨
ㅡ> 그래서 스프링에서 컨트롤러가 동작함
new로 생성하면 문제점
ㅡ> 객체를 new 했는데, MemberService 들어가보면 별 기능이 없음
ㅡ> 얘는 여러개 인스턴스를 생성할 필요가 없음
ㅡ> 하나만 생성하고 공용으로 사용하면 됨
ㅡ> 그래서 스프링 컨테이너에 등록하고 씀
@Autowired를 하면
스프링 컨테이너에서 스프링 빈(MemberController)이 생성될때,
스프링 컨테이너에 있는 MemberService를 연결시켜준다
(P) 오류
그러나, MemberService가 순수한 Java클래스여서
스프링이 얘의 존재를 알 수가 없음
(S) Annotation 사용
서비스에는
@Service
리포지토리에는
@Repository
이렇게 하면 스프링에 맴버서비스, 맴버리포지토리가 등록되고
@Autowired가 발동될 때
맴버 컨트롤러에 넣어준다
이것을 D.I.라고 한다. (Dependency Injection)
(의존관계 주입)
컴포넌트 스캔 원리
@Component 애노테이션이 있으면 스프링 빈으로 자동 등록된다.
@Component 를 포함하는 다음 애노테이션도 스프링 빈으로 자동 등록된다.
- @Controller
- @Service
- @Repository
회원 서비스 스프링 빈 등록
@Service
public class MemberService {
private final MemberRepository memberRepository;
@Autowired
public MemberService(MemberRepository memberRepository) {
this.memberRepository = memberRepository;
}
}
참고: 생성자에 @Autowired 를 사용하면 객체 생성 시점에 스프링 컨테이너에서 해당 스프링 빈을 찾아 서 주입한다. 생성자가 1개만 있으면 @Autowired 는 생략할 수 있다.
회원 리포지토리 스프링 빈 등록
@Repository
public class MemoryMemberRepository implements MemberRepository {}
스프링 빈 등록 이미지
참고: 스프링은 스프링 컨테이너에 스프링 빈을 등록할 때, 기본으로 싱글톤으로 등록한다(유일하게 하나만 등록해서 공유한다) 따라서 같은 스프링 빈이면 모두 같은 인스턴스다. 설정으로 싱글톤이 아니게 설정할 수 있지 만, 특별한 경우를 제외하면 대부분 싱글톤을 사용한다.
스프링은 어디서부터 스캔할까?
데모
데모라는 걸 만들고 여기에 @Service를 쓰면 컴포넌트 스캔을 할까?
ㅡ> No
헬로스프링
hello.hellospring 패키지에서 시작해서 (@SpringBootApplication이 선언된 패키지)
이 패키지와 하위들을 스프링이 다 뒤져서 스프링 빈으로 등록한다
나머지는 기본적으로 스캔 안한다
사실 @SpringBootApplication안에 들어가보면
@ComponentScan이라는게 있다.
자바 코드로 직접 스프링 빈 등록하기
일단 @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();
}
}
@Configuration 해주고
@Bean 해준다
ㅡ> 빈을 등록한다는 의미
MemberRepository = 인터페이스
MemoryMemberRepository = 구현체
컨트롤러
ㅡ> 컨트롤러는 어차피 스프링이 관리
ㅡ> @Contoller, @Autowired사용
여기서는 향후 메모리 리포지토리를 다른 리포지토리로 변경할 예정이므로,
컴포넌트 스캔 방식 대신에 자바 코드로 스 프링 빈을 설정하겠다.
결론
XML로 설정하는 방식도 있지만 최근에는 잘 사용하지 않으므로 생략한다
Dependency Injection 3가지 방법
DI에는 필드 주입, setter 주입, 생성자 주입 이렇게 3가지 방법이 있다.
의존관계가 실행중에 동적으로 변하는 경우는 거의 없으므로 생성자 주입을 권장한다.
ㅡ> 의존관계 변할땐, config파일을 수정하고 서버를 다시 올리지, 런타임에 동적으로 변경하지 않는다.
1.생성자 주입
2.필드 주입
생성자 없애고 필드에 @Autowired
ㅡ> 별로 안좋은 방법
ㅡ> 뭔가 바꿀 수 있는 방법이 별로 없어서
3. setter 주입
Setter Injection 방식
단점
ㅡ> 누군가가 맴버 컨트롤을 호출 했을 때, 세터가 public으로 열려있어야 함
ㅡ> 맴버 서비스를 바꿀 이유가 없는데, public으로 노출되는게 위험함
ㅡ> (로딩 시점에 세팅하고 나면 바꿀 일이 없음)
결론적으로, 생성자 주입을 쓰는게 좋다.
실무에서는 주로 정형화된 컨트롤러, 서비스, 리포지토리 같은 코드는 컴포넌트 스캔을 사용한다.
그리고 정형화 되지 않거나, 상황에 따라 구현 클래스를 변경해야 하면 설정을 통해 스프링 빈으로 등록한다.
ㅡ> 우리는 DB가 선정되지 않았다는 가상의 시나리오가 있었다
ㅡ> 그래서 메모리 리포지토리로 일단 구현하고 나중에 교체하기로 했었다
ㅡ> 근데 기존의 코드를 하나도 손대지 않고 바꿔치기 하는 방법이 있다
ㅡ> 그게바로 직접 스프링 빈으로 등록하는 방법이다
DB변경 (직접 스프링 빈으로 등록하는 방법)
'프레임워크 > Spring' 카테고리의 다른 글
[김영한] 스프링 입문(5) - 회원 관리 예 - MVC 개발 (1) | 2024.08.30 |
---|---|
[김영한] 스프링 입문(6) - 스프링 DB 접근 기술 (0) | 2024.08.22 |
[김영한] 스프링 입문(3) - 회원 관리 예제 - 백엔드 개발 (0) | 2024.08.05 |
[김영한] 스프링 입문(2) - 스프링 웹 개발 기초 (0) | 2024.08.01 |
[김영한] 스프링 입문(1) - 프로젝트 환경설정 (1) | 2024.07.29 |