Caching 如何使用实例转发器缓存插入指令的类?

Caching 如何使用实例转发器缓存插入指令的类?,caching,java-8,byte-buddy,vavr,Caching,Java 8,Byte Buddy,Vavr,用例是实现脏场跟踪器。为此,我有一个接口: public interface Dirtyable { String ID = "dirty"; Set<String> getDirty(); static <T> T wrap(final T delegate) { return DirtyableInterceptor.wrap(delegate, ReflectionUtils::getPropertyName);

用例是实现脏场跟踪器。为此,我有一个接口:

public interface Dirtyable {

    String ID = "dirty";

    Set<String> getDirty();

    static <T> T wrap(final T delegate) {
        return DirtyableInterceptor.wrap(delegate, ReflectionUtils::getPropertyName);
    }

    static <T> T wrap(final T delegate, final Function<Method, String> resolver) {
        return DirtyableInterceptor.wrap(delegate, resolver);
    }

}
从日志中可以看出,缓存未命中的指令时间从70-100ms下降到缓存命中的0-2ms

为了完整起见,这里是拦截器方法:

@RuntimeType
@SuppressWarnings("unused")
public Object intercept(final @Origin Method method, final @This Dirtyable dirtyable,
                        final @Pipe Function<Object, Object> pipe) throws Throwable {

    if (ReflectionUtils.isSetter(method)) {
        final String property = resolver.apply(method);
        dirtyable.getDirty().add(property);

        logger.debug("Intercepted setter [{}], resolved property " +
                "[{}] flagged as dirty.", method, property);
    }

    return pipe.apply(this.delegate);
}
@RuntimeType
@抑制警告(“未使用”)
公共对象拦截(final@Origin Method,final@This Dirtyable Dirtyable,
最终@管道功能(管道)可丢弃{
if(反射率直到isSetter(方法)){
最终字符串属性=resolver.apply(方法);
dirtyable.getDirty().add(属性);
debug(“截获的setter[{}],已解析属性”+
“[{}]标记为脏。”,方法,属性);
}
回流管。应用(此。委托);
}
此解决方案运行良好,除了缓存命中的
DirtyableInterceptor
始终相同,因此委托实例也相同


是否可以将转发器绑定到实例的供应商,以便截获的方法转发给它?如何做到这一点?

您可以通过使
拦截
方法
静态
来创建无状态拦截器。要访问对象的状态,请在子类上定义两个字段,您可以使用现在静态的拦截器中的
@FieldValue
注释来访问这些字段。您还需要使用
FieldAccessor
实现来读取值,而不是使用
FixedValue::reference
检测。您还需要使用
defineField
builder方法定义字段

您可以通过以下方式设置这些字段:

  • Dirtyable
    接口中添加setter方法,并使用
    FieldAccessor
    实现拦截它们
  • 定义向其提供值的显式构造函数。这还允许您将字段定义为
    final
    。要实现构造函数,首先需要调用超级构造函数,然后多次调用
    FieldAccessor
    来设置字段

  • 这样,您就创建了一个完全无状态的类,可以重用,但需要初始化。Byte Buddy已经提供了内置的
    类型缓存
    ,以便于重用。

    您设置了字段吗?也许你在设置器上应用了拦截器?在多次尝试失败后,我通过了解决方案1。它更详细,字段不能是最终的,但它可以工作!下面是我的解决方案2尝试:
    .defineConstructor()。带参数(delegateClass,Function.class)。截取(MethodCall.invoke(delegateClass.getConstructor())。然后(ofField(DELEGATE\u字段)。setArgumentat(0)。然后(ofField(RESOLVER\u字段)。setArgumentat(1)))
    对于解决方案1,我添加了matcher条件
    isDeclaredBy(delegateClass)
    以避免拦截为委托和解析程序添加的setter。同样在
    Class::newInstance
    之后,我通过反射调用setter,以避免将它们添加到
    Dirtyable
    接口。我想保持它干净:)
    private static <T> Try<Class<? extends T>> dirtyableFor(final T delegate,
                                                            final Class<T> clazz,
                                                            final Function<Method, String> resolver) {
    
        long start = System.currentTimeMillis();
    
        Try<Class<? extends T>> r = Try.of(() -> ofCheckedSupplier(() ->
                new ByteBuddy().subclass(clazz)
                        .defineField(Dirtyable.ID, Set.class, Visibility.PRIVATE)
                        .method(nameMatches("getDirty"))
                        .intercept(reference(new HashSet<>()))
                        .implement(Dirtyable.class)
                        .method(not(isDeclaredBy(Object.class))
                                .and(not(isAbstract()))
                                .and(isPublic()))
                        .intercept(withDefaultConfiguration()
                                .withBinders(Pipe.Binder.install(Function.class))
                                .to(new DirtyableInterceptor(delegate, resolver)))
                        .make().load(clazz.getClassLoader())
                        .getLoaded())
                .withCache(getCache())
                .decorate()
                .apply(clazz));
    
        System.out.println("Instrumentation time: " + (System.currentTimeMillis() - start));
    
        return r;
    }
    
    private static <T> Cache<Class<? super T>, Class<T>> getCache() {
    
        final CachingProvider provider = Caching.getCachingProvider();
        final CacheManager manager = provider.getCacheManager();
    
        final javax.cache.Cache<Class<? super T>, Class<T>> cache =
                manager.getCache(Dirtyable.ID);
    
        final Cache<Class<? super T>, Class<T>> dirtyCache = Cache.of(cache);
        dirtyCache.getEventStream().map(Object::toString).subscribe(logger::debug);
    
        return dirtyCache;
    }
    
    @RuntimeType
    @SuppressWarnings("unused")
    public Object intercept(final @Origin Method method, final @This Dirtyable dirtyable,
                            final @Pipe Function<Object, Object> pipe) throws Throwable {
    
        if (ReflectionUtils.isSetter(method)) {
            final String property = resolver.apply(method);
            dirtyable.getDirty().add(property);
    
            logger.debug("Intercepted setter [{}], resolved property " +
                    "[{}] flagged as dirty.", method, property);
        }
    
        return pipe.apply(this.delegate);
    }