육감적 코딩

[Spring] 1. IoC컨테이너와 빈 본문

정리/Spring

[Spring] 1. IoC컨테이너와 빈

감감감감감감 2020. 7. 20. 22:32

기존에 이미 스프링을 사용하며 프로젝트를 진행해 왔지만,

더 깊게 스프링을 공부할 수록 자신이 스프링 프레임워크에 대한 이해가 부족하다는 걸 알게되었습니다.

다시 생각하고 복습할겸 공부 내용을 정리해 보려고합니다.


IoC컨테이너 

  •  스프링이 제공. 

  • IoC컨테이너 안에 들어있는 객체를 빈이라고 부른다.

  • 우리는 컨테이너 안의 빈을 가져와 사용할 수 있다.

 

더보기
공부하던 중 POJO란 단어를 접하게 되었고, 간단히 조사하여 정리해보았습니다.

POJO 란

Plain Old Java Object, 말 그대로 해석을 하면 오래된 방식의 간단한 자바 오브젝트라는

말로 Java EE등의 중량 프레임워크들을 사용하게 되면서 해당 프레임워크에 종속된

“무거운” 객체를 만들게 된 것에 반발해서 사용하게 된 용어이다.

쉽게 말해, 특정 ‘기술’에 종속되어 동작하는 것이 아닌 순수한 자바 객체를 말하는 겁니다.

 

특정 기술과 환경에 종속되어 의존하게 된 자바 코드는 가독성이 떨어져 유지보수에

어려움이 생겼습니다. 또한, 특정 기술의 클래스를 상속받거나, 직접 의존하게 되어

확장성이 매우 떨어지는 단점이 있었습니다. 이 말은 객체지향의 화신인 자바가 객체지향

설계의 장점들을 잃어버리게 된 것입니다.

 

진정한 POJO?

그럼 특정 기술규약과 환경에 종속되지 않으면 모두 POJO라고 말할 수 있는가? 많은 개발자가 크게 오해하는 것 중의 하나가 바로 이것이다. …..(중략)..... 진정한 POJO란객체지향적인 원리에 충실하면서, 환경과 기술에 종속되지 않고 필요에 따라 재활용 될수 있는 방식으로 설계된 오브젝트를 말한다.

 

 

IoC컨테이너의 가장 최상위 interface 는 BeanFactory 이며

IoC컨테이너의 가장 핵심 클래스 중 하나이다.

 

빈이란?

  • Ioc컨테이너의 안에 들어있는객체.

  • Ioc컨테이너가 관리하는 객체.

public class Book {
 private Date created;
 private BookStatus bookStatus;
 public Data getCreated(){
  return created;
 }
 public void setCreated(Date created){ 
…
}

Q1. 이건 빈 일까 ?

A1. 빈이아니다.

     이건 IoC컨테이너가 관리하지 않기때문이다. 그냥 자바빈으로 볼 수는 있지만(자바빈 스펙을 준수하기 떄문) 아니다.

더보기

자바빈이란 ? 

- 자바로 작성된 객체이며, 데이터 표현을 목적으로 한다.

- SpringBean이 항상 JavaBean일 필요는 없다.

- Java.io.Serializable interface를 구현하지 않을 수 있으며 생성자 등에 인수를 가질수 있다.

 

생성 관례

1. 직렬화가 가능해야 한다.

2. 기본 생성자를 가진다.

  - 만약 파라미터가 존재하는 생성자를 추가한 경우에는 기본 생성자 코드를 작성하지 않으면 기본

     생성자가 없는 상황이 되므로 주의해야 한다.

3. 멤버 변수는 private 속성이다.

4. 멤버 변수에 대한 설정자와 접근자를 가진다.

 

 

 

@Repository
public class BookRepository{
 public Book save(Book book){
  return null;
 }
}

Q2. 이건 빈 일까?

A2. 빈이 맞다.

