TIL - 2022

[TIL 019] 스프링부트 - 컴포넌트 스캔과 의존관계 설정

바랄 희 2022. 3. 27. 21:13

김영한 강사님의 '스프링 입문 - 코드로 배우는 스프링 부트, 웹 MVC, DB 접근 기술' 강의를 듣고 작성했습니다.

오류가 존재할 가능성이 다분합니다! 댓글로 알려주세요

 


MemberController 생성

 

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 // dependency injection
    public MemberController(MemberService memberService){
        this.memberService = memberService;
    }


}

 

Controller 어노테이션을 사용하여 이가 컨트롤러임을 명시한다. 

Controller 라는 어노테이션을 명시하면 스프링에서는 스프링 컨테이너에 MemberController 라는 객체를 생성하여 넣어둔다. 

 

...중략
 *
 * @author Arjen Poutsma
 * @author Juergen Hoeller
 * @since 2.5
 * @see Component
 * @see org.springframework.web.bind.annotation.RequestMapping
 * @see org.springframework.context.annotation.ClassPathBeanDefinitionScanner
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Controller {

...중략
}

 

Controller 어노테이션에 들어가보면 @Component 가 존재하는 것이 보일 것이다. 

스프링에서는 Component 방식으로 스프링 빈을 등록하기 때문에 Controller 도 스프링 컨테이너에 빈으로 등록되는 것이다.

스프링 빈을 등록하는 이유는 스프링이 객체를 인지하게 하기 위해서이다. 스프링이 객체를 인지하게 하기 위해서는 ApplicationContext 에 담아줘야 하는데, 이것이 스프링 컨테이너에 빈으로 등록한다는 의미이다. 

 

private final MemberService memberService = new MemberService();

 

기존에는 위와 같이 서비스 객체를 생성했지만, 이렇게 될 경우에는 컨트롤러와 멤버 서비스가 연결되지 못한다. 스프링 컨테이너에서 두 객체가 의존관계로 설정되지 않았기 때문이다.

 

@Autowired // dependency injection
    public MemberController(MemberService memberService){
        this.memberService = memberService;
    }

 

여기서 Autowired 라는 어노테이션은 의존 관계 설정에서 중요한 역할을 한다. 

wired라는 이름과 일맥상통하는 역할을 하는데, 해당 컨트롤러와 멤버 서비스를 자동으로 찾아서 연결해준다. 

그렇게 되기 위해서는 당연히 멤버 서비스 역시도 스프링 컨테이너에 빈 등록이 되어 있어야 한다. 

따라서 아래와 같이 멤버 서비스 클래스를 수정해준다.

 

@Service
//스프링이 컨테이너에 등록할 수 있도록. 이걸 하지 않으면 스프링이 해당 클래스를 인식하지 못함.
public class MemberService {

    //final 은 불변이 아니라 재할당할 수 없도록 한다.
    // 최초 초기화나 상속 이후 변할 수 없는 값.
    private final MemberRepository memberRepository;

    @Autowired //레포지토리와 서비스를 연결시켜준다.
    public MemberService(MemberRepository memberRepository){
        this.memberRepository = memberRepository;
    }

 

위의 Service 라는 어노테이션 역시 들어가보면 Component 어노테이션이 존재하는 것을 확인할 수 있다. 이를 해야만이 스프링 컨테이너에서 해당 객체를 스프링 빈으로 등록시킨다. 

그러나, 멤버 서비스 역시도 생성자로 멤버 레포지토리가 필요하다. 따라서 이도 위와 같은 이유로 Autowired 어노테이션을 달아 주었다.

 

위와 같은 과정과 이유로 멤버레포지토리 클래스도 스프링 빈으로 등록 될 수 있도록 해야 한다.

따라서 아래와 같이 수정하였다.

 

@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;
    }

 

Repository 어노테이션 역시도 안에 들어가보면 Component 어노테이션이 존재한다. 

 

이렇게 스프링 빈을 등록하는 방식을 컴포넌트 스캔 방식이라고 한다. 

그렇다면 어디에서든지 컴포넌트 어노테이션을 선언하면 이가 스프링 빈으로 등록되는 것일까?

답은 아니다.

 

 

아래는 HelloSpringApplication.java 이다.

 

package hello.hellospring;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class HelloSpringApplication {

	public static void main(String[] args) {
		SpringApplication.run(HelloSpringApplication.class, args);
	}

}

 

해당 파일에서 선언된 패키지 hello.hellospring 이하의 폴더 혹은 파일 중에서 컴포넌트를 선언한 경우에만 스프링 빈으로 등록된다.