Customer.class
), property literals would make it possible to refer to the properties of bin classes as type-safe. This would be useful for an API design where there is a need to perform actions on properties or to configure them in some way. new SearchMapping().entity(Address.class) .indexed() .property("city", ElementType.METHOD) .field();
validateValue()
method from the Bean Validation API allows you to check the value of the property constraints: Set<ConstraintViolation<Address>> violations = validator.validateValue(Address.class, "city", "Purbeck" );
String
type is used to refer to the city
property of the Address
object.city
property at all. Or someone might forget to update the string property name after renaming the get / set methods when refactoring.validateValue()
, we are not able to verify that the type of the passed value corresponds to the type of the property. mapping.entity(Address.class) .indexed() .property(Address::city, ElementType.METHOD ) .field();
validator.validateValue(Address.class, Address::city, "Purbeck");
validateValue()
property literals would help ensure that we pass the value of the correct type. validator.validateValue(Address.class, Address::getCity, "Purbeck");
validateValue()
method declaration look like? The key point is the use of the new Function
type: public <T, P> Set<ConstraintViolation<T>> validateValue( Class<T> type, Function<? super T, P> property, P value);
Address::
. But how to display the property name from the Function
object in the implementation of the validateValue()
method?apply()
, which executes the function code for the passed-in instance T
This is not what we needed. public interface PropertyNameCapturer { String getPropertyName(); void setPropertyName(String propertyName); }
PropertyNameCapturer
: public <T> T /* & PropertyNameCapturer */ getPropertyNameCapturer(Class<T> type) { DynamicType.Builder<?> builder = new ByteBuddy() (1) .subclass( type.isInterface() ? Object.class : type ); if (type.isInterface()) { (2) builder = builder.implement(type); } Class<?> proxyType = builder .implement(PropertyNameCapturer.class) (3) .defineField("propertyName", String.class, Visibility.PRIVATE) .method( ElementMatchers.any()) (4) .intercept(MethodDelegation.to( PropertyNameCapturingInterceptor.class )) .method(named("setPropertyName").or(named("getPropertyName"))) (5) .intercept(FieldAccessor.ofBeanProperty()) .make() .load( (6) PropertyNameCapturer.class.getClassLoader(), ClassLoadingStrategy.Default.WRAPPER ) .getLoaded(); try { @SuppressWarnings("unchecked") Class<T> typed = (Class<T>) proxyType; return typed.newInstance(); (7) } catch (InstantiationException | IllegalAccessException e) { throw new HibernateException( "Couldn't instantiate proxy for method name retrieval", e ); } }
public class PropertyNameCapturingInterceptor { @RuntimeType public static Object intercept(@This PropertyNameCapturer capturer, @Origin Method method) { (1) capturer.setPropertyName(getPropertyName(method)); (2) if (method.getReturnType() == byte.class) { (3) return (byte) 0; } else if ( ... ) { } // ... handle all primitve types // ... } else { return null; } } private static String getPropertyName(Method method) { (4) final boolean hasGetterSignature = method.getParameterTypes().length == 0 && method.getReturnType() != null; String name = method.getName(); String propName = null; if (hasGetterSignature) { if (name.startsWith("get") && hasGetterSignature) { propName = name.substring(3, 4).toLowerCase() + name.substring(4); } else if (name.startsWith("is") && hasGetterSignature) { propName = name.substring(2, 3).toLowerCase() + name.substring(3); } } else { throw new HibernateException( "Only property getter methods are expected to be passed"); (5) } return propName; } }
@Origin
and @This
are used to specify the appropriate parameters so that ByteBuddy can generate the correct intercept () calls in a dynamic proxy.getPropertyName()
(4) we can get the name of the property corresponding to the given Method Reference, and save it in PropertyNameCapturer
(2). If the method is not a getter, then the code throws an exception (5). The return type of the getter does not matter, so we return null based on the type of property (3).validateValue()
method: public <T, P> Set<ConstraintViolation<T>> validateValue( Class<T> type, Function<? super T, P> property, P value) { T capturer = getPropertyNameCapturer(type); property.apply(capturer); String propertyName = ((PropertyLiteralCapturer) capturer).getPropertyName(); // }
User::login.name
Source: https://habr.com/ru/post/420533/
All Articles