Java + Spring + Maven - AspectJ implementation instead of SpringAOP - java

Java + Spring + Maven application:
Can somebody provide me with the link or advise me on a pure AspectJ implementation without proxy-based Spring AOP?
My application is purely Spring + Maven based. I have currently implemented aspects with Spring AOP which is not solving my problems.
If I try to access a private method2() from public method1() within the same class A this is not supported.
I would like to know :
1) How to write an aspectj with pointcut that supports intraclass method calls?
2) How to configure that into my current Spring,maven project with AspectJ load-time weaving?
3) how to configure AspectJ Maven Plugin for compile-time weaving in Tomcat server + eclipse.
#Controller
class A {
public void method1() {
method2("foo");
}
private String method2(String text) {
return text;
}
}
Expected output:
log.entering(method1)
log.entering(method2)
print abc
log.exiting(method2)
log.exiting(method1)

My recommendation for you is to make a dummy project with Spring Roo (so you can see how the Maven pom.xml and Spring applicationContext.xml file looks) and download Spring STS version of Eclipse which has AspectJ setup correctly.

You can use Maven and AspectJ project together by converting Maven project into AspectJ project by Right Click - Configuration - Convert to AspectJ project. From there you can create Aspect class without annotation or Java class of Aspect using annotation.
As for the result that you want, you can use Around method like below:
#Around("execution ( * A.method1(..))")
public void captureMethodOne(ProceedingJoinPoint joinPoint) throws Throwable{
System.out.println("log.entering(method1)");
joinPoint.proceed();
System.out.println("log.exiting(method1)");
}
Also do not forget to input aspectj and aspectj maven plugin in the pom.xml, such as
<properties>
<maven.compiler.plugin.version>3.5.1</maven.compiler.plugin.version>
</properties>
<dependencies>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.8.7</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.8</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>aspectj-maven-plugin</artifactId>
<version>1.8</version>
<configuration>
<complianceLevel>1.8</complianceLevel>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>${maven.compiler.plugin.version}</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
Hope it helps.
PS Check AspectJ tutorial first, plenty around in the internet.

Related

Aspectj with Lombok

There are two different projects in which we need to use AspectJ.
Plugin in pom.xml:
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>aspectj-maven-plugin</artifactId>
<version>1.10</version>
<configuration>
<verbose>true</verbose>
<showWeaveInfo>true</showWeaveInfo>
<source>1.8</source>
<target>1.8</target>
<complianceLevel>1.8</complianceLevel>
<!-- <encoding>UTF-8</encoding> -->
<verbose>false</verbose>
<Xlint>ignore</Xlint>
<outxml>true</outxml>
<forceAjcCompile>true</forceAjcCompile>
<reweavable>false</reweavable>
<!-- this is important: start-->
<sources/>
<weaveDirectories>
<weaveDirectory>${project.build.directory}/classes</weaveDirectory>
</weaveDirectories>
<!-- this is important: end-->
</configuration>
<executions>
<execution>
<!-- The right phase is very important! Compile and weave aspects after all classes compiled by javac -->
<phase>process-classes</phase>
<goals>
<goal>compile</goal>
</goals>
</execution>
</executions>
<dependencies>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.9</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjtools</artifactId>
<version>1.8.9</version>
</dependency>
</dependencies>
</plugin>*
Dependencies:
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>${aspectj.version}</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>${aspectj.version}</version>
</dependency>
Exception:
Caused by: org.aspectj.weaver.tools.UnsupportedPointcutPrimitiveException: Pointcut expression 'handler(*) && args(e)' contains unsupported pointcut primitive 'handler'
The first project does not contain a Lombok, so the "Build Project" or "Rebuild Project" helps in this situation. But there is weaving during compilation, and everything works out correctly.
But the second project uses Lombok, and the solution with "Build/Rebuild" does not help, because build means weaving at compile time and => AspectJ does not see the functionality of Lombok (For example, getters).
At the same time, a setting has been introduced in the plugin so that weaving works on the spot, and not during compilation: <forceAjcCompile>true</forceAjcCompile> and empty <sources/>.
The combination of AspectJ and Lombok is mentioned in the news documents for frequently asked questions on Lambda power tools:
https://awslabs.github.io/aws-lambda-powertools-java/FAQs/
Poweretools uses aspectj-maven-plugin to compile-time weave (CTW) aspects into the project. In case you want to use Lombok or other compile-time preprocessor for your project, it is required to change aspectj-maven-plugin configuration to enable in-place weaving feature. Otherwise the plugin will ignore changes introduced by Lombok and will use .java files as a source.
To enable in-place weaving feature you need to use following aspectj-maven-plugin configuration:
<configuration>
<forceAjcCompile>true</forceAjcCompile>
<sources/>
<weaveDirectories>
<weaveDirectory>${project.build.directory}/classes</weaveDirectory>
</weaveDirectories>
...
<aspectLibraries>
<aspectLibrary>
<groupId>software.amazon.lambda</groupId>
<artifactId>powertools-logging</artifactId>
</aspectLibrary>
</aspectLibraries>
</configuration>
A source with the same information, the comments helped a lot:
https://palesz.wordpress.com/2011/12/03/howto-maven-lombok-and-aspectj-together/
The key of the solution is the empty sources tag in the configuration and the forceAjcCompile=true setting.
Please tell me why only "Build" (i.e. weaving during compilation) can help solve this error? What is missing (what is there when starting "build") to start weaving in place? How is it possible to resolve this situation?
The UnsupportedPointcutPrimitiveException message tells you that you seem to be mixing native AspectJ aspects with Spring AOP ones, or maybe you forgot to configure Spring in a way that makes it stop from trying to wire AspectJ aspects redundantly as Spring AOP ones again.
How can I know that without having seen your aspect code or Spring config? Because native AspectJ knows handler() pointcuts, but Spring AOP does not. So it must be Spring picking it up while wiring the application. This problem is completely unrelated to Lombok.
As you might have noticed, I cannot answer more precisely, because your question is so generic and is specifically lacking a sample project or any code, for that matter. If you would please be so kind to post an MCVE on GitHub, I can help you fix your project, in case my general explanation what went wrong is not comprehensive enough for you.

