The A :

728x90
반응형

Today I Lean

Spring Framework 핵심 개념 _ DI

 

 

 

 

학습목표 및 개념정리

# DI (Dependency Injection)

- 버거퀸 프로그램을 스프링으로 전환해 보면서 스프링의 핵심 개념에 대해 이해할 수 있다.

- 기본적인 테스트 케이스의 개념과 구조를 이해하고, 간단한 테스트 케이스를 작성할 수 있다.

- 스프링 컨테이너가 싱글톤 컨테이너 임을 이해하고, 그 내용을 설명할 수 있다.

- @Configuration과 @Bean 애너테이션을 사용하는 수동 주입과 @ComponentScan과 @Component를 사용하는 자동 주입의 차이에 대해 이해하고, 이를 활용할 수 있다.

- @Autowired 애너테이션의 특성과 작동 방식에 대해 이해하고, 적절하게 활용할 수 있다.

 

 

 

배운 것

# DI (Dependency Injection)

1. 버거퀸 - 스프링 전환

 → 기존 버거퀸 코드 보러가기

 

- 스프링 전환

▪ 기존 버거퀸 실습과제의 문제점

  • 새로운 할인 정책을 도입하는데 있어 부수적으로 변경해야 하는 코드들이 많았음 (객체지향적 설계원칙에 위반; OCP)
  • 문제 발생의 핵심 지점 : 특정 객체가 어떤 객체를 사용할 것인지 직접 결정한다는 것
  • 즉, 역할이 아닌 구현에 의존하고 있었음(의존성 역전원칙 위반; DIP)

 → new 연산자를 통해 구현객체를 직접 선택했음

public class CozDiscountCondition {

 private FixedRateDiscountPolicy fixedRateDiscountPolicy = new FixedRateDiscountPolicy(10);
 
}

 

객체들의 관계를 관리 해 줄 클래스 설정 (의존성을 주입해 주는 역할)

  • @Configuration : 스프링 컨테이너가 만들어 질 때 AppConfigurer 클래스를 컨테이너의 구성 정보로 사용한다는 의미
  • @Bean : 스프링이 실행되었을 때 @Bean으로 등록된 메서드를 모두 호출하여 반환된 객체를 스프링 컨테이너에 등록하고 관리하겠다는 의미
  • Cart 클래스를 싱글톤 패턴에서 스프링객체로 변경할 수 있었던 이유 : 스프링 컨테이너가 기본적으로 싱글톤으로 빈 객체들을 관리해주기 때문
// AppConfigurer 클래스는 각 역할과 책임에 따라 필요한 객체를 생성한 후에 생성한 객체의 참조값을 생성자를 통해 주입(연결)
// DI을 통해 각 객체들 간 의존 관계를 연결

@Configuration
public class AppConfigurer {

    // private Cart cart = new Cart(productRepository(), menu()); 순수 자바 코드로 DI 구현
    // Cart 클래스 객체를 한번 생성하여 구현한것 : 싱글톤 패턴(Singleton Pattern)

    @Bean
    public Menu menu() {
        return new Menu(productRepository());
    }

    @Bean
    public ProductRepository productRepository() {
        return new ProductRepository();
    }

    @Bean
    public Cart cart() {
        return new Cart(productRepository(), menu());
    }

    @Bean
    public Order order() {
        return new Order(cart(), discount());
    }

	// 여기 코드를 변경하여 할인 정책을 추가하거나, 의존관계 변경이 가능
    @Bean
    public Discount discount() {
        return new Discount(new DiscountCondition[] {
                new CozDiscountCondition(new FixedRateDiscountPolicy()),
                new KidDiscountCondition(new FixedAmountDiscountPolicy())
        });
    }
}

 

객체를 주입받을 클래스

 : 구현객체가 객체의 참조값을 받으려면

 → 관련 필드 선언

 → 생성자의 참조값으로 주입받을 필드 설정

 → 각각의 필드 값이 외부의 영향으로 의도치 않게 변경되는 것을 방지하기 위해 접근 제어자를 private으로 선언

public class OrderApp {

