Spring에서는 Servlet filter와 같은 기능을 지원하기 위해서 HandlerInterceptor를 지원한다.
HandlerInterceptor은 Controller가 실행되기 전, 후에 호출이 되기 때문에 공통적으로 처리해야할 작업(예를 들면 인증처리, 로그남기기)을 손쉽게 구현 할 수 있다.
서블릿 필터와 개념은 같지만 Spring의 핸들러 인터셉터를 사용하게 되면 HttpServletRequest, HttpServletResponse, 실행 될 컨트롤러 빈 오브젝트, 컨트롤러가 돌려주는 ModelAndView, 발생한 예외등을 제공받을 수 있기 때문에 서블릿 필터보다 더 정교하게 만들수 있다.
서블릿 필터
- web.xml에 별도 등록
- 웹어플리케이션에 들어오는 모든 요청에 적용 됨
핸들러인터셉터
- DispatcherServlet의 특정 핸들러 맵핑으로만 제한됨
- web.xml 수정할 필요 없음
- 컨트롤러 오브젝트 접근 가능하며 ModelAndView와 같은 컨트롤러가 리턴하는 정보를 활용가능
핸들러인터셉터를 이용하는 간단한 예제는 다음과 같다.
1. HandlerInterceptor 인터페이스를 이용해 핸들러인터셉터 구현
HandlerInterceptorTest.java
package com.mungchung.sample;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
public class HandlerInterceptorTest implements HandlerInterceptor {
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler) throws Exception {
System.out.println("===== preHandler =====");
return true;
}
public void postHandle(HttpServletRequest request,
HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
System.out.println("===== postHandle =====");
}
public void afterCompletion(HttpServletRequest request,
HttpServletResponse response, Object handler, Exception ex)
throws Exception {
System.out.println("===== afterCompletion =====");
}
}
preHandler, postHandler, afterCompletion 3개의 메서드를 Override해서 구현해야한다.
preHandler
- Controller 가 호출되기 전에 실행된다.
- return false일 경우 Controller로 다음 처리를 넘기지 않고 종료처리 해버린다.
- 인증처리와 같은 곳에서 사용될듯 -_-;
postHandler
- Controller 가 요청을 처리한 뒤에 호출 됨
- Controller 실행도중 예외가 발생하면 이 메서드는 실행 안된다.
afterCompletion
- 뷰를 통해서 클라이언트에 응답을 전송한 뒤에 실행된다.
- Controller나 View에 오류가 발생해도 실행된다.
- 로그처리에 좋을듯 -_-
2. 핸들러 인터셉터 동작하도록 xml 파일 수정
spring template project를 이용해 spring mvc project를 이용 할 경우
어플리케이션 컨텍스트 설정 파일인 root-context.xml
서블릿 컨텍스트 설정 파일은 servlet-context.xml
이렇게 2개의 설정파일이 생긴다.
어떤 xml 파일에 핸들러 인터셉터 동작하도록 처리해야하나? 나도 잘 모른다. -_-
root-context.xml과 servlet-context.xml의 설정 방법이 각기 다르다.
아직 내가 Root Application Context와 Servlet Context의 개념이 약해서 계속 헷갈려하는것 일수도있다.
일단, 이해못하는것은 일단 넘어가고(-_-) 대신 2가지 방법의 설정이 어떻게 다른지 확인해봤다.
3. src/main/webapp/WEB-INF/spring/root-context.xml 의 설정을 이용 할 경우
appServlet/servlet-context.xml에서 자동으로 Bean 찾는 부분 주석처리 한다.
<!--
자동으로 Bean 찾는 부분 주석처리
<context:component-scan base-package="com.mungchung.sample" />
-->
root-context.xml에 HomeController 수동으로 등록하고 마찬가지로 인터셉터 처리관련 내용들 모두 수동등록한다.
root-context.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
<!-- Root Context: defines shared resources visible to all other web components -->
<bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping">
<property name="interceptors">
<list>
<ref bean="interceptorTest"/>
</list>
</property>
</bean>
<bean name="/" class="com.mungchung.sample.HomeController"/>
<bean name="interceptorTest" class="com.mungchung.sample.HandlerInterceptorTest"/>
</beans>
이렇게 설정하고 http://localhost:8080/SampleHandlerInterceptor/ 으로 접속하면 Hello world 메지시가 보이고

STS에서 로그 콘솔을 보면 아래와 같이 핸들러에서 구현한 각 3개의 메서드가 실행되었음을 알 수 있다.

4. src/main/webapp/WEB-INF/spring/appServlet/servlet-context.xml 을 이용
이번엔 서블릿컨텍스트 설정파일을 이용하는 방법이다.
위에서 root-context.xml에서 설정한 내용은 모두 주석처리 한다.
spring mvc project예제에 있는 servlet-context.xml 파일은 컴퍼넌트 스캔을 이용해서 자동 빈 등록한다.
이렇게 자동 빈 등록을 사용할 경우 xml에 HandlerInterceptor의 내용을 어떻게 기술할지에 대한 방법이다.
servlet-context.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/mvc"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<!-- DispatcherServlet Context: defines this servlet's request-processing infrastructure -->
<!-- Enables the Spring MVC @Controller programming model -->
<annotation-driven />
<!-- Handles HTTP GET requests for /resources/** by efficiently serving up static resources in the ${webappRoot}/resources directory -->
<resources mapping="/resources/**" location="/resources/" />
<!-- Resolves views selected for rendering by @Controllers to .jsp resources in the /WEB-INF/views directory -->
<beans:bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<beans:property name="prefix" value="/WEB-INF/views/" />
<beans:property name="suffix" value=".jsp" />
</beans:bean>
<context:component-scan base-package="com.mungchung.sample" />
<mvc:interceptors>
<mvc:interceptor>
<mapping path="/*"/>
<beans:bean class="com.mungchung.sample.HandlerInterceptorTest" />
</mvc:interceptor>
</mvc:interceptors>
</beans:beans>
root-context.xml에서 처리했던 방법과는 완전 다르다.
root-context.xml에서는 핸들러인터셉터에 어떤 컨트롤러를 이용할지 지정했다면
servlet-context.xml는 URL 맵핑을 이용해서 핸들러인터셉터를 지정했다.
어떤 방식이 더 나은지는 아직 경험이 없어서 모르겠으나 첫번재 방식인 일일이 컨트롤러에 핸들러인터셉터 맵핑을 해주는것에 몇가지 단점이 있다고 한다.
그래서 두번재 방식은 URL을 이용한 방식을 더 이용하는것 같기도하고.....실전에서 사용안해봐서 정말 모르겠다 -_-
이렇게 HandlerInterceptor에서 공부하면서 몇가지 자료 찾아보니 HandlerInterceptor의 단점을 보안한
새로운 인터셉터인 MappedInterceptor이 3.0에 추가되었다고 한다. (관련글)
기껏 예제만들고 공부해놨더니 -_-; MappedInterceptor에 대해서는 나중에 기회되면(?) 공부해야겠다.