Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/hibernate/5.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java Hibernate中使用ByteBuddy代理的MethodHandler陷入了无休止的循环中_Java_Hibernate_Byte Buddy - Fatal编程技术网

Java Hibernate中使用ByteBuddy代理的MethodHandler陷入了无休止的循环中

Java Hibernate中使用ByteBuddy代理的MethodHandler陷入了无休止的循环中,java,hibernate,byte-buddy,Java,Hibernate,Byte Buddy,我目前正在迁移Hibernate的一个旧工具,以基于实体统计自动预取。旧的工具使用Hibernate3.1,因此有一些工作要做。传统上,Hibernate使用CGlib代理,但在我为最新版本的Hibernate开发时,我将代理生成更改为ByteBuddy 然而,自从更改为ByteBuddy之后,我在让MethodHandler工作方面遇到了一些问题。我的工具中的methodhandler应该处理对代理的所有调用,以便能够收集必要的统计信息。目前,我的方法处理程序如下所示: public clas

我目前正在迁移Hibernate的一个旧工具,以基于实体统计自动预取。旧的工具使用Hibernate3.1,因此有一些工作要做。传统上,Hibernate使用CGlib代理,但在我为最新版本的Hibernate开发时,我将代理生成更改为ByteBuddy

然而,自从更改为ByteBuddy之后,我在让MethodHandler工作方面遇到了一些问题。我的工具中的methodhandler应该处理对代理的所有调用,以便能够收集必要的统计信息。目前,我的方法处理程序如下所示:

public class EntityProxyMethodHandler implements ProxyConfiguration.Interceptor, Serializable {

private final EntityTracker entityTracker;
private final Object proxiedObject;
private final String proxiedClassName;

EntityProxyMethodHandler(
        Object proxiedObject,
        String proxiedClassName,
        Set<Property> persistentProperties,
        ExtentManager extentManager) {
    this.proxiedObject = proxiedObject;
    this.proxiedClassName = proxiedClassName;
    this.entityTracker = new EntityTracker(persistentProperties, extentManager );
}

@Override
@RuntimeType
public Object intercept(@This Object instance, @Origin Method method, @AllArguments Object[] arguments)
        throws Exception {
    final String methodName = method.getName();
    if ( "toString".equals( methodName ) ) {
        return proxiedClassName + "@" + System.identityHashCode( instance );
    }
    else if ( "equals".equals( methodName ) ) {
        return proxiedObject == instance;
    }
    else if ( "hashCode".equals( methodName ) ) {
        return System.identityHashCode( instance );
    }
    else if ( arguments.length == 0 ) {
        switch ( methodName ) {
            case "disableTracking": {
                boolean oldValue = entityTracker.isTracking();
                entityTracker.setTracking( false );
                return oldValue;
            }
            case "enableTracking": {
                boolean oldValue = entityTracker.isTracking();
                entityTracker.setTracking( true );
                return oldValue;
            }
            case "isAccessed":
                return entityTracker.isAccessed();

            default:
                break;
        }
    }
    else if ( arguments.length == 1 ) {
        if ( methodName.equals( "addTracker" ) && method.getParameterTypes()[0].equals( Statistics.class ) ) {
            entityTracker.addTracker( (Statistics) arguments[0] );
            return null;
        }
        else if ( methodName.equals( "addTrackers" ) && method.getParameterTypes()[0].equals( Set.class ) ) {
            @SuppressWarnings("unchecked")
            Set<Statistics> newTrackers = (Set) arguments[0];
            entityTracker.addTrackers( newTrackers );
            return null;
        }
        else if ( methodName
                .equals( "extendProfile" ) && method.getParameterTypes()[0].equals( Statistics.class ) ) {
            entityTracker.extendProfile( (Statistics) arguments[0], instance );
            return null;
        }
        else if ( methodName
                .equals( "removeTracker" ) && method.getParameterTypes()[0].equals( Statistics.class ) ) {
            entityTracker.removeTracker( (Statistics) arguments[0] );
            return null;
        }
    }
    entityTracker.trackAccess( instance );
    return method.invoke( instance, arguments ); // Gets stuck here, the method interception gets intercepted endlessly
}
public class EntityProxyFactory {
private static SessionFactoryImplementor sessionFactory = AutofetchIntegrator.getSessionFactory();
private static final ConcurrentMap<Class<?>, Constructor<?>> entityConstructorMap = new ConcurrentHashMap<>();

private static <T> Constructor<T> getDefaultConstructor(Class<T> clazz) throws NoSuchMethodException {
    Constructor<T> constructor = clazz.getDeclaredConstructor();
    if ( !constructor.isAccessible() ) {
        constructor.setAccessible( true );
    }

    return constructor;
}

static Object getProxyInstance(
        Class persistentClass,
        Set<Property> persistentProperties,
        ExtentManager extentManager)
        throws NoSuchMethodException, IllegalAccessException, InstantiationException, InvocationTargetException {

    if ( Modifier.isFinal( persistentClass.getModifiers() ) ) {
        // Use the default constructor, because final classes cannot be inherited.
        return useDefaultConstructor( persistentClass );
    }
    final ProxyConfiguration proxy = (ProxyConfiguration) Environment.getBytecodeProvider()
            .getProxyFactoryFactory()
            .buildBasicProxyFactory( persistentClass, new Class[] { TrackableEntity.class } )
            .getProxy();
    proxy.$$_hibernate_set_interceptor( new EntityProxyMethodHandler(proxy,persistentClass.getName(),
            persistentProperties,
            extentManager
    ) );
    return proxy;
}

private static Object useDefaultConstructor(Class<?> clazz)
        throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
    if ( !entityConstructorMap.containsKey( clazz ) ) {
        entityConstructorMap.put( clazz, getDefaultConstructor( clazz ) );
    }