    // 주입 받을 필드 선언
    private ProductRepository productRepository;
    private Menu menu;
    private Cart cart;
    private Order order;

    // 생성자를 통한 주입
    public OrderApp(ProductRepository productRepository, Menu menu, Cart cart, Order order) {
        this.productRepository = productRepository;
        this.menu = menu;
        this.cart = cart;
        this.order = order;
    }
}

 

▪ Main 클래스

  • ApplicationContext : 스프링 컨테이너 (인터페이스로 구현되어 있음)
  • AnnotationConfigApplicationContext : ApplicationContext의 구현객체 (AppConfigurer.class 를 구성정보로 넘김)
  • 전달받은 클래스 구성정보를 바탕으로 스프링 컨테이너는 빈 객체 생성, 객체들 간의 의존 관계 연결
  • getBean() 메서드로 필요한 객체(스프링 빈)를 불러서 사용 가능
public class Main {
    public static void main(String[] args) {

        // AppConfigurer 객체 생성
        // AppConfigurer appConfigurer = new AppConfigurer();

            // AppConfigurer 클래스에서 정의한 메서드 호출하여 DI 해줌
            // OrderApp orderApp = new OrderApp(
            // appConfigurer.productRepository(),
            // appConfigurer.menu(),
            // appConfigurer.cart(),
            // appConfigurer.order()
        // );
        
        // 스프링 컨테이너 생성
        ApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfigurer.class);

        // 스프링 빈 조회
        ProductRepository productRepository = applicationContext.getBean("productRepository", ProductRepository.class);
        Menu menu = applicationContext.getBean("menu", Menu.class);
        Cart cart = applicationContext.getBean("cart", Cart.class);
        Order order = applicationContext.getBean("order", Order.class);

        // 불러온 빈의 사용
        OrderApp orderApp = new OrderApp(
                productRepository,
                menu,
                cart,
                order
        );

        orderApp.start();
    }
}

 

 

 

 

2. 버거퀸 - DI

- 의존성 주입의 특징

 : 빈번하게 사용되는 스프링의 핵심

 

주입 받는 객체

  • 외부로부터 특정 객체를 주입받아 this 키워드를 사용하여 내부 필드에 할당 후 값 사용
  • 모두 생성자를 통해 객체를 주입받고 있음
  • 필드의 접근 제어자가 private 키워드를 통해 선언됨 (캡슐화)
  • 생성자 주입에서도 private 키워드와 함께 final 키워드를 같이 사용하기도 함

 → 이 외에 setter 메서드를 사용하는 주입 (setter 주입)과 필드에 직접 주입 (필드 주입)하는 방식이 있지만 권장되지 않음

 → final 키워드 : 해당 값이 들어오지 않는 경우 컴파일러가 에러를 알려줌 (개발자의 실수에 의한 오류가 줄어듬)

 

주입 하는 클래스

  • 객체를 생성하고 의존 관계를 연결시키는 역할 (버거퀸의 AppConfigurer)

 

 

- 순수 자바 코드 vs 스프링 프레임워크

 : 차이점 → 스프링 컨테이너의 존재 여부

        (1) 순수 자바 코드로 만들어진 AppConfigurer
        AppConfigurer appConfigurer = new AppConfigurer();

        OrderApp orderApp = new OrderApp(
            appConfigurer.productRepository(),
            appConfigurer.menu(),
            appConfigurer.cart(),
            appConfigurer.order()
        );

        (2) 스프링 컨테이너의 관리 하에 있는 AppConfigurer
        ApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfigurer.class);
        ProductRepository productRepository = applicationContext.getBean("productRepository", ProductRepository.class);
            Menu menu = applicationContext.getBean("menu", Menu.class);
            Cart cart = applicationContext.getBean("cart", Cart.class);
            Order order = applicationContext.getBean("order", Order.class);

        OrderApp orderApp = new OrderApp(
            productRepository,
            menu,
            cart,
            order
        );

 

 

 

 

3. 스프링 컨테이너 (Spring Container), 스프링 빈 (Spring Bean)

- 스프링 컨테이너 (Spring Container)

