Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Custom annotation filter Spring boot

I see that with Spring boot is really simple create filters. Just follow post like this one https://www.baeldung.com/spring-boot-add-filter

What I have not able to find, is how to create annotations that subscribe specifics endpoints in the controller to one filter.

Something like in Jax-RS it would looks like

 @GET
    @Path("jax-rs-single")
    @Reactive(ttlRequest = 2000)
    @Produces(MediaType.APPLICATION_JSON)
    public Single getSingle() {
        return Single.just("Hello world single");
    }

Where @Reactive it would trigger the ReactiveFilter implementation per request.

I also saw the @WebFlow annotation, but it's not what I want. I want to create a library where the consumers decide which filter use, just adding the annotation in the controller.

Any idea how to do something similar with Spring boot/MVC ?

Regards

like image 708
paul Avatar asked Jun 16 '26 04:06

paul


2 Answers

I will try to describe here more about Custom annotation and the processor in Spring.

I don't know what you want or what you need, but I will give an generic example.

You have 2 options:

  • BeanProcessor
  • HandlerInterceptor

BeanProcessor

You need to build 3 things basically: Annotaton, BeanProcessor and a Callback to execute your logic if annotated. Here is an example of it and how it works:

1 - Create the annotation

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD})
@Documented
public @interface Reactive {
    Integer ttlRequest;
}

2 - Implement a BeanPostProcessor

 @Component 
 public class ReactiveAnnotationProcessor implements BeanPostProcessor {

    private ConfigurableListableBeanFactory configurableBeanFactory;

    @Autowired
    public ReactiveAnnotationProcessor(ConfigurableListableBeanFactory beanFactory) {
        this.configurableBeanFactory = beanFactory;
    }

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) 
      throws BeansException {
        this.scanReactiveAnnotation(bean, beanName);
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) 
      throws BeansException {
        return bean;
    }

    protected void scanReactiveAnnotation(Object bean, String beanName) {
        this.configureMethodInjection(bean);
    }

    private void configureMethodInjection(Object bean) {
        Class<?> managedBeanClass = bean.getClass();
        MethodCallback methodCallback = 
          new ReactiveMethodCallback(configurableBeanFactory, bean);
        ReflectionUtils.doWithMethod(managedBeanClass, methodCallback);
    } 
}

3 - Create the method callback (here is the logic to execute)

public ReactiveMethodCallback implements MethodCallback {

    private ConfigurableListableBeanFactory configurableBeanFactory;
    private Object bean;

    public ReactiveMethodCallback(ConfigurableListableBeanFactory bf, Object bean) {
        configurableBeanFactory = bf;
        this.bean = bean;
    }

    @Override
    public void doWith(Method method) throws IllegalArgumentException, IllegalAccessException {
    if (!method.isAnnotationPresent(Reactive.class)){
      return;
    }
    //YOUR LOGIC HERE
  }

}

Here is a good source about annotation processing, it is about FieldProcessing but you can just change the interfaces to implement what you need if you have doubts: https://www.baeldung.com/spring-annotation-bean-pre-processor

[UPDATED] You can also create a HandlerInterceptor instead:

HandlerInterceptor

public class ReactiveFilterHandlerInterceptor extends HandlerInterceptorAdapter {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws
    Exception {

        if (handler instanceof HandlerMethod) {
            HandlerMethod handlerMethod = (HandlerMethod) handler;
            // Test if the controller-method is annotated with @CustomFilter
            Reactive filter = handlerMethod.getMethod().getAnnotation(Reactive.class);
            if (filter != null) {
                // ... do the filtering, or call the Component for filtering
            }
        }
        return true;
    }
}

And register your handler:

@Configuration
public class WebMvcConfig extends WebMvcConfigurer {

  @Autowired 
  ReactiveFilterHandlerInterceptor reactiveFilterHandlerInterceptor;

  @Override
  public void addInterceptors(InterceptorRegistry registry) {
    registry.addInterceptor(reactiveFilterHandlerInterceptor);
  }
}
like image 178
Brother Avatar answered Jun 18 '26 18:06

Brother


If I understand what you want correctly the main problem is how to apply filter based on custom annotation.

So first of all, yes you can use a regular Spring filter (WebFilter in case of Spring Webflux or Filter in case of Spring MVC), but you'll need to write some custom logic.

To do filtering based on annotation you should:

  1. Use RequestMappingHandlerMapping#getHandlerInternal() method to retrieve a reference to the method that handles the request (the getSingle() in your case)
  2. When you manage to retrieve the HandlerMethod then you can check if that method has your custom annotation applied with hasMethodAnnotation(Class<A> annotationType) method.
  3. When you know that, then you can react accordingly: either chain.doFilter(request, response) without performing any actions, or apply your custom logic, and then trigger the rest of the filter chain.
like image 20
Rozart Avatar answered Jun 18 '26 18:06

Rozart



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!