我正在尝试实现一个缓存,该缓存保存来自特定业务方法调用的结果,然后每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。
编辑:i以这种方式修改了拦截器方法:
@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: (如果不够清楚),我需要访问有关被拦截方法的调用者类的信息,而不是被拦截的方法被调用的类的信息;这就是为什么我要遍历堆栈跟踪来访问调用者的类,但我认为这不是一个优雅的解决方案,即使它可以工作。
发布于 2019-11-01 14:13:15
对于您需要做的事情,我会说使用拦截器或装饰器。但是你的拦截器错了。首先,您缺少了基本部分,即对InvocationContext.proceed()的调用,该调用将调用转发到下一行拦截器(如果有)或方法调用本身。其次,您放置在那里的注入点非常具体,只有当您拦截这种类型的bean时,才能帮助您。通常,拦截器方法如下所示:
@AroundInvoke
Object intercept(InvocationContext ctx) throws Exception {
// do something before the invocation of the intercepted method
return ctx.proceed(); // this invoked next interceptor and ultimately the intercepted method
// do something after the invocation of the intercepted method
}此外,如果您想要有关被截获的bean的元数据信息,每个拦截器都可以为此注入一个特殊的内置bean。从元数据中,您可以收集关于当前正在拦截的bean的信息。下面是如何获得元数据的方法:
@Inject
@Intercepted
private Bean<?> bean;请注意,拦截器不知道它们拦截哪种类型,它可以是任何类型,因此通常需要在普通的Object上操作。如果您需要更具体的东西,CDI提供了一个装饰模式,基本上是一个类型感知的拦截器。它有一个特殊的注入点(一个委托),它允许您直接访问修饰的bean。它可能更适合您的场景,看看CDI规范的这一部分解释译码器。
发布于 2019-10-30 17:44:49
这是个误会。不将被拦截的对象注入拦截器,而是使用invocationContext。只需调用invocationContext.proceed(),就没有递归了。进程()的结果,您可以缓存。
发布于 2019-10-31 23:11:46
迭代堆栈跟踪以检查TopAlbumsHolder是否存在并不是一种好方法。
为了避免在从getTopAlbums()类调用DataAgent时调用拦截器,可以在DataAgent中直接指定调度程序,后者收集数据并将其推送到TopAlbumsHolder中。您可以采用另一种方法,但是您的要点是在没有参与代理的情况下直接在getTopAlbums() bean中调用DataAgent (在本例中,拦截器不适用)。
请注意缓存的数据应该是不可变的(集合及其对象)。
https://stackoverflow.com/questions/58583469
复制相似问题