▪ ApplicationContext

  • ApplicationContext 인터페이스를 일반적으로 스프링 컨테이너 라고 부름
  • 빈을 관리하고 조회함
  • 웹 애플리케이션을 개발하는데 필요한 다양한 부가기능 제공

▪ AnnotationConfigApplicationContext

  • ApplicationContext의 구현 객체
  • 매개변수로 구성정보를 넘겨줌

 → 빈을 생성하고 호출할 때, 스프링 컨테이너는 호출되는 메서드의 이름을 기준으로 빈의 이름을 등록

 → 각각의 빈은 해당 빈에 대한 메타정보를 가짐, 스프링컨테이너는 이 메타정보를 기반으로 스프링빈을 생성

 

 

- 스프링 빈 (Spring Bean)

 : 클래스의 등록정보, getter/setter메서드를 가짐

 → 구성정보(설정메타정보)를 통해 생성됨

 

▪ 빈 조회

 : getBean()

// (1) getBean(빈 이름, 타입)
Cart cart = applicationContext.getBean("cart", Cart.class);

------------------------------------------------------------------------------------------

// (2) getBean(타입)
applicationContext.getBean(Menu.class);

------------------------------------------------------------------------------------------

// (3) 변경에 유연하지 않아 추천하지 않음
getBean(FixedAmountDiscountPolicy.class)

------------------------------------------------------------------------------------------

