Search code examples
javaspringaspectj

Why does enabling an AspectJ expression make the class that is supposed to work on disappear from the context


I'm learning how to use AspectJ expressions using the book "Spring starts here" and after running the same program 5 times(It was ok) a change in the AspectJ expression made an exception that said

Exception in thread "main"
org.springframework.beans.factory.NoSuchBeanDefinitionException:
  No qualifying bean of type
  'julian.gonzalezlopez.aop.Comment.CommentRepository'
  available

That error only happends if the AspectJ expression fits the criterea

File structore

Aspecto.java

import java.util.logging.Logger;
import org.springframework.stereotype.Component;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;

@Aspect
@Component
public class Aspecto {
    private static final Logger logger = Logger.getLogger(Aspect.class.getName());
    
    @Around("execution (* julian.*.*.Comment.*.*(..))")
    public void log(ProceedingJoinPoint joinPoint) {
        logger.info("Comenzó un metodo atrapado por el aspecto");
        try {
            joinPoint.proceed();
        }
        catch (Throwable e) {
            System.err.println(e);
        }
        logger.info("Terminó");
    }
}

CommentRepository.java

import julian.gonzalezlopez.aop.POJO.Comment;
import org.springframework.stereotype.Service;

@Service
public class CommentRepository implements CommentRepositoryInterface {
    public void pie(Comment comment){
        System.out.println(comment.getText());
        System.out.println("Escrito por: " + comment.getOwner());
    }
}

Main.java

import julian.gonzalezlopez.aop.Comment.CommentRepository;
import julian.gonzalezlopez.aop.Comment.CommentRepositoryInterface;
import julian.gonzalezlopez.aop.POJO.Comment;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class Main {
    public static void main(String[] args) {
        System.out.println("Hello World!");
        AnnotationConfigApplicationContext c = new AnnotationConfigApplicationContext(ProjectConfig.class);
        System.out.println(c);
        CommentRepositoryInterface commentRepository = c.getBean(CommentRepository.class);
        Comment comment = new Comment("hola","julian");
        commentRepository.pie(comment);
    }
}

Solution

  • Before we proceed to your main problem, a few hints:

    • You ought to adhere to naming conventions, e.g. not use upper-case package names, because they are easily confused with class names.

    • Your logger uses class Aspect, but probably you want to use Aspecto. Besides, that is also a bad class name. You should use an aspect name describing what kind of aspect it is, such as LoggingAspect.

    • In your screenshot, I can see a class named CommentService, but in your sample code you use class CommentRepository with a @Service annotation. That is utterly confusing. Do you even remember yourself why you did that?

    Now, how can you fix your problem? Probably, your config class looks somewhat like this:

    @Configuration
    @EnableAspectJAutoProxy
    public class ProjectConfig {}
    

    I.e., it is missing a @ComponentScan annotation.

    Another possibility is that you actually did add that annotation, but still get the same exception. In that case, you can either fix your main class, searching for a bean of the interface class, ...

    CommentRepositoryInterface commentRepository = c.getBean(CommentRepositoryInterface.class);
    

    ..., which is what I recommend. As an alternative, you can keep your bean instantiation as-is but modify the way Spring creates proxies:

    @Configuration
    @EnableAspectJAutoProxy(proxyTargetClass = true)
    @ComponentScan
    public class ProjectConfig {}
    

    I warmly recommend to use the first option, because it is cleaner from a design point of view. You have the interface and its implementation already, which is a clean design. Utilise it, do not work around it.