WebArgumentResolver를 활용한 Command 배열 처리 한성곤 커미터 ...

tenuousdrunkshipInternet και Εφαρμογές Web

12 Νοε 2013 (πριν από 3 χρόνια και 10 μήνες)

1.045 εμφανίσεις

1


WebArgumentResolver


활용한

Command
배열





한성곤

커미터
(
딸기아빠
)


1.
개요

Spring MVC


사용하는

경우


HanderAdapter



통해

다양한

형태의

파라미터를

Controller
에서

활용할





.

표준프레임워크의

MVC
서비스



CommandMapArgumentResolver




HandlerAdapter


통해

web request


parameter


Map
형태로

변환해

주고

있다
.

다른

예로는


준프레임워크의

UI Adapter


있는데
..
이것도

HandlerAdapter


통해

UI
솔루션이

던지는

다양한

형태의

데이터를

DTO
형태로

변환해

준다
.

이를

활용하면

다양한

custom argument


처리할



있는데
..
여러

데이터를

한번에

처리
해야




경우에

대한

처리를

구축해

보자
.

이는

AJAX
환경이나

여러

데이터를

한번의

호출로

처리할

때에

유용
하다
.


2. WebArgumentResolver
적용

2.1 AnnotationMet
hodHandlerAdapter
사용

우선

Spring MVC


HandlerAdaper


대하

알아
보자
. HanderAdapter


DispatcherServlet


호출
되는데
..
클라이언트의

요청에

대한

처리를

위임

받아서
.. Controller


호출
한다
.



Controller


DispatcherServlet


의해

직접

호출되는

것이

아니라

HandlerAdapter


의해

호출
되는

것이다
.




2


Spring
에서는

기본적으로



개의

HandlerAdapter
구현체가

제공
되느데
.. support
s(Object
handler)
인터페이스

메소드를

통해

여러

HandlerAdapter
구현체



해당되는

HandlerAdapter


호출
한다
.
.

아무

HandlerAdapter


지정되지

않는

경우는

다음과

같은

HandlerAdapter


순서대로

등록
되어
처리된다
.

(2.5.6
기준
)

-

o
rg.springframework.web.servlet.mvc.HttpRequestHandlerAdapter

-

org.springframework.web.servle
t.mvc.SimpleControllerHandlerAdapter

-

org.springframework.web.servlet.mvc.throwaway.ThrowawayControllerHandlerAdapter

-

org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter




중에서

AnnotationMehtod
Handler
Adapter


경우는

다양한

properties


통해

custom


처리


가능하도록

제공하고

있다
.

대표적인

property


다음과



.

-

customerArgumentResolver(WebArgumentResolver) :
method

parameter type


대한

처리

-

webBindingInitializer(WebBindingInitializer) : Controller


Command
객체에

대한

DataBinder
처리


2.2 WebArgumentResolver
활용

Annotation
MethodHandlerAdapter


customerArgumentResolver
속성을

지정하면
..
지정된

Web
-
Argument
Resolver


의해

Controller


메소드

argument

참조할



있다
.
물론

참조뿐만

아니라

변경이

가능한데

객체

type


대한

제약사항은

존재한다
. (

,
다른

유형의

객체로

전환은

불가
)


그럼
..
우선

WebArgumentResolver interface


살펴보자
.

public

interface

We
bArgumentResolver {


Object
UNRESOLVED

=
new

Object();


Object resolveArgument(MethodParameter methodParameter, NativeWebRequest
webRequest)
throws

Exception;


}

MethodParameter


대상

Controller




메소드

arugment
정보를

제공하고
,
NativeWebRequest


client


web reques
t
정보를

제공한다
.

이를

활용한

예를

보도록

하자
.

public class MySpecialArgumentResolver implements ArgumentResolver {



public Object resolveArgument(MethodParameter methodParameter, NativeWebRequest
webRequest) {


if (methodParameter.getParameterType().equals(MySpecia
lArg.class)) {


return new MySpecialArg("myValue");

//



}

3



return UNRESOLVED;


}


}


Controller


경우는

다음과

같이

구성될



있다
.

@RequestMapping("/test.do")

public String test(MySpecialArg mine) throws Exception {








①에

의해

생성된

객체가

浩n
e
으로

지정됨


ret畲n• e獴∻

}


2.3 CommandMapArgumentResolver
적용



추가적으로

표준프레임워크에서

제공하고

있는

CommandMapArgumentResolver


살펴보자
.

Spring MVC


경우는

Command/form
객체라고

해서

메소드의

argument


VO
형태의

객체만

가능하지만
,


CommandMapArgumentResolver


사용하게

되면



parameter
정보를

