서블릿 컨테이너란 웹서버 확장 프로그램의 하나이며
이름 그대로 여러 서블릿을 담고 있으면서 실행하거나
HTTP Response 생성하고 스레드, 세션 관리를 도와준다.
대표적으로 Tomcat, Jetty 등이 서블릿 컨테이너라고 불린다.스프링 컨테이너란 프로그램이 유연하게 작동될 수 있도록
스프링에서 관리하는 객체(Bean)들의 생성, 관리, 의존성 주입(Dependency Injection), 라이프사이클 관리 등을 담당하는 컨테이너이다.각각의 서블릿들은 스프링 컨테이너로 빈(Bean)객체를 주입받아 사용할 수 있어
두 컨테이너는 스프링에서 밀접한 관계에 있으며 서로 상호보완적으로 동작한다.
웹 서버라고 하면 웹페이지를 사용자에게 전송하는 서버를 말하는데
기본적으로 사용자는 HTTP 정적인 웹페이지만이 요청이 가능하다.
사용자의 상황에 따라서, 사용자가 입력한 값에 따라서 동적으로 웹페이지가 생성되어야 한다면
Tomcat, Jetty 같은 Servlet Container를 사용함으로써 소켓 생성 및 API를 구성하지 않고도 사용자와 통신할 수 있게 해준다.
HTTP 요청이 들어오면 Servlet Container에서 요청을 분석하여 적절한 서블릿의 서비스를 실행시키고 응답을 보내는 과정을 실행한다.
Servlet Container는 서블릿을 초기화 후 담고 있으며
그때 그때 사용자의 요청을 처리할 수 있는 서블릿이 사용되는 환경을 제공하며
서블릿들의 생명주기 또한 관리한다.
Servlet Container에 의해 동작하는 Servlet은 javax.servlet 패키지안에 정의된 인터페이스이며 HTTP 요청, 응답을 처리하며 웹페이지를 동적으로 생성하는 역할을 한다.
기본적으로 init, service, destroy 메소드가 정의되어 있다.
대표적으로 DispatcherServlet
, HttpServlet
등의 구현체가 있으며 서블릿들은 일반적으로 HttpServlet
을 상속받는다.
1. 서블릿의 생성
2. 웹 브라우저에서 요청이 들어왔을때
HttpServletRequest
및 HttpServletResponse
객체를 생성한 뒤 서블릿을 service() 메소드로 호출한다.스프링부트를 사용한다면 내장 서블릿 컨테이너를 포함하고있기때문에 web.xml 파일이 없어도 @EnableAutoConfiguration, @ComponentScan, @Configuration 등의 어노테이션으로 필요한 서블릿을 자동으로 설정된다.
3. 요청을 처리할 Servlet 인스턴스 생성과 처리
HttpServletRequest
와 HttpServletResponse
객체를 사용하여 HTTP요청 및 응답을 처리한다.HttpServletResponse
객체에 설정해주고 HTTP 응답 정보를 반환한다. HttpServletRequest
및 HttpServletResponse
객체를 소멸시킨다.4. 서블릿의 소멸 시기
웹 어플리케이션이 종료되면 Servlet Container는 destroy() 메서드를 통해 Servlet 객체를 제거한다.
destroy() 메서드는 Servlet 객체가 사용한 자원을 해제하는 작업을 담당하며 이후, Servlet 객체는 Garbage Collector에 의해 메모리에서 제거된다.
그림으로 요약하면 아래와 같다.
위 Servlet Container는 서블릿들을 초기화할 때,
Spring Container는 각각의 서블릿이 제대로 동작하기 위해 필요한 의존성 주입(DI)을 제공한다.
두 컨테이너는 이렇게 밀접한 관계에 있기 때문에 Spring Container에 짚고 넘어갈 필요가 있다.Spring Container의 역할은 빈(Bean) 객체들을 생성하고,
빈 객체들 간의 의존성을 관리하며 빈 객체들을 필요한 곳에서 주입(Injection)해주는 등의 역할을 맡는다.빈 객체를 생성하고 생명주기를 관리할 때 스프링 컨테이너가 직접 컨트롤 하기 때문에
Inversion of Control (IoC) 방식이라고 불리며
객체 생성, 관리, 의존성 관련 코드들이 아닌, 서비스에 대한 코드에만 집중할 수 있는 장점이 있다.때문에 Spring Container는 IoC Container라고도 불린다.
IoC는 메소드나 객체의 호출작업을 개발자가 결정하는 것이 아니라,
외부에서 결정되는 디자인패턴을 말하는데 이 객체에 대한 제어권을 스프링 컨테이너가 가지고 있기때문에 제어의 역전이라고 불린다.
대표적 예시 중 하나로 스프링부트에서 @SpringBootApplication 어노테이션이 보통 프로젝트 메인 클래스에 정의되어 있다.
이 어노테이션이 스프링 컨테이너를 생성하고 컴포넌트 스캔을 수행하여 빈 객체를 찾아 의존성을 자동으로 주입한다.주로 @Component, @Service, @Repository, @Controller 등의 어노테이션이 붙은 클래스들이 빈으로 등록될 대상이 되며
@ComponentScan 어노테이션으로 basePackages, basePackageClasses, includeFilters, excludeFilters 등의 옵션으로 Spring Container의 빈 객체 관리에 대해 세밀하게 지정할 수 있다.
개발자가 직접 객체 간의 의존성을 생성하고 관리하면 추후 코드의 가독성과 유지보수성을 떨어뜨리는 문제 등이 발생할 수 있어 고안된 방법이며 코드의 가독성과 유지보수성을 높일 수 있다.
IoC의 구현 방식 중 하나인 DI(Dependency Injection)은 IoC를 구현하는 방법 중 하나이다.
IoC가 제어의 역전 개념을 통틀어 말한다면
DI는 이 개념을 구현하기 위해 의존관계를 외부에서 주입(Injection)하는 방식이다.
간단한 예시로는
// 예시1)
public class Aclass {
private Bclass b = new Bclass();
...
}
// 예시2)
public class Aclass {
private Bclass b;
public Aclass(Bclass b) {
this.b = b;
}
}
예시1과 2모두 A클래스는 B클래스에 대해 의존성을 가지고 있어 B가 변경되면 A도 변경된다.
하지만 예시2는 외부에서 생성자를 통해 B를 주입받는다. 이런 생성자를 활용한 DI말고도
Setter주입, 인터페이스 주입 방식이 있다.
스프링에서는 이 방식을 사용하여 이 의존관계가 빈으로 등록되어 있다면 자동으로 @Autowired와 같은 어노테이션 하나로 의존성을 주입해주며 때문에 객체 간의 결합도도 낮추고 유연하고 확장성 높은 코드를 작성할 수 있다.
BeanFactory
, ApplicationContext
크게 2가지로 나눌수 있다.
BeanFactory
는 빈을 등록하고 생성하고 조회하고 돌려주는 등 빈을 관리하는 역할을 한다.
getBean() 메소드를 통해 Bean을 인스턴스화 할 수 있으며 그 외에 부가적인 빈을 관리하는 기능을 담당한다.
즉 스프링컨테이너의 가장 기본적이며 핵심적인 기능을 담고있다.
getBean()이 호출되면 BeanFactory
는 의존성 주입을 통해 빈을 인스턴스화하고, 빈의 특성을 설정하기 시작하며 여기서 빈의 라이프사이클이 시작된다.
스프링이 제공하는 각종 부가 서비스를 추가로 제공하며 BeanFactory
를 상속한 인터페이스이다. 이 인터페이스를 구현하는 여러 컨테이너가 있으며
BeanFactory
와 다른 점은 BeanFactory
는 getBean() 메소드가 호출된 시점에서 지연로딩(LAZY)을 시작하여 생성해주지만 ApplicationContext
초기화 시점에 모든 빈을 미리 로드한 후(EAGER) 필요할때 빈을 지연 없이 얻을 수 있다.
ApplicationContext
을 구현하는 컨테이너는 크게
FileSystemXmlApplicationContext
, AnnotationConfigApplicationContext
또는
ClassPathXmlApplicationContext
등이 있다.
ClassPathXmlApplicationContext 와 FileSystemXmlApplicationContext
xml 파일로 빈객체들의 생성과 의존관계를 정의하면 이 xml 파일을 이용하여 빈 객체를 생성하고 관리한다. 스프링 프레임워크 초기부터 사용된 가장 간단한 형태의 스프링 컨테이너라고 할 수 있다.
두 컨테이너의 가장 큰 차이점은 xml 파일의 경로이여
ClassPathXmlApplicationContext
는 리소스 폴더(src/main/resources/...)에서,
FileSystemXmlApplicationContext
는 보통 프로젝트 외부 경로의 xml 파일을 사용할 때 사용된다.
xml 파일 기반으로 빈객체들을 관리하기 때문에 다음과 같은 단점이 있다.
이와 같은 단점을 가지고 있지만 비교적 쉽게 사용할 수 있기 때문에 간단한 작은 규모의 테스트 프로젝트 등에서 종종 사용된다.
AnnotationConfigApplicationContext
가장 자주 사용되는 스프링 컨테이너이며 xml 파일을 사용하는 대신 자바 어노테이션으로 구성된 설정 클래스를 사용한다.
이 설정 클래스를 생성하고, 이를 바탕으로 스프링 빈을 생성한다.
@Component, @Service, @Repository, @Controller 등으로 자동으로 빈객체를 구성할 수 있고
@Autowired, @Qualifier, @Value 등의 어노테이션을 사용하여 빈객체 간의 의존성을 관리할 수 있다.
자바 코드로 작성되기 때문에 컴파일 단계에서 에러를 검출할 수 있다.