【发布时间】:2019-10-27 21:20:44
【问题描述】:
我正在尝试实现一个缓存,它保存来自特定业务方法调用的结果,然后每 30 分钟刷新一次。
我能够通过使用预定方法的单例 EJB 来实现这一点;但是,现在调用该业务方法的每个类都必须改为从公开缓存结果的单例中调用该方法。
我想避免这种行为并保持这些类中的代码原样,所以我想使用一个拦截器来拦截对该特定业务方法的每次调用,并从缓存单例中返回结果。
但是,由于单例调用被拦截的业务方法本身来缓存其结果,因此该解决方案使应用程序停止,因此拦截器拦截调用(原谅重复)并尝试返回暴露缓存的单例方法的结果值,而单例仍在等待对业务方法的调用继续进行。
最明显的解决方案是从拦截器中获取方法调用者,并检查它是否
类对应于单例的;如果是,则继续调用,否则从单例返回缓存的结果。但是,拦截器使用的InvocationContext 对象似乎没有公开任何方法来访问有关被拦截方法的调用者的信息。有没有其他方法可以访问调用者的类,或者有解决这个问题的方法?
这是我的单例类:
@Singleton
@Startup
public class TopAlbumsHolder {
private List<Album> topAlbums;
@Inject
private DataAgent dataAgent;
@PostConstruct
@Schedule(hour = "*", minute = "*/30", persistent = false)
private void populateCache() {
this.topAlbums = this.dataAgent.getTopAlbums();
}
@Lock(LockType.READ)
public List<Album> getTopAlbums() {
return this.topAlbums;
}
}
这是我的拦截器:
@Interceptor
@Cacheable(type = "topAlbums")
public class TopAlbumsInterceptor {
@Inject
private TopAlbumsHolder topAlbumsHolder;
@AroundInvoke
public Object interceptTopAlbumsCall(InvocationContext invocationContext) throws Exception {
// if the caller's class equals that of the cache singleton, then return invocationContext.proceed();
// otherwise:
return this.topAlbumsHolder.getTopAlbums();
}
}
注意@Cacheable注解是自定义拦截器绑定,而不是javax.persistence.Cacheable。
编辑:我这样修改了拦截器方法:
@AroundInvoke
public Object interceptTopAlbumsCall(InvocationContext invocationContext) throws Exception {
for (StackTraceElement stackTraceElement : Thread.currentThread().getStackTrace())
if (TopAlbumsHolder.class.getName().equals(stackTraceElement.getClassName()))
return invocationContext.proceed();
return this.topAlbumsHolder.getTopAlbums();
}
但我怀疑这是最干净的解决方案,我不知道它是否便携。
编辑 2: 如果不够清楚,我需要访问有关被拦截方法的 invoker 类的信息,而不是具有其方法的被调用类的信息截获;这就是为什么我要遍历堆栈跟踪以访问调用者的类,但我认为这不是一个优雅的解决方案,即使它有效。
【问题讨论】:
-
dataAgent.getTopAlbums()是你口中的拦截业务方法吗?能贴出截取的业务方法的代码吗?此外,您是否希望缓存每 30 分钟刷新一次,即使没有客户端感兴趣,还是希望在客户端请求且上次刷新时间 > 30 分钟时刷新缓存?
标签: java caching singleton ejb cdi