레이블이 java 8인 게시물을 표시합니다. 모든 게시물 표시
레이블이 java 8인 게시물을 표시합니다. 모든 게시물 표시

2017년 6월 24일 토요일

java 8, Spring 에서 requestMapping method의 parameter name을 generic으로 획득하기

최종 목표는 @GetMapping으로 바인딩된 호출의 parameterName을 구하는 것이다.


springframework를 사용하기 전 우선 java 8에서 method의 parameterName을 generic으로 구할 수 있는가에 대해 알아보자.

우선 java 에서 generic으로 다음과 같이 parameter name을 구할 수 있다.


String name = String.class.getMethod("substring", int.class).getParameters()[0].getName()
System.out.println(name);

하지만 막상 획득해보면 원래 이름이 아닌 arg0, arg1, arg2 ... 와 같은 이름으로 반환된다.

java 가 컴파일 시 이름에 대해 메모리 공간을 낭비하지 않기 위해 이와 같이 통일된 값으로 변환하여 사용하기 때문이다.

이를 원래 parameter name으로 사용하고자 하는 경우 vm 옵션으로 -parameters 를 사용하면 된다.

Spring에서는 parameter name을 사용하여 외부 요청의 key 값을 매핑하고 있기 때문에 이에 대해 저장, 관리를 하고 있다.

이를 사용하기 위해 ParameterNameDiscoverer 를 제공하고 있다.

다음과 같이 사용하면 된다.


private static DefaultParameterNameDiscoverer discoverer = new DefaultParameterNameDiscoverer();
String[] parameterNames = discoverer.getParameterNames(method);

2016년 10월 13일 목요일

int array 를 list 로 변환하기

array 를 list로 변환할 때 보통 다음과 같이 사용한다.
String[] strings = new String[] {"a", "b", "c"};
List<string> stringList = Arrays.asList(strings);

이 변환은 non primitive 타입에 대해서만 가능한 변환이다.

만약 primitive 타입인 int array를 Arrays.asList를 사용하면 반환값은 다음과 같다.
int[] ints = {1, 4, 3};
List<int[]> intList = Arrays.asList(ints);

즉 Arrays.asList를 통해 primitive 타입을 list로 변환을 할 수 없다.

다음과 같이 변환해야 한다.
int[] ints = {1, 4, 3};
List<Integer> intList = Arrays.stream(ints).boxed().collect(Collectors.toList());


2016년 7월 8일 금요일

spring boot mvc에서 java 8 date 객체 json 처리하기

spring mvc를 사용하면 date 객체를 주고받는 형태에 대해 고민을 하게 된다.

특히 java 8의 date 객체들을 쓰게 되면 더더욱 그렇다.


응답 처리


java 8의 LocalDateTime이나 LocalDate를 사용하면 응답하는 MappingJackson2JsonView의 기본 json 결과는 다음과 같다.
{ "localDateSample" : [2016,7,8], "localDateTimeSample" : [2016,7,1,0,0] }


serialized as numeric timestamps 형태로 년월일 / 년월일시분 값을 넘겨주는데 이런 형태로 응답하면 client의 javascript 객체에서 변환해서 쓸 때 불편한 점이 많다.

date format string 을 넘겨받아 사용하는게 더 사용하기 편하다고 생각을 해서 다음과 같이 boot properties 설정을 한다.
# response datetime 처리 설정
spring.jackson.serialization.write-dates-as-timestamps=false


위와 같이 설정을 하면 응답처리는 다음과 같이 된다.
{ "localDateSample" : "2016-07-08", "localDateTimeSample" : "2016-07-31T00:00" }

이렇게 사용하면 javascript에서 사용하기가 편해지고 timezone 설정이 들어간 경우도 moment.js 와 같은 라이브러리를 사용하면 쉽게 사용할 수 있다.


요청 처리


위와 같이 설정하는 경우 응답에 대해서 처리는 되었지만 요청이 온 경우에 대해서도 처리를 해주어야 한다.
요청에 대해 date format string 을 넘겨받는 설정은 WebMvcConfigurerAdapter에 다음과 같이 선언을 해준다.
@Configuration
public class WebMvcConfig extends WebMvcConfigurerAdapter {
    ...

    // request date conversion 처리
    @Override
    public void addFormatters(FormatterRegistry registry) {
        DateTimeFormatterRegistrar registrar = new DateTimeFormatterRegistrar();
        registrar.setUseIsoFormat(true);
        registrar.registerFormatters(registry);
    }
}


여기까지 설정을 하면 @ResponseBody로 리턴되는 produces = MediaType.APPLICATION_JSON_VALUE 응답처리에 올바르게 적용된다.
하지만 html, json에 대한 contentNegotiation 처리에도 올바른 응답을 처리하기 위해선 WebMvcConfigurerAdapter에 다음과 같이 선언해야한다.
@Configuration
public class WebMvcConfig extends WebMvcConfigurerAdapter {
    ...

    @Autowired
    private ObjectMapper objectMapper;

    @Override
    public void configureViewResolvers(ViewResolverRegistry registry) {
        registry.viewResolver(thymeleafViewResolver);  // thymeleaf를 쓰는 경우
        registry.enableContentNegotiation(new MappingJackson2JsonView(objectMapper));
    }
}


위에서 중요한 핵심은 objectMapper를 spring boot에서 생성한 것을 써야한다는 점이다. 만약 new MappingJackson2JsonView() 로 생성한다면 spring boot 설정으로 선언한 objectMapper가 아닌 spring web의 Jackson2ObjectMapperBuilder로 생성한 objectMapper를 사용하기 때문에 jackson 설정이 동작하지 않는다.