Map
으로

처리할



있다
. (
당연히

VO


매번

만들

필요가

없어

편리하지만
,
그에

따른

단점도

존재한다
.)


그럼

CommandMapArgumentResolver


확인해

보자
.

public

class

CommandMapArgumentResolver
implements

WebArgumentResolver{



public

Object resolveArgument(MethodParameter methodParameter,

NativeWebRequest webRequest
)
throws

Exception {




Class<?> clazz = methodParameter.getParameterType();

String paramName = methodParameter.getParameterName();




if
(clazz.equals(Map.
class
) && paramName.equals(
"commandMap"
)){

//


Map<String, Object> commandMap =
new

HashMap<String,
Object>();

HttpServletRequest request = (HttpServletRequest)
webRequest.getNativeRequest();




Enumeration<?> enumeration = request.getParameterNames();





while
(enumeration.hasMoreElements()){

String key = (String) enumeration.nextElement();

String[] val
ues = request.getParameterValues(key);

if
(values!=
null
){

commandMap.put(key, (values.
length

> 1) ? values:values[0] );

}

}

return

commandMap;

}

return

UNRESOLVED
;

}

}


우선


처럼

Controller


argument
유형이

Map
이어야

하고
, parameter
이름이

4



commandMap

이어야

처리가

됨을

확인할



있다
.



Controller
메소드는

다음과

같이

작성되어야

한다
.

@RequestMapping
(
"/test.do"
)

public

void

test(HttpServletRequest request,

Map<String, String> commandMap
){




for
(
Iterator

it=comman
dMap.keySet().iterator();it.hasNext();){

String key = (String) it.next();

String value = (String) commandMap.get(key);

request.setAttribute(key, value);

}

}




이후는

request


파라미터

정보들을

map


등록하고

있다
.

, request
데이터를

Map
데이터


변환해

제공하는

것이다
.


2.4 CommandArrayAr
gument



이제

CommandArrayArgument


다음과

같이

작성해

보자
.

package

egovframework.rte.ptl.mvc.bind;


import

java.beans.PropertyDescriptor;

import

java.lang.reflect.Array;

import

java.lang.reflect.InvocationTargetException;

import

java.lang.reflect.Method;

import

java
.util.ArrayList;

import

java.util.List;


import

javax.servlet.http.HttpServletRequest;


import

org.apache.log4j.Logger;

import

org.springframework.beans.BeanUtils;

import

org.springframework.core.MethodParameter;

import

org.springframework.web.bind.support
.WebArgumentResolver;

import

org.springframework.web.context.request.NativeWebRequest;


public

class

CommandArrayArgumentResolver
implements

WebArgumentResolver {


private

Logger
logger

= Logger.
getLogger
(
this
.getClass());

//
Logger
처리




/**


*

Setter


통해

객체에

대한

property

name

list


얻음
.


*



*

@param

clazz


*

@return


*/


protected

String[] getPropertyName(Class<?> clazz) {


List<String> list =
new

ArrayList<String>();




Method[] methods = clazz
.getMethods();




for

(Method method : methods) {



if

(method.getName().startsWith(
"set"
) &&
method.getParameterTypes().
length

== 1) {

5




list.add(method.getName().substring(3, 4).toLowerCase() +
method.getName().substring(4));



}


}




return

list.
toArray(
new

String[0]);


}




/**


*

Property


setter


호출함
.


*



*

@param

vo


*

@param

propertyName


*

@param

value


*

@throws

IllegalArgumentException


*

@throws

IllegalAccessException


*

@throws

InvocationTargetException


*/


protected

void

callSetter(Obje
ct vo, String propertyName, String value)
throws

IllegalArgumentException, IllegalAccessException,
InvocationTargetException {


PropertyDescriptor pd =
BeanUtils.
getPropertyDescriptor
(vo.getClass(), propertyName);




pd.getWriteMethod().invoke(vo,
new

Obje
ct[] {value});




}



/**


*

Array
유형에

대한

argument

처리
.


*/


public

Object resolveArgument(MethodParameter methodParameter,
NativeWebRequest webRequest)
throws

Exception {



Class<?> clazz = methodParameter.getParameterType();




//logger.de
bug("Parameter Class : " + clazz.getCanonicalName());




if

(clazz.isArray()) {

//





Class<?> targetClazz = clazz.getComponentType();

//
대상

VO






logger
.debug(
"Target object in array : "

+
targetClazz.getCanonicalName());






HttpServle
tRequest request =
(HttpServletRequest)webRequest.getNativeRequest();






//


VO


property Name
얻기
..



String[] properties = getPropertyName(targetClazz);






//


parameter
배열

최대

구하기
..



int

max = 0;



for

(String getter : properti
es) {



logger
.debug(
"Property : "

+ getter);






if

(request.getParameterValues(getter) ==
null
) {

6





continue
;



}






logger
.debug(
"ParameterValues's length : "

+
request.getParameterValues(getter).
length
);






if

(max < request.getParameterValues
(getter).
length
) {




max = request.getParameterValues(getter).
length
;



}



}






logger
.debug(
"Parameter array's MAX length : "

+ max);






//


리턴할

객체

배열

생성하기
..



Object ret = Array.
newInstance
(targetClazz, max);



Object targe
t =
null
;



for

(
int

index = 0; index < max; index++) {

// index



target = targetClazz.newInstance();






for

(String property : properties) {




String[] values = request.getParameterValues(property);








if

(values !=
null

&& index < val
ues.
length
) {




callSetter(target, property, values[index]);

//





}



}



//


배열에



파라미터

객체를

지정
..



Array.
set
(ret, index, target);




}






return

ret;


}



return

UNRESOLVED
;


}


}

