自定义注解过滤器Spring Boot

6

我发现使用Spring Boot创建过滤器非常简单,只需按照这篇文章https://www.baeldung.com/spring-boot-add-filter的说明即可。

但我没有找到如何创建注释以将控制器中的特定端点订阅到一个过滤器中。

类似于Jax-RS中的做法:

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

当使用@Reactive注解时,每个请求将会触发ReactiveFilter的实现。

我也看到了@WebFlow注解,但这不是我想要的。我想创建一个库,让消费者决定使用哪个过滤器,只需在控制器中添加注解即可。

有没有办法在Spring Boot/MVC中实现类似的功能呢?

谢谢!


我怀疑Jax-rs中的过滤器不是普通的Servlet过滤器。为什么呢?因为Servlet过滤器只能映射到URL或Servlet。在过滤器中,它对最终处理请求的内容一无所知。除非有另一个框架提供此功能,并在配置时将其添加到过滤器中,否则您需要编写自己的处理和注册。 - M. Deinum
Jax-rs 提供了将过滤器 ResourceInfo 传递以获取被调用端点信息的可能性。 - paul
这不是由默认的servlet过滤器提供的,因此它必须是一个扩展或特定的JAX-RS过滤器。所以,除非有一些额外的框架来完成这个任务,否则你需要自己编写代码(如其中一个答案所提供的)。在Spring MVC中没有解决方案,因为它基于阻塞式Servlet API,但你可能可以为反应式对应项Spring WebFlux配置一些内容。 - M. Deinum
@paul用第二种方法更新了我的答案。 - Brother
非常感谢,看起来好多了。 - paul
2个回答

10
我将尝试在这里更详细地描述有关Spring中自定义注释和处理器的内容。
我不知道你想要什么或需要什么,但我会给出一个通用的例子。
你有两个选择:
BeanProcessor HandlerInterceptor BeanProcessor 你基本上需要构建三件事:注释、BeanProcessor和回调函数,以执行你的逻辑(如果进行了注释)。以下是一个示例及其工作原理:
1-创建注释
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD})
@Documented
public @interface Reactive {
    Integer ttlRequest;
}

2 - 实现一个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 - 创建回调方法(这里是执行逻辑)

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
  }

}

这里有一个关于注解处理的良好资源,它是关于FieldProcessing的,但是如果您有疑问,只需更改接口以实现您需要的内容即可:https://www.baeldung.com/spring-annotation-bean-pre-processor

[更新] 您还可以创建一个HandlerInterceptor:

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;
    }
}

并注册您的处理程序:

@Configuration
public class WebMvcConfig extends WebMvcConfigurer {

  @Autowired 
  ReactiveFilterHandlerInterceptor reactiveFilterHandlerInterceptor;

  @Override
  public void addInterceptors(InterceptorRegistry registry) {
    registry.addInterceptor(reactiveFilterHandlerInterceptor);
  }
}

3
感谢您的解决方案。很奇怪您不能只使用Spring的过滤器,而必须做所有这些复杂的事情。 - paul
我宁愿避免使用反射。 - paul
我认为最终你甚至会使用反射来检查注释,除非像dagger或micronaut注释那样在编译时发生。我会尝试考虑使用过滤器。 - Brother
@paul 如果您同意并且这有所帮助,您可以接受这个答案吗? - Brother

0

如果我正确理解您的要求,主要问题是如何基于自定义注释应用过滤器。

首先,是的,您可以使用常规的Spring过滤器(在Spring Webflux中为WebFilter,在Spring MVC中为Filter),但您需要编写一些自定义逻辑。

要基于注释进行过滤,您应该:

  1. 使用RequestMappingHandlerMapping#getHandlerInternal()方法检索处理请求的方法的引用(在您的情况下为getSingle()
  2. 当您成功检索到HandlerMethod时,您可以使用hasMethodAnnotation(Class<A> annotationType)方法检查该方法是否应用了您的自定义注释。
  3. 知道了这一点后,您可以根据情况做出反应:要么chain.doFilter(request, response)而不执行任何操作,要么应用您的自定义逻辑,然后触发其余的过滤器链。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接