0

I'm new to AOP and AspectJ. I'm trying to write a custom logic around a custom annotation with element type TYPE. I also want to use the parameters under my annotation to write a custom logic inside the around method. I'm getting the below mentioned error. I tried reading multiple sources, and most of the sources are related to using @annotation. I'm finding hard to understand the same. Can someone explain, why the following way with @within didn't work with some clear example?

Annotation(Slf4jTrace.java):

package io.ud.project.falcon.logging;

import io.ud.project.falcon.logging.generator.AbstractLogTraceIdGenerator;
import io.ud.project.falcon.logging.generator.DefaultLogTraceIdGenerator;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Slf4jTrace {

    String traceKey() default "traceId";

    Class<? extends AbstractLogTraceIdGenerator> traceValueGenerator() default DefaultLogTraceIdGenerator.class;

}

Aspect Component(Slf4jTraceAspect.java):

package io.ud.project.falcon.logging;

import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

@Aspect
@Component
@SuppressWarnings("unused")
@Slf4j
public class Slf4jTraceAspect {

    @Pointcut("execution(public * *(..))")
    public void publicOperation() {
        //Using this pointcut annotation for Around method.
    }

    @Around(value = "@within(slf4jTrace) && publicOperation()")
    public void logTrace(ProceedingJoinPoint joinPoint, Slf4jTrace slf4jTrace) throws Throwable {
        //Custom logic that uses slf4jTrace's parameters        
        joinPoint.proceed();
        //Custom logic that uses slf4jTrace's parameters
    }
}

Annotated class(Foo.java):

package io.ud.project.falcon.logging;

import io.ud.project.falcon.logging.Slf4jTrace;

@Slf4jTrace(traceKey = "auditID")
public class Foo {

    public void doSomething() {
        //Something happens here.
    }

}

Error on application startup:

, 2021-01-02 22:16:41,340, ERROR [main] o.s.b.SpringApplication.reportFailure(833) | Application run failed
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'objectMapperConfigurer' defined in springfox.documentation.spring.web.SpringfoxWebMvcConfiguration
: BeanPostProcessor before instantiation of bean failed; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.transaction.annotation.ProxyTransactionManagementConfiguration': BeanPostProcessor before instantiation of bean failed; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.cache.annotation.ProxyCachingConfiguration': Initialization of bean failed; nested exception is java.lang.IllegalArgumentException: error Type referred to is not an annotation type: slf4jTrace
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:497)
        at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:312)
        at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:228)
        at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:310)
        at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:205)
        at org.springframework.context.support.PostProcessorRegistrationDelegate.registerBeanPostProcessors(PostProcessorRegistrationDelegate.java:238)
        at org.springframework.context.support.AbstractApplicationContext.registerBeanPostProcessors(AbstractApplicationContext.java:709)
        at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:534)
        at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:140)
        at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:752)
        at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:388)
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:327)
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:1246)
        at org.springframework.boot.SpringApplication.run(SpringApplication.java:1234)
        at io.ud.project.falcon.FalconApplication.main(FalconServiceApplication.java:65)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
        at java.lang.reflect.Method.invoke(Unknown Source)
        at org.springframework.boot.loader.MainMethodRunner.run(MainMethodRunner.java:48)
        at org.springframework.boot.loader.Launcher.launch(Launcher.java:87)
        at org.springframework.boot.loader.Launcher.launch(Launcher.java:50)
        at org.springframework.boot.loader.JarLauncher.main(JarLauncher.java:51)

1 Answers1

1

I did not try to run your code, but two things I immediately noticed when glancing at it:

  • BeanCreationException for internal Spring types often occur if aspect pointcuts are too broad, i.e. aspects are also woven into (even if not executed in) Spring classes. In such cases you want to limit the aspect scope by adding something like ... && within(my.package..*) to your pointcut. Alternatively, if your aspect is in a library potentially running against unknown package names you can exclude the Spring packages or classes causing problems by !within(org.springframework..*) or whatever package names are applicable in your case. See also my answer here.

  • Your @Around advice returns void, i.e. it will also only match void methods, which probably is not what you want. In order to match non-void methods, the advice must return something other than void, usually just Object if you want to match any return type. Also make sure that you return the result of proceed() or whatever other result you want to be returned.

kriegaex
  • 63,017
  • 15
  • 111
  • 202
  • Okay got it, I wanted to have this as a library, so I will use `!within(org.springframework..*)` definitely. Regarding your second point, I want to match both void and non-void methods and that's the reason for me having my `@Around` advice as void. Please do tell me if it's really a wrong design, I'll improve there. Let me try `!within()` and reply back. Thanks! – Udayabharathi Thiagarajan Jan 03 '21 at 06:06
  • Would you mind reporting back as promised? I would like to make sure we can close this question. It is listed as open because you did not accept any answers yet. – kriegaex Jan 08 '21 at 06:19
  • Yes, your points actually helped. Thanks a lot. Regarding my question on fetching the annotation instance for a pointcut which is within a class having the annotation. I still couldn't help it. As a workaround, I've used reflection to get the instance like below. Can you tell if there's a better way? `@Around("@within(com.tcl.common.util.logging.slf4jtrace.annotation.Slf4jTrace) && publicOperation()") public Object logTrace(ProceedingJoinPoint joinPoint) ...` as the advice and inside the advice method, I've done like this `joinPoint.getTarget().getClass().getAnnotation(Slf4jTrace.class)` – Udayabharathi Thiagarajan Jan 09 '21 at 07:24
  • You did not ask anything about annotation usage. According to your sample code you already know how to bind an annotation to an advice method parameter. There is no need to use reflection at all if you just follow your own sample code. I just explained to you what was wrong with it elsewhere. The `@within(slf4jTrace) ...` in combination with `public Object logTrace(ProceedingJoinPoint joinPoint, Slf4jTrace slf4jTrace)` is the way to go. Why did you change that? I did not tell you to. – kriegaex Jan 10 '21 at 05:50
  • Yes, but when I tried with the same, as I mentioned in the question, I'm getting the same following error, `java.lang.IllegalArgumentException: error Type referred to is not an annotation type: slf4jTrace`. – Udayabharathi Thiagarajan Jan 10 '21 at 07:54
  • 1
    We are talking past each other and I cannot read your mind. First you said my hints helped you and now your are saying you have the same problem as before. I suggest you post a full [MCVE](https://stackoverflow.com/help/mcve) on GitHub and let me see it. With incomplete and self-contradictory information I cannot help you. – kriegaex Jan 10 '21 at 13:56
  • I've accepted your answer as I was able to resolve this issue with the points provided. Regarding that `IllegalArgumentException`, I tried reproducing but I couldn't. Now it is working as expected with, `@within(slf4jTrace) ...` in combination with `public Object logTrace(ProceedingJoinPoint joinPoint, Slf4jTrace slf4jTrace)` – Udayabharathi Thiagarajan Jan 11 '21 at 02:47