0

I have java project named Test which have all aspects in it. I wan to use these aspects in another spring boot project. I am working on a poc to weave aspects in Test project into a spring boot application. What is the efficient way to do so and how it can be done, can some one who implemented please suggest.

Code for Java project Test with aspects

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Secured {
    public boolean isLocked() default false; 
}
@Aspect
public class SecuredMethodAspect {

    @Pointcut("@annotation(secured)")
    public void callAt(Secured secured) {}

    @Around("callAt(secured)")
    public Object around(ProceedingJoinPoint pjp, Secured secured) throws Throwable {
        if (secured.isLocked()) {
             System.out.println(pjp.getSignature().toLongString() + " is locked");
            return pjp.proceed();
        } else {
            return pjp.proceed();
        }
    }
}
        <project xmlns="http://maven.apache.org/POM/4.0.0"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
    
        <groupId>aspect</groupId>
        <artifactId>test</artifactId>
        <version>0.0.1-SNAPSHOT</version>
        <packaging>jar</packaging>
    
        <name>AspectJ-POC</name>
        <url>http://maven.apache.org</url>
    
        <properties>
            <maven.compiler.source>11</maven.compiler.source>
            <maven.compiler.target>11</maven.compiler.target>
            <java.version>11</java.version>
        </properties>
    
        <!-- nickwongdev aspectj maven plugin, this is for weaving of aspects -->
        <build>
            <plugins>
                <plugin>
                    <groupId>com.nickwongdev</groupId>
                    <artifactId>aspectj-maven-plugin</artifactId>
                    <version>1.12.6</version>
                    <configuration>
                        <source>11</source>
                        <target>11</target>
                        <complianceLevel>11</complianceLevel>
                    </configuration>
                    <executions>
                        <execution>
                            <goals>
                                <goal>compile</goal>
                                <goal>test-compile</goal>
                            </goals>
                        </execution>
                    </executions>
                </plugin>
            </plugins>
        </build>
    
        <!-- aspectj runtime dependency -->
        <dependencies>
            <dependency>
                <groupId>org.aspectj</groupId>
                <artifactId>aspectjrt</artifactId>
                <version>1.9.6</version>
            </dependency>
        </dependencies>
    
    </project>
kriegaex
  • 63,017
  • 15
  • 111
  • 202
