首页 > 解决方案 > 同一类中的 Spring @Cachable 方法(自调用,代理问题) - 解决它的最佳方法是什么?

问题描述

我正在尝试从同一个类中调用 @Cacheable 方法。

它没有用。因为:

在代理模式(默认)下,仅拦截通过代理传入的外部方法调用。这意味着自调用(实际上,目标对象中的一个方法调用目标对象的另一个方法)不会导致运行时的实际缓存,即使调用的方法标记有@Cacheable。在这种情况下考虑使用 aspectj 模式。此外,代理必须完全初始化以提供预期的行为,因此您不应在初始化代码(即@PostConstruct)中依赖此功能。

这意味着,@Cachable(也称为@Transactional)由 Spring AOP 中的代理类工作。同一类中的内部调用通过“this”而不是代理类进行调用。

为了解决这个问题,我应该通过代理或使用AspectJ(另一个AOP)调用一个方法。所以,我找到了 4 个解决方案。

你的选择是什么?为什么不推荐其他人?

请分享您的意见!

  1. 使用 AspectJ(另一个 AOP)
  2. 从 ApplicationContext 获取 Bean 并使用它
@Service
public class UserService implements Service {

    @Autowired
    private ApplicationContext applicationContext;

    private Service self;

    @PostConstruct
    private void init() {
        self = applicationContext.getBean(UserService.class);
    }
}
  1. 使用 @Resource 自动装配 //自 Spring 4.3
@Component
@CacheConfig(cacheNames = "SphereClientFactoryCache")
public class CacheableSphereClientFactoryImpl implements SphereClientFactory {

    /**
     * 1. Self-autowired reference to proxified bean of this class.
     */
    @Resource
    private SphereClientFactory self;

    @Override
    @Cacheable(sync = true)
    public SphereClient createSphereClient(@Nonnull TenantConfig tenantConfig) {
        // 2. call cached method using self-bean
        return self.createSphereClient(tenantConfig.getSphereClientConfig());
    }

    @Override
    @Cacheable(sync = true)
    public SphereClient createSphereClient(@Nonnull SphereClientConfig clientConfig) {
        return CtpClientConfigurationUtils.createSphereClient(clientConfig);
    }
}
  1. 将类的 Bean 范围设为“原型”而不是“单例”
@Service
@Scope(proxyMode = ScopedProxyMode.TARGET_CLASS)
class AService {

    private final AService _aService;

    @Autowired
    public AService(AService aService) {
        _aService = aService;
    }

    @Cacheable("employeeData")
    public List<EmployeeData> getEmployeeData(Date date){
        ..println("Cache is not being used");
        ...
    }

    public List<EmployeeEnrichedData> getEmployeeEnrichedData(Date date){
        List<EmployeeData> employeeData = _aService.getEmployeeData(date);
        ...
    }
}

我是春天的新手:)

实际上,我选择了第4种解决方案,但我觉得这不是一个好方法。因为我只需要通过代理调用缓存方法,它会做几个bean来实现它。

看完文章,我觉得AspectJ是最好的选择。看起来很酷,Spring推荐,很多人也推荐。

但是我不明白 AspectJ 的工作原理(我会学习),我也不知道为什么不推荐其他人。

参考

标签: springcachingaspectjehcacheself-invoking-function

解决方案


推荐阅读