본문 바로가기

WINK-(Web & App)/Spring Boot 스터디

[Spring Boot 스터디] 이지원 #3 주차 - 섹션 4, 5

반응형

컴포넌트 스캔과 자동 의존관계 설정

 

서비스를 구축하다 보면 동일한 객체 인스턴스를 프로젝트 전역에 걸쳐 공유해야할 경우가 많다. 이 경우 하나의 인스턴스를 생성한 후 어딘가에 저장해두고 다른 코드에서 그 경로를 찾아 import하는 번거로운 작업이 요구된다.

 

Spring에서는 이러한 의존관계를 자동으로 설정해준다.

@Controller
@Repository
@Service

 

등의 방법을 통해 해당 클래스를 하나의 객체 인스턴스만 생성하고 프로젝트 전역에 걸쳐 자동으로 공유되도록 구현할 수 있다.

Spring 어플리케이션이 시작되면 초기 단계에서 어플리케이션이 자동으로 어플리케이션의 위치를 기준으로 모든 하위 디렉토리를 스캔하여 이런 annotation이 붙은 클래스들을 찾는다.

이후 찾은 클래스들의 인스턴스를 모두 하나씩 생성한 후 Spring Bean이라는 곳에 등록한다.

이제 이 인스턴스들은 Spring 어플리케이션 전역에 걸쳐 공유될 준비를 마친 것이다.

 

이제 다른 코드에서 이 인스턴스를 공유받으려면

@Autowired

 

를 사용한다.

 

Spring 이 어노테이션이 붙은 코드를 읽고 매개변수로 일치하는 타입의 인스턴스를 넘겨준다. 이때 넘겨주는 인스턴스는 초기에 Spring Bean에 등록되어 전역으로 공유될 준비를 마친 바로 그 인스턴스이다.

 

이 방식을 통해 직접 import문을 작성하지 않고 인스턴스를 공유할 수 있으며 관리하기 더 수월하다.

 

 

아래는 실제 코드 적용 예시이다.

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;
    }
}
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();
    }
}
package hello.hellospring.service;

import hello.hellospring.domain.Member;
import hello.hellospring.repository.MemberRepository;
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);
    }
}

 

 

 

 

자바 코드로 직접 Spring Bean 등록

 

컴포넌트 스캔과 자동 의존관계 설정을 이용하지 않고 직접 JAVA코드로 Spring Bean에 등록할 수도 있다.

 

 Spring 어플리케이션의 위치에 SpringConfig 클래스 파일을 만든 다음 다음과 같이 입력한다.

 

@Configuration
public class SpringConfig {
    @Bean
    public MemberService memberService() {
        return new MemberService(memberRepository());
    }
    @Bean
    public MemberRepository memberRepository() {
        return new MemoryMemberRepository();
    }
}

 

이제 Spring 어플리케이션이 시작될때 Spring이 이 Config파일을 읽어 내부에 있는 함수들을 실행하게 된다.

각 함수들은 특정 객체를 Spring Bean에 등록하게 된다.

 

 

이후 이 내용을 응용한 다음과 같은 코드를 사용하여 간단한 회원 가입 / 조회 기능을 구현할 수 있다.

 

@GetMapping("/members/new")
public String createForm() {
    return "members/createMemberForm";
}
@PostMapping("/members/new")
public String create(MemberForm form) {
    Member member = new Member();
    member.setName(form.getName());
    memberService.join(member);
    return "redirect:/";
}
@GetMapping("/members")
public String list(Model model) {
    List<Member> members = memberService.findMembers();
    model.addAttribute("members", members);
    return "members/memberList";
}
package hello.hellospring.controller;

public class MemberForm {
    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}
반응형