삽질 주도 개발
article thumbnail

백기선님의 강의를 기반으로 내용을 포스팅하고 있다.

 


 

Spring에서 사용되는 IoC란 객체가 내부적으로 조작할 객체를 직접 생성하지 않고 외부로부터 주입받는 기법을 의미한다. 이때 객체를 외부로부터 주입해주는 작업을 DI(의존성 주입)이라고 부른다.

 

이때 IoC Container에 의해 관리되는 오브젝트들은 Bean 이라고 부른다. Bean은 일반 객체와는 다르게 라이프 사이클 인터페이스를 제공한다.

OwnerController bean = applicationContext.getBean(OwnerController.class); // Bean
OwnerController notBean = new OwnerController();	// Bean이 아님

 

IoC Container는 Bean을 저장하고 관리하기 때문에 BeanFactory 라고도 불린다. BeanFactory는 하나의 인터페이스이며, Application Context는 BeanFactory의 구현체를 상속받고 있는 인터페이스이다. (이외에도 EventPublisher, MessageSource(i18n), ResourceLoader 등)

 

빈 설정 방법

XML을 이용한 등록

<bean id="helloController" class="com.example.springlec.controller.HelloController">
    <property name="helloService" ref="myHelloService">
</bean>

<bean id="helloService" class="com.example.springlec.service.HelloService">
</bean>
 
  • id와 class를 필수적으로 지정한다.
  • property는 의존성 주입을 해준다. HelloController에 선언된 myHelloService라는 필드에 HelloService 빈을 주입한다.
  • scope는 singleton, request, session, prototype이 있다.

 

자동인식을 이용한 빈 등록

빈 스캐너는 지정한 classpath 밑에 있는 모든 패키지의 클래스를 대상으로 특정 애노테이션이 존재하는지를 파악하고 빈으로 등록한다. 빈 스캐너에 의해 필터링 되는 애노테이션을 스테레오타입 애노테이션이라고 부른다.

  • @Component : 빈으로 지정하는 가장 기본적인 애노테이션
  • @Repository : 데이터 액세스 계층의 DAO 또는 리포지토리 클래스에 사용된다.
  • @Service : 서비스 계층의 클래스에 사용된다.
  • @Controller : MVC 컨트롤러에 사용된다. 스프링 웹 서블릿에 의해 웹 요청을 처리하는 컨트롤러 빈으로 선정된다.
 

@ComponentScan

컴포넌트 스캔은 스캔 위치와 어떤 애노테이션을 스캔할지에 대한 필터를 지정할 수 있다. 디폴트 값으로 @Component를 포함하는 경우 Bean으로 등록한다. (Spring 2.5 ~)

 

@SpringBootApplication 에서 확인할 수 있다.

 

실제 스캔은 ConfigurationClassPostProcessor라는 객체가 수행하며, BeanFactoryPostProcessor 인터페이스를 구현한 객체이다.

 

스프링은 어플리케이션을 구동하기 전에 컴포넌트 스캔을 하고 빈을 등록하기 때문에 구동 시간이 길어질 수 있다. 하지만 서비스 시에는 보통 싱글톤 스코프의 빈을 사용하기 때문에 서비스 시간은 짧은 편에 속한다.

 

@Configuration, @Bean

@Configuration
public class AnnotatedHelloConfig{
    @Bean
    public Hello hello(Printer printer){
        return new Hello(printer);
    }
    
    @Bean
    public Printer printer(){
        return new Printer();
    }
}

 

  • @Configuration 또한 @Component를 사용하기 때문에 빈 스캐너에 의해 자동 검색 된다.
  • @Configuration를 사용한 클래스 자체도 Bean으로 등록된다.
  • @Bean으로 등록된 메서드의 return 객체를 Bean으로 등록한다.
  • @Bean("name")으로 이름을 지정할 수 있으며 이름을 지정하지 않을 시 메서드 명이 id가 된다.
  • new 연산을 사용하지만, 매번 다른 객체가 생성되지 않고 싱글톤으로 주입된다.

 

 

빈 주입 방법(= 의존성 주입, DI)

 

필드 주입

필드 주입은 필드에 선언된 객체에 어노테이션을 붙여 자동으로 의존성을 주입하는 방식이다.

 

보통 @Resource, @Inject, @Autowired로 의존성 주입을 하는데, 각각의 특징이 있지만 스프링 강의이기 때문에 주제에 벗어나지 않도록 @Autowired로 다루겠다. 

 