// (4) 스프링 빈에 있는 모든 빈 조회
String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();

    for (String beanDefinitionName : beanDefinitionNames) {
Object bean = applicationContext.getBean(beanDefinitionName);

------------------------------------------------------------------------------------------

// (5) 빈 메타정보 조회
String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();

for (String beanDefinitionName : beanDefinitionNames) {
BeanDefinition beanDefinition = applicationContext.getBeanDefinition(beanDefinitionName);

if(beanDefinition.getRole() == BeanDefinition.ROLE_APPLICATION) {

 * BeanFactory : 스프링 컨테이너의 최상위 인터페이스

 

▪ 빈 이름 설정

 : 기본적으로 조회시 호출하는 이름으로 설정됨

@Bean (name = "cart2")
public Cart cart() {
	return new Cart(productRepository(), menu());
}

 → 만약 위 코드와 같이 빈의 이름을 등록할 경우, 같은 이름의 빈이 등록 되지 않도록 주의 필요

 

 

 

 

4. 테스트 케이스 (Test Case)

- 단위 테스트 (Unit Test)

 : 작은 단위의 어떤 특정 기능을 테스트하고, 검증하기 위한 도구 (테스트케이스를 작성한다고 표현)

 → 주로 입력 데이터, 실행 조건, 기대결과 에 대한 값을 테스트 함

 

 

- JUnit

 : 스프링에서 제공하는 오픈소스 테스트 프레임워크

 → 각각의 단위 테스트는 메서드 단위로 작성

 구조

  • 기본적으로 test 디렉토리 안에서 작성하는것이 원칙
  • main 패키지와 동일한 디렉토리 구조로 작성
  • 테스트케이스 작성을 위한 테스트클래스는 테스트의 대상이 되는 클래스의 이름에 Test를 붙여서 클래스 생성

public class JunitDefaultStructure {
		
    @Test
    public void test1() {
     // 테스트하고자 하는 대상에 대한 테스트 로직 작성
    }

    @Test
    public void test2() {
     // 테스트하고자 하는 대상에 대한 테스트 로직 작성
    }
		
    @Test
    public void test3() {
     // 테스트하고자 하는 대상에 대한 테스트 로직 작성
    }
}

 

 

- 로직 작성

▪ 빈 조회 단위 테스트 작성

import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.Test;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class MainTest {

    // 스프링 컨테이너 생성
    AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfigurer.class);

    // 빈 조회 테스트케이스
    @Test
    void findBean() {

        // (1) given → 초기화 || 테스트에 필요한 입력 데이터
        // AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfigurer.class);

        // (2) when → 테스트 할 동작
        Cart cart = applicationContext.getBean("cart", Cart.class);

        // (3) then → 검증
        Assertions.assertThat(cart).isInstanceOf(Cart.class);
    }
}

▪ BDD (Behavior Driven Development)

 : 테스트 로직 작성 템플릿

 

* 위 예제의 경우를 예시로 들어 설명 *

(1) given (입력 데이터)

  • 테스트에 필요한 초기화 값 or 입력 데이터 의미
  • 빈 조회 테스트에 필요한 초기화 세팅 : AppConfigurer 클래스를 구성 정보로 하는 스프링 컨테이너를 생성

(2) when (실행 동작)

  • 테스트 할 실행 동작 지정
  • 일반적으로 단위 테스트 에서는 메서드 호출을 통해 테스트를 진행 (보통 한 두줄 작성으로 끝남)
  • 빈 조회 테스트에서 실행할 동작 : getBean() 메서드를 사용하여 빈을 불러오는 것

 (3) then (결과 검증)

  • 테스트의 결과를 최종적으로 검증
  • 일반적으로 예상되는 기대값(expected)과 실제 실행 결과값을 비교
  • 주로 JUnit / AssertJ 라이브러리에서 제공하는 Assertions 클래스의 기능 사용
  • 위 코드는 AssertJ 라이브러리의 Assertions 클래스의 assertThat() 메서드 사용
  • AssertJ는 메서드 체이닝(method cjaining)을 지원함 (스트림과 유사, 여러 메서드를 연속하여 호출이 가능)
  • AssertJ에서 모든 테스트코드는 assertThat()을 사용 (테스트를 실행할 대상을 파라미터로 전달하여 호출)
  • 호출결과는 ObjectAssert 타입의 인스턴스 반환 (isInstanceOf(), isCameAs(), isNotNull(), isNotEmpty() 사용 가능)

 

 

 → 테스트가 성공했을 경우 (왼쪽에 초록체크가 보임)

 

→ 테스트가 실패했을 경우 (왼쪽에 노란 엑스가 보임)

오류 메시지 해석
java.lang.AssertionError: 
Expecting actual: // "조회된 Menu@55b62629 인스턴스가"
  com.codestates.burgerqueenspring.Menu@55b62629
to be an instance of: // "Cart 클래스의 인스턴스 객체인 것으로 기대되었으나"
  com.codestates.burgerqueenspring.Cart
but was instance of: // "실제로는 Munu 클래스의 인스턴스 객체인 것으로 보여짐"
  com.codestates.burgerqueenspring.Menu

 

→ 잘못된 이름의 빈 조회 (메서드 레벨에 @DisplayName 애너테이션을 사용하면 테스트 케이스에 이름을 붙일 수 있음)

class MainTest {

    @Test
    @DisplayName("빈의 이름이 잘못된 경우")
    void findBeanX() {

        //given => 초기화 또는 테스트에 필요한 입력 데이터
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfigurer.class);

        //when => 테스트 할 동작
        Menu menu = applicationContext.getBean("car", Menu.class);

        //then => 검증
        Assertions.assertThat(menu).isInstanceOf(Cart.class);
    }

 

→ 존재하지 않는 빈 검증 (Assertions가 AssertJ의 API가 아닌 JUnit 메서드 API를 사용)

import org.junit.jupiter.api.Assertions;

@Test
    @DisplayName("빈이 존재하지 않는 경우2")
    void findBeanX2() {

        //given => 초기화 또는 테스트에 필요한 입력 데이터
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfigurer.class);

				//when => 불필요

        //then => 검증
        Assertions.assertThrows(NoSuchBeanDefinitionException.class, () -> applicationContext.getBean("xxx", Menu.class ));
    }

 

 

 

 

5. 스프링 컨테이너 = 싱글톤 컨테이너

- 싱글톤 패턴 (Singleton Pattern)

 : 인스턴스가 단 한 번만 생성되게 만들어 객체의 참조값을 공유할 수 있도록 하는 것(싱글톤 패턴)

 → 동시다발적인 고객 요청을 처리할 때 사용

 

 

- 스프링컨테이너와 싱글톤 패턴

 : 스프링 컨테이너는 직접 싱글톤패턴 코드(결합도가 높은 코드)를 작성하지 않아도 객체 인스턴스를 싱글톤으로 관리

 → 싱글톤으로 객체를 생성 및 관리하는 기능을 싱글톤 레지스트리(Singleton Registry)라 부름

 → 스프링 컨테이너가 싱글톤 레지스트리 기능을 가지고 있는 싱글톤 컨테이너이기 때문에 그 안에서 생성되고 관리되는 객체들이 싱글톤으로 관리 됨

 

▪ CGLIB 내부 동작 코드

 : 스프링은 CGLIB라는 바이트코드 조작 라이브러리를 사용하여 싱글톤 레지스트리를 가능하게 함

@Bean
public Cart cart() {

    if(cart가 이미 스프링 컨테이너에 있는 경우) {
        return 이미 있는 객체를 찾아서 반환
    } else {
        새로운 객체를 생성하고 스프링 컨테이너에 등록
        return 생성한 객체 반환
    }
}

 

 

 

 

6. 빈 생명주기와 범위

 : 스프링 컨테이너는 '초기화' 와 '종료'라는 생명주기(Life-cycle)를 가지고 있음

 

- 스프링 컨테이너 생명주기

public class Main {
    public static void main(String[] args) {
            
        // (1) 컨테이너 초기화
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(TestConfigurer.class);

        // (2) 컨테이너 사용
        ProductRepository productRepository = applicationContext.getBean("productRepository", ProductRepository.class);
        Menu menu = applicationContext.getBean("menu", Menu.class);
        Cart cart = applicationContext.getBean("cart", Cart.class);
        Order order = applicationContext.getBean("order", Order.class);

        // (3) 컨테이너 종료
        applicationContext.close();
    }
}

(1) 컨테이너 초기화

  • AnnotationConfigApplicationContext를 통해 객체 생성, 스프링 컨테이너 초기화
  • 스프링 컨테이너는 구성 정보를 기반으로 빈 객체 생성, 각 의존 관계를 연결하는 작업을 수행

(2) 컨테이너 사용

  • 스프링 컨테이너의 관리하에 있는 빈 객체들을 조회하여 사용 가능
  • 주로 getBean() 메서드를 사용하여 빈을 조회

 (3) 컨테이너 종료

  • 컨테이너를 close() 메서드를 통해 종료시킬 수 있음

 

 

- 빈 객체 생명주기

: 위 스프링 컨테이너의 생명주기와 비슷함

(1) 빈 객체 생성

  • 스프링 컨테이너가 초기화 될 때 빈 객체 생성

(2) 의존관계 주입

  • 스프링 컨테이너가 초기화 될 때 생성된 객체들 간의 의존관계 설정

 (3) 초기화

  • 메서드를 호출하여 빈 객체의 초기화

 (4) 소멸

  • 스프링 컨테이너가 종료되면, 메서드를 호출하여 빈 객체들을 소멸시킴

 → 데이터베이스의 커넥션 풀, 채팅 클라이언트의 기능을 구현 등에 사용

 → 스프링은 의존 관계 설정이 완료된 시점스프링 컨테이너의 종료 직전 시점에 지정된 메서드를 호출하여 개발자가 각각의 시점에 필요한 작업을 수행할 수 있도록 지원

 

 

▪ 공통 main 코드

public class ClientMain {
    public static void main (String[] argss) {
        // 컨테이너 생성
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext((ClientConfig.class));

        // 컨테이너 사용
        TestClient testClient = applicationContext.getBean("testClient", TestClient.class);
        testClient.connect();

        // 컨테이너 종료
        applicationContext.close();
    }
}

 

 

▪ InitializingBean & DisposableBean 인터페이스

public class TestClient implements InitializingBean, DisposableBean {
    private String url;

    public TestClient(String url) {
        System.out.println("생성자 호출.");
        this.url = url;
    }
    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("초기화 메서드 실행.");
    }
    public void connect() {
        System.out.println("클라이언트를" + url + "로 연결.");
    }
    @Override
    public void destroy() throws Exception {
        System.out.println("종료 메서드 실행.");
	}
}
    
