I have a spring boot 2 java app and would like to use interpreted (not compiled) groovy code to inject aop. From reading the spring documentation this sounds like it is possible, but I could'nt find any examples. AOP - advising scripted beans:
You are of course not just limited to advising scripted beans… you can also write aspects themselves in a supported dynamic language and use such beans to advise other Spring beans. This really would be an advanced use of the dynamic language support though.
At the end I would like to have a directory where I could add groovy scripts (for business logic) to the application that will inject themself via spring-aop.
What I am not sure about is what is spring boot 2 automagically doing in such a case, or do I have to integrate code based on org.springframework.scripting manually?
So here is my small test project:
project
|-pom.xml
|src/main/java/de/test
|-Commandline.java
|-MytestApplication.java
|-TestConfig.java
|-GetText.java
|src/main/resources/groovy
|-testAspect.groovy
|src/main/resources
|-application.properties (empty at the moment)
|-applicationContext.xml
pom.xml
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>de.test</groupId>
<artifactId>mytest</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>mytest</name>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.4.RELEASE</version>
<relativePath/>
</parent>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.codehaus.groovy</groupId>
<artifactId>groovy</artifactId>
</dependency>
</dependencies>
</project>
TestConfig.java
package de.test;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
@Configuration
@EnableAspectJAutoProxy
@ComponentScan({"de.test", "groovy"})
@ImportResource({"classpath*:applicationContext.xml"})
public class TestConfig
{
}
applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.springframework.org/schema/beans" xmlns:lang="http://www.springframework.org/schema/lang" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/lang http://www.springframework.org/schema/lang/spring-lang.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
<lang:groovy id="testAspect" refresh-check-delay="30000" script-source="classpath:groovy/TestAspect.groovy"/>
</beans>
MytestApplication.java
package de.test;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;
@ComponentScan({"de.test", "groovy"})
@SpringBootApplication
public class SpringboottestApplication
{
public static void main(final String[] args)
{
SpringApplication.run(SpringboottestApplication.class, args);
}
}
GetText.java
package de.test;
import org.springframework.stereotype.Component;
@Component
public class GetText
{
public String getText()
{
return "test1";
}
}
Commandline.java
package de.test;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
@Component
public class Commandline implements CommandLineRunner
{
@Autowired
GetText textbean;
public Commandline()
{
super();
}
@Override
public void run(final String... args) throws Exception
{
System.out.println((this.textbean.getText());
}
}
testAspect.groovy
package groovy
import org.springframework.stereotype.Component
import org.aspectj.lang.ProceedingJoinPoint
import org.aspectj.lang.annotation.Around
import org.aspectj.lang.annotation.Aspect
@Aspect
@Component
class TestAspect
{
@Around("execution(String de.test.GetText.getText())")
public String doChangeGetText(ProceedingJoinPoint pjp) throws Throwable
{
String retVal = (String)pjp.proceed();
return retVal + "; test2";
}
}
After
mvn clean install
I start it with
java -jar target/mytest-0.0.1-SNAPSHOT.jar
Which results in only "test1" as output instead of the wanted "test1; test2".
Update1:
Update2:
Update3:
This is a classic question and was asked dozens of times here:
Spring AOP does not intercept self-invocation within beans, only calls from other classes to public Spring bean methods. This is also documented in the Spring manual.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With