본문 바로가기

Spring 공부

spring 공부 - 컴포넌트 스캔, 빈 등록

728x90
반응형

TEST

 

개별적으로 실행할 수 있음


alt + insert 하면 자동생성


assertThat(e.getMessage()).isEqualTo("이미 존재하는 회원입니다.");


첫번째와 두번째 주소를비교한 후 같으면 진행 틀리면 알림

 

package hello.hellospring.service;

import hello.hellospring.domain.Member;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;

import static org.junit.jupiter.api.Assertions.*;

import hello.hellospring.domain.Member;
import hello.hellospring.repository.MemoryMemberRepository;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.*;
import static org.junit.jupiter.api.Assertions.*;


class MemberServiceTest {
  MemberService memberService;
  MemoryMemberRepository memberRepository;

  @BeforeEach
  public void beforeEach() { // 테스트 실행마다 독립적으로 하기 위해 생성해줌
    memberRepository = new MemoryMemberRepository();
    memberService = new MemberService(memberRepository); // 인스턴스 생성해서 보내줌
  } @AfterEach
  public void afterEach() {
    memberRepository.clearStore(); // 돌고 난 후 메모리 클리어
  }
  @Test
  public void 회원가입() throws Exception {
    //Given
    Member member = new Member();
    member.setName("hello");
    //When
    Long saveId = memberService.join(member); // 회원가입하고 아이디 반환
    //Then
    Member findMember = memberRepository.findById(saveId).get();
    assertEquals(member.getName(), findMember.getName());
  }
  @Test
  public void 중복_회원_예외() throws Exception {
    //Given
    Member member1 = new Member();
    member1.setName("spring");
    Member member2 = new Member();
    member2.setName("spring");
    //When
    memberService.join(member1);
    IllegalStateException e = assertThrows(IllegalStateException.class, // IllegalStateException.class 예외가 터져야함
            // e 에다가 예외 담음
            () -> memberService.join(member2));//예외가 발생해야 한다.
    assertThat(e.getMessage()).isEqualTo("이미 존재하는 회원입니다.");
  }
}

 

컴포넌트 스캔

 

생성자에 @Autowired 가 있으면 스프링이 연관된 객체를 스프링 컨테이너에서 찾아서 넣어준다. 이렇게 객체 의존관계를 외부에서 넣어주는 것을 DI (Dependency Injection), 의존성 주입이라 한다.

 

 

컨트롤러 통해서 외부 요청 받고, 서비스에서 비즈니스 로직 만들고, 레포지토리에서 데이터 저장 만들고

 

 

 

메인 있는 패키지 하위나 동일한거 아니면 컴포넌트 스캔안함
싱글톤으로 등록함

같은 스프링 빈이면 같은 인스턴스 사용함

 

MemberController


@Contolloer 어노테이션

 

@Contolloer
public class MemberController {
  private final MemberService memberService;
  @Autowired //스프링이 연관된 객체를 스프링 컨테이너에서 찾아서 넣어준다.
  public MemberController(MemberService memberService) {
    this.memberService = memberService;
  }
}

 

@Autowired : 스프링이 연관된 객체를 스프링 컨테이너에서 찾아서 넣어준다.

 

MemberService


@Service 어노테이션

 

@Service
public class MemberService {


  private final MemberRepository memberRepository;
  @Autowired // MemberRepository랑 연결
  public MemberService(MemberRepository memberRepository) { // 외부에서 넣어줘서 생성되도록 함
    this.memberRepository = memberRepository;
  }

  /**
   * 회원가입
   */
  public Long join(Member member) { //회원가입
    // null이 아니라 이미 값이 있으면
    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);
  }
}

 

MemberRepository
@Repository 어노테이션

 

import java.util.*;
/**
 * 동시성 문제가 고려되어 있지 않음, 실무에서는 ConcurrentHashMap, AtomicLong 사용 고려
 */
@Repository
public class MemoryMemberRepository implements MemberRepository {
  public 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 List<Member> findAll() {
    return new ArrayList<>(store.values());
  }
  @Override
  public Optional<Member> findByName(String name) {
    return store.values().stream()
            .filter(member -> member.getName().equals(name))
            .findAny();
  }
  public void clearStore() {
    store.clear();
  }
}

 

자바코드로 빈 직접 등록


스프링이 뜰때 configration 읽고 스프링 등록하라는 뜻이네 하고 등록함

configuration 파일 만듦, 컨트롤러는 어쩔수없이 컴포넌트 스캔해야함

 

@Configuration
public class SpringConfig {
  @Bean // 빈을 등록하겠다
  public MemberService memberService() {
    return new MemberService(memberRepository());
  }
  @Bean
  public MemberRepository memberRepository() {
    return new MemoryMemberRepository();
  }
}

 

 

DI에는 필드 주입, setter 주입, 생성자 주입 이렇게 3가지 방법이 있다. 의존관계가 실행중에 동적으로 변하는 경우는 거의 없으므로 생성자 주입을 권장한다.
(위에서 쓴거.)

 

실무에서는 주로 정형화된 컨트롤러, 서비스, 리포지토리 같은 코드는 컴포넌트 스캔을 사용한다. 그리고 정형화 되지 않거나, 상황에 따라 구현 클래스를 변경해야 하면 설정을 통해 스프링 빈으로 등록한다.

 

@Autowired 를 통한 DI는 helloController , memberService 등과 같이 스프링이 관리하는 객체에서만 동작한다. 스프링 빈으로 등록하지 않고 내가 직접 생성한 객체에서는 동작하지 않는다.

 

 

 

 

반응형