본문 바로가기
카테고리 없음

230218 TIL

by hbIncoding 2023. 2. 19.

1.  스프링 기초

  1) 스프링 컨테이너와 스프링 빈

  • 스프링 컨테이너  : 자바 객체의 생명 주기를 관리하며, 새성된 자바 객체들에게 추가적인 기능을 제공하는 역할
  • 빈 : 위에서 말하는 자바 객체를 스프링에서 빈(Bean)이라고 부른다.
  • 개발자가 new 연산자, 인터페이스 호출, 팩토리 호출 방식으로 객체의 생명주기를 관리했었는데 이것을 스프링 컨테이너가 대신하주는 것이다. 즉 제어 흐름을 외부에서 관리하는 IoC가 발생한 것이다.
  • 객체들간의 의존 관계를 스프링 컨테이너가 런타임 과정에서 알아서 만들어준다.
  • 스프링 컨테이너의 종류
    • BeanFactory : 빈을 등록하고 생성, 조회, 돌려주는 역할 > 빈을 관리하는 역할을 한다.
    • BaenFactory를 AnnotationConfigApplicationContext로 정의하되, AppConfig를 구성 정보로 지정
    • 기존에는 개발자가 직업 AppConfig를 사용해 필요 객체를 직접 조회했지만, 이제 스프링 컨테이너를 통해서 필요한 스프링 빈 객체를 찾을 수 있다. 

  2) 스프링 컨테이너의 종류

  • BeanFactory : 빈을 등록하고 생성, 조회, 돌려주는 역할 > 빈을 관리하는 역할을 한다.
    • BaenFactory를 AnnotationConfigApplicationContext로 정의하되, AppConfig를 구성 정보로 지정
    • 기존에는 개발자가 직업 AppConfig를 사용해 필요 객체를 직접 조회했지만, 이제 스프링 컨테이너를 통해서 필요한 스프링 빈 객체를 찾을 수 있다.
    • 처음으로 getBean() 메소드가 호출된 시점에서야 해당 빈을 생성
public class Main {

    public static void main(String[] args) {
        final BeanFactory beanFactory = new AnnotationConfigApplicationContext(AppConfig.class);
        final OrderService orderService = beanFactory.getBean("orderService", OrderService.class);
        final Order order = orderService.createOrder(15, "샤프", 3000);
        System.out.println(order.getDiscountPrice());
    }
}
  • ApplicationContext : BeanFactory처럼 빈을 관리 할 수 있다.
    • 아래와 같이 실행해도 BeanFactory 와 동일한 결과를 얻을 수 있다.
    • ApplicationContext가 BeanFactory를 상속 받았기 때문이다.
    • 그 외에도 국제화가 지원되는 텍스트 메시지 관리, 이미지 같은 파일 자원 로드, 리소스로 등록된 빈에게 이벤트 발생 알림 등 부가적인 기능을 가지고 있다.
    • Context 초기화 시점에 모든 싱글톤 빈을 미리 로드한 후 어플리케이션 가동 후에 빈을 지연없이 받을 수 있다. 
    • 부가 기능과 지연 없는 장점에서 BeanFactory 보다 주로 사용한다.
public class Main {

    public static void main(String[] args) {
        final ApplicationContext beanFactory = new AnnotationConfigApplicationContext(AppConfig.class);
        final OrderService orderService = beanFactory.getBean("orderService", OrderService.class);
        final Order order = orderService.createOrder(15, "샤프", 3000);
        System.out.println(order.getDiscountPrice());
    }
}

  3) 싱글톤 컨테이너

  • 스프링 컨테이너는 객체의  인스턴스를 싱글톤으로 관리하므로 싱글톤 컨테이너라고도 불린다.
  • 그래서 AppConfig 클래스를 읽어서 빈 목록에 있는 빈을 여러번 불러도 그 주소는 똑같다.
@Configuration
public class AppConfig {

    @Bean
    public OrderService orderService() {
        return new OrderServiceImpl(discountPolicy());
    }

    @Bean
    public FixDiscountPolicy discountPolicy() {
        return new FixDiscountPolicy();
    }
}
  • 각각 OrderService 와 discountPolicy를 등록한다면, Discountpolicy를 두번 호출하여 싱글톤이 깨지는 것 처럼 보인다.
  • 하지만 실제로는 @Configuration을 통해 싱글톤을 보장한다.
  • CGLIB라는 바이트코드 조작 라이브러리를 이용해 AppConfig 클래스를 상속받은 임의의 클래스를 만들고, 이 클래스를 스프링 빈으로 등록한다. 이 클래스가 내부적으로 @Bean이 붙은 메소드 마다 스프링 빈이 존재하는지 확인하고 있다면 존재하는 빈을 반환하는 형식으로 역할을 한다. 특정 객체를 빈으로 등록하는 순간 그 객차의 자식 객체들도 연쇄적으로 모두 빈에 등록된다.
  • 즉 @Configuration 을 적용하지 않으면 싱글톤을 보장 받지 못한다.

 

 

