Use AspectJ aspects in Spring project - spring

I am working on a large Spring Boot application and want to introduce an apsect. As I need "if()" pointcut designators, I need more than Spring AOP. But:
- I don't explicitly define my Aspect as a Spring Aspect, yet it seems to be handled by Spring
-If I define an aspect in a ".js" file, it is not found by the weaver.
I followed the instruction in https://www.baeldung.com/aspectj very closely. Basically I use a runtime weaver plugin for my maven project and register the aspect in an "aop.xml" file.
How can I separate the life cylce of my aspect from Spring?
package myApp;
public aspect MyAspect {
pointcut prepInFile() :
execution(public void MySpringBootApp.execute());
after(): MyMethod() {
System.out.println("*** I am here ***");
}
}
<aspectj>
<aspects>
<aspect name="myApp.MyAspect"/>
<weaver options="-verbose -showWeaveInfo">
<include within="myApp"/>
</weaver>
</aspects>
</aspectj>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.10</version>
<configuration>
<argLine>
-javaagent:"${settings.localRepository}"/org/aspectj/
aspectjweaver/${aspectj.version}/
aspectjweaver-${aspectj.version}.jar
</argLine>
<useSystemClassLoader>true</useSystemClassLoader>
<forkMode>always</forkMode>
</configuration>
</plugin>
</plugins>
</build>

First of all, AspectJ source files are not JavaScript, i.e. they don't have a *.js file extension, rather *.aj.
Then, instead of #EnableAspectJAutoProxy (which activates proxy-based Spring AOP) you need to to what is describes in the Spring manual section of AspectJ, e.g. use #EnableLoadTimeWeaving as described in the section about load-time weaving.
Please also note that native syntax AspectJ files need to be compiled by the AspectJ compiler. For that you want to use the AspectJ Maven plugin.

Related

Maven compiler plugin issue with custom annotation processor

I have written a custom annotation processor and configured with maven compiler plugin as shown below, I am facing issue with Immutables annotation processor which is in my application class path. When I add my annotation processor via maven compiler plugin, the Immutables is giving compilation errors. I need Immutables as well in my project.
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
<generatedSourcesDirectory>${project.build.directory}/generated-sources/</generatedSourcesDirectory>
<annotationProcessors>
<annotationProcessor>
org.smarttechie.TraceAnnotationProcessor
</annotationProcessor>
</annotationProcessors>
</configuration>
</plugin>
Any hints to use Immutables/any annotation processors along with my custom annotation processor.
Package your annotation processor into a JAR and include that JAR as a
compilation dependency. Be sure to add
META-INF/services/javax.annotation.processing.Processor to your JAR
(contents single line with your processor class name):
org.smarttechie.TraceAnnotationProcessor
If you don't want your new JAR included as a dependency of your generated
artifact, mark it prodided and/or true.

Spring Boot - Spring Data REST generate swagger documentation from repository

