Spring의 객체 관리 : 첫째, 스프링 컨테이너와 빈

Eojin·2023년 6월 20일
0

➡️ 스프링 컨테이너

스프링에서 제공하는 스프링 컨테이너는 빈을 생성하고 관리한다. 다시말해, 빈이 생성되고 소멸되는 빈의 생명주기를 스프링 컨테이너에서 관리하는 것이다.

뿐만 아니라 스프링은 @Autowired 같은 애너테이션을 사용해 빈을 주입할 수 있게 DI를 지원하기도 한다.

✅ 빈(Bean)

스프링 컨테이너가 생성하고 관리하는 객체로, 어렵게 생갈할 필요없이 스프링만의 객체라고 생각하면 된다

스프링은 빈을 스프링 컨테이너에 등록하기 위해 XML 파일 설정, 애너테이션 추가 등의 여러가지 방법을 제공한다.

@Component
//클래스 MyBean을 빈으로 등록하는 애너테이션

public class MyBean {
}


➡️ 클래스를 빈으로 등록하는 방법

클래스 용도에 따라 다른 애너테이션을 사용하기는 하지만, 사실 내부 코드를 보면 @Component 애네터이션이 모두 달려 있다.

✅ @Component

클래스를 빈으로 등록하기만 해주면 될 때 사용하는 애너테이션


✅ @Configuration

클래스를 설정 파일로 등록하면서 빈으로 등록해줄 때 사용하는 애너테이션


✅ @Repository

클래스를 ORM 매핑을 하면서 빈으로 등록해줄 때 사용하는 애너테이션


✅ @Controlelr와 @RestController

클래스를 라우터로 만들어주면서 빈으로 등록해줄 때 사용하는 애너테이션이다.

예를 들어 아래와 같은 코드가 있다고 가졍해보자.

@GetMapping("/test")
public String test() {
	return "Hello, world!";
}

/test라는 GET 요청이 왔을 때


✅ @Service

클래스가 비즈니스 로직을 담당하게 하면서 빈으로 등록시킬 때 사용하는 애너테이션



❓ Bean이 같은 타입으로 2개 등록이 된다면 ❓

자주 발생하는 케이스는 아니며 등록 자체도 문제없다! 하지만 부를 때 같은 타입인 2개의 Bean 중에서 무엇을 선택할지 지정해주지 않으면 에러가 발생한다. 그럴땐 아래와 같이 원하는 Bean의 이름을 지정해주면 된다.


첫번째 방법. Bean의 이름으로 지정

public interface Food {
//인터페이스의 구현클래스를 빈에 등록하면 인터페이스도 빈에 등록된다

    void eat();
}


import org.springframework.stereotype.Component;
@Component
class Pizza implements Food {
    @Override
    public void eat() {
        System.out.println("피자를 먹습니다.");
    }
}


@Component //빈 등록
class Chicken implements Food {
    @Override
    public void eat() {
        System.out.println("치킨을 먹습니다.");
    }
}

import com.sparta.springauth.food.Food;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;


@SpringBootTest
//DI 주입 사용할 수 있게 해주는 annotation

public class BeanTest {
    //Food 인터페이스 주입받기
    //인터페이스에 @Autowired 할 수 있을까? 있다! 인터페이스의 구현체가 주입된다.
    @Autowired
    //Food food; //오류 발생!! Could not autowire. There is more than one bean of 'Food' type. 
    // Food 타입의 빈이 2개이므로 어떤 것을 주입해줄지 모르겠다는 의미
    Food pizza; //빈의 이름으로 직접 지정해주는 방법
    
    @Autowired
    Food chicken;
}



두번째 방법. @Primary

import com.sparta.springauth.food.Food;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;


@SpringBootTest
public class BeanTest {
    @Autowired
    Food food; //주입할 Bean을 @Primary로 지정해줬기 때문에 오류가 발생하지 않음
}   


@Component
@Primary
class Pizza implements Food {
    @Override
    public void eat() {
        System.out.println("피자를 먹습니다.");
    }
}



세번째 방법. @Qualifier

package com.sparta.springauth;

import com.sparta.springauth.food.Food;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.test.context.SpringBootTest;


@SpringBootTest
public class BeanTest {

	@Autowired
    @Qualifier("chicken")
    Food food; 


    @Test
    @DisplayName("@Qualifier 와 @Primary 우선순위 비교") 
    //@Qualifier 가 @Primary 보다 우선순위가 높다
    
    void test2() {
        food.eat();
    }
}

같은 Bean 타입의 객체에 @Qualifier와 @Primary가 같이 걸려있다면, @Qualifier의 우선순위가 높다.


참고로 범용적으로 사용되는 Bean 객체에는 @Primary, 일부에게만 달아줘야하는 Bean 객체에는 @Qualifier를 하나하나 붙여주는 것이 효율적이다.



➡️ 참고 자료

[교재]
스프링부트3 백엔드 개발자되기 자바편


profile
'함께'가 즐거운 개발 공부하는 사람입니다.

0개의 댓글

관련 채용 정보