@Configuration
public class ClientConfig {

    @Bean
    public TestClient testClient() {
        // 생성자로 url 주소값 전달
        TestClient testClient = new TestClient("www.codestates.com");
        return testClient;
    }
}

 : 빈 객체가 InitialzingBean과 DisposableBean 인터페이스를 구현하면 스프링 컨테이너는 초기화, 소멸과정 각각 빈 객체의 afterPropertiesSet() 메서드와 destroy() 메서드를 실행함

 → 메서드 오버라이딩을 통해 적절한 기능 구현 가능

 

 

 * InitialzingBean인터페이스와 DisposableBean 인터페이스의 한계

  • 스프링 전용 인터페이스 이기 때문에, 초기화와 소멸 메서드 이름을 개발자가 변경하지 못함
  • 직접 구현한 클래스가 아닌, 외부에서 받은 라이브러리나 클래스에 두가지 인터페이스를 적용할 수 없음

 

 

▪ @Bean 태그의 속성 값 활용 (한계를 없애기 위한 수정)

public class TestClient {

    private String url;

    public TestClient(String url) {
        System.out.println("생성자 호출.");
        this.url = url;
    }
    public void init() {
        System.out.println("init() 초기화 메서드 실행");
    }
    public void connect() {
        System.out.println("클라이언트를 " + url + "로 연결");
    }
    public void close() {
        System.out.println("println() 종료 메서드 실행");
    }
}
    
