'Custom'에 해당되는 글 2건

  1. 커스텀 ReturnValueHandler 등록하기 1
  2. 커스텀 ArgumentResolver 등록하기 2

커스텀 ReturnValueHandler 등록하기

개요

이전에는 컨트롤러에 들어오는 파라미터를 가공하는 방법에 대해 알아보았는데, 이번에는 반대로 리턴타입을 기준으로 분기할 수 있는 ReturnValueHandler를 구현해보려 한다. 기존에는 ModelAndViewResolver를 구현해서 AnnotationMethodHandlerAdapter에 등록을 해야 했는데, 스프링 3.2버전 부터는 deprecated 된 상태다. 지금은 ModelAndViewResolver는 남아 있기는 하나 앞으로 HandlerMethodReturnValueHandler 사용하기를 권하고 있다. 모든 HandlerMethodReturnValueHandler를 검색해봐도 리턴된 클래스를 처리 하는 핸들러를 못 찾을 경우에만 ModelAndViewResolver를 검색하기 시작한다.

예제

이전에 파라미터를 Map 형태로 수집하는 클래스를 만들었는데, 이번에는 그 안에 내용에 따라 view를 분기 시켜보려 한다. 파라미터 수집하는 예제는 (http://jekalmin.tistory.com/entry/%EC%BB%A4%EC%8A%A4%ED%85%80-ArgumentResolver-%EB%93%B1%EB%A1%9D%ED%95%98%EA%B8%B0) 이 글을 참고하길 바란다. 먼저 HandlerMethodReturnValueHandler를 구현한 클래스를 생성하고 dispatcherServlet에 등록해주자.


ParamCollectorReturnValueHandler.java

package com.tistory.jekalmin;

import org.springframework.core.MethodParameter;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodReturnValueHandler;
import org.springframework.web.method.support.ModelAndViewContainer;

public class ParamCollectorReturnValueHandler implements HandlerMethodReturnValueHandler {

    @Override
    public boolean supportsReturnType(MethodParameter returnType) {
        // TODO Auto-generated method stub
        return ParamCollector.class.isAssignableFrom(returnType.getParameterType());
    }

    @Override
    public void handleReturnValue(Object returnValue,
            MethodParameter returnType, ModelAndViewContainer mavContainer,
            NativeWebRequest webRequest) throws Exception {
        // TODO Auto-generated method stub
        ParamCollector collector = (ParamCollector)returnValue;
        if(collector.get("viewType").equals("json")){
            // json View 등록
            System.out.println("json View 등록하는 곳");
        }
    }

}

다음 dispatcherServlet의 설정이다.


mvc-config.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"
    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.xsd
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

    <context:component-scan base-package="com.tistory.jekalmin"/>


    <mvc:annotation-driven>
        <mvc:argument-resolvers>
            <bean class="com.tistory.jekalmin.ParamCollectorArgumentResolver"></bean>
        </mvc:argument-resolvers>
        <mvc:return-value-handlers>
            <bean class="com.tistory.jekalmin.ParamCollectorReturnValueHandler"></bean>
        </mvc:return-value-handlers>
    </mvc:annotation-driven>

    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
            <!-- Example: a logical view name of 'showMessage' is mapped to '/WEB-INF/jsp/showMessage.jsp' -->
            <property name="prefix" value="/WEB-INF/view/"/>
            <property name="suffix" value=".jsp"/>
    </bean>

</beans>

마지막으로 TestController 이다.


TestController.java

package com.tistory.jekalmin;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
@RequestMapping("/test")
public class TestController {

    protected final Logger logger = LoggerFactory.getLogger(getClass());

    @RequestMapping("/index")
    public ParamCollector index(ParamCollector collector){
        System.out.println(collector);
        return collector;
    }
}


http://localhost:8080/test/index?viewType=json 일때만 아래 글이 출력되는 것을 확인할 수 있다.

json View 등록하는 곳

결론

HandlerMethodReturnValueHandler를 구현하면 리턴타입이나 리턴된 데이터에 따라서 내가 원하는 view로 보낼 수 있다. 그 View는 흔히 사용하는 JstlView가 될 수도 있지만, AbstractView 등을 구현하여 커스터마이징 해서 사용할 수도 있다.

커스텀 ArgumentResolver 등록하기

개요

스프링 사용시 컨트롤러에 들어오는 파라미터나 리턴타입을 나한테 맞게 가공해서 사용하고 싶을 때가 있다.. 기존에는 WebArgumentResolver를 구현해서 AnnotationMethodHandlerAdapter에 등록을 해야 했는데, 스프링 3.2버전 부터는 deprecated 된 상태다. 지금은 RequestMappingHandlerAdapter 클래스가 담당하는데, 이 클래스에서는 WebArgumentResolver가 HandlerMethodArgumentResolver로 바뀌었다.

예제

request 파라미터 수집을 Map 형태로 하는 클래스를 만들어서 등록해보자. 먼저 Spring Project로 생성한 뒤, 파라미터 수집하는 클래스를 생성하자.



ParamCollector.java

package com.tistory.jekalmin;

import java.util.HashMap;
import java.util.Map;

public class ParamCollector {

    Map<String, String> map = new HashMap<String, String>();

    public String get(String key){
        return map.get(key);
    }

    public void put(String key, String value){
        map.put(key, value);
    }

    public String toString() {
        return map.toString();
    }


}

여기서 주의해야 할 점은 Map을 상속 받거나 구현하면, 다른 ArgumentResolver로 갈 수도 있다. 다음은 ArgumentResolver를 구현해서 DispatcherServlet에 등록해주자.



ParamCollectorArgumentResolver.java

package com.tistory.jekalmin;

import java.util.Iterator;

import org.springframework.core.MethodParameter;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;

public class ParamCollectorArgumentResolver implements HandlerMethodArgumentResolver{

    @Override
    public boolean supportsParameter(MethodParameter parameter) {
        // TODO Auto-generated method stub
        return ParamCollector.class.isAssignableFrom(parameter.getParameterType());
    }

    @Override
    public Object resolveArgument(MethodParameter parameter,
            ModelAndViewContainer mavContainer, NativeWebRequest webRequest,
            WebDataBinderFactory binderFactory) throws Exception {
        // TODO Auto-generated method stub
        ParamCollector collector = new ParamCollector();
        for(Iterator<String> iterator = webRequest.getParameterNames(); iterator.hasNext();){
            String key = iterator.next();
            collector.put(key, webRequest.getParameter(key));
        }
        return collector;
    }

}


mvc-config.xml (dispatcherServlet 설정파일)

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    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.xsd
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

    <context:component-scan base-package="com.tistory.jekalmin"/>


    <mvc:annotation-driven>
        <mvc:argument-resolvers>
            <bean class="com.tistory.jekalmin.ParamCollectorArgumentResolver"></bean>
        </mvc:argument-resolvers>
    </mvc:annotation-driven>

    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
            <!-- Example: a logical view name of 'showMessage' is mapped to '/WEB-INF/jsp/showMessage.jsp' -->
            <property name="prefix" value="/WEB-INF/view/"/>
            <property name="suffix" value=".jsp"/>
    </bean>

</beans>

이제 테스트를 위한 컨트롤러를 생성한다.



TestController.java

package com.tistory.jekalmin;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
@RequestMapping("/test")
public class TestController {

    @RequestMapping("/index")
    public void index(ParamCollector collector){
        System.out.println(collector);
    }
}



다음 http://localhost:8080/test/index?aaa=bbb&ccc=ddd로 호출을 해결과는 다음과 같다.

{aaa=bbb, ccc=ddd}

결론

ArgumentResolver를 활용하면 파라미터 수집 이 외에도 다양하게 커스터마이징 할 수 있다. 어떻게 활용하느냐는 여러분에게 달려있다.