     @Repositoy라는 어노테이션을 사용하여 AutoScan을 통해 빈으로 등록된다. 


 

 

@Service , @Repository 같은 어노테이션이 붙은 클래스는 왜 빈으로 사용하는 것일까?

  • 의존성 주입때문이다. 

  • 의존성 주입을 받으려면 빈으로 등록되어있어야 의존성을 주입받을 수 있다.

  • 빈의 스코프 때문이다.

    • 애플리케이션 전반적으로 ex) @Service가 붙은 BookService라는 인스턴스는 오직하나만 사용되도 되기때문에 ( 굳이 여러개를 만들어서 사용할 필요가없기때문) 싱글톤으로 만들어 관리하기 위해서이다. 

    • 기본적으로 빈들은 기본적으로 싱글톤 스코프로 빈으로 등록된다.

    • 메모리적으로 효율적. ( 매번 만들어 사용하는것이 아니기 때문에)

  • 라이프사이클 인터페이스

    • 스프링IoC컨테이너에 등록된 빈들에만 국한된 이야기.

      • 어떤 빈이 만들어 졌을때, 내가 추가적인 작업을 하고싶을 때

      • @PostConstruct

@Service
public class BookService {
    private BookRepository bookRepository;

    public BookService(BookRepository bookRepository){
        this.bookRepository=bookRepository;
    }
    public Book save(Book book){
        book.setCreated(new Date());
        book.setBookStatus("DRAFT");
        return bookRepository.save(book);
    }

    @PostConstruct
    public void postConstruct(){
        System.out.println("==========");
        System.out.println("Hello");
    }
}

Tip)

 

 

Spring IoC 컨테이너의 중요한 interface 

  • BeanFactory

  • ApplicationContext (우리가 주로 사용할 BeanFactory)

  • BeanFactory를 상속받았음

  • BeanFactory기능에 추가적으로

    • ApplicationEventPublisher

    • EnvironmentCapable,

    • HierachicalBeanFactory

    • ListableBeanFactory

    • MessageSource // 메시지 다국화 ( 영어로는 이렇게 ~ 한글로는 이렇게

    • ResourceLoader // 클래스 패스에 있는 특정에 파일등의 리소스를 읽어옴

    • 등등


TEST

public class BookServiceTest {

    @Test
    public void save(){
        Book book = new Book();

        //BookRepository 를 구현하지 않고는 BookService 만을 테스트 할 수 없다.
        // 왜냐하면 BookRepository 의 save()가 null 을 리턴하기 때문에
        // 의존성문제 !
        // 의존성을 가진 BookService 를 단위테스트로 만들기 힘든 상황.
        BookRepository bookRepository = new BookRepository();
        BookService bookService = new BookService(bookRepository);

        Book result = bookService.save(book);

        assertThat(book.getCreated()).isNotNull();
        assertThat(book.getBookStatus()).isEqualTo("DRAFT");
        assertThat(result).isNotNull();
    }

}

-> 애초에 save()는 null을 리턴하게 해놓았기 때문에 테스트 실패.

 

@Mock 어노테이션을 사용하여 DI를 주입.

@RunWith(SpringJUnit4ClassRunner.class)
public class BookServiceTest {
    @Mock
    BookRepository bookRepository;

    @Test
    public void save(){
        Book book = new Book();

        when(bookRepository.save(book)).thenReturn(book);
        BookService bookService = new BookService(bookRepository);

        Book result = bookService.save(book);

        assertThat(book.getCreated()).isNotNull();
        assertThat(book.getBookStatus()).isEqualTo("DRAFT");
        assertThat(result).isNotNull();
    }

}

 DI를 주입받아 사용하는것의 장점.

References

https://gmlwjd9405.github.io/2018/11/10/spring-beans.html  
https://stackoverrun.com/ko/q/5977519
https://velog.io/@damiano1027/Spring-Java-Bean-Spring-Bean

 

 

Comments