처리되는

흐름은

다음과

같다
.



우선

메소드

argument


array
인지

확인한다
.



대상


체가

가지고

있는

property
정보를

getPropertyName
()
메소드를

통해

얻어온다
.
(setter


검색
)





parameter
중에

배열

형태의

파라미터의

최대

개수를

구한다
. (
대상

객체에

대한

배열


만들기

위해
)



리턴
(Controller


메소드

argument


지정될
)


객체의

배열을

reflection


통해

생성한

.



callSetter()
메소드를

통해



객체에

대한



parameter


지정한다
.



배열의



요소에

해당
되는

객체를

생성하여



다음으로

AnnotationmethodHandlerAdapter
설정을

통해

CommandArrayArgumentResolver



록한다
.

7


<
bean

class
=
"org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandl
erAdapter"
>

<
property

name
=
"customArgumentResolvers"
>


<
list
>



<
bean

class
=
"
egov
framework.rte.ptl.mvc.bind.CommandArrayArgumentResolver
"
/>


</
list
>

</
property
>

</
bean
>


이제

테스트용

Controller


작성해

보자
.

@RequestMapping
(
"/test/commandArray.do"
)

public

String testCommandArray(
@ModelAttribute
(
"searchVO"
) NotificationVO
notificationVO,
LoginVO
[] loginVO
, ModelMap model)
throws

Exception {




for

(
int

i = 0; i < loginVO.
length
; i++) {



System.
out
.println(
"Unique ID : "

+ loginVO[i].getUniqId());



System.
out
.println(
"Name : "

+ loginVO[i].getName());


}




if

(loginVO.
length

== 0) {



model.addAttribute(
"resultList"
,
new

LoginVO[2]);


}
else

{



model.addAttribute(
"resultList"
, loginVO);


}




return

"test/commandArray"
;
// JSP or Tiles definition
처리


}


테스트용

JSP(test/cmmandArray


viewName


해당되는

jsp)


다음과

같이

작성된다
.

<
table

width
=
"1
00%"

cellpadding
=
"8"

class
=
"table
-
line"
>

<
thead
>

<
tr
>

<
th

class
=
"title"

width
=
"10%"
>
번호
</
th
>

<
th

class
=
"title"

width
=
"45%"
>
Unique Id
</
th
>

<
th

class
=
"title"

width
=
"45%"
>
Name
</
th
>


</
tr
>

</
thead
>


<
tbody
>

<
c:forEach

var
=
"result"

items
=
"
${resultList}
"
va
rStatus
=
"status"
>

<
tr
>


<
td

class
=
"lt_text"
><
c:out

value
=
"
${status.count}
"
/></
td
>




<
td

class
=
"lt_text"
>

<
input

type
=
'text'

name
=
'uniqId'

value
=
'
${result.uniqId}
'
>

</
td
>

<
td

class
=
"lt_text"
>

<
input

type
=
'text'

name
=
'name'

value
=
'
${result.name}
'
>


</
td
>

</
tr
>

</
c:forEach
>

</
tbody
>

</
table
>


8


3.
결론

지금까지

AnnotationHandlerAdatper


customArgumentResolver


통해

Controller


메소드

argument


대한

변경



활용에

대해

알아보았고
,
이를

활용한

전자정부

표준프레임워크의

CommandMapArgumentResolver


살펴보았다
.

또한

이를

활용하여

배열

형태의



parameter


VO
객체의

배열형태로

처리하는

Command
-
ArrayArgumentResolver


구현해

보았다
.



외에도

AnnotationHandlerAdatper


다양한

property


통해

Spring MVC


확장할



있다
.