ak219
  • 1
  • 2
  • 1
    You work in a Spring context, and Spring has its own aspect framework. Please explain why you don't want to use that. It works out of the box the way you wish. Which special requirements do you have for using native AspectJ? – kriegaex Jun 19 '21 at 00:28
  • @kriegaex I want to develop a native aspect framework, I want to use it in three different modules, one is a springboot module, another one is just java module and the third one is a struts module.In design perspective I might be correct, is there any good way to do it. – ak219 Jun 20 '21 at 13:38
  • The AspectJ Maven plugin documentation contains a chapter on [multi-module projects](https://www.mojohaus.org/aspectj-maven-plugin/multimodule/multimodule_strategy.html). In my own fork which also works with more recent Java versions, I have [fixed the the faulty graphics](https://dev-aspectj.github.io/aspectj-maven-plugin/multimodule/multimodule_strategy.html). I also answered several related questions here before. If you still have questions after reading this or cannot find my related answers, please let me know. – kriegaex Jun 20 '21 at 14:58
  • @kriegaex I have one spring boot application A, there is other non spring application B which is used as dependency in spring boot application A. I have all logging related Annotations and Aspects in non spring application C. I want to make my annotations work in both Spring related classes and non spring related classes. Which is the best approach to follow, I cannot post code here as it is restricted. Provide any similar example if you have any I am exhausted trying to make this work. – ak219 Jul 09 '21 at 22:26
  • @kriegaex I do not understand how to use document you mentioned to my use case. Is it applicable to my scenario or should I follow different approach. I am trying this for more than a month still spinning in my head not able to figure out and make it work. Please post example id you have one. – ak219 Jul 09 '21 at 22:29
  • Sorry to disappoint you, but this is not how SO works. I am not going to do the whole job for you, trying to guess what you want from a few sentences of prose. Please publish an [MCVE](https://stackoverflow.com/help/mcve) on GitHub, then I can take a look. What you want sounds straightforward enough to me, but I need to see a minimal version of your project. Remove everything not necessary to reproduce the problem, rename class and package names in order to anonymise it. But do publish something. One month, why? In 2 hours or less you should be able to create an MCVE. Waste of time. – kriegaex Jul 10 '21 at 00:29
  • @kriegaex I have created sample projects and pushed to git, these are similar to projects which I am working on but with only minimal code which is required. Git url for all three modules, – ak219 Jul 11 '21 at 00:03
  • @kriegaex https://github.com/codeallday123/aspect-test-project - this project have aspects(it is used as dependency in both spring boot project and dependency project, methods are annotated), https://github.com/codeallday123/parentproject-springboot - this project is main spring boot project, https://github.com/codeallday123/dependencyprojet- this project is java project (non spring) and used as dependency in spring boot project – ak219 Jul 11 '21 at 00:03
  • Your projects all seem to appear empty, because you set the GitHub default branch to `main` (the new standard), but committed to `master`. I figured it out, but maybe you should fix that by setting `master` as default and be more careful next time. – kriegaex Jul 12 '21 at 03:00

1 Answers1

1

Some quick first impressions I got when browsing your projects before cloning them:

  • You should not use Lombok + native AspectJ together in a compile-time weaving scenario, see my answer here.
  • Possible workarounds would be multi-phase compilation, i.e. first Java + Lombok and then post-compile-time weaving with AspectJ, see my answer here. Alternatively, use delombok in order to first generate source code with Lombok annotations expanded into equivalent source code and then compile the generated sources with AspectJ.
  • On a second look, your sample aspect module does not seem to use any Lombok, so you do not need that dependency. But probably your real project does, hence my remarks above.
  • Of course, you can use compile-time weaving in order to weave your aspects, as you suggested. You need to correctly configure AspectJ Maven Plugin in this case, though, which you did not. You need to tell it in the Spring Boot module where to find the aspect library and which dependencies to weave, in addition to the main application. Did you ever read the plugin documentation, e.g. chapters Weaving classes in jars, Using aspect libraries and Multi-module use of AspectJ? I also answered tons of related questions here already.
  • But actually, the preferred approach for weaving aspects into Spring applications is to use load-time weaving (LTW), as described in the Spring manual. That should be easier to configure and you would not need any AspectJ Maven Plugin stuff and probably would not have any Lombok issues during compilation either.

Judging from your sample projects and the absence of any aspectLibraries and weaveDependencies sections in your AspectJ Maven configuration, you simply did not bother to read any documentation, which leaves me to wonder what you did during the one month you tried to get this working.

I would appreaciate a comment, explaining why you want to use CTW instead of LTW. The only reason I can think of is that in addition to your native aspect, you also want to use Spring AOP aspects withing your Spring application. But if you activate native AspectJ LTW, you cannot use Spring AOP at the same time anymore. If that is not an issue, I recommend LTW, as it is documented and tested well in Spring. CTW is no problem either, but more difficult to understand and manage in your POMs.


Update: OK, I did not want to wait any longer for your reason to use CTW and simply decided to show you a LTW solution instead. What I did was:

  • Remove AspectJ Maven Plugin and AspectJ Tools from your Spring project
  • Add src/main/resources/org/aspectj/aop.xml, configuring it to use your aspect and target your base package + subpackages
  • Fix pointcut to also use your base package + subpackages, because in your base package there are no classes. This is a typical beginner's mistake. You need to use execution(* com.ak..*(..)) with .., not just com.ak.*.

Non-essential cosmetics I applied are:

  • Call context.getBean(TestController.class).mainRequest() from the Spring Boot application's main method in order to automatically produce some aspect output, in order to avoid having to use curl or a web browser in order to call a local URL.
  • Use the Spring application as an auto-closeable via try-with-resources in order to make the application close after creating the desired log output.
  • Change the aspect to also log the joinpoint in order to see in the log, which methods it actually intercepts. Otherwise all log lines look the same, which is not quite helpful.
  • Use try-finally in order to make sure that the log message after proceeding to the original method is also logged in case of an exception.

The diff looks like this (I hope you can read diffs):

diff --git a/aspect-test-project/src/main/java/com/ak/aspect/MethodLogAspect.java b/aspect-test-project/src/main/java/com/ak/aspect/MethodLogAspect.java
--- a/aspect-test-project/src/main/java/com/ak/aspect/MethodLogAspect.java  (revision Staged)
+++ b/aspect-test-project/src/main/java/com/ak/aspect/MethodLogAspect.java  (date 1626233247623)
@@ -7,14 +7,14 @@
 @Aspect
 public class MethodLogAspect {
 
-   @Around("@annotation( MethodLog) && (execution(* com.ak.*(..)))")
+   @Around("@annotation(MethodLog) && execution(* com.ak..*(..))")
    public Object wrap(final ProceedingJoinPoint joinPoint) throws Throwable {
-       System.out.println("***Aspect invoked before calling method***");
-       
-       final Object obj = joinPoint.proceed();
-       
-       System.out.println("***Aspect invoked after calling method***");
-       
-       return obj;
+       System.out.println("[BEFORE] " + joinPoint);
+       try {
+           return joinPoint.proceed();
+       }
+       finally {
+       System.out.println("[AFTER]  " + joinPoint);
+       }
    }
 }
diff --git a/src/main/java/com/ak/ParentprojectSpringbootApplication.java b/src/main/java/com/ak/ParentprojectSpringbootApplication.java
--- a/src/main/java/com/ak/ParentprojectSpringbootApplication.java  (revision Staged)
+++ b/src/main/java/com/ak/ParentprojectSpringbootApplication.java  (date 1626233555873)
@@ -1,14 +1,17 @@
 package com.ak;
 
+import com.ak.controller.TestController;
 import org.springframework.boot.SpringApplication;
 import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.context.ConfigurableApplicationContext;
 
 @SpringBootApplication(scanBasePackages = { "com.ak.*" })
-//@SpringBootApplication
 public class ParentprojectSpringbootApplication {
 
    public static void main(String[] args) {
-       SpringApplication.run(ParentprojectSpringbootApplication.class, args);
+       try (ConfigurableApplicationContext context = SpringApplication.run(ParentprojectSpringbootApplication.class, args)) {
+           context.getBean(TestController.class).mainRequest();
+       }
    }
 
 }
diff --git a/parentproject-springboot/pom.xml b/parentproject-springboot/pom.xml
--- a/parentproject-springboot/pom.xml  (revision Staged)
+++ b/parentproject-springboot/pom.xml  (date 1626232421474)
@@ -71,13 +71,6 @@
            <version>1.9.6</version>
        </dependency>
 
-       <!-- https://mvnrepository.com/artifact/org.aspectj/aspectjtools -->
-       <dependency>
-           <groupId>org.aspectj</groupId>
-           <artifactId>aspectjtools</artifactId>
-           <version>1.9.6</version>
-       </dependency>
-
        <dependency>
            <groupId>com.ak.aspect</groupId>
            <artifactId>aspect-test-project</artifactId>
@@ -100,24 +93,6 @@
                </configuration>
            </plugin>
 
-           <plugin>
-               <groupId>com.nickwongdev</groupId>
-               <artifactId>aspectj-maven-plugin</artifactId>
-               <version>1.12.6</version>
-               <configuration>
-                   <source>11</source>
-                   <target>11</target>
-                   <complianceLevel>11</complianceLevel>
-               </configuration>
-               <executions>
-                   <execution>
-                       <goals>
-                           <goal>compile</goal>
-                           <goal>test-compile</goal>
-                       </goals>
-                   </execution>
-               </executions>
-           </plugin>
        </plugins>
    </build>
 

For your convenience, here are the complete changed files:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.5.2</version>
        <relativePath /> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.ak</groupId>
    <artifactId>parentproject-springboot</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>parentproject-springboot</name>
    <description>Test project for Spring Boot</description>
    <properties>
        <java.version>11</java.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-rest</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>com.ak.dependency</groupId>
            <artifactId>dependencyprojet</artifactId>
            <version>0.0.1-SNAPSHOT</version>
        </dependency>

        <!-- aspectj runtime dependency -->
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjrt</artifactId>
            <version>1.9.6</version>
        </dependency>

        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.9.6</version>
        </dependency>

        <dependency>
            <groupId>com.ak.aspect</groupId>
            <artifactId>aspect-test-project</artifactId>
            <version>0.0.1-SNAPSHOT</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>

        </plugins>
    </build>

</project>
<!DOCTYPE aspectj PUBLIC "-//AspectJ//DTD//EN" "http://www.eclipse.org/aspectj/dtd/aspectj.dtd">
<aspectj>

    <weaver options="-verbose -showWeaveInfo">
        <!-- only weave classes in our application-specific packages -->
        <include within="com.ak..*"/>
    </weaver>

    <aspects>
        <aspect name="com.ak.aspect.MethodLogAspect"/>
    </aspects>

</aspectj>
package com.ak.aspect;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;

@Aspect
public class MethodLogAspect {

    @Around("@annotation(MethodLog) && execution(* com.ak..*(..))")
    public Object wrap(final ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("[BEFORE] " + joinPoint);
        try {
            return joinPoint.proceed();
        }
        finally {
        System.out.println("[AFTER]  " + joinPoint);
        }
    }
}
package com.ak;

import com.ak.controller.TestController;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;

@SpringBootApplication(scanBasePackages = { "com.ak.*" })
public class ParentprojectSpringbootApplication {

    public static void main(String[] args) {
        try (ConfigurableApplicationContext context = SpringApplication.run(ParentprojectSpringbootApplication.class, args)) {
            context.getBean(TestController.class).mainRequest();
        }
    }

}

Now you simply make sure to add the JVM argument -javaagent:/path/to/aspectjweaver-1.9.6.jar to your Java command line in order to activate native AspectJ LTW. For more details about how to configure LTW within Spring if more sophisticated ways, please read the Spring manual, section Using AspectJ with Spring Applications.

The console log should look something like this:

[AppClassLoader@3764951d] info AspectJ Weaver Version 1.9.6 built on Tuesday Jul 21, 2020 at 13:30:08 PDT
[AppClassLoader@3764951d] info register classloader jdk.internal.loader.ClassLoaders$AppClassLoader@3764951d
[AppClassLoader@3764951d] info using configuration /C:/Users/alexa/Documents/java-src/SO_AJ_SpringCTWMultiModule_68040124/parentproject-springboot/target/classes/org/aspectj/aop.xml
[AppClassLoader@3764951d] info register aspect com.ak.aspect.MethodLogAspect
[AppClassLoader@3764951d] warning javax.* types are not being woven because the weaver option '-Xset:weaveJavaxPackages=true' has not been specified
[RestartClassLoader@1f385e10] info AspectJ Weaver Version 1.9.6 built on Tuesday Jul 21, 2020 at 13:30:08 PDT
[RestartClassLoader@1f385e10] info register classloader org.springframework.boot.devtools.restart.classloader.RestartClassLoader@1f385e10
[RestartClassLoader@1f385e10] info using configuration /C:/Users/alexa/Documents/java-src/SO_AJ_SpringCTWMultiModule_68040124/parentproject-springboot/target/classes/org/aspectj/aop.xml
[RestartClassLoader@1f385e10] info register aspect com.ak.aspect.MethodLogAspect

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::                (v2.5.2)

(...)
[RestartClassLoader@1f385e10] weaveinfo Join point 'method-execution(com.ak.dependency.model.AccountInfo com.ak.service.TestService.incomingRequest())' in Type 'com.ak.service.TestService' (TestService.java:20) advised by around advice from 'com.ak.aspect.MethodLogAspect' (MethodLogAspect.java)
[RestartClassLoader@1f385e10] weaveinfo Join point 'method-execution(com.ak.dependency.model.AccountInfo com.ak.dependency.Route.accountInfo())' in Type 'com.ak.dependency.Route' (Route.java:12) advised by around advice from 'com.ak.aspect.MethodLogAspect' (MethodLogAspect.java)
[RestartClassLoader@1f385e10] weaveinfo Join point 'method-execution(com.ak.dependency.model.BalanceInfo com.ak.dependency.Pipeline.balanceInfo())' in Type 'com.ak.dependency.Pipeline' (Pipeline.java:11) advised by around advice from 'com.ak.aspect.MethodLogAspect' (MethodLogAspect.java)
(...)

Controller
[BEFORE] execution(AccountInfo com.ak.service.TestService.incomingRequest())
[BEFORE] execution(AccountInfo com.ak.dependency.Route.accountInfo())
[BEFORE] execution(BalanceInfo com.ak.dependency.Pipeline.balanceInfo())
[AFTER]  execution(BalanceInfo com.ak.dependency.Pipeline.balanceInfo())
[AFTER]  execution(AccountInfo com.ak.dependency.Route.accountInfo())
[AFTER]  execution(AccountInfo com.ak.service.TestService.incomingRequest())
(...)
kriegaex
  • 63,017
  • 15
  • 111
  • 202
  • I am using lombok in original project, not in these projects. I will go through what you suggested for lombok. I went through the documentation many times, I was not able to implement in my projects as I do not understand most of the it, I am new to development and brand new to AspectJ. – ak219 Jul 13 '21 at 14:52
  • If you are new to development in general, maybe learn programming basics first, before you use sophisticated frameworks like Spring, magic tools like Lombok and concepts like AOP - especially, if you want to combine all of them. The Spring documentation is pretty good. If you do not understand it, it is a clear sign that you are trying to use something you do not understand. Give yourself a chance to learn and grow as a developer. Without a foundation, of your career will be shaky like a house of cards and eventually fall apart. For now, don't despair, I am here to help you. – kriegaex Jul 14 '21 at 02:30
  • The Lombok + AspectJ issue is a general one, but not the reason why no aspects are woven into your application. So once again, like I said in my answer: _I would appreaciate a comment, explaining why you want to use CTW instead of LTW._ I understand now why Spring AOP is not your choice, if you want to apply aspects to non-Spring components. Fine. But why CTW, not LTW? Before I suggest a solution, I would like to know. – kriegaex Jul 14 '21 at 02:33
  • OK, I updated my answer, explaining you how to use LTW, because it is simpler than CTW. For CTW to work, you would need AspectJ Maven in all 3 modules. – kriegaex Jul 14 '21 at 03:41
  • I am trying to use CTW rather than LTW for this reason, If I use CTW the annotation is not working if I want to use it in both spring component and plain java class simultaneously. If I use Component on aspect class it is working only in place where bean is created not in plain java class, vice versa too if not with @component and defining aspect in aop.xml it is working in plain java class not in spring component. I understand that CTW will solve this, I may be wrong. Please suggest any solution to solve this issue if I use LTW. – ak219 Jul 14 '21 at 04:36
  • As you can see, my LTW solution works for both Spring components and normal POJOs. In the log output, you see `TestService`, `Pipeline` and `Route` methods being intercepted. So what is wrong with my solution? It also has the advantage of not colliding with Lombok, because Lombok is applied during compile-time, while AspectJ LTW sees the compiled classes and weaves into them on the fly. The compiler conflict is gone. Your aspect needs no `@Component` annotation (that one is for Spring AOP only) and LTW needs no dynamic proxies either. I thought you would be happy that I solved your problem. – kriegaex Jul 14 '21 at 04:59
  • I have implemented the same thing in my projects the annotation on incomingRequest in TestService class in parentproject-springboot is not working, when I debug flow is not reaching the aspect and print statements in aspect class are not executed. I did check in all changes to master branch. – ak219 Jul 16 '21 at 04:54
  • Then you made a mistake or your real project is different from the published one. – kriegaex Jul 16 '21 at 06:50