Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spring aliasFor for Annotations with Target(PARAMETER)

I am trying to use the Meta-annotation of spring using the aliasFor annotation to create a custom annotation for the springs RequestParam

Simply 'extend/replace'

@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RequestParam {

   @AliasFor("name")
   String value() default "";

   ----
}

with my annotation

@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface QueryParam {

    @AliasFor(annotation = RequestParam.class, attribute = "name")
    String name() default "";

    @AliasFor(annotation = RequestParam.class, attribute = "required")
    boolean required() default false;

    @AliasFor(annotation = RequestParam.class, attribute = "defaultValue")
    String defaultValue() default ValueConstants.DEFAULT_NONE;

}

This way it throws the Exception

org.springframework.core.annotation.AnnotationConfigurationException:   @AliasFor declaration on attribute [name] in annotation [package.QueryParam] declares an alias for attribute [name] in meta-annotation [org.springframework.web.bind.annotation.RequestParam] which is not meta-present.

Problem is that without the RequestParam annotated on the QueryParam this doesn't work. And it is not possible to put the RequestParam as it PARAMETER targeted.

@RequestParam <--This is not possible. 
public @interface QueryParam

So is there another way to achieve this ?

like image 913
lawal Avatar asked Aug 08 '16 11:08

lawal


1 Answers

Basically what you want to achieve is not possible now, at least for the Spring v 4.3.3 There are main two problems, the first one is the fact that annotations like @RequestParam are declared with @Target(ElementType.PARAMETER) which make it impossible to be used as part of meta annotations. Furthermore, Spring MVC looks up annotations on method parameters using org.springframework.core.MethodParameter.getParameterAnnotations() which does not support meta-annotations or composed annotations. But if you really need some customizations there you can use HandlerMethodArgumentResolver instead of meta annotations.

So you code will look something like

@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface QueryParam {

   String name() default "";

   boolean required() default false;

   String defaultValue() default ValueConstants.DEFAULT_NONE;

}

Then using the HandlerMethodArgumentResolver add the custom logic which you need.

public class QueryParamResolver implements HandlerMethodArgumentResolver {

   public boolean supportsParameter(MethodParameter parameter) {
       return parameter.getParameterAnnotation(QueryParam.class) != null;
   }

   public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest,
        WebDataBinderFactory binderFactory) throws Exception {
       QueryParam attr = parameter.getParameterAnnotation(QueryParam.class);
       // here you can use any logic which you need
       return webRequest.getParameter(attr.value());
   }
}

Then we need to register our HandlerMethodArgumentResolver

@Configuration
@EnableWebMvc
public class Config extends WebMvcConfigurerAdapter {
    @Override
    public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
      argumentResolvers.add(new QueryParamResolver());
    }
}

And last lets use our custom annotation

 @GetMapping("/test")
 public String test(@QueryParam("foo") String foo){
      // something here 
 }
like image 85
Babl Avatar answered Sep 26 '22 02:09

Babl