각 어노테이션의 차이를 알고 싶으면 baeldung 사이트를 참고하자. (예제 코드로 아주 쉽게 스프링과 관련된 다양한 지식, 기술, 라이브러리 사용법들을 정리해놓은 사이트)

@Autowired
private FieldService fieldService;

위와 같이 코드를 작성하면 BeanPostProcessor가 Bean으로 등록된 FieldSerivce를 찾아서 주입시켜준다.

 

 

생성자 주입

어노테이션을 사용하지 않고 의존성을 주입할 수 있는데, 그것이 바로 생성자 주입이다.

단, 생성자 주입을 사용할 때 해당 클래스의 생성자는 하나여야 하며, 주입받을 객체는 빈으로 등록되어 있어야 한다.

@RestController
public class guguController {

    private final ConstructorService constructorService;

    public guguController(ConstructorService constructorService) {
        this.constructorService = constructorService;
    }
    
}

 

 

Setter 주입

수정자 주입이라고 불리는 Setter 주입은 @Autowired를 *필드가 아닌 프로퍼티에 작성하는 것으로 볼 수 있다.

@RestController
public class guguController {

    private SetterService setterService;

    @Autowired
    public void setSetterService(SetterService setterService) {
        this.setterService = setterService;
    }
}

 

오라클의 glossary을 보면

 

field: A data member of a class. Unless specified otherwise, a field is not static.
          - 클래스의 데이터 멤버. 특별한 이유가 없다면, field는 정적이지 않다.

 

property: Characteristics of an object that users can set, such as the color of a window
          - 윈도우의 색과 같이, 사용자가 설정할 수 있는 객체의 특성

 

 

What is the difference between a field and a property in c의 내용을 번역하면

Property들이 field들을 (외부 세상에) 노출한다. Field들은 (거의 항상) 클래스 내에 private으로 유지되어야 하고 get/set property들을 통해서만 접근이 가능해야 한다.
Property들은 Field가 변경될 수 있도록 추상 계층(a level of abstraction)을 제공해야 하는 반면, 그 클래스를 사용하는 다른 객체가 필드에 접근할 수 있는 외적인 방법으로서 작동해서는 안 된다.

 

우리는 위의 세가지 방식으로 빈을 주입할 수 있다.

 

여기서 자연스럽게 각각의 장단점을 설명하면야 좋겠지만, 객체지향 프로그래밍 그리고 스프링이 가지는 철학에 대한 요소가 있기 때문에 따로 포스팅을 할 예정이다.

 

빈 스코프

스코프로는 크게 싱클톤과 프로토타입으로 나눈다. (위에서 작성한 것과 같이 여러 스코프가 있지만, 두 스코프에 대해서만 다룬다.)

 

우리가 빈을 등록할 때 스코프를 지정할 수 있는데(xml의 scope 속성, @Scope()), default는 싱글톤으로 생성이 된다.

 

각각의 특징은 다음과 같다.

 

  • 싱글톤
    • 해당 Bean의 인스턴스를 오직 한번만 생성
  • 프로토타입
    • 해당 Bean의 인스턴스를 매번 생성

 

간단하게 테스트를 해보자. 우선 싱글톤 빈과 프로토타입 빈을 만들었다.

@Component
public class SingletonObj {
}
@Component
@Scope("prototype")
public class ProtoObj {
}

 

테스트 코드를 작성해서 RepeatedTest로 빈 해시값을 확인해보자.

@SpringBootTest
public class ScopeTest {

    @Autowired
    ApplicationContext context;

    @RepeatedTest(value = 5)
    void singletonScope() {
    	assertEquals(context.getBean(SingletonObj.class), context.getBean(SingletonObj.class));
    }

    @RepeatedTest(value = 5)
    void prototypeScope() {
    	assertNotEquals(context.getBean(ProtoObj.class), context.getBean(ProtoObj.class));
    }
}

 

각 테스트마다 5번을 실행하도록 하고 매번 빈 인스턴스를 비교해보았다. (싱글톤은 매번 같도록, 포로토타입은 매번 다르도록)

 

결과는 다음과 같다.

 

 

Bean Lifecycle에 대해서는 추후 포스팅에서 조금 더 자세히 다뤄본다.

 

 

Reference

'Spring' 카테고리의 다른 글

Unit test naming convention  (0) 2022.11.15
Tomcat과 Spring의 상호작용  (0) 2022.11.15
RequestParam으로 List 형식 받기  (0) 2022.07.07
@Primary, @Qualifier  (0) 2022.04.29
@Valid, @NotNull, @NotEmpty, @NotBlank 그리고 @NonNull  (0) 2022.04.27