Posts [Spring] HandlerMethodArgumentResolver
Post
Cancel

[Spring] HandlerMethodArgumentResolver

HandlerMethodArgumentResolver?

  • Strategy interface for resolving method parameters into argument values in the context of a given request.
  • 컨트롤러 메소드에서 특정 조건에 맞는 파라미터가 있을 때 원하는 값을 바인딩 해주는 인터페이스
1
public interface HandlerMethodArgumentResolver
1
2
3
boolean supportsParameter(MethodParameter parameter)

Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory)

Example

1. Write Annotation

  • 어노테이션 클래스를 지정한다.
  • @AuthenticationPrincipal 어노테이션을 생성한다.
1
2
3
4
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public @interface AuthenticationPrincipal {
}

2. Write DTO

  • DTO로 직렬화를 구현한다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class LoginMember {
    private Long id;
    private String email;

    public LoginMember() {
    }

    public LoginMember(Long id, String email) {
        this.id = id;
        this.email = email;
    }

    public Long getId() {
        return id;
    }

    public String getEmail() {
        return email;
    }
}

3. Write Resolver

  • AuthenticationPrincipalArgumentResolver
    • HandlerMethodArgumentResolver 인터페이스를 구현하는 클래스
    • supportsParameter(), resolveArgument()를 구현해야 한다.
  • supportsParameter()
    • 파라미터가 Resolver에 의해 수행될 수 있는 타입인지 true/false를 리턴하는 메소드
    • 만약, true를 리턴하면 resolveArgument()를 실행한다.
  • resolveArgument()
    • 실제로 파라미터와 바인딩할 객체를 리턴하는 메소드
    • NativeWebRequest로 클라이언트 요청이 담긴 파라미터를 컨트롤러보다 먼저 받아 작업을 수행할 수 있다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class AuthenticationPrincipalArgumentResolver implements HandlerMethodArgumentResolver {
    private final AuthService authService;

    public AuthenticationPrincipalArgumentResolver(AuthService authService) {
        this.authService = authService;
    }

    @Override
    public boolean supportsParameter(MethodParameter parameter) {
        return parameter.hasParameterAnnotation(AuthenticationPrincipal.class);
    }

    @Override
    public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) {
        HttpServletRequest request = (HttpServletRequest) webRequest.getNativeRequest();
        Member member = authService.findMemberByToken(AuthorizationExtractor.extract(request));
        return new LoginMember(member.getId(), member.getEmail());
    }
}

4. Register Resolver

  • 앞에서 작성한 AuthenticationPrincipalArgumentResolver를 스프링에 등록한다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Configuration
public class AuthenticationPrincipalConfig implements WebMvcConfigurer {
    private final AuthService authService;

    public AuthenticationPrincipalConfig(AuthService authService, LoginInterceptor loginInterceptor) {
        this.authService = authService;
    }

    @Override
    public void addArgumentResolvers(List argumentResolvers) {
        argumentResolvers.add(createAuthenticationPrincipalArgumentResolver());
    }

    @Bean
    public AuthenticationPrincipalArgumentResolver createAuthenticationPrincipalArgumentResolver() {
        return new AuthenticationPrincipalArgumentResolver(authService);
    }
}

5. Write Controller

  • 컨트롤러 메소드 파라미터에 @AuthenticationPrincipal을 사용해서 LoginMember 정보를 가져온다.
1
2
3
4
5
@GetMapping("/me")
public ResponseEntity<MemberResponse> findMemberOfMine(@AuthenticationPrincipal LoginMember loginMember) {
    MemberResponse member = memberService.findMember(loginMember);
    return ResponseEntity.ok().body(member);
}

References

This post is licensed under CC BY 4.0 by the author.