Application Context
는 스프링 프레임워크에서 제공하는 핵심 컨테이너이며, IoC(Inversion of Control) 컨테이너
의 한 종류이다.
이러한 Application Context는 스프링에서 객체(빈)의 생성, 관리, 의존성 주입 등을 담당하며, 다양한 기능과 서비스를 제공합니다.
ContextLoaderListener
를 사용하여 웹 애플리케이션 컨텍스트를 초기화할 수 있습니다.
<!-- 변경전 -->
<listener>
<listener-class>mycom.test.MyListener</listener-class>
</listener>
<!-- 변경후 -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<context-param>
<param-name>contextClass</param-name>
<param-value>org.springframework.web.context.ContextLoader</param-value>
</context-param>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/applicationContext.xml</param-value>
</context-param>
ServletListener를 직접 구현한 클래스의 패키지 경로를 작성했던 코드에서 스프링에서 제공해주는 API 경로로 변경한 것을 볼 수 있다.
이처럼 Spring 프레임워크에서 제공하는 ContextLoaderListener
는 웹 응용프로그램을 초기화하고 Spring Context를 설정하기 위한 API를 제공한다.
그리고 ContextLoaderListener
를 웹 애플리케이션이 시작될 때 Spring ApplicationContext를 초기화하고 필요한 빈들을 로드하는 역할을 한다.
실제로 ContextLoaderListener
가 등록되면 웹 애플리케이션이 호출되고, 내부적으로 WebApplicationContext
를 생성하고 ServletContext
에 등록한다.
public class MyContextLoaderListener implements ServletContextListener {
private WebApplicationContext context;
@Override
public void contextInitialized(ServletContextEvent event) {
// WebApplicationContext 생성 및 초기화
this.context = createWebApplicationContext(event.getServletContext());
// ServletContext에 WebApplicationContext 등록
event.getServletContext().setAttribute(WebApplicationContext.ROOT_WEBAPPLICATION_CONTEXT_ATTRIBUTE, this.context);
}
@Override
public void contextDestroyed(ServletContextEvent event) {
// 애플리케이션 종료 시의 작업
}
private WebApplicationContext createWebApplicationContext(ServletContext servletContext) {
// 실제 WebApplicationContext 생성 로직
// 여기서는 예시로 GenericWebApplicationContext를 사용
GenericWebApplicationContext context = new GenericWebApplicationContext();
// 설정 등을 추가로 수행할 수 있음
return context;
}
}
이때, this.context
는 ApplicationContext
를 나타내며, ServletContext
에 WebApplicationContext
형태로 생성하고 등록하는 것이다.
@Configuration
@ComponentScan
public class AppConfig {
}
ApplicationContext를 만들기 위해서는, 자바 설정 클래스 반드시 있어야한다.
왜냐하면 Servlet Context와 연동을 해야하기 때문이다.
자바 설정 클래스를 자세히 살펴보면 @Component
어노테이션이 붙어있어 @Compoenent
, @Service
, @Controller
등 어노테이션이 붙은 클래스들을 찾아서 빈으로 등록한다.
그리고 ContextLoaderListener
가 AppConfig(자바 설정 클래스)
를 가진 상태로 AnnotationConfigWebApplicationContext
를 만들고 그 안에, HelloService
라는 Bean 객체가 등록되어 있는 것이다.
<web-app>
<!-- ContextLoaderListener 등록 -->
<listener>
<listener-class>
org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>
<!-- Spring 설정 파일의 경로 지정 -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>com.example.AppConfig</param-value>
</context-param>
</web-app>
어노테이션을 이용한 방법이 아니라, web.xml에 명시적으로 선언하여 자바 설정 클래스를 등록할 수 있다.
위 2개 코드는 모두 AppConfig
클래스를 로드하고, 그 안에 정의된 HelloService
Bean을 등록합니다.
@Service
public class HelloService {
public String getName(){
return "jinminChoi";
}
}
servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);
ContextLoaderListener
에서 초기화될 때 작성된 코드를 살펴보면 위 코드와 같이 ApplcationContext를 등록되기 때문에 ApplicationContext를 get하여 사용할 수 있다.
왜냐하면, ContextLoaderListener
가 초기화될 때, 모든 Servlet이 접근할 수 있도록 바인딩이 이루어지기 때문에 Servlet에서 ApplicationContext를 불러와서 사용이 가능하다.
즉, 위에서 AppConfig 클래스를 생성한 이유가 이와같이 Servlet과 연동하기 위한 것이다.
public class HelloServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
ApplicationContext context = (ApplicationContext) getServletContext().getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);
HelloService helloService = context.getBean(HelloService.class);
resp.getWriter().println(helloService.getName());
}
}
따라서, HTTP 요청을 처리하는 Servlet 객체를 생성한 후, 클라이언트로부터 요청이 들어왔을 때 AppConfig(자바 설정 클래스)를 이용하여 ApplicationContext를 초기화하면 Bean으로 등록 가능한 객체들을 등록하여 사용이 가능하다.
public class MainApplication {
public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
// 이제 MyComponent 빈을 가져올 수 있음
HelloService hs = context.getBean(HelloService.class);
hs.getName();
}
}
또 다른 방법으로는 main()
를 이용해 AppConfig 클래스를 이용해서 ApplicationContext를 초기화하면, @ComponentScan
에 의해서 빈들이 등록된다.
그리고 getBean()
메서드를 통해 해당 Bean을 가져와서 사용할 수 있다.
Spring Boot 프로젝트에서는 @SpringBootApplication
어노테이션을 사용하면 자동으로 ApplicationContext
의 초기화가 이루어집니다.
Spring Boot는 내장 톰캣을 사용하고 있습니다. 그렇기 때문에 AppplicationContext
를 초기화합니다.
따라서, Spring Boot 애플리케이션은 @SpringBootApplication
어노테이션이 붙은 클래스를 기준으로 자동으로 구성되며, @SpringBootApplication
어노테이션은 @Configuration
, @EnableAutoConfiguration
, @ComponentScan
을 포함하고 있습니다.
@SpringBootApplication
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
}
위 코드에서 SpringApplication.run()
메서드는 ApplicationContext를 초기화하고 애플리케이션을 실행하는 역할을 합니다.
크게 SpringBoot는 내부적으로 톰캣을 내장하고 있어서 초기화가 자동으로 이루어지는 반면에 ContextLoaderListener
를 사용하는 경우에는 외부 서버(톰캣)에서 애플리케이션 실행하는 경우가 일반적이기 때문에, web.xml
에 명시적으로 작성하여 초기화가 이루어집니다.
즉, @SpringBootApplication
어노테이션이 선언된 MyApplication
클래스는 자바 설정 클래스(@Configuration
, @ComponentScan
포함)이며, main()
에서 MyApplication
클래스를 이용하여 ApplicationContext
를 초기화 한것이다