art+insert 단축키로 생성자 생성
ctrl+p 단축키 작성해 함수 안에 들어갈 매개변수 찾기
🔻 member, service, repository 생성
🔻 service를 통해 member 가입, repository에 저장 및 가져오기
🔻 test
🔻 화면 붙이고 싶음 => controller와 view template 필요
🔻 memberController 필요
memberController는 memberService를 통해 회원가입 및 데이터 조회 가능
=> "memberController가 memberService를 의존한다"
컴포넌트 스캔과 자동 의존관계 설정, 자바 코드로 직접 스프링 빈 등록하기
anotation 넣기 @Contoller, @Service, @Repository
main/.../controller/MemberController.java
controller 아래에 MemberController 이름의 class 생성
🔸 @Controller
spring container에 MemberController 객체 생성 및 관리 (=스프링 빈 관리)
💡 MemberService 가져와서 써야함 => 스프링 컨테이너에 등록
🔸 private final MemberService memberService; 작성 후 alt+insert 단축키로 생성자 생성
🔸 @Autowired
생성자 위에 @Autowired 작성
💡 spring container에 있는 memberService를 가져와 연결
MemberController는 spring container가 뜰 때 생성-> 만들어진 생성자 호출
💎 MemberController는 spring container가 뜰 때 생성 후 만들어진 생성자 호출. MemberController가 생성될 때 springbean에 등록되어있는 MemberService 객체를 넣어줌 : dependency injection
🏅 최종 코드_MemberController.java
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;
}
}
MemberService는 순수 java 코드이기에 spring container가 알 수 없어 MemberService가 없다는 error 발생.
main/.../service/MemberService.java
🔸 @Service
MemberService.java의 MemberService class 위에 @Service 코드 추가 => spring이 올라올때 spring이 spring container에 MemberService 등록
🔸 @Autowired
MemberService 위에 @Autowired 추가 => MemberService를 생성하여 container에 등록.
🔹 즉, spring container에 있는 memberRepository에 구현체인MemoryMemberRepository 넣어줌
🏅 최종 코드_MemberService
package hello.hellospring.service;
import hello.hellospring.domain.Member;
import hello.hellospring.repository.MemberRepository;
import hello.hellospring.repository.MemoryMemberRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Optional;
@Service
public class MemberService {
private final MemberRepository memberRepository;
@Autowired
public MemberService(MemberRepository memberRepository) {
this.memberRepository = memberRepository;
}
/**
* 회원가입
*/
public Long join(Member member) {
// 같은 이름이 있는 중복 회원 검증
validateDuplicateMember(member);
// 회원 가입
memberRepository.save(member);
return member.getId();
}
private void validateDuplicateMember(Member member) {
memberRepository.findByName(member.getName())
.ifPresent(m -> {
throw new IllegalStateException("이미 존재하는 회원입니다");
});
}
/**
* 전체 회원 조회
*/
public List<Member> findMembers() {
return memberRepository.findAll();
}
public Optional<Member> findOne(Long memberId) {
return memberRepository.findById(memberId);
}
}
main/.../repository/MemoryMemberRepository.java
🔸 @Repository
MemoryMemberRepository.java에 @Repository 코드 추가
🏅 최종 코드_최종코드_MemoryMemberRepository
package hello.hellospring.repository;
import hello.hellospring.domain.Member;
import org.springframework.stereotype.Repository;
import java.util.*;
@Repository
public class MemoryMemberRepository implements MemberRepository{
private static Map<Long, Member> store = new HashMap<>();
private static long sequence = 0L;
@Override
public Member save(Member member) {
member.setId(++sequence);
store.put(member.getId(), member);
return member;
}
@Override
public Optional<Member> findById(Long id) {
return Optional.ofNullable(store.get(id));
}
@Override
public Optional<Member> findByName(String name) {
return store.values().stream()
.filter(member -> member.getName().equals(name))
.findAny();
}
@Override
public List<Member> findAll() {
return new ArrayList<>(store.values());
}
public void clearStore() {
store.clear();
}
}
🌟 컴포넌트는 원래 @Component
을 사용해 스프링빈으로 자동 등록
@Component를 포함하는 다음 annotation도 스프링 빈으로 자동 등록
@Controller, @Service, @Repository
💎 @Autowired
💡 MemberController와 MemberService 안에 작성
💡 Controller를 통해 외부 요청 받고, Service를 통해 business logic 만들고, Repository에 data 저장해야하므로 의존관계가 존재해야함
🔸 spring이 올라올 때, Component와관련된 annotation이 있을 경우 모두 스프링 객체를 생성한 후 스프링 컨테이너에 등록. Autowired는 연관관계를 나타냄. MemberController가 MemberService를 쓸 수 있고 MemberService가 MemberRepository를 쓸 수 있음
🔻 연관관계 표
🌟 실행 파일인 HelloSpringApplication.java 코드에서 package hello.hellospring을 가져오는데, 이 패키지를 포함한 그 하위 파일들만 컴포넌트 스캔의 대상이 됨. 다른 위치(ex.main/java)에 패키지를만들고 코드에 @Component 있어도 실행 파일 밖에 존재하기에 컴포넌트 스캔의 대상이 되지않고, 따라서 스프링빈에 등록되지않음
🔸 참고. 스프링은 스프링 컨테이너에 스프링 빈을 등록할 때 기본으로 싱글톤으로 기록(유일하게 하나만 등록해서 공유). 따라서 같은 스프링 빈이면 모두 같은 인스턴스. ex.memberService와 orderService에서 memberRepository를 @autowired하면 같은 인스턴스를 넣어 메모리 절약
💎 @Service, @Repository, @Autowired 와 @Autowired 사용하지 않고 코드로 직접 스프링 빈 등록
🔸 MemberController 파일의 @Controller, @Autowired만 남겨두고 다른 파일의 annotation 제거
main/.../hello/hellospring/SpringConfig.java
main/.../hello.hellospring 아래에 SpringConfig.java 파일 생성
🔸 @Configuration 작성
SpringConfig의 class 위에 @Configuration 작성
🔸 memberService
memberService bean에 등록
아래 코드 작성 후
@Bean
public MemberService memberService() {
return new MemberService(memberRepository());
}
단축키 ctrl+p 작성해 MemberService() 안에 들어갈 매개변수(memberRepository()) 찾기 => memberRepository가 필요하므로 얘도 bean에 존재해야함
🔸 MemoryMemberRepository
@Bean
public MemberRepository memberRepository() {
return new MemoryMemberRepository();
}
🏅 최종 코드_SpringConfig.java
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();
}
}
💡 참고) DI(Dependency Injection)에는 필드 주입, setter 주입, 생성자 주입 3가지 방법 존재 => 주로 생성자 주입 사용
@Autowired private MemberService memberService;
private MemberService memberService;
@Autowired
public void setMemberService(MemberService memberService) {this.memberService memberService;}
private final MemberService memberService;
@Autowired
public MemberController(MemberService memberService) {
this.memberService = memberService;
}
💡 참고
실무에서는 주로 정형화된 컨트롤러, 서비스, 리포지토리 같은 코드는 컴포넌트 스캔
을 사용. 정형화되지 않거나 상황에 따라 구현 클래스를 변경해야하면 코드 작성
을 통해 스프링빈으로 등록
=> 현재 데이터 저장소가 선정되지 않아 구현체로 MemoryMemberRepository 만든 후 교체하기로했음. 기존의 코드 변경없이 MemoryMemberRepository를 데이터베이스에 실제 연결하는 repository로 바꿀 것 => 스프링빈 등록할 경우 코드 변경 간단
1) 기존 구현체인 MemoryMemberRepository
@Bean
public MemberRepository memberRepository() {
return new MemoryMemberRepository();
}
2) 바꿀 구현체인 DbMemberRepository
@Bean
public MemberRepository memberRepository() {
return new DbMemberRepository();
}
💡 스프링빈으로 등록되어있지않읅경우 또는 내가 직접 생성한 객체의 경우 @Autowired 동작X. 스프링 컨테이너에 올라가있는 것들만 동작
Q. Test만 실행되고 main 실행 안 됨
A. Run -> edit configurations 선택 -> 아래 그림과 같이 Application 추가