Spring -如何在aspectJ的自调用中缓存?

vddsk6oq  于 2023-03-07  发布在  Spring
关注(0)|答案(4)|浏览(109)

谢谢你点击我的问题。我想在自调用中调用一个缓存方法,所以我需要使用AspectJ。(缓存的配置是可以的)
1.添加AspectJ依赖项

implementation 'org.springframework.boot:spring-boot-starter-aop'

1.将@EnableCaching(模式=建议模式. ASPECTJ)添加到我的www.example.com application.java

@EnableJpaAuditing
@EnableCaching(mode = AdviceMode.ASPECTJ) // <-- here 
@SpringBootApplication
public class DoctorAnswerApplication {

    public static void main(String[] args) {
        SpringApplication.run(DoctorAnswerApplication.class, args);
    }

}

1.我的网站 service.java

@Service
public class PredictionService {

    @Cacheable(value = "findCompletedRecordCache")
    public HealthCheckupRecord getRecordComplete(Long memberId, String checkupDate) {
        Optional<HealthCheckupRecord> recordCheckupData;
        recordCheckupData = healthCheckupRecordRepository.findByMemberIdAndCheckupDateAndStep(memberId, checkupDate, RecordStep.COMPLETE);

        return recordCheckupData.orElseThrow(NoSuchElementException::new);
    }
}

1.我的测试代码

@Test
    public void getRecordCompleteCacheCreate() {
        // given
        Long memberId = (long)this.testUserId;
        List<HealthCheckupRecord> recordDesc = healthCheckupRecordRepository.findByMemberIdAndStepOrderByCheckupDateDesc(testUserId, RecordStep.COMPLETE);
        String checkupDate = recordDesc.get(0).getCheckupDate();
        String checkupDate2 = recordDesc.get(1).getCheckupDate();

        // when
        HealthCheckupRecord first = predictionService.getRecordComplete(memberId,checkupDate);
        HealthCheckupRecord second = predictionService.getRecordComplete(memberId,checkupDate);
        HealthCheckupRecord third = predictionService.getRecordComplete(memberId,checkupDate2);

        // then
        assertThat(first).isEqualTo(second);
        assertThat(first).isNotEqualTo(third);
    }

我没有做任何与aspectJ相关的类。我认为@EnableCaching(mode = AdviceMode.ASPECTJ)使@Cacheable由AspectJ而不是Spring AOP(代理)工作。

sz81bmfz

sz81bmfz1#

感谢@kriegaex,他指出了spring-aspects依赖性和load-time-weaving javaagent需求,为方便其他人,下面是Spring Boot和Maven的配置片段。
(Note:最后,我觉得这一切(以及副作用)对我的项目来说都不值得。请看我的另一个答案,一个简单的,但有点丑陋的解决方案。)
POM:

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

    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-aspects</artifactId>
    </dependency>

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

应用程序配置:

@Configuration
@EnableCaching(mode = AdviceMode.ASPECTJ)
public class ApplicationConfig { ... }

目标方法:

@Cacheable(cacheNames = { "cache-name" })
public Thingy fetchThingy(String identifier) { ... }

织造机构:
选项1:加载时间编织(Spring默认值)
使用JVM javaagent参数或添加到Servlet容器库中

-javaagent:<path-to-jar>/aspectjweaver-<version>.jar

选项2:编译时编织
(This应该是有效的,但我发现缺乏与Spring缓存一起使用的连贯示例-请参阅下面的进一步阅读)
使用aspectj-maven插件:https://www.mojohaus.org/aspectj-maven-plugin/index.html

<plugin>
    <groupId>org.codehaus.mojo</groupId>
    <artifactId>aspectj-maven-plugin</artifactId>
    <version>1.11</version>
    <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>
    <executions>
        <execution>
            <goals>
                <goal>compile</goal>
                <goal>test-compile</goal>
            </goals>
        </execution>
    </executions>
    <configuration>
        <outxml>true</outxml>
        <showWeaveInfo>false</showWeaveInfo>
        <Xlint>warning</Xlint>
        <verbose>false</verbose>
        <aspectLibraries>
            <aspectLibrary>
                <groupId>org.springframework</groupId>
                <artifactId>spring-aspects</artifactId>
            </aspectLibrary>
        </aspectLibraries>
        <complianceLevel>${java.version}</complianceLevel>
        <source>${java.version}</source>
        <target>${java.version}</target>
    </configuration>
</plugin>

出于参考/搜索目的,以下是启动这一切的错误:

Caused by: java.io.FileNotFoundException: class path resource [org/springframework/cache/aspectj/AspectJCachingConfiguration.class] cannot be opened because it does not exist

更多阅读:

j5fpnvbx

j5fpnvbx2#

