Reputation: 6141
Is there an easy way to use Spring's @Cacheable
annotation with a non-singleton (e.g. session-scoped) bean and have the cache have the same scope as said bean?
Example:
import javax.inject.Inject;
import javax.inject.Named;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.context.annotation.Scope;
import org.springframework.security.core.context.SecurityContextHolder;
@Named
@Scope("session")
public class UserNameRetriever {
@Inject private UserDao userDao;
@Cacheable("userName")
public String getUserName() {
return userDao.getUserByLogin((String)SecurityContextHolder.getContext().getAuthentication().getPrincipal()).getName();
}
}
Ideally, UserNameRetriever.getUserName()
would fetch the username from the UserDao
once per session, but this code actually caches application-wide.
Upvotes: 8
Views: 13208
Reputation: 2867
You can create CacheManager bean with Session (or Request if you want only for a request) scope. It works perfectly fine.
@Bean
@SessionScope
public CacheManager requestCacheManager() {
return new ConcurrentMapCacheManager();
}
Consider limiting/setting bounds for you cache (as some ppl mentioned about memory 'leaks') So you can try using CaffeineCacheManager, where you can set maximum size, expire ttl etc.
Upvotes: 0
Reputation: 597106
If you want a session cache, use the session. That is, store the user in a private field in the session-scoped bean, or access the HttpSession
object directly. @Cacheable
is generally meant for application-wide resources.
Well, you can use key="#user.id"
, and to invalidate the cache (manually or with @CacheEvict
) on a @PreDestroy
method, but it would be better if you don't mix the two - caching and session data.
Upvotes: 5
Reputation: 20316
I haven't tried it, but according to Spring reference I think this would work:
@Cacheable(value = "userName", key = "#root.target")
Upvotes: 2
Reputation: 340733
I think you are approaching the problem from the wrong side. Instead of having "local" session-scoped caches storing only one value have one global cache storing a map from user name to User
values.
In your case drop @Cacheable
on getUserName()
and put it on UserDao
:
public class UserDao {
@Cacheable("users")
public User getUserByLogin(String user) {
//...
}
}
Not only this is more idiomatic usage of cache, but also it will prove to be easier to maintain and better performing.
Back to your original question: no, this is not possible out-of-the-box. You would have to write your own CacheManager
- probably a decorator around existing implementation.
Upvotes: 2