이제 ServletWebApplication에 Spring을 도입하도록 하겠습니다. 기존 ServletWeb Application에서 Spring을 도입한다면 다음과 같은 것을 이용한다는 의미 입니다.
- Spring 에서 제공하는 IoC Continer를 사용.
- Spring MVC를 사용
1. Servlet에서 Spring IoC Container를 사용하기
1.1 의존성 추가
<!-- Spring mvc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.1.3.RELEASE</version>
</dependency>
pom.xml
에 SpringFramework를 사용하기 위한 의존성을 추가합니다.
그러면 SpringFramework를 사용하기 위해 필요한 Libraies가 자동으로 추가됩니다.
1.2 Web.xml
ServletContainer는 web.xml에 기술되어있는 내용을 바탕으로 ServletContainer를 초기화 하게 됩니다. 따라서 이곳에IoC Container
를 사용하겠다고 기술을 해주어야합니다.
x
<web-app>
<display-name>Archetype Created Web Application</display-name>
<context-param>
<param-name>contextClass</param-name>
<param-value>org.springframework.web.context.support.AnnotationConfigWebApplicationContext</param-value>
</context-param>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>galid.com.AppConfig</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<servlet>
<servlet-name>helloServlet</servlet-name>
<servlet-class>galid.com.HelloServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>helloServlet</servlet-name>
<url-pattern>/hello</url-pattern>
</servlet-mapping>
</web-app>
우선 <listener>
태그가 보입니다. listener클래스 속성에는 ContextLoaderlistener
을 입력했습니다. 그 아래에는 <context-param>
태그를 가진 두개의 속성이 보입니다. 각각의 param-name
은 contextClass, contextConfigLocation
입니다. 아래에서 각각에 대해 조금 더 상세히 설명드리겠습니다.
ContextLoaderListener
ContextLoaderListener는 ServletContext의 라이프사이클에 맞추어 ApplicationContext를 ServletContext에 추가/삭제 하도록 합니다.
ContextLoader
ContextLoaderListener
가 상속받을 Class로 실질적으로 ApplicationContext를 생성하고 ServletContext에 setting하는 역할을 합니다. ContextLoad
가 ApplicationContext를 ServletContext에 등록하기 위해서는 ApplicationContext Class Type
과 ApplicationConetxt Configuration 파일
이 필요합니다. 그러한 Class Type과 Configuration 파일을 ServletContext의 Parameter로부터 전달받게 되는데 그것들이 바로 아래의 두개의 태그들입니다.(contextClass, contextConfigLocation)
contextClass
contextClass
의 경우 ContextLoader에서 생성할 IoC Container 클래스를 받아들일 Parameter Name을 의미합니다. 저희는 Annotation을 기반으로 Bean을 등록할 것이므로 Annotation을 인식할 수 있는 AnnotationConfigWebApplicationContext
를 등록해주었습니다.
contextConfigLocation
contextConfigLocation
은 말그대로 IoC Continaer의 설정 파일의 위치를 의미합니다. xml의 위치를 입력할 수도있고 개발자가 정의한 Java Class 파일을 입력할 수도 있습니다.
1.3 ContextLoaderListener 자세히
우선 Spring에서 제공하는 IoC Container를 이용하기 위해서는 ServletContext
에 ApplicationContext
를 추가해야 합니다. 그러한 역할을 하는 것이 ContextLoaderListener
입니다. 즉, ContextLoaderListener
는 ServletContext의 라이프 사이클에 맞추어 ServletContext에 자동으로 ApplicationContext를 추가합니다.
위의 그림은 ContextLoaderListener
Class입니다. ContextLoader
를 상속받으며, ServletContextListener
를 구현하고 있습니다. 앞선 포스팅에서 알아 보았듯이 ServletContextListener
는 ServletContext의 라이프사이클에 변화를 감지합니다. contextInitialized()
메소드에서 상속받은 ContextLoader
클래스에 존재하는initWebAppliationContext()
라는 메소드를 호출 한것을 볼 수있습니다. 즉, ServletContext가 초기화 되는 시점에 WebApplicationContext를 초기화 한다는 의미인것 같습니다.
ClassLoader의 initWebApplicationContext()
ContextLoaderListener
에서 상속받은 ContextLoader
클래스의 initWebApplicationContext() 메소드
를 조금 더 자세히 살펴보겠습니다. 다른 코드들은 복잡하여 일부분을 발췌한 사진입니다. servletContext
에 WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE
라는 키 값으로 WebApplicationContext
를 담는 것를 볼 수 있습니다.
2. IoC Container 사용 예제
이제 위에서 설정한 IoC Container를 이용하여 Bean을 주입받아 사용해보도록 하겠습니다.
2.1 Servlet 생성
public class HelloServlet extends HttpServlet {
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.getWriter().println("<html><body>");
resp.getWriter().println("hello Servlet");
resp.getWriter().println("</body></html>");
}
}
우선 간단히 HelloServlet
을 만들어 보도록하겠습니다. HttpServlet을 상속받고 doGet메소드를 오버라이딩하여 인자로 전달되는 resp를 이용해 페이지를 만들어주면 됩니다.
xxxxxxxxxx
<web-app>
<display-name>Archetype Created Web Application</display-name>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<context-param>
<param-name>contextClass</param-name>
<param-value>org.springframework.web.context.support.AnnotationConfigWebApplicationContext</param-value>
</context-param>
<servlet>
<servlet-name>helloServlet</servlet-name>
<servlet-class>galid.com.HelloServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>helloServlet</servlet-name>
<url-pattern>/hello</url-pattern>
</servlet-mapping>
</web-app>
그 후 Servlet을 맵핑합니다.
테스트를 해봅니다. 정상적으로 출력이 되네요.
2.2 Bean Configuration Class 생성
어노테이션 기반으로 빈을 등록하기 위해서는 xml설정 파일에 <context-component:scan>
태그를 추가하거나 Java Configuration 파일을 생성해야 합니다. Java Class를 이용할 것입니다.
AppConfig class를 생성합니다.
xxxxxxxxxx
public class AppConfig {}
AppConfig Class의 경우에는 BeanConfiguration Class를 알리기 위한 @Configuration
어노테이션과, Bean 등록을 위한 @ComponentScan
어노테이션을 부여했습니다.
x
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>galid.com.AppConfig</param-value>
</context-param>
IoC Container에 사용될 Configuration file의 위치를 알려주는 contextConfigLocation
parameter를 추가하고 방금 생성한 AppConfig Class의 풀경로를 입력해줍니다.
2.3 HelloService Class 생성
Bean으로 등록될 HelloService Class입니다.
public class HelloService {
public String getName(){
return "galid";
}
}
간단히 이름을 반환하는 getName()메소드만을 작성하였고, ComponentScan
에 의해 Bean Container에 등록되기 위하여 @Service
어노테이션을 부여하였습니다.
이렇게 까지 설정을 하면 ServletContext에는 IoC Continaer가 존재할 것이고, IoC Container에는 우리가 설정한 Bean들이 들어가 있을것입니다. 따라서 IoC Container를 이용하여 Bean들을 필요에따라 꺼내어 사용할 수 있습니다.
2.4 IoC Container를 이용하여 HelloService Bean 주입하기
이제 앞서 생성한 IoC Container를 이용해 HelloService를 주입받고 사용해볼 것 입니다. 우선 IoC Container를 사용하려면 당연히 가져와야 합니다.
xxxxxxxxxx
public class HelloServlet extends HttpServlet {
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
ApplicationContext applicationContext = (ApplicationContext) getServletContext().getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);
HelloService helloService = applicationContext.getBean("helloService", HelloService.class);
resp.getWriter().println("<html><body>");
resp.getWriter().println("hello Servlet" + helloService.getName());
resp.getWriter().println("</body></html>");
}
}
앞서 설명한 ContextLoader
에서는 ContextLoaderListener
에 의해 ServletContext
에 개발자가 지정한 IoC Container를 ServletContext의 생명주기에 맞추어 자동으로 생성/삭제를 한다고 했습니다. 또한 ContextLoader
Class에서는 실질적으로 IoC Container를 생성하고 WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE
라는 키값으로 ServletContext에 담는 역할을 한다고 말씀드렸습니다. 따라서 getServletContext().getAttribute("키값")
으로 IoC Container를 가져올 수 있습니다. ServletContext의 getAttribute() 메소드에 의해서 반환되는 객체들은 모두 Object형태로 반환 되기 때문에 적절히 Casting해 사용해야 합니다.
이어서 생성된 applicationContext객체의 getBean() 메소드를 이용해 HelloService를 가져와 getName()메소드를 실행하면 끝입니다.
성공입니다~
요약
- 따라서 ContextLoaderListener은 ServletContext
가 초기화 되는 시점에 ServletContext
에 WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE
라는 키 값으로 제공받은 WebApplicationContext
를 담도록 요청해주는 역할을 하는것을 알 수 있습니다.
- ContextLoader
는 실질적으로 IoC Container를 생성하고 ServletContext에 담는 역할을 합니다.