@Configuration
public class ClientConfig {

    @Bean(initMethod = "init", destroyMethod = "close")
    public TestClient testClient() {
        TestClient testClient = new TestClient("www.codestates.com");
        return testClient;
    }
}

 

destroyMethod 속성 : 디폴트값이 (inferred), 즉 추론되도록 등록되어 있음

 → destroyMethod를 사용하지 않아도 빈번하게 사용되는 close 메서드 || shutdown 메서드를 자동 호출

 → destroMethod = "" 처럼 빈 공백으로 두면 close() 메서드가 호출되지 않음

 

 

▪ @PostConstruct와 @PreDestory 애너테이션 (가장 권장하는 방법)

public class TestClient {

    private String url;

    public TestClient(String url) {
        System.out.println("생성자 호출.");
        this.url = url;
    }

    @PostConstruct
    public void init() {
        System.out.println("init() 초기화 메서드 실행.");
    }
    public void connect() {
        System.out.println("클라이언트를 " + url + "로 연결.");
    }
    @PreDestroy
    public void close() {
        System.out.println("close() 종료 메서드 실행.");
    }
}

 

- 빈 객체의 관리 범위

스프링 빈은 스프링 컨테이너의 생성과 종료를 같이 함

이유 : 빈 객체가 별도의 설정이 없는 경우 싱글톤 범위(scope)를 가짐

빈객체의 스코프 : 빈 객체가 존재할 수 있는 범위

▪ 종류

싱글톤 (Singleton) : 제일 많이 사용

프로토 타입 (Prototype) : 스프링 컨테이너는 프로토타입 빈의 생성, 의존성 주입, 초기화 단계까지만 관여하고 그 이후에는 더 이상 관여하지 않음

세션 (Session)

리퀘스트 (Request)

 

 

 

그 중 프로토 타입 범위를 예시로 설명

(1) 싱글톤 범위

package com.codestates.burgerqueenspring.scope;

import org.junit.jupiter.api.Test;
import org.assertj.core.api.Assertions;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;

public class ScopeTest {

    @Test
    public void scopeTest() {
        // 컨테이너 생성
        AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(TestBean.class);

        // 컨테이너 사용
        TestBean bean = annotationConfigApplicationContext.getBean(TestBean.class);
        TestBean bean2 = annotationConfigApplicationContext.getBean(TestBean.class);

        System.out.println("bean = " + bean);
        System.out.println("bean2 = " + bean2);

        // 검증
        Assertions.assertThat(bean).isSameAs(bean2);

        // 컨테이너 종료
        annotationConfigApplicationContext.close();
    }

    static class TestBean {
        // 초기화 메서드 생성
        @PostConstruct
        public void init() {
            System.out.println("init() 초기화 메서드 실행.");
        }

