simple Spring AOP, but Aspect won't be triggered

79 Views Asked by At

I am trying to Practice Spring AOP. The Problem is, My program is not what I expect.

It should Look something like this when Aspect works.

Sep 27, 2020 1:11:11 PM aspects.LoggingAspect log
INFO: Method will execute
Sep 27, 2020 1:11:11 PM com.cms.services.CommentService publishComment
INFO: Publishing comment:Demo comment
Sep 27, 2020 1:11:11 PM aspects.LoggingAspect log
INFO: Method executed

However, It doesnt' log. Like Aspects haven't work at all.

com.cms.services.CommentService publishComment
INFO: Publishing Comment: Demo Comment

Main.java executes overall code Using Instance spring Spring Context and also uses CommentService.java.

CommentService.java is nothing but of printing one lined log. It is placed under services package.

Then there is LoggingAspect.java For Aspect Programming.

Finally, CommentConfig.java is for configuring EnableAspectJAutoProxy and registering Beans for Aspect class.



Here is my code.

Main.java

public class Main {
    public static void main(String[] args) {
        var c = new AnnotationConfigApplicationContext(CommentConfig.class);
        var service = c.getBean(CommentService.class);

        Comment comment = new Comment();
        comment.setText("Demo Comment");
        comment.setAuthor("Natasha");

        service.publishComment(comment);
    }
}

CommentService.java

@Service
public class CommentService {

    private Logger logger = Logger.getLogger(CommentService.class.getName());
    public void publishComment(Comment comment) {
        logger.info("Publishing Comment: " + comment.getText());
    }
}

LoggingAspect.java

@Aspect
@Component
public class LoggingAspect {

    private Logger logger = Logger.getLogger(LoggingAspect.class.getName());

    @Around("execution(* services.*.*(..))")
    public void log(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("method will execute");
            logger.info("Method will execute");
            joinPoint.proceed();
            logger.info("Method has executed");
        System.out.println("method has executed");
    }
}

CommentConfig.java

@Configuration
@ComponentScan(basePackages = {"com.cms"})
@EnableAspectJAutoProxy
public class CommentConfig {
    @Bean
    public LoggingAspect aspect() {
        return new LoggingAspect();
    }
}

Also, this is my dependency.

    <properties>
        <maven.compiler.source>17</maven.compiler.source>
        <maven.compiler.target>17</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <dependencies>
        <!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.3.23</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.springframework/spring-aspects -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aspects</artifactId>
            <version>5.3.23</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.springframework/spring-aop -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aop</artifactId>
            <version>5.3.24</version>
        </dependency>

My Package Name


├─src
│  ├─main
│  │  ├─java
│  │  │  └─com
│  │  │      └─cms
│  │  │          │  Main.java
│  │  │          │
│  │  │          ├─aspect
│  │  │          │      LoggingAspect.java
│  │  │          │
│  │  │          ├─config
│  │  │          │      CommentConfig.java
│  │  │          │
│  │  │          ├─model
│  │  │          │      Comment.java
│  │  │          │
│  │  │          ├─proxies
│  │  │          │      CommentNotificationProxy.java
│  │  │          │      CommentPushNotificationProxyImpl.java
│  │  │          │      EmailCommentNotificationProxyImpl.java
│  │  │          │
│  │  │          ├─repositories
│  │  │          │      CommentRepository.java
│  │  │          │      DBCommentRepositoryImpl.java
│  │  │          │
│  │  │          └─services
│  │  │                  CommentService.java
│  │  │
│  │  └─resources

Can't figure out what is missing...


Problem Solved. The Problem was indeed Because of I omitted the Full package name in the @Around Annotation. Below code makes what I expected in the beginning. LoggingAspect.java

@Aspect
@Component
public class LoggingAspect {
    private Logger logger = Logger.getLogger(LoggingAspect.class.getName());

    @Around("execution(* com.cms.services.*.*(..))")    // this is where I missed.
    public Object log(ProceedingJoinPoint joinPoint) throws Throwable {
        String methodName = joinPoint.getSignature().getName();
        Object arguments[] = joinPoint.getArgs();

        logger.info("Method " + methodName + " with Parameters " +
                Arrays.asList(arguments) + " will execute");
        Object returnedByMethod = joinPoint.proceed();
        logger.info("Method executed and returned " + returnedByMethod);
        return returnedByMethod;
    }
}

For those who are struggling like me, below documentation will help.
https://docs.spring.io/spring-framework/docs/2.0.x/reference/aop.html
It is about the The AspectJ pointcut expression.

3

There are 3 best solutions below

1
tuhin47 On

Within

Try with simple modification on LoggingAspect. within matches any join point within classes in packages containing "services".

@Around("within(*..services..*)")
public Object log(ProceedingJoinPoint joinPoint) throws Throwable {
    System.out.println("method will execute");
    logger.info("Method will execute");
    Object proceed = joinPoint.proceed();
    logger.info("Method has executed");
    System.out.println("method has executed");
    return proceed;
}

Around

execution specifically matches method executions of methods defined in classes within the "services" package. You have to specify full package for this. Follow the below code in your case. Your service method should be inside this package com.cms.services

@Around("execution(* com.cms.services.*.*(..))")
public Object log(ProceedingJoinPoint joinPoint) throws Throwable {
    System.out.println("method will execute");
    logger.info("Method will execute");
    Object proceed = joinPoint.proceed();
    logger.info("Method has executed");
    System.out.println("method has executed");
    return proceed;
}
1
Francisco Valadares On

Have you tried including the path of your service?

@Around("execution(* com.cms.services.CommentService.*(..))")

1
kriegaex On

My educated guess - but still just a guess - is, concluding from what little information I have and hoping that you did not just make up fake package names but forgot to edit them consistently:

@ComponentScan(basePackages = {"com.cms"})

The above means that no components outside of this base package will be found. But your pointcut says:

@Around("execution(* services.*.*(..))")

If your service really is in that package, it will never be registered as a Spring component and therefore also never be intercepted by the aspect, even if the aspect happens to be in package com.cms. But it might just as well be the other way around, the aspect not being registered as a component if it is is another package.