4) 컴포넌트 스캔과 의존관계 자동 주입

  • @Bean을 사용하지 않고 빈을 등록할 수 있다.
  • 관리하거나 등록할 빈이 많아질 때 일일이 등록/관리가 번거러움으로 사용한다.
    • @Component와 Component Scan 를 이용해 등록한다.
    • @Autowired를 사용해 자동 의존관계 주입을 한다.

  ㄱ. @Component를 이용한 @Bean 등록 방법

  • 아래와 같이 빈으로 등록할 클래스에 @Component를 붙여주고 설정 파일에는 @Configuration과 @ComponentScan을 붙여준다. @ComponentScan이 @Conponent가 붙은 모든 객체를 찾아서 빈으로 등록한다.
@Component
public class OrderServiceImpl implements OrderService {

    private final DiscountPolicy discountPolicy;

    public OrderServiceImpl(DiscountPolicy discountPolicy) {
        this.discountPolicy = discountPolicy;
    }

    @Override
    public Order createOrder(int age, String itemName, int itemPrice) {
        int discountPrice = discountPolicy.discount(age, itemPrice);
        return new Order(itemName, itemPrice, discountPrice);
    }
}

@Component
public class RateDiscountPolicy implements DiscountPolicy {

    private static final int ADULT = 20;
    private static final int DISCOUNT_PERCENT = 10;

    @Override
    public int discount(int age, int price) {
        if (age < ADULT) {
            return price * DISCOUNT_PERCENT / 100;
        }
        return 0;
    }
}

@Configuration
@ComponentScan
public class AppConfig {

}

  ㄴ. @Autowired를 통한 의존 관계 자동 주입

  • 컴포넌트 스캔으로 빈을 등록하고 나면 의존 관계를 만들어 줘야한다. 
  • 자동으로 빈을 등록할 때는 @Autowired를 이용하며, 총 4가지 방법이 있다.
    • 생성자 주입 : 생성자를 통해서 의존 관계를 주입 받는 방법
    • 수정자 주입 : setter를 통한 방법, 주로 석택이나 변경 가능성이 있는 의존 관계에 사용
    • 필드 주입 : 필드에 바로 주입하는 방법, 외부에서 변경이 불가능하여 테스트가 힘듬
    • 일반 메소드 주입 : setter가 아닌 일반 메서드를 통해 의존 관계 주입, setter와 달리 한번에 여러 필드를 주입 받을 수 있는 장점이 있느나, 잘 사용하지 않는다.
// 생성자 주입
@Component
public class OrderServiceImpl implements OrderService {

    private final DiscountPolicy discountPolicy;
	
    @Autowired // 생략 가능
    public OrderServiceImpl(DiscountPolicy discountPolicy) {
        this.discountPolicy = discountPolicy;
    }

    @Override
    public Order createOrder(int age, String itemName, int itemPrice) {
        int discountPrice = discountPolicy.discount(age, itemPrice);
        return new Order(itemName, itemPrice, discountPrice);
    }
}

// 수정자 주입
@Component
public class OrderServiceImpl implements OrderService {

    private DiscountPolicy discountPolicy;

    @Override
    public Order createOrder(int age, String itemName, int itemPrice) {
        int discountPrice = discountPolicy.discount(age, itemPrice);
        return new Order(itemName, itemPrice, discountPrice);
    }

    @Autowired
    public void setDiscountPolicy(DiscountPolicy discountPolicy) {
        this.discountPolicy = discountPolicy;
    }
}

// 필드 주입
@Component
public class OrderServiceImpl implements OrderService {

    @Autowired
    private DiscountPolicy discountPolicy;

    @Override
    public Order createOrder(int age, String itemName, int itemPrice) {
        int discountPrice = discountPolicy.discount(age, itemPrice);
        return new Order(itemName, itemPrice, discountPrice);
    }
}

// 일반 메서드 주입
@Component
public class OrderServiceImpl implements OrderService {

    private MemberRepository memberRepository;
    private DiscountPolicy discountPolicy;

    @Autowired
    public void init(MemberRepository memberRepository, DiscountPolicy
        discountPolicy) {
        this.memberRepository = memberRepository;
        this.discountPolicy = discountPolicy;
    }
}
  • @Component가 붙은 대상 외에도 @Controller, @Service, @Repository, @Configuration도 등록 대상이 된다.
  •  

2. 참조 

  1) 인프런 강의 정리 해준 블로그 : https://steady-coding.tistory.com/460

 

[Spring] 컴포넌트 스캔과 의존 관계 자동 주입

안녕하세요? 제이온입니다. 오늘은 컴포넌트 스캔과 의존 관계 자동 주입에 대해서 알아 보겠습니다. 컴포넌트 스캔 지금까지는 @Configuration이 붙은 설정 파일을 이용하여 빈을 수동 주입하였습

steady-coding.tistory.com