0

I'm wondering if it's okay to create my own Spring Advisor via Java configuration, similar to what's proposed here. I too wanted to have annotated methods intercepted.

I had such an Advisor defined in our application, but after removing a completely unrelated @Configuration class, the whole thing collapsed and the advice no longer worked. After a lot of debugging, I figured that this could work

@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public Advisor sessionContextAdvisor(OpenSessionInterceptor openSessionInterceptor)
{
    AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
    pointcut.setExpression("@annotation(my.app.SessionContext)");
    return new DefaultPointcutAdvisor(pointcut, openSessionInterceptor);
}

and indeed, that worked. I needed to declare the bean as an infrastructure bean @Role(BeanDefinition.ROLE_INFRASTRUCTURE) for Spring. I gotta say, this feels even more hackey now.

On the other hand, there is a way to add custom Advisors via XML, using <aop:advisor> ..., as documented in the Spring docs. But I couldn't find a Spring-documented way to register an Advisor via Java configuration.

Also, Spring JavaDoc has to say this about the Advisor interface:

This interface is not for use by Spring users, but to allow for commonality in support for different types of advice.

That doesn't sound like you're supposed to create instances of this interface. But if there's an XML-way, is there really no Java configuration way to achieve the same thing? Is my solution not so bad after all?

sorrymissjackson
  • 2,395
  • 1
  • 19
  • 18
  • Just so I understand, why are you not writing an aspect, which is also the accepted answer in the question you refer to? – Nico Van Belle May 02 '22 at 13:44
  • Fair question. I guess I like the idea of an advisor. Quoting the Spring docs: "An advisor is like a small self-contained aspect that has a single piece of advice. The advice itself is represented by a bean and must implement one of the advice interfaces described in Advice Types in Spring." The code I shared is literally all I had to do to set up the advisor. It's not so much about "How can I solve the problem" than it is "How can I create an Advisor in Java code (or why shouldn't I)". – sorrymissjackson May 03 '22 at 06:38
  • Perhaps this question/answer provides some insights? https://stackoverflow.com/questions/25092302/what-is-the-difference-between-advisor-and-aspect-in-aop IMHO you should use `@AspectJ` instead of implementing an `Advisor` as this seems to be the more up-to-date/standard approach. This will most likely cause less problems (like the one you described) in the future. – dpr May 03 '22 at 07:35
  • It provided some more insight, but `Advisor`s play an important role internally for Spring (so it seems to me), but as a user of the Framework, the only documented way to create and Advisor is via XML, which seems strange. As per Spring documentation, Advisors don't seem to be an outdated concept per se. – sorrymissjackson May 03 '22 at 10:59

1 Answers1

1

Any aop-specific Java configuration for Advisor beans is not required (except @EnableAspectJAutoProxy) cause spring-aop discovers such beans via BeanFactory, please check org.springframework.aop.framework.autoproxy.BeanFactoryAdvisorRetrievalHelper

As regards to the question whether is it legitimate to use custom Advisor beans or not - just think about why @Transactional and @Cacheable are not implemented via @Aspect or check the example below:

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.TYPE})
@Inherited
public @interface DemoAnnotation {
}
public interface DemoAnnotationApi {

    @DemoAnnotation
    default void method1() {

    }

    @DemoAnnotation
    void method2();

    void method3();

}
@Component
public class DemoAnnotationImpl implements DemoAnnotationApi {

    @Override
    public void method2() {

    }

    @DemoAnnotation
    @Override
    public void method3() {

    }
}
@Aspect
@Component
public class DemoAnnotationAspect {

    @Pointcut("@annotation(DemoAnnotation)")
    public void annotated() {
        // nop - pointcut
    }

    @Around("annotated()")
    public Object invoke(ProceedingJoinPoint pjp) {
        return doProceed(pjp);
    }

    @SneakyThrows
    protected Object doProceed(ProceedingJoinPoint pjp) {
        System.out.println("\tDemoAnnotationAspect: " + pjp.getSignature().getName());
        return pjp.proceed();
    }

}
@Component
public class DemoAnnotationAdvisor extends AbstractPointcutAdvisor {

    private final Advice advice;

    private final Pointcut pointcut;

    public DemoAnnotationAdvisor() {
        this.advice = new MethodInterceptor() {
            @Nullable
            @Override
            public Object invoke(@Nonnull MethodInvocation invocation) throws Throwable {
                System.out.println("\tDemoAnnotationAdvisor: " + invocation.getMethod().getName());
                return invocation.proceed();
            }
        };
        this.pointcut = new AnnotationMatchingPointcut(null, DemoAnnotation.class, true);
    }

    @Override
    public Pointcut getPointcut() {
        return pointcut;
    }

    @Override
    public Advice getAdvice() {
        return advice;
    }
}
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
public class DemoAnnotationTestIT {

    @Autowired
    DemoAnnotationApi api;

    @Test
    void test() {
        System.out.println("Calling method1");
        api.method1();
        System.out.println("Calling method2");
        api.method2();
        System.out.println("Calling method3");
        api.method3();
    }

}

output:

Calling method1
    DemoAnnotationAdvisor: method1
    DemoAnnotationAspect: method1
Calling method2
    DemoAnnotationAdvisor: method2
Calling method3
    DemoAnnotationAdvisor: method3
    DemoAnnotationAspect: method3
Kirill Gamazkov
  • 3,277
  • 1
  • 18
  • 22
Andrey B. Panfilov
  • 4,324
  • 2
  • 12
  • 18
  • I'm not entirely sure what your advice (pun intended) is. You seem to argue for the usage for an `Advisor`, correct? Also note that I didn't write a new implementation of an `Advisor` (and would be hesitant to do so), I simply instantiated one as a Spring bean. – sorrymissjackson May 05 '22 at 06:45
  • @sorrymissjackson I just wanted to demonstrate AspectJ advices do not cover some scenarios, and, yes, my own preference is to implement custom Advisors rather than use AspectJ – Andrey B. Panfilov May 05 '22 at 07:50
  • I wasn't aware of this difference before, so thanks for that! – sorrymissjackson May 05 '22 at 11:10