        // 종료 메서드 생성
        @PreDestroy
        public void close() {
            System.out.println("close() 종료 메서드 실행.");
        }
    }
// 먼저 static으로 선언된 테스트용 TestBean 클래스를 생성하고 그 안에 초기화 메서드와 종료 메서드를 각각 선언
// 다음으로, TestBean 클래스를 구성 정보로 하는 스프링 컨테이너를 생성한 다음,
// getBean() 메서드를 통해 빈을 두 번 조회하여 동일한 객체를 반환하는지 출력하고
// Assertions 클래스의 isSameAs() 메서드를 통해 다시 검증
// 마지막으로 이렇게 컨테이너 사용이 끝나면 close() 메서드를 통해 컨테이너를 종료
}

(2) 프로토타입 범위

TestBean 클래스 위에 @Scope("prototype") 애너테이션을 붙이면 끝
---------------------------------------------------------
init() 초기화 메서드 실행.
init() 초기화 메서드 실행.
bean = co m.codestates.burgerqueenspring.scope.ScopeTest$TestBean@b91d8c4 
bean2 = co m.codestates.burgerqueenspring.scope.ScopeTest$TestBean@4b6166aa
---------------------------------------------------------
빈의 관리 범위가 프로토타입이기 때문에 빈을 조회했을 때 각기 다른 객체가 반환되고, 테스트가 통과되지 못함
빈의 생성, 의존성 주입, 초기화 까지만 스프링 컨테이너가 관여하기 때문에 종료메서드는 호출되지 않음

 

 

 

7. 컴포넌트 스캔과 의존성 자동 주입

- 

 : 

 → 

 

 

 

 

8. @Autowired

- 

 : 

 → 

 

 

 

*****

- 객체지향 프로그램의 핵심

  • 객체 지향 패러다임 (OOP; Object-oriented Programming Paradigm)의 출발점은 객체이다.
  • 각각의 객체가 적절한 역할과 책임을 가지고 특정한 기능을 수행하기위해 협력하도록 만드는 일
  • 객체 지향적 설계 : 변화와 확장에 유연하도록 프로그램을 고안하는 것 (인터페이스를 활용한 역할과 구현의 분리)
  • 객체 간의 의존 관계가 높은 경우 그 관계의 결합도가 높다고 할 수 있음 (유연한 코드 설계에 위배되기에 지양해야 함)
  • 캡슐화를 통해 각 객체의 자율성 보장
  • 응집도를 높이고 다른 객체와의 강한 결합도를 낮춰 유기적인 협력관계에 참여할 수 있도록 코드를 설계 해야 함

 

- 스프링 프레임워크의 핵

  • 프레임워크 (Framework)어떤 애플리케이션을 만들기 위한 틀 또는 구조를 의미
  • 코드 흐름의 제어권이 개발자가 아닌 프레임워크에 있음
  • 개발자가 주도권을 가지는 라이브러리 (Library)와 구분
  • 스프링 프레임워크는 POJO 프로그래밍을 지향하면서 IoC/DI, AOP, PSA라는 세가지축을 가짐
  • 스프링 프레임워크는 다양한 기능들을 제공하는 모듈들의 묶음
  • 스프링 부트는 의존 라이브러리의 자동 관리, 설정의 자동구성, 손쉬운 빌드 및 배포등의 장점을 가짐
  • 개발자가 비즈니스 로직에만 집중할 수 있도록 도와줌

 

 

 

Tomorrow Chapter

# AOP (Aspect Oriented Programming)

 

 

 


 

 

↓ 이전 글 ↓

 

[코드스테이츠] 05_30_TIL : Spring Framework 기본

Today I Lean Spring Framework 기본 학습목표 및 개념정리 # Spring Framework 소개 - Spring Framework이 무엇인지 이해할 수 있다. - Spring Framework을 배워야 하는 이유를 알 수 있다. - Spring Framework과 다른 Framework의

theflower01.tistory.com

 

↓ 코트스테이츠 부트캠프 관련 글 한번에 보기 ↓

 

'IT/코드스테이츠 부트캠프' 카테고리의 글 목록

Flower, Plant, Study

theflower01.tistory.com

728x90
반응형

공유하기

facebook twitter kakaoTalk kakaostory naver band
loading