    final Constructor<?> c = entityConstructorMap.get( clazz );
    return c.newInstance();
}
出于某种原因,它不调用真正的实体,而是调用代理本身,陷入了无尽的拦截循环。我不知道如何解决这个问题,我试图找到一种方法来调用方法处理程序中的“目标”,但找不到任何东西,因为出于某种原因,代理工厂生成的代理没有实现HibernateProxy接口。也许这是ByteBuddy有一些方法可以解决的问题,但是由于我对框架缺乏经验,我还没有找到一种方法

而且,在我以前使用Javassist代理时,它工作得非常完美。然后我上了类似的课:

public class EntityProxyFactory {

private static final CoreMessageLogger LOG = CoreLogging.messageLogger(AutofetchLazyInitializer.class);

private static final MethodFilter FINALIZE_FILTER = new MethodFilter() {
    @Override
    public boolean isHandled(Method m) {
        // skip finalize methods
        return !(m.getParameterTypes().length == 0 && m.getName().equals("finalize"));
    }
};

private static final ConcurrentMap<Class<?>, Class<?>> entityFactoryMap = new ConcurrentHashMap<>();

private static final ConcurrentMap<Class<?>, Constructor<?>> entityConstructorMap = new ConcurrentHashMap<>();

//if entityFactoryMap doesnt contain that specific class, add it
private static Class<?> getProxyFactory(Class<?> persistentClass, String idMethodName) {
    // Not sure how enhancer work, but it seems like you tell enhancer what type of subclasses that can be created, and all the method calls will be intercepted by entitycallbackfilter
    if (!entityFactoryMap.containsKey(persistentClass)) {
        ProxyFactory factory = new ProxyFactory();
        factory.setSuperclass(persistentClass);
        factory.setInterfaces(new Class[]{TrackableEntity.class});
        factory.setFilter(FINALIZE_FILTER);
        entityFactoryMap.putIfAbsent(persistentClass, factory.createClass());
    }

    return entityFactoryMap.get(persistentClass);
}

private static <T> Constructor<T> getDefaultConstructor(Class<T> clazz) throws NoSuchMethodException {
    Constructor<T> constructor = clazz.getDeclaredConstructor();
    if (!constructor.isAccessible()) {
        constructor.setAccessible(true);
    }

    return constructor;
}

public static Object getProxyInstance(Class persistentClass, String idMethodName, Set<Property> persistentProperties,
                                      ExtentManager extentManager)
        throws InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {

    if (Modifier.isFinal(persistentClass.getModifiers())) {
        // Use the default constructor, because final classes cannot be inherited.
        return useDefaultConstructor(persistentClass);
    }

    Class<?> factory = getProxyFactory(persistentClass, idMethodName);
    try {
        final Object proxy = factory.newInstance();
        ((Proxy) proxy).setHandler(new EntityProxyMethodHandler(persistentProperties, extentManager));
        return proxy;
    } catch (IllegalAccessException | InstantiationException e) {
        return useDefaultConstructor(persistentClass);
    }
}

private static Object useDefaultConstructor(Class<?> clazz) throws NoSuchMethodException, InstantiationException,
        InvocationTargetException, IllegalAccessException {

    if (!entityConstructorMap.containsKey(clazz)) {
        entityConstructorMap.put(clazz, getDefaultConstructor(clazz));
    }

    final Constructor<?> c = entityConstructorMap.get(clazz);

    return c.newInstance((Object[]) null);
}

如果要调用原始方法,可以使用
@SuperMethod method m
@SuperCall Callable c
获得它


使用可调用代理是开销最小的方法,这样做,您就不需要掌握参数数组。

谢谢您的建议。我尝试将拦截方法更改为以下内容:
public Object intercept(@This Object instance、@SuperMethod method、@AllArguments Object[]arguments)
但是,我得到的结果与之前完全相同。我无法更改为尝试
@SuperCall Callable c
,因为我正在从ProxyConfiguration.Interceptor实现拦截方法。还有什么问题吗?你有溢出的堆栈跟踪吗?我在第一篇文章中添加了stacktrace。我尝试了一些不同的注释,并在截取方法中添加了参数,但没有一个会改变结果。这似乎很奇怪,所以我开始试验我自己的BasicProxyFactoryImpl实现,试图改变代理的行为。首先,你知道为什么@Supermethod和其他注释在我的例子中没有什么不同吗?第二,您对我在ProxyFactory的实现中可能进行的更改有什么想法吗?谢谢你的帮助!
public class EntityProxyFactory {

private static final CoreMessageLogger LOG = CoreLogging.messageLogger(AutofetchLazyInitializer.class);

private static final MethodFilter FINALIZE_FILTER = new MethodFilter() {
    @Override
    public boolean isHandled(Method m) {
        // skip finalize methods
        return !(m.getParameterTypes().length == 0 && m.getName().equals("finalize"));
    }
};

private static final ConcurrentMap<Class<?>, Class<?>> entityFactoryMap = new ConcurrentHashMap<>();

private static final ConcurrentMap<Class<?>, Constructor<?>> entityConstructorMap = new ConcurrentHashMap<>();

//if entityFactoryMap doesnt contain that specific class, add it
private static Class<?> getProxyFactory(Class<?> persistentClass, String idMethodName) {
    // Not sure how enhancer work, but it seems like you tell enhancer what type of subclasses that can be created, and all the method calls will be intercepted by entitycallbackfilter
    if (!entityFactoryMap.containsKey(persistentClass)) {
        ProxyFactory factory = new ProxyFactory();
        factory.setSuperclass(persistentClass);
        factory.setInterfaces(new Class[]{TrackableEntity.class});
        factory.setFilter(FINALIZE_FILTER);
        entityFactoryMap.putIfAbsent(persistentClass, factory.createClass());
    }

    return entityFactoryMap.get(persistentClass);
}

private static <T> Constructor<T> getDefaultConstructor(Class<T> clazz) throws NoSuchMethodException {
    Constructor<T> constructor = clazz.getDeclaredConstructor();
    if (!constructor.isAccessible()) {
        constructor.setAccessible(true);
    }

    return constructor;
}

public static Object getProxyInstance(Class persistentClass, String idMethodName, Set<Property> persistentProperties,
                                      ExtentManager extentManager)
        throws InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {

    if (Modifier.isFinal(persistentClass.getModifiers())) {
        // Use the default constructor, because final classes cannot be inherited.
        return useDefaultConstructor(persistentClass);
    }

    Class<?> factory = getProxyFactory(persistentClass, idMethodName);
    try {
        final Object proxy = factory.newInstance();
        ((Proxy) proxy).setHandler(new EntityProxyMethodHandler(persistentProperties, extentManager));
        return proxy;
    } catch (IllegalAccessException | InstantiationException e) {
        return useDefaultConstructor(persistentClass);
    }
}

private static Object useDefaultConstructor(Class<?> clazz) throws NoSuchMethodException, InstantiationException,
        InvocationTargetException, IllegalAccessException {

    if (!entityConstructorMap.containsKey(clazz)) {
        entityConstructorMap.put(clazz, getDefaultConstructor(clazz));
    }

    final Constructor<?> c = entityConstructorMap.get(clazz);

    return c.newInstance((Object[]) null);
}
public class EntityProxyMethodHandler implements MethodHandler, Serializable {

private final EntityTracker entityTracker;

public EntityProxyMethodHandler(Set<Property> persistentProperties, ExtentManager extentManager) {
    this.entityTracker = new EntityTracker(persistentProperties, extentManager);
}

@Override
public Object invoke(Object obj, Method thisMethod, Method proceed, Object[] args) throws Throwable {
    if (args.length == 0) {
        if (thisMethod.getName().equals("disableTracking")) {
            boolean oldValue = entityTracker.isTracking();
            entityTracker.setTracking(false);
            return oldValue;
        } else if (thisMethod.getName().equals("enableTracking")) {
            boolean oldValue = entityTracker.isTracking();
            entityTracker.setTracking(true);
            return oldValue;
        } else if (thisMethod.getName().equals("isAccessed")) {
            return entityTracker.isAccessed();
        }
    } else if (args.length == 1) {
        if (thisMethod.getName().equals("addTracker") && thisMethod.getParameterTypes()[0].equals(Statistics.class)) {
            entityTracker.addTracker((Statistics) args[0]);
            return null;
        } else if (thisMethod.getName().equals("addTrackers") && thisMethod.getParameterTypes()[0].equals(Set.class)) {
            @SuppressWarnings("unchecked")
            Set<Statistics> newTrackers = (Set) args[0];
            entityTracker.addTrackers(newTrackers);
            return null;
        } else if (thisMethod.getName().equals("extendProfile") && thisMethod.getParameterTypes()[0].equals(Statistics.class)) {
            entityTracker.extendProfile((Statistics) args[0], obj);
            return null;
        } else if (thisMethod.getName().equals("removeTracker") && thisMethod.getParameterTypes()[0].equals(Statistics.class)) {
            entityTracker.removeTracker((Statistics) args[0]);
            return null;
        }
    }

    entityTracker.trackAccess(obj);

    return proceed.invoke(obj, args);
}
    java.lang.reflect.InvocationTargetException
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.autofetch.hibernate.EntityProxyMethodHandler.intercept(EntityProxyMethodHandler.java:105)
    at org.autofetch.test.Employee$HibernateBasicProxy$dbvGRz97.getSupervisor(Unknown Source)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.autofetch.hibernate.AutofetchLazyInitializer.intercept(AutofetchLazyInitializer.java:123)
    at org.hibernate.proxy.ProxyConfiguration$InterceptorDispatcher.intercept(ProxyConfiguration.java:95)
    at org.autofetch.test.Employee$HibernateProxy$PajATc2c.getSupervisor(Unknown Source)
    at org.autofetch.test.ExtentTest.secondLevelSupervisorAccess(ExtentTest.java:533)
    at org.autofetch.test.ExtentTest.testSecondLevelSupervisorAccess(ExtentTest.java:246)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
    at org.hibernate.testing.junit4.ExtendedFrameworkMethod.invokeExplosively(ExtendedFrameworkMethod.java:45)
    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
    at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
    at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:27)
    at org.junit.internal.runners.statements.FailOnTimeout$CallableStatement.call(FailOnTimeout.java:298)
    at org.junit.internal.runners.statements.FailOnTimeout$CallableStatement.call(FailOnTimeout.java:292)
    at java.util.concurrent.FutureTask.run(FutureTask.java:266)
    at java.lang.Thread.run(Thread.java:748)
Caused by: java.lang.reflect.InvocationTargetException
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.autofetch.hibernate.EntityProxyMethodHandler.intercept(EntityProxyMethodHandler.java:105)
    at org.autofetch.test.Employee$HibernateBasicProxy$dbvGRz97.getSupervisor(Unknown Source)
    ... 30 more