Search code examples
javaspringspring-bootaop

Component is not detected by Spring despite being in same package and a scancomponent


So I have a spring application, I've added a loggingHandler class using annotation along with a @Loggable custom annotation. I've successfully log method calls defined in a @RestController class, however, classes annotated as @Component seems to be not detected by spring...

Here is a portion of the code:

package company;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.EnableAspectJAutoProxy;


@SpringBootApplication
@EnableAspectJAutoProxy(proxyTargetClass=true)
@ComponentScan({"company", "document", "logger", "model.bodyComponents"})
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

Then the API

package company;

@RestController
public class Api {

    @PostMapping(value = "/", consumes = MediaType.APPLICATION_XML_VALUE)
    @Loggable
    public ResponseEntity<?> convertXmlToPdf(HttpServletRequest request) {
                // some code
                root.render(outputStream); //it is called correctly
                // some code
    }

Then the method render that is called :

package company;

@Component
public class Root {

    @Loggable
    public void render(OutputStream target) throws IOException, ParseException, TypesetElementWidthException, ClassNotFoundException {

     //some code  

    }

}

Then the LoggingHandler:

@Aspect
@Configuration
public class LoggingHandler {

    private Logger log = LoggerFactory.getLogger(this.getClass());

    @Pointcut("@annotation(logger.Loggable)")
    public void pcLoggable(){
    }

    @Before("@annotation(logger.Loggable)")
    public void beforeAnnotLog(JoinPoint joinPoint){

        log.info(joinPoint.getSignature() + "Something will be called called with loggable annot.", joinPoint);
        System.out.println("Test Loggable annotation Before the call");

    }

    @After("@annotation(logger.Loggable)")
    public void annotLog(JoinPoint joinPoint){

        log.info(joinPoint.getSignature() + "Something is called with loggable annot.", joinPoint);
        System.out.println("Test Loggable annotation");

    }

}

Finally the Loggable annotation :

package logger;

import java.lang.annotation.*;

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

}

The logger is called when the convertXmlToPdf is called (I posted an XML and everything is running fine).

That methods calls the Root.render method, but nothing is logged then, despite Root being a component and render being annotated with @Loggable. So this makes me think that spring do not detect the Root class as a component...


Solution

  • How does the Api obtain a reference to a Root class? My guess you are doing a new Root() in there somewhere instead of getting the Spring managed instance. – M. Deinum Jul 11 at 10:49

    Yes, there is a new Root(), but I'm not sure to understand why I should use a spring managed instance... – Cromm Jul 11 at 11:04

    The answer is simple: Because Spring AOP only works on Spring components. It creates dynamic proxies in order to register aspects or advisors to them and execute them whenever a proxy method is called. By instantiating via new Root() you are circumventing proxy creation, thus you cannot expect any Spring AOP aspect to work there. In order to apply AOP to non-Spring-managed objects you would need to use full AspectJ.

    @M. Deinum, I wrote this answer in order to close the topic. I know you could have written it yourself but didn't do so in 2+ weeks, so please forgive me for kinda stealing this from you. Feel free to write your own answer, then I will delete mine and you can get yours accepted.