[Spring] Bean생성, 의존성 주입

정원석·2024년 9월 9일

제어의 역전(IoC)

개발자가 클래스를 만들고 인스턴스를 관리하면 코드가 길어졌을 때 관리하기 힘들고 실수가 나올 수 있다. 이러한 문제를 예방하기 위해 프레임워크가 객체 생성과 프로그램 흐름을 제어하는 것을 제어의 역전이라 한다.

  • 개발자는 코드의 흐름이나 객체 생성에 관련된 코드를 직접 작성하지 않는다.
  • 개발자는 프레임워크가 제공하는 정의 방법을 사용해 코드를 정의만 한다.
  • 프레임워크는 이 정의를 보고 객체를 생성해 코드가 동작하는 순서를 결정하여 실행한다.

Spring Bean

  • Spring Bean은 Spring IoC 컨테이너에서 관리되는 객체를 말한다.
  • Spring 프레임워크에서는 애플리케이션의 객체들을 Bean으로 등록하고 관리를 컨테이너가 담당한다.
  • 보통 어노테이션 등으로 의존성 주입이 되고, 이를 통해 느슨한 결합과 코드 재사용성, 테스트 용이성 등의 이점을 얻을 수 있다.

ApplicationRunner

  • Spring 어플리케이션이 시작될 때 같이 실행되는 코드

Bean 생성 2가지 방법

@Configuration + @Bean 를 이용한 방법

@Configuration
public class GreetingConfig {

    @Bean
    Greeting englishGreeting() {
        return new EnglishGreeting();
    }
}

@Configuration
public class OrderConfig {

    @Bean
    public OrderReceiverBean orderReceiverBean() { return new OrderReceiverBean(); }

    @Bean
    public PaymentProcessorBean paymentProcessorBean() { return new PaymentProcessorBean(); }

    @Bean
    OrderProcessorBean orderProcessorBean() { return new OrderProcessorBean(orderReceiverBean(), paymentProcessorBean()); }
}

@Component 를 이용한 방법

@Component
public class EnglishFarewell implements Farewell {

    @Override
    public void sayGoodBye() {
        System.out.println("good bye");
    }
}

등록된 Bean을 가져와서 사용 가능

@RequiredArgsConstructor
@Component
public class AppStartupRunner implements ApplicationRunner {
    private ChefBean chefBean;
    private final OrderProcessorBean orderProcessorBean;
    @Autowired
    DeliveryServiceBean deliveryServiceBean;

    @Autowired
    public void setChefBean(ChefBean chefBean) {
        this.chefBean = chefBean;
    }

    public void run(ApplicationArguments args) {
        orderProcessorBean.processOrder();
        chefBean.cook();
        deliveryServiceBean.deliver();
    }
}

[@Configuration + @Bean] vs [@Component]

  • Component 에노테이션은 클래스 레벨에서 사용되며, 해당 클래스의 인스턴스를 스프링 컨테이너가 빈으로 등록한다.
  • @Configuration 에노테이션은 클래스가 하나 이상의 @Bean 메소드를 포함하고 있으며, 이 메소드 들이 애플리케이션 컨텍스트에 Bean 정의를 제공한다.

스테레오타입(Stereotype) 에노테이션

  • @Controller
    • 프레젠테이션 계층에서 사용되며, 클라이언트의 요청을 처리하고 뷰를 반환하는 역할
  • @Service
    • 비즈니스 로직을 처리하는 서비스 계층에서 사용
  • @Repository
    • 데이터 엑세스 계층에서 사용되며, 데이터베이스와의 상호작용을 담당하는 클래스

Spring Bean 객체의 생명주기 설정

Bean Scope

  • 싱글톤 : ApplicationContext가 시작될 때 초기화되고, ApplicationContext가 종료될 때 소멸된다. 이는 애플리케이션 전체이서 단 하나의 인스턴스만 유지함을 의미한다.
  • 프로토타입 : 요청될 때마다 새로운 인스턴스가 생성. 생성된 각 인스턴스는 호출자에 의해 관리되며, Spring컨테이너는 생성 이후에 이를 관리하지 않는다.
  • request(web) : HTTP 요청 하나가 들어오고 나갈 때 까지 유지되는 스코프, 각각의 HTTP 요청마다 별도의 빈 인스턴스가 생성되고 관리된다.
  • session(web) : HTTP Session과 동일한 생명주기를 가지는 스코프
  • application(web) : 서블릿 컨텍스트(ServletContext)와 동일한 생명주기를 가지는 스코프
  • websocket(web) : 웹 소켓과 동일한 생명주기를 가지는 스코프

의존성 주입

객체가 필요로 하는 의존성을 외부에서 직접 주입받는 것을 의존성 주입이라 한다.

  • 객체간 의존성을 낮춘다.
  • 외부에서 객체를 생성하고 전달한다.
  • 구성요소간 의존관계가 소스코드 내부가 아닌 외부의 설정파일 등을 통해 정의되게 하는 디자인 패턴중 하나이다.

의존성 역전 원칙

  • 상위 모듈이 하위 모듈에 의존관계를 가지지 않도록 구현해야 한다는 원칙
  • 추상클래스는 그 구현체의 내용에 의존관계를 갖지 않는다.
  • 구현체가 추상클래스에 의존관계를 가질 수 있다.

public class OrderReceiverBean {
    public void receiveOrder(){
        System.out.println("주문 받는다.");
    }
}

 public void processPayment(){
        System.out.println("결제 처리를 한다.");
    }
}

@RequiredArgsConstructor
public class OrderProcessorBean {

    private final OrderReceiverBean orderReceiverBean;
    private final PaymentProcessorBean paymentProcessorBean;

    public void processOrder(){
        orderReceiverBean.receiveOrder();
        paymentProcessorBean.processPayment();
    }
}

Setter Injection

  • 주입 받고 싶은 객체를 선언한다.
  • 주입 받고 싶은 객체의 세터를 생성한다.
  • 세터에 @Autowired 를 붙여준다.
@Component
public class AppStartupRunner implements ApplicationRunner {
    private ChefBean chefBean;
    
    @Autowired
    public void setChefBean(ChefBean chefBean) {
        this.chefBean = chefBean;
    }

    @Component
public class AppStartupRunner implements ApplicationRunner {

    private Greeting greeting;

    @Autowired
    public void setGreeting(Greeting greeting) {
        this.greeting = greeting;
    }
 
    @Override
    public void run(ApplicationArguments args) {
        greeting.sayHello();
    }
}

Field Injection

  • 주입 받고 싶은 객체를 선언한다.
  • 주입 받고 싶은 객체에 @Autowired 를 붙여준다.
@Component
public class AppStartupRunner implements ApplicationRunner {
@Autowired
    DeliveryServiceBean deliveryServiceBean;
    }

@Component
public class AppStartupRunner implements ApplicationRunner {

    @Autowired
    private Greeting greeting;


    @Override
    public void run(ApplicationArguments args) {
        greeting.sayHello();
    }
}

profile
Back-End-Dev

0개의 댓글