你看过Javadoc for EnableCaching了吗?
注意,如果mode()被设置为AdviceMode.ASPECTJ,那么proxyTargetClass()属性的值将被忽略。还注意,在这种情况下,spring-aspects模块JAR必须存在于类路径中,编译时编织或加载时编织将方面应用于受影响的类。在这种情况下不涉及代理;本地呼叫也将被拦截。
所以请检查一下
1.在类路径上具有spring-aspects,并且
1.使用参数java -javaagent:/path/to/aspectjweaver.jar启动应用程序。
还有一种方法可以替代第二种方法,但是使用Java代理是最简单的。我不是Spring用户,所以我不是Spring配置Maven,但是即使像我这样的Spring新手也成功地使用了Java代理,所以请先给予一下。

6ojccjat

6ojccjat3#

TL; DR:如果AspectJ让你头疼,而且除了解决Spring Caching自调用之外,你并不真正需要它,那么添加一个简单的服务层可以重用的"缓存委托" bean实际上可能会更干净/更轻/更容易。没有额外的依赖关系,没有性能影响,也没有改变Spring代理默认工作方式的意外副作用。
代码:

@Component
public class CacheDelegateImpl implements CacheDelegate {
    @Override @Cacheable(cacheNames = { "things" })
    public Object getTheThing(String id) { ... }
}

@Service
public class ThingServiceImpl implements ThingService {
    @Resource
    private CacheDelegate cacheDelegate;

    public Object getTheThing(String id) {
        return cacheDelegate.getTheThing(id);
    }

    public Collection<Object> getAllTheThings() {
        return CollectionUtils.emptyIfNull(findAllTheIds())
                .parallelStream()
                .map(this::getTheThing)
                .collect(Collectors.toSet());
    }
}

我再补充一个答案,因为为了解决我自己的这个问题,我最终改变了方向。@kriegaex和我自己在前面提到了更直接的解决方案,但对于那些在根本不需要AspectJ时无法使用它的人来说,这是一个不同的选择。
对于我的项目来说,添加AspectJ只允许可缓存的相同bean引用是一个灾难,它导致了10个新的头痛问题,而不是一个简单的(但令人讨厌的)问题。
简要的非详尽纲要如下:

  • 引入多个新依赖项
  • 将复杂的POM插件引入到编译时编织(我从来没有正确使用过)或将运行时字节编织jar编组到正确的位置
  • 向所有部署添加运行时javaagent JVM参数
  • 在构建时或启动时(进行编织)的性能差得多
  • AspectJ在代码库的其他区域中拾取SpringTransactional注解并失败(否则我很乐意使用Spring代理)
  • Java版本控制问题
  • 某种程度上依赖于早期的Sun Microsystems tools.jar(在以后的OpenJDK版本中不存在)
  • 关于如何实现与Spring事务隔离的缓存用例和/或没有使用AspectJ的成熟AOP(我不需要/不想要)的文档通常很差且零散

最后,我又回到了Spring Caching by proxy,并引入了一个我的两个服务方法都可以引用的"缓存委托"。这个解决方案不是最好的,但对我来说,它比我在真正不需要AspectJ时跳过的所有AspectJ环都要好。我只想要无缝缓存和DRY服务代码,这正是这个解决方案所实现的。

9jyewag0

9jyewag04#

经过大量的研究,我得到了使用编译时编织、 Boot 和jdk 17的自调用缓存方法。
我在Spring配置中需要的唯一注解是:

@EnableCaching(mode = AdviceMode.ASPECTJ)

不需要@启用加载时间编织和@启用方面JAutoProxy。
这些是pom.xml的相关部分(dev.aspectj:aspectj-maven-plugin seams是唯一一个可以与最新java版本一起工作的部分):

<properties>
    <java.version>17</java.version>
    <maven.compiler.source>17</maven.compiler.source>
    <maven.compiler.target>17</maven.compiler.target>
    <maven.compiler.release>17</maven.compiler.release>
    <spring-boot.version>3.0.2</spring-boot.version>
    <spring.version>6.0.4</spring.version>
    <aspectj.version>1.9.19</aspectj.version>
</properties>
<build>
    <plugins>
        <plugin>
            <groupId>dev.aspectj</groupId>
            <artifactId>aspectj-maven-plugin</artifactId>
            <version>1.13.1</version>
            <configuration>
                <complianceLevel>${maven.compiler.target}</complianceLevel>
                <proc>none</proc>
                <showWeaveInfo>true</showWeaveInfo>
                <forceAjcCompile>true</forceAjcCompile>
                <aspectLibraries>
                    <aspectLibrary>
                        <groupId>org.springframework</groupId>
                        <artifactId>spring-aspects</artifactId>
                    </aspectLibrary>
                </aspectLibraries>
            </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>
            <executions>
                <execution>
                    <?m2e execute onConfiguration,onIncremental?>
                    <goals>
                        <goal>compile</goal>
                        <goal>test-compile</goal>
                    </goals>
                </execution>
            </executions>
        </plugin>
    </plugins>
</build>
<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-cache</artifactId>
        <version>${spring-boot.version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-aspects</artifactId>
        <version>${spring.version}</version>
    </dependency>
    <dependency>
        <groupId>org.aspectj</groupId>
        <artifactId>aspectjrt</artifactId>
        <version>${aspectj.version}</version>
    </dependency>
</dependencies>

相关问题