Is it possible to make java project that use another library which implements AOP using aspectj mave plugin to use the annotations from the library?

I have java library that I created which implements AOP using AspectJ runtime and AspectJ maven plugin. The AspectJ point cut is getting triggered when I add the annotations to the functions which are defined in the same library itself.
I want to use this library in another project so that I don't have to implement AOP in all of the projects that I will be using this library with regardless of their nature: native java app, spring or spring-boot apps. And regardless of they were using maven or gradle.
I tried to add my library to another gradle project spring configured with xml file. I added the annotation to function which is declared in the host app but the AOP doesn't get triggered, however when I call function from the library itself that has the annotation the AOP gets triggered even if I call it from host application.
Is there way to be able to do this or should the implementation of AOP be done in the same host project as well ?
For my example now the host application is using spring-framework and is built with gradle.
NOTE: the library is compiled and exported as jar using maven shade plugin
library POM.xml
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.8.9</version>
</dependency>
<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>aspectj-maven-plugin</artifactId>
<version>1.10</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<complianceLevel>1.8</complianceLevel>
</configuration>
<executions>
<execution>
<goals>
<goal>compile</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
Aspect class
#Aspect
public class TimerAspect {
#Around("#annotation(Timer) && execution(* *(..))")
public Object timerMethod(ProceedingJoinPoint joinPoint) throws Throwable{
...code
}
}
on the other application which uses gradle and spring-framework I just added the library as a dependency
implementation('com.exmple.library:aspetLib:1.0.0')
As I mentioned previously, the annotation is working fine if the functions are from the same library which has the AOP, however when I add this annotation to a function from the host app which uses spring the AOP doesn't trigger.

aspects.jcabi: #Loggable doesn't add logs to the output

I am bored of adding manual logs for debugging each and every method that I write.
I came to know about #Loggable annotation of jcabi but am not successful in implementing that and your help is highly appreciated.
Below is the code that I have tried.
import com.jcabi.aspects.Loggable;
import lombok.extern.slf4j.Slf4j;
#Slf4j
public class Jcabi {
#Loggable
private static String checkJcabi(String stringToPrint) {
log.info("Print Successfull");
return stringToPrint;
}
public static void main(String[] args) {
checkJcabi("Hello World!");
}
}
IDE console prints this:
[main] INFO com.amazon.optimus.cpamutil.utils.Jcabi - Print Successfull
This is the log for the log.info() I have added in the method and there is no log for the #Loggable annotation something like this (below) as mentioned in this post
[INFO] com.example.Foo #power(2, 10): 1024 in 12μs
[INFO] com.example.Foo #power(3, 3): 27 in 4μs
Below are the dependencies packages that I use:
JCabiAspects = 1.0;
AspectJ = 6.0;
Slf4j = 1.7;
Slf4j_Simple = 1.7;
Let me know if you need more details. Thanks.
You need to do weaving of your binaries, as explained here: http://aspects.jcabi.com/example-weaving.html
You need to do the binary weaving. In the post that you linked, it says so and it also says that you can use the jcabi-maven-plugin to do the weaving for you, given that you are using maven.
Just add a few dependencies to your classpath and configure jcabi-maven-plugin for aspect weaving (get their latest versions in Maven Central)
<project>
<dependencies>
<dependency>
<groupId>com.jcabi</groupId>
<artifactId>jcabi-aspects</artifactId>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>com.jcabi</groupId>
<artifactId>jcabi-maven-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>ajc</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
If you are using gradle, you can use this gradle-aspectj-binary plugin

Using object from other module in aspect

