HHH-12857 Avoid using a static ByteBuddy object
In the end, it does not change anything as the BytecodeProvider is still static but it helps as a general cleanup for the following commits.
This commit is contained in:
parent
c9de4b8ce7
commit
129530e464
|
@ -68,7 +68,7 @@ public class EnhancerImpl implements Enhancer {
|
|||
private static final CoreMessageLogger log = CoreLogging.messageLogger( Enhancer.class );
|
||||
|
||||
protected final ByteBuddyEnhancementContext enhancementContext;
|
||||
private final ByteBuddyState bytebuddy;
|
||||
private final ByteBuddyState byteBuddyState;
|
||||
|
||||
private final TypePool classPool;
|
||||
|
||||
|
@ -77,11 +77,11 @@ public class EnhancerImpl implements Enhancer {
|
|||
*
|
||||
* @param enhancementContext Describes the context in which enhancement will occur so as to give access
|
||||
* to contextual/environmental information.
|
||||
* @param bytebuddy refers to the ByteBuddy instance to use
|
||||
* @param byteBuddyState refers to the ByteBuddy instance to use
|
||||
*/
|
||||
public EnhancerImpl(final EnhancementContext enhancementContext, final ByteBuddyState bytebuddy) {
|
||||
public EnhancerImpl(final EnhancementContext enhancementContext, final ByteBuddyState byteBuddyState) {
|
||||
this.enhancementContext = new ByteBuddyEnhancementContext( enhancementContext );
|
||||
this.bytebuddy = bytebuddy;
|
||||
this.byteBuddyState = byteBuddyState;
|
||||
this.classPool = buildClassPool( this.enhancementContext );
|
||||
}
|
||||
|
||||
|
@ -103,7 +103,7 @@ public class EnhancerImpl implements Enhancer {
|
|||
try {
|
||||
final TypeDescription managedCtClass = classPool.describe( safeClassName ).resolve();
|
||||
DynamicType.Builder<?> builder = doEnhance(
|
||||
bytebuddy.getCurrentyByteBuddy().ignore( isDefaultFinalizer() ).redefine( managedCtClass, ClassFileLocator.Simple.of( safeClassName, originalBytes ) ),
|
||||
byteBuddyState.getCurrentByteBuddy().ignore( isDefaultFinalizer() ).redefine( managedCtClass, ClassFileLocator.Simple.of( safeClassName, originalBytes ) ),
|
||||
managedCtClass
|
||||
);
|
||||
if ( builder == null ) {
|
||||
|
|
|
@ -36,7 +36,7 @@ public class BasicProxyFactoryImpl implements BasicProxyFactory {
|
|||
|
||||
final Class<?> superClassOrMainInterface = superClass != null ? superClass : interfaces[0];
|
||||
|
||||
this.proxyClass = bytebuddy.getCurrentyByteBuddy()
|
||||
this.proxyClass = bytebuddy.getCurrentByteBuddy()
|
||||
.with( new NamingStrategy.SuffixingRandom( PROXY_NAMING_SUFFIX, new NamingStrategy.SuffixingRandom.BaseNameResolver.ForFixedValue( superClassOrMainInterface.getName() ) ) )
|
||||
.subclass( superClass == null ? Object.class : superClass, ConstructorStrategy.Default.DEFAULT_CONSTRUCTOR )
|
||||
.implement( interfaces == null ? NO_INTERFACES : interfaces )
|
||||
|
|
|
@ -31,42 +31,34 @@ public final class ByteBuddyState {
|
|||
|
||||
private static final CoreMessageLogger LOG = messageLogger( ByteBuddyProxyFactory.class );
|
||||
|
||||
/**
|
||||
* Ideally shouldn't be static but it has to until we can remove the
|
||||
* deprecated static methods.
|
||||
*/
|
||||
private static final ByteBuddy buddy = new ByteBuddy().with( TypeValidation.DISABLED );
|
||||
private final ByteBuddy byteBuddy;
|
||||
|
||||
/**
|
||||
* This currently needs to be static: the BytecodeProvider is a static field of Environment and
|
||||
* is being accessed from static methods.
|
||||
* It will be easier to maintain the cache and its state when it will no longer be static
|
||||
* in Hibernate ORM 6+.
|
||||
* Opted for WEAK keys to avoid leaking the classloader in case the SessionFactory isn't closed.
|
||||
* Avoiding Soft keys as they are prone to cause issues with unstable performance.
|
||||
*/
|
||||
private static final TypeCache<TypeCache.SimpleKey> CACHE = new TypeCache.WithInlineExpunction<TypeCache.SimpleKey>(
|
||||
TypeCache.Sort.WEAK );
|
||||
private final TypeCache<TypeCache.SimpleKey> typeCache;
|
||||
|
||||
private static final MethodHandles.Lookup LOOKUP = MethodHandles.lookup();
|
||||
|
||||
ByteBuddyState() {
|
||||
this.byteBuddy = new ByteBuddy().with( TypeValidation.DISABLED );
|
||||
this.typeCache = new TypeCache.WithInlineExpunction<TypeCache.SimpleKey>( TypeCache.Sort.WEAK );
|
||||
}
|
||||
|
||||
/**
|
||||
* Access to ByteBuddy. It's almost equivalent to creating a new ByteBuddy instance,
|
||||
* yet slightly preferrable so to be able to reuse the same instance.
|
||||
* @return
|
||||
*/
|
||||
public ByteBuddy getCurrentyByteBuddy() {
|
||||
return buddy;
|
||||
public ByteBuddy getCurrentByteBuddy() {
|
||||
return byteBuddy;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated as we should not need static access to this state.
|
||||
* This will be removed with no replacement.
|
||||
* It's actually likely that this whole class becomes unnecessary in the near future.
|
||||
*/
|
||||
@Deprecated
|
||||
public static TypeCache<TypeCache.SimpleKey> getCacheForProxies() {
|
||||
return CACHE;
|
||||
public TypeCache<TypeCache.SimpleKey> getCacheForProxies() {
|
||||
return typeCache;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -78,17 +70,7 @@ public final class ByteBuddyState {
|
|||
* of re-creating the small helpers should be negligible.
|
||||
*/
|
||||
void clearState() {
|
||||
CACHE.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated as we should not need static access to this state.
|
||||
* This will be removed with no replacement.
|
||||
* It's actually likely that this whole class becomes unnecessary in the near future.
|
||||
*/
|
||||
@Deprecated
|
||||
public static ByteBuddy getStaticByteBuddyInstance() {
|
||||
return buddy;
|
||||
typeCache.clear();
|
||||
}
|
||||
|
||||
public static ClassLoadingStrategy<ClassLoader> resolveClassLoadingStrategy(Class<?> originalClass) {
|
||||
|
|
|
@ -18,6 +18,7 @@ import org.hibernate.bytecode.enhance.spi.Enhancer;
|
|||
import org.hibernate.bytecode.spi.BytecodeProvider;
|
||||
import org.hibernate.bytecode.spi.ProxyFactoryFactory;
|
||||
import org.hibernate.bytecode.spi.ReflectionOptimizer;
|
||||
import org.hibernate.proxy.pojo.bytebuddy.ByteBuddyProxyHelper;
|
||||
|
||||
import net.bytebuddy.NamingStrategy;
|
||||
import net.bytebuddy.description.method.MethodDescription;
|
||||
|
@ -37,7 +38,6 @@ import net.bytebuddy.matcher.ElementMatchers;
|
|||
|
||||
public class BytecodeProviderImpl implements BytecodeProvider {
|
||||
|
||||
private static final ByteBuddyState bytebuddy = new ByteBuddyState();
|
||||
private static final String INSTANTIATOR_PROXY_NAMING_SUFFIX = "HibernateInstantiator";
|
||||
private static final String OPTIMIZER_PROXY_NAMING_SUFFIX = "HibernateAccessOptimizer";
|
||||
private static final ElementMatcher.Junction newInstanceMethodName = ElementMatchers.named( "newInstance" );
|
||||
|
@ -45,9 +45,18 @@ public class BytecodeProviderImpl implements BytecodeProvider {
|
|||
private static final ElementMatcher.Junction setPropertyValuesMethodName = ElementMatchers.named( "setPropertyValues" );
|
||||
private static final ElementMatcher.Junction getPropertyNamesMethodName = ElementMatchers.named( "getPropertyNames" );
|
||||
|
||||
private final ByteBuddyState byteBuddyState;
|
||||
|
||||
private final ByteBuddyProxyHelper byteBuddyProxyHelper;
|
||||
|
||||
public BytecodeProviderImpl() {
|
||||
this.byteBuddyState = new ByteBuddyState();
|
||||
this.byteBuddyProxyHelper = new ByteBuddyProxyHelper( byteBuddyState );
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProxyFactoryFactory getProxyFactoryFactory() {
|
||||
return new ProxyFactoryFactoryImpl( bytebuddy );
|
||||
return new ProxyFactoryFactoryImpl( byteBuddyState, byteBuddyProxyHelper );
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -61,7 +70,7 @@ public class BytecodeProviderImpl implements BytecodeProvider {
|
|||
// we only provide a fast class instantiator if the class can be instantiated
|
||||
final Constructor<?> constructor = findConstructor( clazz );
|
||||
|
||||
fastClass = bytebuddy.getCurrentyByteBuddy()
|
||||
fastClass = byteBuddyState.getCurrentByteBuddy()
|
||||
.with( new NamingStrategy.SuffixingRandom( INSTANTIATOR_PROXY_NAMING_SUFFIX,
|
||||
new NamingStrategy.SuffixingRandom.BaseNameResolver.ForFixedValue( clazz.getName() ) ) )
|
||||
.subclass( ReflectionOptimizer.InstantiationOptimizer.class )
|
||||
|
@ -79,7 +88,7 @@ public class BytecodeProviderImpl implements BytecodeProvider {
|
|||
final Method[] setters = new Method[setterNames.length];
|
||||
findAccessors( clazz, getterNames, setterNames, types, getters, setters );
|
||||
|
||||
final Class bulkAccessor = bytebuddy.getCurrentyByteBuddy()
|
||||
final Class bulkAccessor = byteBuddyState.getCurrentByteBuddy()
|
||||
.with( new NamingStrategy.SuffixingRandom( OPTIMIZER_PROXY_NAMING_SUFFIX,
|
||||
new NamingStrategy.SuffixingRandom.BaseNameResolver.ForFixedValue( clazz.getName() ) ) )
|
||||
.subclass( ReflectionOptimizer.AccessOptimizer.class )
|
||||
|
@ -104,6 +113,10 @@ public class BytecodeProviderImpl implements BytecodeProvider {
|
|||
}
|
||||
}
|
||||
|
||||
public ByteBuddyProxyHelper getByteBuddyProxyHelper() {
|
||||
return byteBuddyProxyHelper;
|
||||
}
|
||||
|
||||
private static class GetPropertyValues implements ByteCodeAppender {
|
||||
|
||||
private final Class clazz;
|
||||
|
@ -271,11 +284,12 @@ public class BytecodeProviderImpl implements BytecodeProvider {
|
|||
|
||||
@Override
|
||||
public Enhancer getEnhancer(EnhancementContext enhancementContext) {
|
||||
return new EnhancerImpl( enhancementContext, bytebuddy );
|
||||
return new EnhancerImpl( enhancementContext, byteBuddyState );
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resetCaches() {
|
||||
bytebuddy.clearState();
|
||||
byteBuddyState.clearState();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -11,22 +11,26 @@ import org.hibernate.bytecode.spi.ProxyFactoryFactory;
|
|||
import org.hibernate.engine.spi.SessionFactoryImplementor;
|
||||
import org.hibernate.proxy.ProxyFactory;
|
||||
import org.hibernate.proxy.pojo.bytebuddy.ByteBuddyProxyFactory;
|
||||
import org.hibernate.proxy.pojo.bytebuddy.ByteBuddyProxyHelper;
|
||||
|
||||
public class ProxyFactoryFactoryImpl implements ProxyFactoryFactory {
|
||||
|
||||
private final ByteBuddyState bytebuddy;
|
||||
private final ByteBuddyState byteBuddyState;
|
||||
|
||||
public ProxyFactoryFactoryImpl(ByteBuddyState bytebuddy) {
|
||||
this.bytebuddy = bytebuddy;
|
||||
private final ByteBuddyProxyHelper byteBuddyProxyHelper;
|
||||
|
||||
public ProxyFactoryFactoryImpl(ByteBuddyState byteBuddyState, ByteBuddyProxyHelper byteBuddyProxyHelper) {
|
||||
this.byteBuddyState = byteBuddyState;
|
||||
this.byteBuddyProxyHelper = byteBuddyProxyHelper;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProxyFactory buildProxyFactory(SessionFactoryImplementor sessionFactory) {
|
||||
return new ByteBuddyProxyFactory();
|
||||
return new ByteBuddyProxyFactory( byteBuddyProxyHelper );
|
||||
}
|
||||
|
||||
@Override
|
||||
public BasicProxyFactory buildBasicProxyFactory(Class superClass, Class[] interfaces) {
|
||||
return new BasicProxyFactoryImpl( superClass, interfaces, bytebuddy );
|
||||
return new BasicProxyFactoryImpl( superClass, interfaces, byteBuddyState );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,16 +6,13 @@
|
|||
*/
|
||||
package org.hibernate.proxy.pojo.bytebuddy;
|
||||
|
||||
import static org.hibernate.internal.CoreLogging.messageLogger;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.Locale;
|
||||
import java.util.Set;
|
||||
|
||||
import org.hibernate.HibernateException;
|
||||
import org.hibernate.bytecode.internal.bytebuddy.ByteBuddyState;
|
||||
import org.hibernate.cfg.Environment;
|
||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||
import org.hibernate.internal.CoreMessageLogger;
|
||||
|
@ -26,28 +23,11 @@ import org.hibernate.proxy.ProxyConfiguration;
|
|||
import org.hibernate.proxy.ProxyFactory;
|
||||
import org.hibernate.type.CompositeType;
|
||||
|
||||
import net.bytebuddy.NamingStrategy;
|
||||
import net.bytebuddy.TypeCache;
|
||||
import net.bytebuddy.description.modifier.Visibility;
|
||||
import net.bytebuddy.dynamic.scaffold.subclass.ConstructorStrategy;
|
||||
import net.bytebuddy.implementation.FieldAccessor;
|
||||
import net.bytebuddy.implementation.MethodDelegation;
|
||||
import net.bytebuddy.implementation.SuperMethodCall;
|
||||
import net.bytebuddy.implementation.bytecode.assign.Assigner;
|
||||
|
||||
import static net.bytebuddy.matcher.ElementMatchers.isFinalizer;
|
||||
import static net.bytebuddy.matcher.ElementMatchers.isSynthetic;
|
||||
import static net.bytebuddy.matcher.ElementMatchers.isVirtual;
|
||||
import static net.bytebuddy.matcher.ElementMatchers.nameStartsWith;
|
||||
import static net.bytebuddy.matcher.ElementMatchers.named;
|
||||
import static net.bytebuddy.matcher.ElementMatchers.not;
|
||||
import static net.bytebuddy.matcher.ElementMatchers.returns;
|
||||
import static org.hibernate.internal.CoreLogging.messageLogger;
|
||||
|
||||
public class ByteBuddyProxyFactory implements ProxyFactory, Serializable {
|
||||
|
||||
private static final CoreMessageLogger LOG = messageLogger( ByteBuddyProxyFactory.class );
|
||||
private static final String PROXY_NAMING_SUFFIX = Environment.useLegacyProxyClassnames() ? "HibernateProxy$" : "HibernateProxy";
|
||||
|
||||
private final ByteBuddyProxyHelper byteBuddyProxyHelper;
|
||||
|
||||
private Class persistentClass;
|
||||
private String entityName;
|
||||
|
@ -59,6 +39,10 @@ public class ByteBuddyProxyFactory implements ProxyFactory, Serializable {
|
|||
|
||||
private Class proxyClass;
|
||||
|
||||
public ByteBuddyProxyFactory(ByteBuddyProxyHelper byteBuddyProxyHelper) {
|
||||
this.byteBuddyProxyHelper = byteBuddyProxyHelper;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postInstantiate(
|
||||
String entityName,
|
||||
|
@ -75,7 +59,7 @@ public class ByteBuddyProxyFactory implements ProxyFactory, Serializable {
|
|||
this.componentIdType = componentIdType;
|
||||
this.overridesEquals = ReflectHelper.overridesEquals( persistentClass );
|
||||
|
||||
this.proxyClass = buildProxy( persistentClass, this.interfaces );
|
||||
this.proxyClass = byteBuddyProxyHelper.buildProxy( persistentClass, this.interfaces );
|
||||
}
|
||||
|
||||
private Class[] toArray(Set<Class> interfaces) {
|
||||
|
@ -86,35 +70,6 @@ public class ByteBuddyProxyFactory implements ProxyFactory, Serializable {
|
|||
return interfaces.toArray( new Class[interfaces.size()] );
|
||||
}
|
||||
|
||||
public static Class buildProxy(
|
||||
final Class persistentClass,
|
||||
final Class[] interfaces) {
|
||||
Set<Class<?>> key = new HashSet<Class<?>>();
|
||||
if ( interfaces.length == 1 ) {
|
||||
key.add( persistentClass );
|
||||
}
|
||||
key.addAll( Arrays.<Class<?>>asList( interfaces ) );
|
||||
|
||||
final TypeCache<TypeCache.SimpleKey> cacheForProxies = ByteBuddyState.getCacheForProxies();
|
||||
|
||||
return cacheForProxies.findOrInsert( persistentClass.getClassLoader(), new TypeCache.SimpleKey(key), () ->
|
||||
ByteBuddyState.getStaticByteBuddyInstance()
|
||||
.ignore( isSynthetic().and( named( "getMetaClass" ).and( returns( td -> "groovy.lang.MetaClass".equals( td.getName() ) ) ) ) )
|
||||
.with( new NamingStrategy.SuffixingRandom( PROXY_NAMING_SUFFIX, new NamingStrategy.SuffixingRandom.BaseNameResolver.ForFixedValue( persistentClass.getName() ) ) )
|
||||
.subclass( interfaces.length == 1 ? persistentClass : Object.class, ConstructorStrategy.Default.IMITATE_SUPER_CLASS_OPENING )
|
||||
.implement( (Type[]) interfaces )
|
||||
.method( isVirtual().and( not( isFinalizer() ) ) )
|
||||
.intercept( MethodDelegation.to( ProxyConfiguration.InterceptorDispatcher.class ) )
|
||||
.method( nameStartsWith( "$$_hibernate_" ).and( isVirtual() ) )
|
||||
.intercept( SuperMethodCall.INSTANCE )
|
||||
.defineField( ProxyConfiguration.INTERCEPTOR_FIELD_NAME, ProxyConfiguration.Interceptor.class, Visibility.PRIVATE )
|
||||
.implement( ProxyConfiguration.class )
|
||||
.intercept( FieldAccessor.ofField( ProxyConfiguration.INTERCEPTOR_FIELD_NAME ).withAssigner( Assigner.DEFAULT, Assigner.Typing.DYNAMIC ) )
|
||||
.make()
|
||||
.load( persistentClass.getClassLoader(), ByteBuddyState.resolveClassLoadingStrategy( persistentClass ) )
|
||||
.getLoaded(), cacheForProxies );
|
||||
}
|
||||
|
||||
@Override
|
||||
public HibernateProxy getProxy(
|
||||
Serializable id,
|
||||
|
@ -142,83 +97,4 @@ public class ByteBuddyProxyFactory implements ProxyFactory, Serializable {
|
|||
throw new HibernateException( LOG.bytecodeEnhancementFailed( entityName ), t );
|
||||
}
|
||||
}
|
||||
|
||||
public static HibernateProxy deserializeProxy(SerializableProxy serializableProxy) {
|
||||
final ByteBuddyInterceptor interceptor = new ByteBuddyInterceptor(
|
||||
serializableProxy.getEntityName(),
|
||||
serializableProxy.getPersistentClass(),
|
||||
serializableProxy.getInterfaces(),
|
||||
serializableProxy.getId(),
|
||||
resolveIdGetterMethod( serializableProxy ),
|
||||
resolveIdSetterMethod( serializableProxy ),
|
||||
serializableProxy.getComponentIdType(),
|
||||
null,
|
||||
ReflectHelper.overridesEquals( serializableProxy.getPersistentClass() )
|
||||
);
|
||||
|
||||
// note: interface is assumed to already contain HibernateProxy.class
|
||||
try {
|
||||
final Class proxyClass = buildProxy(
|
||||
serializableProxy.getPersistentClass(),
|
||||
serializableProxy.getInterfaces()
|
||||
);
|
||||
final HibernateProxy proxy = (HibernateProxy) proxyClass.newInstance();
|
||||
( (ProxyConfiguration) proxy ).$$_hibernate_set_interceptor( interceptor );
|
||||
return proxy;
|
||||
}
|
||||
catch (Throwable t) {
|
||||
final String message = LOG.bytecodeEnhancementFailed( serializableProxy.getEntityName() );
|
||||
LOG.error( message, t );
|
||||
throw new HibernateException( message, t );
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static Method resolveIdGetterMethod(SerializableProxy serializableProxy) {
|
||||
if ( serializableProxy.getIdentifierGetterMethodName() == null ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
return serializableProxy.getIdentifierGetterMethodClass().getDeclaredMethod( serializableProxy.getIdentifierGetterMethodName() );
|
||||
}
|
||||
catch (NoSuchMethodException e) {
|
||||
throw new HibernateException(
|
||||
String.format(
|
||||
Locale.ENGLISH,
|
||||
"Unable to deserialize proxy [%s, %s]; could not locate id getter method [%s] on entity class [%s]",
|
||||
serializableProxy.getEntityName(),
|
||||
serializableProxy.getId(),
|
||||
serializableProxy.getIdentifierGetterMethodName(),
|
||||
serializableProxy.getIdentifierGetterMethodClass()
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static Method resolveIdSetterMethod(SerializableProxy serializableProxy) {
|
||||
if ( serializableProxy.getIdentifierSetterMethodName() == null ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
return serializableProxy.getIdentifierSetterMethodClass().getDeclaredMethod(
|
||||
serializableProxy.getIdentifierSetterMethodName(),
|
||||
serializableProxy.getIdentifierSetterMethodParams()
|
||||
);
|
||||
}
|
||||
catch (NoSuchMethodException e) {
|
||||
throw new HibernateException(
|
||||
String.format(
|
||||
Locale.ENGLISH,
|
||||
"Unable to deserialize proxy [%s, %s]; could not locate id setter method [%s] on entity class [%s]",
|
||||
serializableProxy.getEntityName(),
|
||||
serializableProxy.getId(),
|
||||
serializableProxy.getIdentifierSetterMethodName(),
|
||||
serializableProxy.getIdentifierSetterMethodClass()
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,168 @@
|
|||
/*
|
||||
* Hibernate, Relational Persistence for Idiomatic Java
|
||||
*
|
||||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
||||
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
||||
*/
|
||||
package org.hibernate.proxy.pojo.bytebuddy;
|
||||
|
||||
import static net.bytebuddy.matcher.ElementMatchers.isFinalizer;
|
||||
import static net.bytebuddy.matcher.ElementMatchers.isSynthetic;
|
||||
import static net.bytebuddy.matcher.ElementMatchers.isVirtual;
|
||||
import static net.bytebuddy.matcher.ElementMatchers.nameStartsWith;
|
||||
import static net.bytebuddy.matcher.ElementMatchers.named;
|
||||
import static net.bytebuddy.matcher.ElementMatchers.not;
|
||||
import static net.bytebuddy.matcher.ElementMatchers.returns;
|
||||
import static org.hibernate.internal.CoreLogging.messageLogger;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.Serializable;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.Locale;
|
||||
import java.util.Set;
|
||||
|
||||
import org.hibernate.HibernateException;
|
||||
import org.hibernate.bytecode.internal.bytebuddy.ByteBuddyState;
|
||||
import org.hibernate.cfg.Environment;
|
||||
import org.hibernate.engine.spi.SharedSessionContractImplementor;
|
||||
import org.hibernate.internal.CoreMessageLogger;
|
||||
import org.hibernate.internal.util.ReflectHelper;
|
||||
import org.hibernate.internal.util.collections.ArrayHelper;
|
||||
import org.hibernate.proxy.HibernateProxy;
|
||||
import org.hibernate.proxy.ProxyConfiguration;
|
||||
import org.hibernate.proxy.ProxyFactory;
|
||||
import org.hibernate.type.CompositeType;
|
||||
|
||||
import net.bytebuddy.NamingStrategy;
|
||||
import net.bytebuddy.TypeCache;
|
||||
import net.bytebuddy.description.modifier.Visibility;
|
||||
import net.bytebuddy.dynamic.DynamicType.Unloaded;
|
||||
import net.bytebuddy.dynamic.scaffold.subclass.ConstructorStrategy;
|
||||
import net.bytebuddy.implementation.FieldAccessor;
|
||||
import net.bytebuddy.implementation.MethodDelegation;
|
||||
import net.bytebuddy.implementation.SuperMethodCall;
|
||||
import net.bytebuddy.implementation.bytecode.assign.Assigner;
|
||||
|
||||
public class ByteBuddyProxyHelper implements Serializable {
|
||||
|
||||
private static final CoreMessageLogger LOG = messageLogger( ByteBuddyProxyHelper.class );
|
||||
private static final String PROXY_NAMING_SUFFIX = Environment.useLegacyProxyClassnames() ? "HibernateProxy$" : "HibernateProxy";
|
||||
|
||||
private final ByteBuddyState byteBuddyState;
|
||||
|
||||
public ByteBuddyProxyHelper(ByteBuddyState byteBuddyState) {
|
||||
this.byteBuddyState = byteBuddyState;
|
||||
}
|
||||
|
||||
public Class buildProxy(
|
||||
final Class persistentClass,
|
||||
final Class[] interfaces) {
|
||||
Set<Class<?>> key = new HashSet<Class<?>>();
|
||||
if ( interfaces.length == 1 ) {
|
||||
key.add( persistentClass );
|
||||
}
|
||||
key.addAll( Arrays.<Class<?>>asList( interfaces ) );
|
||||
|
||||
final TypeCache<TypeCache.SimpleKey> cacheForProxies = byteBuddyState.getCacheForProxies();
|
||||
|
||||
return cacheForProxies.findOrInsert( persistentClass.getClassLoader(), new TypeCache.SimpleKey(key), () ->
|
||||
byteBuddyState.getCurrentByteBuddy()
|
||||
.ignore( isSynthetic().and( named( "getMetaClass" ).and( returns( td -> "groovy.lang.MetaClass".equals( td.getName() ) ) ) ) )
|
||||
.with( new NamingStrategy.SuffixingRandom( PROXY_NAMING_SUFFIX, new NamingStrategy.SuffixingRandom.BaseNameResolver.ForFixedValue( persistentClass.getName() ) ) )
|
||||
.subclass( interfaces.length == 1 ? persistentClass : Object.class, ConstructorStrategy.Default.IMITATE_SUPER_CLASS_OPENING )
|
||||
.implement( (Type[]) interfaces )
|
||||
.method( isVirtual().and( not( isFinalizer() ) ) )
|
||||
.intercept( MethodDelegation.to( ProxyConfiguration.InterceptorDispatcher.class ) )
|
||||
.method( nameStartsWith( "$$_hibernate_" ).and( isVirtual() ) )
|
||||
.intercept( SuperMethodCall.INSTANCE )
|
||||
.defineField( ProxyConfiguration.INTERCEPTOR_FIELD_NAME, ProxyConfiguration.Interceptor.class, Visibility.PRIVATE )
|
||||
.implement( ProxyConfiguration.class )
|
||||
.intercept( FieldAccessor.ofField( ProxyConfiguration.INTERCEPTOR_FIELD_NAME ).withAssigner( Assigner.DEFAULT, Assigner.Typing.DYNAMIC ) )
|
||||
.make()
|
||||
.load( persistentClass.getClassLoader(), ByteBuddyState.resolveClassLoadingStrategy( persistentClass ) )
|
||||
.getLoaded(), cacheForProxies );
|
||||
}
|
||||
|
||||
public HibernateProxy deserializeProxy(SerializableProxy serializableProxy) {
|
||||
final ByteBuddyInterceptor interceptor = new ByteBuddyInterceptor(
|
||||
serializableProxy.getEntityName(),
|
||||
serializableProxy.getPersistentClass(),
|
||||
serializableProxy.getInterfaces(),
|
||||
serializableProxy.getId(),
|
||||
resolveIdGetterMethod( serializableProxy ),
|
||||
resolveIdSetterMethod( serializableProxy ),
|
||||
serializableProxy.getComponentIdType(),
|
||||
null,
|
||||
ReflectHelper.overridesEquals( serializableProxy.getPersistentClass() )
|
||||
);
|
||||
|
||||
// note: interface is assumed to already contain HibernateProxy.class
|
||||
try {
|
||||
final Class proxyClass = buildProxy(
|
||||
serializableProxy.getPersistentClass(),
|
||||
serializableProxy.getInterfaces()
|
||||
);
|
||||
final HibernateProxy proxy = (HibernateProxy) proxyClass.newInstance();
|
||||
( (ProxyConfiguration) proxy ).$$_hibernate_set_interceptor( interceptor );
|
||||
return proxy;
|
||||
}
|
||||
catch (Throwable t) {
|
||||
final String message = LOG.bytecodeEnhancementFailed( serializableProxy.getEntityName() );
|
||||
LOG.error( message, t );
|
||||
throw new HibernateException( message, t );
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static Method resolveIdGetterMethod(SerializableProxy serializableProxy) {
|
||||
if ( serializableProxy.getIdentifierGetterMethodName() == null ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
return serializableProxy.getIdentifierGetterMethodClass().getDeclaredMethod( serializableProxy.getIdentifierGetterMethodName() );
|
||||
}
|
||||
catch (NoSuchMethodException e) {
|
||||
throw new HibernateException(
|
||||
String.format(
|
||||
Locale.ENGLISH,
|
||||
"Unable to deserialize proxy [%s, %s]; could not locate id getter method [%s] on entity class [%s]",
|
||||
serializableProxy.getEntityName(),
|
||||
serializableProxy.getId(),
|
||||
serializableProxy.getIdentifierGetterMethodName(),
|
||||
serializableProxy.getIdentifierGetterMethodClass()
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static Method resolveIdSetterMethod(SerializableProxy serializableProxy) {
|
||||
if ( serializableProxy.getIdentifierSetterMethodName() == null ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
return serializableProxy.getIdentifierSetterMethodClass().getDeclaredMethod(
|
||||
serializableProxy.getIdentifierSetterMethodName(),
|
||||
serializableProxy.getIdentifierSetterMethodParams()
|
||||
);
|
||||
}
|
||||
catch (NoSuchMethodException e) {
|
||||
throw new HibernateException(
|
||||
String.format(
|
||||
Locale.ENGLISH,
|
||||
"Unable to deserialize proxy [%s, %s]; could not locate id setter method [%s] on entity class [%s]",
|
||||
serializableProxy.getEntityName(),
|
||||
serializableProxy.getId(),
|
||||
serializableProxy.getIdentifierSetterMethodName(),
|
||||
serializableProxy.getIdentifierSetterMethodClass()
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -9,6 +9,11 @@ package org.hibernate.proxy.pojo.bytebuddy;
|
|||
import java.io.Serializable;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import org.hibernate.bytecode.internal.bytebuddy.BytecodeProviderImpl;
|
||||
import org.hibernate.bytecode.internal.bytebuddy.ProxyFactoryFactoryImpl;
|
||||
import org.hibernate.bytecode.spi.BytecodeProvider;
|
||||
import org.hibernate.bytecode.spi.ProxyFactoryFactory;
|
||||
import org.hibernate.cfg.Environment;
|
||||
import org.hibernate.proxy.AbstractSerializableProxy;
|
||||
import org.hibernate.proxy.HibernateProxy;
|
||||
import org.hibernate.type.CompositeType;
|
||||
|
@ -125,7 +130,12 @@ public final class SerializableProxy extends AbstractSerializableProxy {
|
|||
}
|
||||
|
||||
private Object readResolve() {
|
||||
HibernateProxy proxy = ByteBuddyProxyFactory.deserializeProxy( this );
|
||||
BytecodeProvider bytecodeProvider = Environment.getBytecodeProvider();
|
||||
if ( !( bytecodeProvider instanceof BytecodeProviderImpl ) ) {
|
||||
throw new IllegalStateException( "The bytecode provider is not ByteBuddy, unable to deserialize a ByteBuddy proxy." );
|
||||
}
|
||||
|
||||
HibernateProxy proxy = ( (BytecodeProviderImpl) bytecodeProvider ).getByteBuddyProxyHelper().deserializeProxy( this );
|
||||
afterDeserialization( (ByteBuddyInterceptor) proxy.getHibernateLazyInitializer() );
|
||||
return proxy;
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
||||
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
||||
*/
|
||||
package org.hibernate.test.proxy.bytebuddy;
|
||||
package org.hibernate.bytecode.internal.bytebuddy;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
|
@ -4,7 +4,7 @@
|
|||
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
|
||||
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
|
||||
*/
|
||||
package org.hibernate.test.bytecode.enhancement.bytebuddy;
|
||||
package org.hibernate.bytecode.internal.bytebuddy;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
Loading…
Reference in New Issue