首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >Spring -如何使用aspectJ缓存自调用?

Spring -如何使用aspectJ缓存自调用?
EN

Stack Overflow用户
提问于 2020-07-16 09:59:49
回答 3查看 2.2K关注 0票数 3

谢谢你点击我的问题。我想在自调用中调用一个缓存方法,所以我需要使用AspectJ。(缓存的配置没有问题)

  1. 添加AspectJ依赖项

代码语言:javascript
运行
复制
implementation 'org.springframework.boot:spring-boot-starter-aop'

在application.java中添加@EnableCaching(= AdviceMode.ASPECTJ)

代码语言:javascript
运行
复制
@EnableJpaAuditing
@EnableCaching(mode = AdviceMode.ASPECTJ) // <-- here 
@SpringBootApplication
public class DoctorAnswerApplication {

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

}

我的service.java

代码语言:javascript
运行
复制
@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. my测试代码

代码语言:javascript
运行
复制
    @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 (代理)。

EN

回答 3

Stack Overflow用户

发布于 2020-07-16 12:51:23

你读过Javadoc for EnableCaching吗?

注意,如果mode()被设置为AdviceMode.ASPECTJ,那么proxyTargetClass()属性值将被忽略。还请注意,在本例中,spring-aspects模块JAR必须出现在类路径上,编译时编织或加载时编织将方面应用于受影响的类。在这种情况下不涉及代理;本地呼叫也将被截获。

所以请查查你

类路径上有java -javaagent:/path/to/aspectjweaver.jar.,

  • 使用参数spring-aspects启动应用程序

除了#2,还有一个替代方案,但使用Java代理是最简单的。我不是Spring用户,所以我不是Spring配置方面的专家,但即使像我这样的Spring也成功地使用了Java代理,所以请先试一试。

票数 3
EN

Stack Overflow用户

发布于 2020-11-24 21:38:29

感谢@kriegaex,他通过指出spring-方面的依赖关系和加载时编织javaagent的需求,修正了我的观点。为了方便其他人,Spring和Maven的配置片段如下。

(注:最后,我不觉得这一切(和副作用)值得我的项目。看看我的另一个答案,一个简单的,如果有点丑陋,解决办法。)

POM:

代码语言:javascript
运行
复制
    <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>

应用程序Config:

代码语言:javascript
运行
复制
@Configuration
@EnableCaching(mode = AdviceMode.ASPECTJ)
public class ApplicationConfig { ... }

目标方法:

代码语言:javascript
运行
复制
@Cacheable(cacheNames = { "cache-name" })
public Thingy fetchThingy(String identifier) { ... }

编织机构:

选项1:加载时间编织(Spring默认值)

使用javaagent参数或向servlet容器库添加

代码语言:javascript
运行
复制
-javaagent:<path-to-jar>/aspectjweaver-<version>.jar

选项2:编译时编织

(据推测,这是可行的,但我发现缺乏用于Spring缓存的连贯示例-请参阅下面的进一步阅读)

使用aspectj plugin:https://www.mojohaus.org/aspectj-maven-plugin/index.html

代码语言:javascript
运行
复制
<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>

为了便于参考/搜索,下面是启动所有这些的错误:

代码语言:javascript
运行
复制
Caused by: java.io.FileNotFoundException: class path resource [org/springframework/cache/aspectj/AspectJCachingConfiguration.class] cannot be opened because it does not exist

更多阅读:

https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#aop

票数 3
EN

Stack Overflow用户

发布于 2020-11-30 18:35:51

TL;DR:如果AspectJ给您带来了麻烦,而您除了使用Spring缓存自调用之外,并不真正需要它,那么添加一个简单的“缓存委托”bean可能会更干净/更轻/更容易,这样您的服务层就可以重用它了。没有额外的依赖关系,没有性能影响,也没有意外的副作用来改变spring代理的默认工作方式。

代码:

代码语言:javascript
运行
复制
@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个新的麻烦,而不是一个简单的(但令人讨厌的)问题。

一个简短的、并非详尽无遗的概述是:

dependencies

  • Introduction

  • 引入了多个新的复杂POM插件的weaving)

  • AspectJ,要么编译时编织(这对我从来都不太合适)要么将运行时字节编织jar编组到正确的位置

  • ,在构建时或开始时为我们的所有deployments

  • Much添加一个运行时javaagent JVM参数(在代码库的其他区域执行weaving)

  • AspectJ拾取和失败Spring事务注释(否则,我很乐意使用Spring proxies)

  • Java版本控制问题H114< )。versions)

对古代太阳微系统tools.jar的依赖(在后来的OpenJDK tools.jar中不存在)

  • 通常都很糟糕,而且对如何在与Spring事务隔离的情况下实现缓存用例和/或没有使用AspectJ (我不需要/不想要的)

的完整AOP的问题进行了讨论。

最后,我通过代理返回Spring缓存,并引入了我的两个服务方法都可以引用的“缓存委托”。这个解决方案并不是最漂亮的,但对我来说,比我在真正不需要AspectJ时跳过的所有AspectJ箍都更好。我只想要无缝缓存和干服务代码,这个解决方案实现了这一点。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/62932146

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档