I have one aspect with using aspectJ as below:
public aspect TestAspect {
pointcut classicPointcut(PersistenceManagerImpl object) : execution(manager.PersistenceManagerImpl.new(..)) && target(object);
after(PersistenceManagerImpl object) : classicPointcut(object){
System.err.println(object.getClass().getSimpleName());
}
}
this aspect is in module aspect. this module is packaking as jar. PersistenceManagerImpl is in other module but i need use it in module aspect. For dependency management i use maven. But here is of course problem with cyclic reference. Exists some way how can a resolve this problem ?
----------EDIT----------
I get only this error:
java.lang.NoSuchMethodError:TestAspect.ajc$after$TestAspect$1$cc149106(Ljava/lang/Object;)V
When i move my aspect to same module, when is PersistenceManagerImpl i obtain correct solution(of course). But this is not, what i wanted.
Could you put the error result of the compiling code? You could try to put another module as dependency first then later put the dependency on the aspectj maven plugin at weaveDependency in pom.xml as follow:
....
<dependency>
<groupId>com.maventest</groupId>
<artifactId>mytest</artifactId>
<version>1.0-SNAPSHOT</version>
<scope>compile</scope>
</dependency>
....
....
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>aspectj-maven-plugin</artifactId>
<version>1.8</version>
<executions>
<execution>
<goals>
<goal>compile</goal>
</goals>
</execution>
</executions>
<configuration>
<source>${maven.compiler.source}</source>
<target>${maven.compiler.target}</target>
<showWeaveInfo>true</showWeaveInfo>
<complianceLevel>${maven.compiler.target}</complianceLevel>
<encoding>${project.build.sourceEncoding}</encoding>
<weaveDependencies>
<weaveDependency>
<groupId>com.maventest</groupId>
<artifactId>mytest</artifactId>
</weaveDependency>
</weaveDependencies>
</configuration>
<dependencies>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>${aspectj.version}</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjtools</artifactId>
<version>${aspectj.version}</version>
</dependency>
</dependencies>
</plugin>
ps: You could see my question post asking the same thing here
Your aspect seems to be specific for the module in which PersistenceManagerImpl is located, so that module should be a dependency of the aspect module. On the other hand, that module depends on the aspect module because it needs it as an <aspectLibrary> in the AspectJ Maven configuration. Indeed a circular dependency, but an unnecessary one. You have two options:
Move the aspect to the application module which it is specific for because IMO it belongs there if it explicitly uses specific classes from there. Only aspects which implement cross-cutting concerns in a way applicable to multiple modules should be in their own aspect library.
Following the previous thought, you could make your aspect more general, e.g. do something like this:
public aspect TestAspect {
pointcut classicPointcut(Object object) :
execution(*..PersistenceManagerImpl.new(..)) &&
target(object);
after(Object object) : classicPointcut(object){
System.err.println(object.getClass().getSimpleName());
}
}

Adding aspects to maven project

I created aspectJ class in seperate Maven project:
#Aspect
public class AspectE {
#Pointcut("execution(#EntryPoint * *.*(..))")
public void defineEntryPoint() {
}
#Before("defineEntryPoint()")
public void setThreadName(JoinPoint joinPoint) {
...
}
#After("defineEntryPoint()")
public void removeThreadName(JoinPoint joinPoint) {
...
}
}
Then in second project I annotated several methods and added to pom.xml:
<dependency>
<groupId>first-project</groupId>
<artifactId>first-project</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.7.0</version>
</dependency>
But still aspects aren't seen at all. Am I missing some steps? What should I do?
Did you take a look at this?
AspectJ compiler Maven Plugin - Usage
In order to weave correctly your code with your libraries, you should declare them within your dependencies AND within the aspectj weaver:
<dependencies>
<!-- Aspectj lib -->
<dependency>
<groupId>com.my.group</groupId>
<artifactId>my-aspect-lib</artifactId>
<version>1.0</version>
</dependency>
<!-- Other dependencies -->
</dependencies>
<build>
<!-- Specific build configuration -->
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>aspectj-maven-plugin</artifactId>
<configuration>
<aspectLibraries>
<aspectLibrary>
<groupId>com.my.group</groupId>
<artifactId>my-aspect-lib</artifactId>
</aspectLibrary>
</aspectLibraries>
</configuration>
</plugin>
<!-- Other plugins configuration -->
</plugins>
</build>
<!-- Other settings -->
You have to weave the aspects with the code. This can be done in 2 ways:
Compile-time weaving, using the AspectJ compiler Maven plugin as Andrei suggested
Load-time weaving (LTW), using an agent or a custom class-loader
Load-time weaving is a bit more versatile, but can be a bit challenging to set up properly. It consumes more CPU during startup (when the weaving happens), and also has a memory footprint.
Compile-time weaving consumes more CPU during the compilation, obviously, but then you don't pay the price on each restart.
I had the same problem ... but after I added this maven repo it's working
<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.9</version>
</dependency>

Categories

Resources