BAEL-6300: Invoking Spring Cache @Cacheable from Another Method of the Same Bean (#14689)

* BAEL-6300: Problem Reproduction

* BAEL-6300: Internal Invocation with External Reference

* BAEL-6300: compile-time weaving

* BAEL-6300: load-time weaving
This commit is contained in:
Forb Yuan 2023-09-02 22:23:29 +08:00 committed by GitHub
parent 29c9c5dba9
commit 8c3d581fa9
9 changed files with 300 additions and 0 deletions

View File

@ -51,4 +51,119 @@
</plugins>
</build>
<profiles>
<profile>
<id>no-weaving</id>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<excludes>
<exclude>com.baeldung.selfinvocation.CompileTimeWeavingIntegrationTest</exclude>
<exclude>com.baeldung.selfinvocation.LoadTimeWeavingIntegrationTest</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
</profile>
<profile>
<id>compile-time-weaving</id>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<includes>
<include>com.baeldung.selfinvocation.CompileTimeWeavingIntegrationTest</include>
</includes>
</configuration>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>aspectj-maven-plugin</artifactId>
<version>${aspectj-plugin.version}</version>
<configuration>
<source>${java.version}</source>
<target>${java.version}</target>
<complianceLevel>${java.version}</complianceLevel>
<Xlint>ignore</Xlint>
<encoding>UTF-8</encoding>
<aspectLibraries>
<aspectLibrary>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
</aspectLibrary>
</aspectLibraries>
</configuration>
<executions>
<execution>
<goals>
<goal>compile</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
<profile>
<id>load-time-weaving</id>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>${maven-surefire-plugin.version}</version>
<configuration>
<argLine>
--add-opens java.base/java.lang=ALL-UNNAMED
--add-opens java.base/java.util=ALL-UNNAMED
-javaagent:"${settings.localRepository}"/org/aspectj/aspectjweaver/${aspectjweaver.version}/aspectjweaver-${aspectjweaver.version}.jar
-javaagent:"${settings.localRepository}"/org/springframework/spring-instrument/${spring.version}/spring-instrument-${spring.version}.jar
</argLine>
<useSystemClassLoader>true</useSystemClassLoader>
<forkMode>always</forkMode>
<includes>
<include>com.baeldung.selfinvocation.LoadTimeWeavingIntegrationTest</include>
</includes>
</configuration>
</plugin>
</plugins>
</build>
</profile>
</profiles>
<properties>
<aspectj-plugin.version>1.14.0</aspectj-plugin.version>
<spring.version>5.3.27</spring.version>
</properties>
</project>

View File

@ -2,8 +2,16 @@ package com.baeldung;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.FilterType;
@SpringBootApplication
@ComponentScan(basePackages = { "com.baeldung" }, excludeFilters = {
@ComponentScan.Filter(type = FilterType.ANNOTATION,
value = { SpringBootApplication.class})
})
@EnableCaching
public class Application {
public static void main(String[] args) {

View File

@ -0,0 +1,20 @@
package com.baeldung.selfinvocation;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.AdviceMode;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.FilterType;
@SpringBootApplication
@EnableCaching(mode = AdviceMode.ASPECTJ)
@ComponentScan(basePackages = { "com.baeldung" }, excludeFilters = {
@ComponentScan.Filter(type = FilterType.ANNOTATION,
value = { SpringBootApplication.class})
})
public class CompileTimeWeavingApplication {
public static void main(String[] args) {
SpringApplication.run(CompileTimeWeavingApplication.class, args);
}
}

View File

@ -0,0 +1,16 @@
package com.baeldung.selfinvocation;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.AdviceMode;
import org.springframework.context.annotation.EnableLoadTimeWeaving;
@SpringBootApplication
@EnableCaching(mode = AdviceMode.ASPECTJ)
@EnableLoadTimeWeaving
public class LoadTimeWeavingApplication {
public static void main(String[] args) {
SpringApplication.run(LoadTimeWeavingApplication.class, args);
}
}

View File

@ -0,0 +1,43 @@
package com.baeldung.selfinvocation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.context.annotation.Scope;
import org.springframework.context.annotation.ScopedProxyMode;
import org.springframework.stereotype.Service;
import java.util.concurrent.atomic.AtomicInteger;
@Service
@CacheConfig(cacheNames = "square")
@Scope(proxyMode = ScopedProxyMode.TARGET_CLASS)
public class MathService {
@Autowired
private MathService self;
private final AtomicInteger counter = new AtomicInteger();
@CacheEvict(allEntries = true)
public AtomicInteger resetCounter() {
counter.set(0);
return counter;
}
@Cacheable(key = "#n")
public double square(double n) {
counter.incrementAndGet();
return n * n;
}
public double sumOfSquareOf2() {
return this.square(2) + this.square(2);
}
public double sumOfSquareOf3() {
return self.square(3) + self.square(3);
}
}

View File

@ -17,6 +17,8 @@
<logger name="org.springframework.aop.interceptor.PerformanceMonitorInterceptor" level="TRACE" />
<logger name="org.aspectj.weaver" level="WARN" />
<root level="TRACE">
<appender-ref ref="STDOUT" />
</root>

View File

@ -0,0 +1,24 @@
package com.baeldung.selfinvocation;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import javax.annotation.Resource;
import java.util.concurrent.atomic.AtomicInteger;
import static org.assertj.core.api.Assertions.assertThat;
@SpringBootTest(classes = CompileTimeWeavingApplication.class)
class CompileTimeWeavingIntegrationTest {
@Resource
private MathService mathService;
@Test
void givenCacheableMethod_whenInvokingByInternalCall_thenCacheIsTriggered() {
AtomicInteger counter = mathService.resetCounter();
assertThat(mathService.sumOfSquareOf2()).isEqualTo(8);
assertThat(counter.get()).isEqualTo(1);
}
}

View File

@ -0,0 +1,25 @@
package com.baeldung.selfinvocation;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import javax.annotation.Resource;
import java.util.concurrent.atomic.AtomicInteger;
import static org.assertj.core.api.Assertions.assertThat;
@SpringBootTest(classes = LoadTimeWeavingApplication.class)
class LoadTimeWeavingIntegrationTest {
@Resource
private MathService mathService;
@Test
void givenCacheableMethod_whenInvokingByInternalCall_thenCacheIsTriggered() {
AtomicInteger counter = mathService.resetCounter();
assertThat(mathService.sumOfSquareOf2()).isEqualTo(8);
assertThat(counter.get()).isEqualTo(1);
}
}

View File

@ -0,0 +1,47 @@
package com.baeldung.selfinvocation;
import com.baeldung.Application;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import javax.annotation.Resource;
import java.util.concurrent.atomic.AtomicInteger;
import static org.assertj.core.api.Assertions.assertThat;
@SpringBootTest(classes = Application.class)
class MathServiceIntegrationTest {
@Resource
private MathService mathService;
@Test
void givenCacheableMethod_whenInvokedForSecondTime_thenCounterShouldNotIncrease() {
AtomicInteger counter = mathService.resetCounter();
assertThat(mathService.square(2)).isEqualTo(4);
assertThat(counter.get()).isEqualTo(1);
mathService.square(2);
assertThat(counter.get()).isEqualTo(1);
mathService.square(3);
assertThat(counter.get()).isEqualTo(2);
}
@Test
void givenCacheableMethod_whenInvokingByInternalCall_thenCacheIsNotTriggered() {
AtomicInteger counter = mathService.resetCounter();
assertThat(mathService.sumOfSquareOf2()).isEqualTo(8);
assertThat(counter.get()).isEqualTo(2);
}
@Test
void givenCacheableMethod_whenInvokingByExternalCall_thenCacheIsTriggered() {
AtomicInteger counter = mathService.resetCounter();
assertThat(mathService.sumOfSquareOf3()).isEqualTo(18);
assertThat(counter.get()).isEqualTo(1);
}
}