엄범

 

spring core의 공식 docs. 웬만큼 궁금한건 여기 다 나와 있다.

https://docs.spring.io/spring/docs/5.1.x/spring-framework-reference/core.html

 

Core Technologies

Letting qualifier values select against target bean names, within the type-matching candidates, does not require a @Qualifier annotation at the injection point. If there is no other resolution indicator (such as a qualifier or a primary marker), for a non-

docs.spring.io

 

"컨테이너란 당신이 작성한 코드의 처리과정을 위임받은 독립적인 존재 " 와 같은 추상적인 표현을 얘기하는 블로그와 책은 많으니까, 그 동안의 프레임워크 사용 경험을 바탕으로 공식 docs를 읽고 이해한 바를 구체적인 관점에서 정리해보았다.

 

Spring's IoC 컨테이너 (Inversion of Control container)  

일반적인 프로그램을 작성할 때는, 내가 진입점을 컨트롤 할 수 있기 때문에(main함수) 프로그램의 진행 흐름을 컨트롤 할 수 있다.

그러나 프레임워크를 사용할 때는, 진입점에서 프레임워크를 실행하는 작업만 수행하고 내가 작성한 코드를 따로 호출하지 않는다.

```java

@SpringBootApplication
public class DemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }
}

```

main 함수에서 그냥 run()만 불렀을 뿐이고 내가 작성한 코드를 호출하는 부분이 없다. 그럼 내가 작성한 코드(객체)는 언제 생성/초기화/실행/소멸하는가? 이걸 알아서 처리해주는게 프레임워크의 컨테이너다.

 

스프링의 경우 "알아서"라는건 "유저의 요청이 들어왔을 때"라고 보면 된다. 즉 유저가 요청하면 내가 작성한 코드를 실행하고, 그로부터 만들어지는 응답을 내려주는 동작을 대신 해주는 것이다.

 

가령 프레임워크 없이, 간단한 raw webserver를 socket을 이용해 직접 만든다고 생각해보면

```

main()

  while loop 돌면서 HTTP request가 들어왔는지 계속 체크하고,

  들어왔다면

    path가 /1이면 a객체 만들고 a.serve()

    path가 /2이면 b객체 만들고 b.serve()...

```

 

즉, main에서 시작된 실행 흐름을 직접 while 돌면서 제어해야 할 것이다.

조금 더 요청이 많아지면 스레드를 여러 개로 빼거나, 비동기 처리를 해야 하는 등 복잡한 일거리가 생긴다. 

 

그러나 스프링에서는 main에서 시작된 실행 흐름에 직접 관여하지 않는다. 그냥 run()호출하고 끝이다.

즉, 어떤 요청이 들어왔을 때 어떤 객체를 어떻게 조작할 것인가? 에 대한 복잡한 처리를 대신 수행해주는게 컨테이너라고 생각하면 된다.

개발자는 이에 대한 Configuration을 작성하고 객체의 비즈니스 로직을 채우는 작업만 해주면 된다. 어떤 객체를 어느 시점에 생성하고 사용할지는 컨테이너가 제어한다.

따라서 개발자가 비즈니스 로직을 작성하는데 집중할 수 있다. (그 외에도 물론 몇 가지 장점들이 있다.)

그리고 이처럼 프로그램의 실행 흐름이나 객체의 생명 주기를 개발자가 직접 제어하는게 아니라, 컨테이너로 제어권이 넘어가는 것을 Inversion of Control (IoC) 라고 한다. 그래서 IoC container라고 부르는 것이다.

 

Bean

위에서 컨테이너가 객체의 life-cycle을 관리한다고 했다. 이렇게 컨테이너가 관리하는 객체들을 빈이라고 부른다. 빈은 기본적으로 싱글턴이다.

다음 네 가지 애너테이션을 사용하면 해당 클래스를 자동으로 빈으로 등록해준다. 

```java

@Controller    // Persentation layer에서 Controller임을 명시

@Service       // Business layer에서 Service를 명시

@Repository    // Persistence layer에서 DAO를 명시

@Component     // 기타 자동 등록하고 싶은 것. 사실 위 3개는 Component의 use-case에 따른 specializations이다.

```

그 외 ``java @Bean``이라는 애너테이션도 있는데, 이는 내가 직접 작성한 클래스가 아니라 외부 라이브러리의 객체를 빈으로 만들고 싶을 때 사용한다. 외부 라이브러리의 클래스에 애너테이션을 직접 붙일 수는 없으니 다음과 같이 객체를 반환하는 메서드에 붙여서 사용한다.

```java

    @Bean

    public ObjectMapper objectMapper() { return new ObjectMapper(); }

```

 

DI(Dependency Injection)

[Coding/CodingNote] - 의존성 주입(DI, Dependency Injection)이란?

스프링에서는 객체의 생성 소멸 등등에 대한 제어를 컨테이너가 관리하고, 필요할 때 컨테이너에서 DI해준다.

 

