Spring Web MVC 의 가장 핵심이 되는 DispatcherServlet 에 대해 정리한다.
DispatcherServlet 클래스는 Spring 으로 웹 애플리케이션을 개발할 때 일반적으로 사용하는 Front Controller Pattern 에서 Front Controller 역할을 한다.
현재 작성되어있는 web.xml 코드에서 정리 내용에 불필요한 내용들을 삭제해서 다음과 같이 만들어 주었다.
삭제한 내용은 filter 와 MyServlet servlet 그리고 welcome-file-list 이다.
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
id="WebApp_ID" version="2.5">
<display-name>OldStyleDynamicWebApplication</display-name>
<!-- context-param 은 filer 보다 먼저 작성해야 한다. -->
<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>me.nimkoes.sample.MyAppConfig</param-value>
</context-param>
<!-- Spring 이 제공하는 Listener 등록 -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
</web-app>
이제 이곳에 DispatcherServlet 을 등록해 볼건데 다음 그림과 같은 계층 구조를 가지도록 할 예정이다.
DispatcherServler 은 Servlet WebApplicationContext 가 되어 Controller, HandlerMapping, ViewResolver 역할을 하는 bean 을 등록 하고, ContextLoaderListener 가 만드는 ApplicationContext 가 Root WebApplicationContext 가 되어 모든 servlet 에서 공통으로 사용할 수 있도록 Service, Repostory 역할을 하는 bean 을 등록한다.
web.xml 에 DispatcherServlet 을 등록하기에 앞서 Root WebApplicationContext 의 설정 파일로 사용중인 me.nimkoes.sample.MyAppConfig 클래스를 수정해야 한다.
package me.nimkoes.sample;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Controller;
@Configuration
@ComponentScan(excludeFilters = @ComponentScan.Filter(Controller.class))
public class MyAppConfig {
}
@ComponentScan annotation 을 작성할 때 excludeFiulters 에 Controller.class annotation 을 추가하여 @Controller 가 붙은 클래스는 bean 으로 등록하지 않겠다는 것을 의미한다.
이 설정 파일은 Root WebApplicationContext 가 사용하기 때문에 Controller 역할을 하는 bean 을 등록하지 않기 위함이다.
@Controller annotation 을 붙인 클래스를 Servlet WebApplicationContext 의 bean 으로 등록하기 위해 @Controller annotation 이 붙은 클래스를 bean 으로 등록하는 설정 파일을 새로 만들어 준다.
package me.nimkoes.sample;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Controller;
@Configuration
@ComponentScan(useDefaultFilters = false, includeFilters = @ComponentScan.Filter(Controller.class))
public class MyWebConfig {
}
이 설정 파일은 ComponentScan 을 할 때 useDefaultFilters 값을 false 로 할당하고, includeFilters 에 Controller.class annotation 을 추가해 주었다.
userDefaultFilters 값은 API 문서에 다음과 같이 정의되어 있다.
Indicates whether automatic detection of classes annotated with @Component @Repository, @Service, or @Controller should be enabled. |
이 값이 true 인 경우 @Component, @Repository, @Service, @Controller annotation 이 붙은 클래스에 대해 bean 으로 등록 한다.
기본값이 true 이기 때문에 false 로 바꿔주고 @Controller 만 bean 으로 등록 하도록 했다.
설정 파일을 만들었으니 DispatcherServlet 으로 Servlet WebApplicationContext 를 만들 때 사용할 수 있도록 다음과 같이 web.xml 을 수정한다.
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
id="WebApp_ID" version="2.5">
<display-name>OldStyleDynamicWebApplication</display-name>
<!-- context-param 은 filer 보다 먼저 작성해야 한다. -->
<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>me.nimkoes.sample.MyAppConfig</param-value>
</context-param>
<!-- Spring 이 제공하는 Listener 등록 -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!-- Spring Web MVC 사용하기 위한 DispatcherServlet servlet 등록 -->
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!-- ContextLoaderListener 를 사용하기 위해 context-param 으로 parameter 를 정의한 것처럼 -->
<!-- DispatcherServlet 을 사용하기 위해 init-param 으로 parameter 를 정의할 수 있다. -->
<init-param>
<param-name>contextClass</param-name>
<param-value>org.springframework.web.context.support.AnnotationConfigWebApplicationContext</param-value>
</init-param>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>me.nimkoes.sample.MyWebConfig</param-value> <!-- Servlet WebApplicationContext 를 만들 때 사용할 설정 파일 -->
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>/app/*</url-pattern>
</servlet-mapping>
</web-app>
'/app/*' 패턴으로 들어오는 모든 요청에 대해 DispatcherServlet 이 처리하도록 설정 했다.
현재 프로그램이 어떻게 작성되어 있는지 한 번 정리해 보자.
Root WebApplicationContext 의 설정 파일 : MyAppConfig.java
Servlet WebApplicationContext 의 설정 파일 : MyWebConfig.java
MyAppConfig 설정파일에 의해 등록되는 bean : MyHelloServlet.java (@Service)
MyWebConfig 설정파일에 의해 등록되는 bean : 없음
MyServletContextListener.java : ServletContextListener 를 구현한 클래스로 Servlet life cycle 에 의해 실행되는 클래스
DispatcherServlet 이 잘 설정 되었는지 @Controller 클래스를 하나 만들어서 Servlet WebApplicationContext 에 baen 으로 잘 등록 되는지 확인해보자.
package me.nimkoes.sample;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
public class MyHelloController {
@Autowired
MyHelloService myHelloService;
@GetMapping("/hello")
@ResponseBody
public String hello() {
return "Hello, " + myHelloService.getName();
}
}
@Controller annotation 을 사용했기 때문에 Root 가 아닌 Servlet WebApplicationContext 에 bean 으로 등록 된다.
현재 작성한 코드 기준으로 위와 같이 bean 이 등록되어 있다고 생각하면 된다.
실제로 잘 동작하는지 서버를 실행해서 결과를 확인해보자.
브라우저를 통해 의도한 대로 잘 동작하는 것을 확인할 수 있다.
여기서 한가지 더 생각해볼 문제가 있다. 사실 문제는 아니지만, 굳이 이렇게 계층 구조를 가지는 ApplicationContext 를 만들어야 하냐는 것이다.
애플리케이션의 규모가 충분히 크고 계층 구조를 갖는게 유지보수 관점에서 타당하다면 당연히 계층 구조를 가져가는 것에 동의한다.
개인적으로 아직까지 그정도 규모를 가지는 애플리케이션을 만나지 못하기도 했지만 (내 잘못일지도..) 보편적으로 DispatchetServlet 이 만드는 Servlet WebApplicationContext 하나에 모든 bean 을 등록해서 사용했다.
다시 말해서 계층 구조를 가지지 않고 하나의 ApplicationContext 만들 만들어서 사용해왔다.
이번 내용의 마지막으로 Root WebApplicationContext 를 사용하지 않고 (계층 구조를 사용하지 않고) DispatchetServlet 이 만드는 Servlet WebApplicationContext 하나만 사용하는 코드로 수정해보려 한다.
말이 거창해 보이지만 고칠 내용은 많지 않다.
web.xml 파일에서 Root WebApplicationContext 를 만드는 ContextLoaderListener 를 삭제하고, Servlet WebApplicationContext 를 만드는 DispatcherServlet 이 사용하는 설정 파일에서 @ComponentScan 대상을 모든 Component 로 수정하면 된다.
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
id="WebApp_ID" version="2.5">
<display-name>OldStyleDynamicWebApplication</display-name>
<!-- Spring Web MVC 사용하기 위한 DispatcherServlet servlet 등록 -->
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!-- ContextLoaderListener 를 사용하기 위해 context-param 으로 parameter 를 정의한 것처럼 -->
<!-- DispatcherServlet 을 사용하기 위해 init-param 으로 parameter 를 정의할 수 있다. -->
<init-param>
<param-name>contextClass</param-name>
<param-value>org.springframework.web.context.support.AnnotationConfigWebApplicationContext</param-value>
</init-param>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>me.nimkoes.sample.MyWebConfig</param-value> <!-- Servlet WebApplicationContext 를 만들 때 사용할 설정 파일 -->
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>/app/*</url-pattern>
</servlet-mapping>
</web-app>
web.xml 에는 Servlet WebApplicationContext 를 만드는 DispatcherServlet 만 남겨 두었다.
package me.nimkoes.sample;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@Configuration
@ComponentScan
public class MyWebConfig {
}
Servlet WebApplicationContext 를 만드는데 사용하는 설정 파일인 MyWebConfig 클래스에서
@ComponentScan(useDefaultFilters = false, includeFilters = @ComponentScan.Filter(Controller.class))
대신
@ComponentScan
을 사용하여 모든 Component 를 bean 으로 등록하도록 수정 했다.
그리고 사용하지 않은 파일들을 정리했다.
서버를 실행하고 브라우저에서 /app/hello 요청을 보냈을 때 잘 동작 하는지 확인해보자.
계층 구조가 아닌 Servlet WebApplicationContext 하나로 잘 동작 하는 것을 확인할 수 있다.
지금은 Servlet WebApplicationContext 하나를 사용하도록 했지만 Root WebApplicationContext 하나만 사용하도록 할 수도 있다.
하지만 권장하는 방법은 아니기 때문에 굳이 실습해보진 않으려 한다.
지금까지 Spring 의 IoC Container 와 Web MVC 기능을 사용해 보았다.
'Archive > Spring Web MVC' 카테고리의 다른 글
1.5 Spring MVC 동작 원리 마무리 (0) | 2021.06.28 |
---|---|
1.4 DispatcherServlet 기본 동작 원리 (0) | 2021.06.27 |
1.2 Spring IoC Container 연동 (0) | 2021.06.24 |
1.1 MVC 와 Legacy Servlet Application (0) | 2021.06.22 |
Spring Web MVC 정리 개요 (0) | 2021.06.22 |