Spring @Cachable method within the same class (self-invocation, proxy issue) - What is the best way to solve it?
Asked Answered
M

0

9

I'm trying to call a @Cacheable method from within the same class.

And it didn't work. Because of:

In proxy mode (the default), only external method calls coming in through the proxy are intercepted. This means that self-invocation (in effect, a method within the target object that calls another method of the target object) does not lead to actual caching at runtime even if the invoked method is marked with @Cacheable. Consider using the aspectj mode in this case. Also, the proxy must be fully initialized to provide the expected behavior, so you should not rely on this feature in your initialization code (that is, @PostConstruct).

It means, @Cachable(also @Transactional) works by proxy classes which is Spring AOP in. a internal call in the same class make call by 'this' instead of proxy classes.

To solve the problem, I should call a method by proxy or using AspectJ(another AOP). So, I found 4 solutions.

What is your choice? and why others are not recommended?

Please, share your opinion!

  1. using AspectJ (another AOP)
  2. get the Bean from ApplicationContext and use it
@Service
public class UserService implements Service {

    @Autowired
    private ApplicationContext applicationContext;

    private Service self;

    @PostConstruct
    private void init() {
        self = applicationContext.getBean(UserService.class);
    }
}
  1. self-autowiring using @Resource //since 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. make the Bean scope of the class as 'prototype' instead of 'singleton'
@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);
        ...
    }
}

I'm a newbie in spring :)

Actually, I choose the 4th solution, but I felt it isn't a good way. because I just need to call the caching method by proxy, and it make several beans to achieve it.

After reading articles, I think AspectJ is the best choice. It looks cool, Spring recommends it, and many people also recommend too.

But I don't understand how to AspectJ works (I will study) and I also don't know why others is not recommended.

references

Mouthpiece answered 13/7, 2020 at 7:28 Comment(6)
Firstly: welcome to SO! You basically answered your question already, right? I also switched to AspectJ on all my projects due to the proxy issue you described. A pretty good detailed wrap up on AspectJ can be read here: https://mcmap.net/q/923187/-how-does-aspectj-work :-)Thain
Thank you for the first comment :) I also wonder AspectJ is still good to a small project? your project is may bigger then mine (my project has only 4 tables in db)Mouthpiece
There are no disadvantages in using AspectJ in smaller projects. I'd always go for it, regardless of the size! :)Thain
Welcome to SO. Please note that opinion-based questions are considered off-topic here.Matt
@Matt sorry, should I move the post to somewhere?Mouthpiece
On SO you can ask questions if you have concrete technical problems implementing your chosen solution. A user poll like "which way do you like best?" does not fit a Q/A platform like SO, rather a discussion forum. Don't get me wrong, the question is interesting and well-researched. It is just off-topic. And usually the answer to such questions is "it depends" anyway. Given a certain setup or situation, maybe one or the other way is better under the circumstances. You are not asking a question to which a single correct answer exists that you could accept in order to close the question.Matt

© 2022 - 2024 — McMap. All rights reserved.