어떻게 컨테이너가 `` SimpleMovieLister`` 객체에게 `` MovieFinder`` 객체를 넘겨주도록 명시 할 것인가? 방법은 크게 두 가지다.

 

Constructor-based DI

Constructor-based DI is accomplished by the container invoking a constructor with a number of arguments, each representing a dependency.

 

그냥 생성자에서 받는 방식이다. 즉, `` SimpleMovieLister``를 생성할 때, 컨테이너가 알아서 생성자에 `` MovieFinder`` 객체를 넣어주면서 생성하는 방식이다. 이 때 `` final``로 설정하는 것이 좋다. 생성자 방식은 몇 가지 장점이 있다.

  • `` final``이라 immutable이다.
  • final이라 null이 들어갈 때 compile time에 경고가 발생한다
  • 유닛 테스트가 수월하다
  • 생성자의 파라미터를 통해 의존 관계를 바로 파악 가능하다.

```java

public class SimpleMovieLister {

    // the SimpleMovieLister has a dependency on a MovieFinder

    private final MovieFinder movieFinder;

    // a constructor so that the Spring container can inject a MovieFinder

    public SimpleMovieLister(MovieFinder movieFinder) {
        this.movieFinder = movieFinder;
    }

    // business logic that actually uses the injected MovieFinder is omitted...
}

```

lombok을 사용한다면 생성자를 직접 만들지 않고, @RequiredArgsConstructor를 많이들 쓴다. final인 필드로 생성자를 만들어 준다. 생성자 코드를 애너테이션으로 대체해서 코드가 깔끔해짐.

 

Setter-based DI

Setter-based DI
is accomplished by the container calling setter methods on your beans after invoking a no-argument constructor or a no-argument static factory method to instantiate your bean.


즉 컨테이너가 setter method를 불러주면서 주입해주는 방식이다.

```java

public class SimpleMovieLister {
    // the SimpleMovieLister has a dependency on the MovieFinder

    private MovieFinder movieFinder;

    // a setter method so that the Spring container can inject a MovieFinder

    public void setMovieFinder(MovieFinder movieFinder) {
        this.movieFinder = movieFinder;
    }

    // business logic that actually uses the injected MovieFinder is omitted...
}

```

 

Constructor 방식과 Setter 방식 중 반드시 하나만 골라서 써야 하는 것은 아니다. ApplicationContext는 일부 dependency는 Constructor 접근을 통해 주입하고, 나머지에 대해서는 Setter 방식으로 주입하는 것도 지원한다.

그리고 두 방식 중 어떤 것이 좋은가?에 대해서는 다음과 같이 안내하고 있다.

Since you can mix constructor-based and setter-based DI, it is a good rule of thumb to use constructors for mandatory dependencies and setter methods or configuration methods for optional dependencies.
이유가 궁금하다면 공식 docs 참고. 좋은 내용이다.

 

근데 여기서 한 가지 문제가 생긴다. 자바의 다형성(polymorphism)을 생각해 보자.

컨테이너가 `` MovieFinder`` 인스턴스를 알아서 주입해줄 것이라고 했는데, `` MovieFinder``가 인터페이스라면? 비단 인터페이스일 경우 뿐만 아니라, `` MovieFinder``의 상속계층 하위에 있는 어떤 객체든지 파라미터로 넘어갈 수 있다!

이런 경우 컨테이너는 어떤 객체를 넘겨야 할까?

 

XML에 ref 어쩌구..를 작성하는 이유가 바로 이 것이다. 어떤 객체(빈)를 넘길 것인가?를 명시한다. 이를 넓은 의미에서 Container Configuration이라고 한다.

```xml

<bean id="exampleBean" class="examples.ExampleBean" factory-method="createInstance">
    <constructor-arg ref="anotherExampleBean"/>        constructor DI

    <constructor-arg value="1"/>

    <property name="beanTwo" ref="yetAnotherBean"/>    setter DI

</bean>

<bean id="anotherExampleBean" class="examples.AnotherBean"/>

<bean id="yetAnotherBean" class="examples.YetAnotherBean"/>

```

XML을 이용한 방법 이외에도 Java-based Container Configuration, Annotation-based Container Configuration이 있다.

 

 

모호하지 않은 상황에서는 Configuration을 따로 하지 않아도 스프링이 알아서 resolve해준다.

예전에 많이 사용했던 ``java @Autowired`` 애너테이션은 어떤 빈을 주입할지 선택지가 명확하니 컨테이너가 알아서 resolve 해달라는 의미다.

요즘은 이런 Field based DI 보다는 Constructor, Setter based DI를 더 많이 사용한다.

 

예전 버전에서는 constructor DI나 setter DI는 반드시 XML에 의존성을 명시해야 했는데,

Spring 4.3부터 어떤 빈을 주입할지가 명확한 생성자는 implicit constructor injection 해준다. 

어떤 빈을 주입할지가 명확하지 않은 상황에서는 사용할 수 없다.