I have an application in Spring Boot with Spring Data Rest and I am trying to generate the documentation with Swagger using swagger-maven-plugin. The Controller documentation is generated without problems but the repository does not.
I have configured the swagger-maven-plugin of the following form in my pom.xml:
<plugin>
<groupId>com.github.kongchen</groupId>
<artifactId>swagger-maven-plugin</artifactId>
<version>3.1.6</version>
<configuration>
<apiSources>
<apiSource>
<springmvc>true</springmvc>
<locations>
<location>com.abelendo.repository</location>
<location>com.abelendo.controller</location>
</locations>
<schemes>http</schemes>
<host>localhost:8080</host>
<basePath>/</basePath>
<info>
<title>Swagger Maven Plugin Spring Boot for cars</title>
<version>v1</version>
<description>Working sample of Spring Boot for cars annotations</description>
<termsOfService>
http://www.github.com
</termsOfService>
<contact>
<email>abelendo#email.com</email>
<name>Abelendo Cars</name>
<url>http</url>
</contact>
<license>
<url>http://www.license.com</url>
<name>License name</name>
</license>
</info>
<!-- Support classpath or file absolute path here.
1) classpath e.g: "classpath:/markdown.hbs", "classpath:/templates/hello.html"
2) file e.g: "${basedir}/src/main/resources/markdown.hbs",
"${basedir}/src/main/resources/template/hello.html" -->
<templatePath>${basedir}/templates/strapdown.html.hbs</templatePath>
<outputPath>${basedir}/generated/document.html</outputPath>
<outputFormats>yaml</outputFormats>
<swaggerApiReader>com.github.kongchen.swagger.docgen.reader.SpringMvcApiReader</swaggerApiReader>
<swaggerDirectory>generated/swagger-ui</swaggerDirectory>
</apiSource>
</apiSources>
</configuration>
<executions>
<execution>
<phase>compile</phase>
<goals>
<goal>generate</goal>
</goals>
</execution>
</executions>
</plugin>
My CarRepository:
#Api(tags = "CarsRepo")
#RepositoryRestResource(path = "cars")
public interface CarRepository extends CrudRepository<Car, Long> {
<S extends Car> S save(#Valid S cars);
}
Is it possible to generate the repository documentation with swagger-maven-plugin?
In order to generate swagger documentation for a #RepositoryRestResource, all you need to do is:
Pull the io.springfox:springfox-data-rest dependency in your project
Import the springfox.documentation.spring.data.rest.configuration.SpringDataRestConfiguration class in your swagger configuration class.
#Configuration
#EnableSwagger2
#Import(SpringDataRestConfiguration.class)
public class SwaggerConfig {
#Bean
public Docket api() { ... }
}
Edit: the swagger config class doesn't work with spring-boot 2.0, spring-data 2.0 and spring-data-rest 3.0: there's an open issue in swagger (linked to swagger not being Java8 while spring-boot and spring-data are). See here for details: https://github.com/springfox/springfox/issues/2298.
However, I managed to solve it by patching a few swagger classes. The idea behind the patch is to use Java Optional instead of Guava and Java reflection. The patched classes are:
springfox.documentation.spring.data.rest.EntityServicesProvider
springfox.documentation.spring.data.rest.EntityContext
springfox.documentation.spring.data.rest.EntityDeleteExtractor
springfox.documentation.spring.data.rest.EntityFindAllExtractor
springfox.documentation.spring.data.rest.EntityFindOneExtractor
springfox.documentation.spring.data.rest.EntitySaveExtractor
I just 1 dependency in pom.xml
<!-- https://mvnrepository.com/artifact/io.springfox/springfox-data-rest -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-data-rest</artifactId>
<version>3.0.0</version>
</dependency>
Then import #Import(SpringDataRestConfiguration.class) in Respository
and it generates documentation

Spring 4 Java Config Transactions Proxy and Aspecj

I am creating a new project that uses aspectj transactions. It also uses legacy jars that contain services that are using the proxy method where an interface is required.
I am using java config and when I set
#EnableTransactionManagement(mode=AdviceMode.ASPECTJ)
Then I get the following exception thrown with accessing the proxy style services from the legacy libs:
org.hibernate.LazyInitializationException: could not initialize proxy - no Session
If I change to:
#EnableTransactionManagement(mode=AdviceMode.PROXY)
Then I don't get the problem but I can't then use the aspectj style transactions in my new project.
I've tried adding two #EnableTransactionManagement annotations with each adviceMode, but that is not allowed.
Here is the annotated class
#EnableWebMvc
#Configuration
#ComponentScan("com.mydomain")
#EnableTransactionManagement(mode=AdviceMode.ASPECTJ)
public class ApplicationConfig extends WebMvcConfigurerAdapter {
...
I've also added the aspectj maven plugin to the legacy project in the hope that it would handle the weaving at compile time and thus aspectj transactions would work. But this has not solved the problem.
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>aspectj-maven-plugin</artifactId>
<version>1.7</version>
<configuration>
<aspectLibraries>
<aspectLibrary>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
</aspectLibrary>
</aspectLibraries>
<complianceLevel>1.8</complianceLevel>
<source>1.8</source>
<target>1.8</target>
<showWeaveInfo>true</showWeaveInfo>
</configuration>
<executions>
<execution>
<goals>
<goal>compile</goal>
</goals>
</execution>
</executions>
</plugin>
Is it possible to have spring deal with both advice modes? How would I do this?
Or is there another way around this problem.
The problem was with the aspectj config on the legacy project.
When I ran mvn compile it became apparent. I had to add the dependency:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>${spring.version}</version>
</dependency>
That got it working when compiled using maven, but I it would still not work in eclipse. I had to right click on the legacy project in eclipse:
Configure>Convert to Aspectj Project
Then I could deploy from eclipse and I had aspectj transactional support in the legacy jars.

aop performance on spring (idk, aspectj)

I tried to test the performance of AOP on Spring framework 4.1.6 and
AOP methods were clean, jdk dynamic proxy and aspectJ.
I made one to five simple advices to them and checked elapsed time for each.
result:
jdk dynamic proxy:
aspect1: 2.499 sec.
aspect2: 2.574
aspect3: 2.466
aspect4: 2.436
aspect5: 2.563
aspectJ (ctw):
aspect1: 2.648
aspect2: 2.562
aspect3: 2.635
aspect4: 2.520
aspect5: 2.574
clean (no aspect):
aspect1: 2.699
aspect2: 2.513
aspect3: 2.527
aspect4: 2.458
aspect5: 2.402
Before testing them, I expected AspectJ (ctw) will be faster than Jdk dynamic proxy because AspectJ modified bytecode. But it was wrong even there was no performance difference among them.
So, I checked the target class(.class) modified to recognise that AspectJ Compiler used and found bytecode modified.
Here, I have question:
Is there any performance difference among them? (idk dynamic proxy, aspectj, no aop)
My code:
public class HelloAOP {
public static void main(String [] args) {
ApplicationContext ctx = new ClassPathXmlApplicationContext("spring/application-context.xml");
Order order = (Order) ctx.getBean("orderImpl");
SimpleDateFormat format = new SimpleDateFormat("mm:ss.SSS");
StopWatch watch = new StopWatch();
watch.start();
order.placeOrder();
watch.stop();
System.out.println("Elapsed: " + format.format(watch.getTotalTimeMillis()));
}
}
target:
#Service
public class OrderImpl implements Order {
public void placeOrder() {
System.out.println("::Target Object");
for(long i = 0; i < 5000000000L; i++);
}
}
aspect:
#Aspect
#Component
public class Aspect1 {
#Before("execution(* com.cafe.beans.impl.OrderImpl.placeOrder())")
public void aspect() {
System.out.println("Aspect 1 *");
}
}
pom.xml:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>4.1.6</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>4.1.6</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-instrument</artifactId>
<version>4.1.6.RELEASE</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.6</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.8.6</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjtools</artifactId>
<version>1.8.6</version>
</dependency>
<build>
<finalName>testAop</finalName>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>3.3</source>
<target>3.3</target>
</configuration>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<includes>
<include>**/*Tests.java</include>
</includes>
</configuration>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>aspectj-maven-plugin</artifactId>
<version>1.7</version>
<configuration>
<showWeaveInfo>true</showWeaveInfo>
<verbose>true</verbose>
<complianceLevel>1.8</complianceLevel>
</configuration>
<executions>
<execution>
<goals>
<goal>compile</goal>
<goal>test-compile</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
You should not be surprised not to see any difference because you are just measuring one single method call. 99.9% of the time measured is the loop inside your method. Ergo you are not measuring the right thing. You should do it the other way around, maybe similar to what I did here:
The method should do nothing or next to nothing and print nothing.
You should measure the overall time of repeatedly calling an aspect-advised method because you want to find out about the overhead of applying an aspect, not about method body runtime (the method body remains unchanged by your aspect).
Now you can compare Spring AOP to AspectJ performance and should see that AspectJ is superior. A few caveats:
I hope you know that you need to change the Spring configuration so as to switch from Spring AOP to AspectJ and vice versa. E.g. if you use the AspectJ Maven Plugin all the time for your builds, you will use compile-time AspectJ weaving, no matter if you configure Spring to use Spring AOP or AspectJ via load-time weaving as described in the Spring manual, section 10.8 Using AspectJ with Spring applications.
You should measure different types of pointcuts and advice, e.g. #Before/#After vs. #Around, (not) using parameter binding via this(), target() or args() etc.
Please also note that your sample code uses a pointcut on a class rather than an interface. JDK dynamic proxies do not work directly on classes, though, only on interfaces. In order to apply Spring AOP on classes, you need CGLIB as a dependency in Spring, otherwise it simply will not work. Edit: Okay, your class implements the Order interface, so it might still work with JDK dynamic proxies.

How to hijacked/intercept method of a class which from third party(EclipseLink) jar or even JDK?

I try to hijack/intercept a method of a class in EclipseLink. I had tried Spring AOP and AspectJ and I failed. I want to do something when class org.eclipse.persistence.internal.localization.i18n.TraceLocalizationResource throw exception while calling method getString(..); My implementation as below:
#AfterThrowing(pointcut = "execution(* org.eclipse.persistence.internal.localization.i18n.TraceLocalizationResource.getString(..))",throwing="error")
public void logAfterThrowing(JoinPoint joinPoint,Throwable error) {
....
}
And calssTraceLocalizationResource was called (I debug into it). But the hijack method wasn't called.
MY questions:
Was I doing it in the wrong way ?
The target class we want to hijack or intercept must be self-define type?
What if I want to hijack a class in JDK like java.lang.String ?
Basically the syntax looks okay, but I have not tried to replicate a
full aspect from your code snippet here. Furthermore, I only use
native AspectJ syntax and am not quite familiar with
annotation-based syntax, so if there is a problem there I might not
have seen it.
No, but if you want to weave a library (JAR or unpacked into the file system) used by your main application, you must make sure that it is in the in-path for the weaver. Please consult AspectJ documentation for the syntax. Edit: I posted some helpful links in another answer earlier today.
By default, packages org.aspectj, java and javax are excluded from weaving. For the latter two, there are command line switches to enable weaving. But LTW will be tricky at best and a pain in the a** (next to impossible) at worst because you have a hen-and-egg problem there: The AspectJ weaver runtime classes need the JDK, and JDK bootstrapping happens before any Java agent like the weaver is attached. So your best bet is compile-time weaving for JDK classes and then using the woven rt.jar later in your application. Even with compile-time weaving you might run into issues if your advice use JDK woven classes themselves. You want to make sure to avoid infinite recursions there. But it is possible.
Update: Another hint which sometimes seems too simple to mention for someone who has used AspectJ for a while: Instead of an execution pointcut which makes it necessary to weave the target class, you may just use a call pointcut which intercepts (as the name implies) calls to the target everywhere in your client classes. It might be a little less efficient, but maybe your best and easiest bet unless you really want to take the trouble of weaving the JDK.
It is possible to weave third party libraries but you must configure the aspectj-maven-plugin plugin:
<project>
...
<dependencies>
...
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.8.9</version>
</dependency>
<dependency>
<groupId>org.agroup</groupId>
<artifactId>to-weave</artifactId>
<version>1.0</version>
</dependency>
<dependency>
<groupId>org.anothergroup</groupId>
<artifactId>gen</artifactId>
<version>1.0</version>
</dependency>
...
</dependencies>
...
<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>aspectj-maven-plugin</artifactId>
<version>1.9</version>
<configuration>
<weaveDependencies>
<weaveDependency>
<groupId>org.agroup</groupId>
<artifactId>to-weave</artifactId>
</weaveDependency>
<weaveDependency>
<groupId>org.anothergroup</groupId>
<artifactId>gen</artifactId>
</weaveDependency>
</weaveDependencies>
</configuration>
<executions>
<execution>
<goals>
<goal>compile</goal>
</goals>
</execution>
</executions>
</plugin>
...
</plugins>
<build>
...
</project>
Source: http://www.mojohaus.org/aspectj-maven-plugin/examples/weaveJars.html

Resources