Enable serialization of proxied entity objects. Enable the use of default implementation for getter methods to supply default values when the map contains a 'null' (or doesn't contain that key).
This commit is contained in:
parent
89303f9179
commit
1c68ebdcb8
2 changed files with 35 additions and 21 deletions
2
pom.xml
2
pom.xml
|
@ -5,7 +5,7 @@
|
||||||
<modelVersion>4.0.0</modelVersion>
|
<modelVersion>4.0.0</modelVersion>
|
||||||
<groupId>net.helenus</groupId>
|
<groupId>net.helenus</groupId>
|
||||||
<artifactId>helenus-core</artifactId>
|
<artifactId>helenus-core</artifactId>
|
||||||
<version>2.0.17-SNAPSHOT</version>
|
<version>2.0.19-SNAPSHOT</version>
|
||||||
<packaging>jar</packaging>
|
<packaging>jar</packaging>
|
||||||
|
|
||||||
<name>helenus</name>
|
<name>helenus</name>
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
*/
|
*/
|
||||||
package net.helenus.core.reflect;
|
package net.helenus.core.reflect;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
import java.lang.invoke.MethodHandle;
|
import java.lang.invoke.MethodHandle;
|
||||||
import java.lang.invoke.MethodHandles;
|
import java.lang.invoke.MethodHandles;
|
||||||
import java.lang.reflect.Constructor;
|
import java.lang.reflect.Constructor;
|
||||||
|
@ -24,9 +25,11 @@ import java.lang.reflect.Proxy;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
import net.helenus.mapping.annotation.Transient;
|
||||||
import net.helenus.support.HelenusException;
|
import net.helenus.support.HelenusException;
|
||||||
|
|
||||||
public class MapperInvocationHandler<E> implements InvocationHandler {
|
public class MapperInvocationHandler<E> implements InvocationHandler, Serializable {
|
||||||
|
private static final long serialVersionUID = -7044209982830584984L;
|
||||||
|
|
||||||
private final Map<String, Object> src;
|
private final Map<String, Object> src;
|
||||||
private final Class<E> iface;
|
private final Class<E> iface;
|
||||||
|
@ -36,25 +39,31 @@ public class MapperInvocationHandler<E> implements InvocationHandler {
|
||||||
this.iface = iface;
|
this.iface = iface;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Object invokeDefault(Object proxy, Method method, Object[] args) throws Throwable {
|
||||||
|
// NOTE: This is reflection magic to invoke (non-recursively) a default method implemented on an interface
|
||||||
|
// that we've proxied (in ReflectionDslInstantiator). I found the answer in this article.
|
||||||
|
// https://zeroturnaround.com/rebellabs/recognize-and-conquer-java-proxies-default-methods-and-method-handles/
|
||||||
|
|
||||||
|
// First, we need an instance of a private inner-class found in MethodHandles.
|
||||||
|
Constructor<MethodHandles.Lookup> constructor = MethodHandles.Lookup.class.getDeclaredConstructor(Class.class,
|
||||||
|
int.class);
|
||||||
|
constructor.setAccessible(true);
|
||||||
|
|
||||||
|
// Now we need to lookup and invoke special the default method on the interface class.
|
||||||
|
final Class<?> declaringClass = method.getDeclaringClass();
|
||||||
|
Object result = constructor.newInstance(declaringClass, MethodHandles.Lookup.PRIVATE)
|
||||||
|
.unreflectSpecial(method, declaringClass)
|
||||||
|
.bindTo(proxy)
|
||||||
|
.invokeWithArguments(args);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
|
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
|
||||||
|
|
||||||
if (method.isDefault()) {
|
// Transient, default methods should simply be invoked as-is.
|
||||||
// NOTE: This is reflection magic to invoke (non-recursively) a default method implemented on an interface
|
if (method.isDefault() && method.getDeclaredAnnotation(Transient.class) == null) {
|
||||||
// that we've proxied (in ReflectionDslInstantiator). I found the answer in this article.
|
return invokeDefault(proxy, method, args);
|
||||||
// https://zeroturnaround.com/rebellabs/recognize-and-conquer-java-proxies-default-methods-and-method-handles/
|
|
||||||
|
|
||||||
// First, we need an instance of a private inner-class found in MethodHandles.
|
|
||||||
Constructor<MethodHandles.Lookup> constructor = MethodHandles.Lookup.class.getDeclaredConstructor(Class.class, int.class);
|
|
||||||
constructor.setAccessible(true);
|
|
||||||
|
|
||||||
// Now we need to lookup and invoke special the default method on the interface class.
|
|
||||||
final Class<?> declaringClass = method.getDeclaringClass();
|
|
||||||
Object result = constructor.newInstance(declaringClass, MethodHandles.Lookup.PRIVATE)
|
|
||||||
.unreflectSpecial(method, declaringClass)
|
|
||||||
.bindTo(proxy)
|
|
||||||
.invokeWithArguments(args);
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
String methodName = method.getName();
|
String methodName = method.getName();
|
||||||
|
@ -92,15 +101,20 @@ public class MapperInvocationHandler<E> implements InvocationHandler {
|
||||||
|
|
||||||
if (value == null) {
|
if (value == null) {
|
||||||
|
|
||||||
if (returnType.isPrimitive()) {
|
// Default implementations of non-Transient methods in entities are the default value when the
|
||||||
|
// map contains 'null'.
|
||||||
|
if (method.isDefault()) {
|
||||||
|
return invokeDefault(proxy, method, args);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise, if the return type of the method is a primitive Java type then we'll return the standard
|
||||||
|
// default values to avoid a NPE in user code.
|
||||||
|
if (returnType.isPrimitive()) {
|
||||||
DefaultPrimitiveTypes type = DefaultPrimitiveTypes.lookup(returnType);
|
DefaultPrimitiveTypes type = DefaultPrimitiveTypes.lookup(returnType);
|
||||||
if (type == null) {
|
if (type == null) {
|
||||||
throw new HelenusException("unknown primitive type " + returnType);
|
throw new HelenusException("unknown primitive type " + returnType);
|
||||||
}
|
}
|
||||||
|
|
||||||
return type.getDefaultValue();
|
return type.getDefaultValue();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue