spring-framework icon indicating copy to clipboard operation
spring-framework copied to clipboard

Add reverse methed in MethodMatchers and ClassFilters

Open Jinhui-Z opened this issue 3 years ago • 5 comments

Add reverse methed in MethodMatchers and ClassFilters.

Add support for MethodMatchers and ClassFilters to reverse the given MethodMatcher and ClassFilter. It is convenient in some complex conditions,Or is there another way to do it?Thanks.

Jinhui-Z avatar Mar 25 '21 07:03 Jinhui-Z

@cuspymd Thanks for the reply. Some complex combinations are handy, for example in the SpringCloud, if I need to develop a spring-boot-starter project that collects logs for all requests by default and provides @logIgnore to ignore certain requests. For Feign, I could write the Advisor like this:

public class Example {

    /**
     * this FeignClient require logging
     */
    @FeignClient("svr-name")
    public interface Client extends Api {
    }


    /**
     * this FeignClient does not require logging
     */
    @LogIgnore
    @FeignClient("svr-name")
    public interface IgnoreClient extends Api {
    }


    /**
     * in other Jar
     */
    interface Api {
        /**
         * this method does not require logging
         */
        @LogIgnore
        @RequestMapping("/ignoreApi")
        void ignoreApi();

        /**
         * this method require logging
         */
        @RequestMapping("/api")
        void api();
    }


    /**
     * Annotated classes or methods do not need to be logged
     */
    @Target({ElementType.METHOD, ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    @Inherited
    @Documented
    @interface LogIgnore {
    }


    /**
     * The Advisor for Feign
     */
    public class FeignAdvisor extends AbstractPointcutAdvisor {

        @Override
        public Pointcut getPointcut() {
            return new Pointcut() {
                @Override
                public ClassFilter getClassFilter() {
                    //This class mast contain @FeignClient
                    ClassFilter cf1 = new AnnotationClassFilter(FeignClient.class, true);
                    //This class cannot contain @LogIgnore
                    ClassFilter cf2 = ClassFilters.reversion(new AnnotationClassFilter(LogIgnore.class, true));

                    //maybe more ClassFilter

                    return ClassFilters.intersection(cf1, cf2);
                }

                @Override
                public MethodMatcher getMethodMatcher() {
                    //This method mast contain @RequestMapping
                    MethodMatcher cf1 = new AnnotationMethodMatcher(RequestMapping.class, true);
                    //This method cannot contain @LogIgnore
                    MethodMatcher cf2 = MethodMatchers.reversion(new AnnotationMethodMatcher(LogIgnore.class, true));

                    //maybe more MethodMatcher

                    return MethodMatchers.intersection(cf1, cf2);
                }
            };
        }

        @Override
        public Advice getAdvice() {
            //do something
            return (MethodInterceptor) (MethodInvocation invocation) -> invocation.proceed();
        }
    }
}

Jinhui-Z avatar Mar 25 '21 14:03 Jinhui-Z

Some complex combinations are handy, for example in the SpringCloud, if I need to develop a spring-boot-starter project that collects logs for all requests by default and provides @logIgnore to ignore certain requests. For Feign, I could write the Advisor like this:

Thanks for the detailed explanation. It looks very useful.

cuspymd avatar Mar 26 '21 03:03 cuspymd

@rstoyanchev Could you please have a look into this?

Jinhui-Z avatar Dec 30 '21 03:12 Jinhui-Z

Hi, I am a beginner with Open source contributions. This would be my first contribution, can anyone please guide me through it?

Pulkit-Garg15 avatar Jul 20 '22 18:07 Pulkit-Garg15

@Pulkit-Garg15 thanks for the offer but this is already a PR (see the "Files Changed" tab above) so this isn't open for contribution.

snicoll avatar Jul 21 '22 05:07 snicoll

I think a negate on ClassFilter can be added as it is a functional interface. I am wondering if we should do the same for MethodFilter though, and how important the equals/hashCode bit is.

snicoll avatar Aug 26 '23 15:08 snicoll

I think a negate on ClassFilter can be added as it is a functional interface.

I agree. It's analogous to java.util.function.Predicate.negate() in that sense.

Along the same lines, I could see it being useful to have something like the static java.util.function.Predicate.not(Predicate<? super T>) companion method.

I am wondering if we should do the same for MethodFilter though,

I imagine that could be handy in MethodMatcher, too.

and how important the equals/hashCode bit is.

The filters are used to differentiate pointcuts -- for example, in org.springframework.aop.support.ComposablePointcut.equals(Object). So, unless I'm mistaken, proper equals and hashCode implementations will be required (for example, #15899).

sbrannen avatar Aug 27 '23 12:08 sbrannen

Such a shame that we can't do the negate idea as a default method. We have several components in the framework that implements both interface so we're kinda stuck there :/

snicoll avatar Aug 28 '23 12:08 snicoll

@Jinhui-Z thanks very much for making your first contribution to Spring Framework.

snicoll avatar Aug 28 '23 13:08 snicoll

This seems to have been shipped with 6.1.0-M5 but doesn't have a milestone. There's also a typo in "metheds" in the title.

izeye avatar Oct 03 '23 07:10 izeye

Good catch @izeye. I've fixed the title and added the missing entry manually to the release notes.

snicoll avatar Oct 03 '23 08:10 snicoll