下面列出了com.fasterxml.jackson.databind.introspect.BeanPropertyDefinition#getGetter ( ) 实例代码,或者点击链接到github查看源代码,也可以在右侧发表评论。
public static Annotation[] collectAnnotations(BeanPropertyDefinition propertyDefinition) {
List<Annotation> annotations = new ArrayList<>();
if (propertyDefinition.getField() != null) {
Collections.addAll(annotations, propertyDefinition.getField().getAnnotated().getAnnotations());
}
if (propertyDefinition.getGetter() != null) {
Collections.addAll(annotations, propertyDefinition.getGetter().getAnnotated().getAnnotations());
}
if (propertyDefinition.getSetter() != null) {
Collections.addAll(annotations, propertyDefinition.getSetter().getAnnotated().getAnnotations());
}
return annotations.toArray(new Annotation[annotations.size()]);
}
/**
* Create item for property.
* <p>
* Almost always property declaration type is used as binding type (for future binding), but:
* <ul>
* <li>If property type is collection implementation, then collection interface used instead</li>
* <li>If property is Object then type would be taken from value. This means binding type will be Object
* when value null and actual type when value provided. Assuming this case will not happen (bad config).</li>
* </ul>
*
* @param root root property (containing), may be null for roots
* @param prop jackson property descriptor
* @param value property value, may be null
* @param genericsContext generics context
* @return path item object
*/
private static ConfigPath createItem(final ConfigPath root,
final BeanPropertyDefinition prop,
final Object value,
final GenericsContext genericsContext) {
// need generified type to resolve generics manually because jackson's generics resolution
// couldn't handle all required cases
final Type type = prop.getGetter() != null
? prop.getGetter().getAnnotated().getGenericReturnType()
: prop.getField().getAnnotated().getGenericType();
final Class typeClass = Primitives.wrap(genericsContext.resolveClass(type));
// upper possible known type (for introspection): ideally type of actually used configuration value
// note that even when value is null upper type could be different from lower type due to collection projection
final Class upperType = value == null ? typeClass : value.getClass();
final boolean customType = isCustomType(upperType);
// either class declaration or value type (in both cases could be projected to collection interface)
final boolean objectDeclared = Object.class.equals(typeClass);
final Class lowerType = correctValueType(objectDeclared ? upperType : typeClass, customType);
final List<Type> lowerGenerics =
resolveLowerGenerics(genericsContext, type, typeClass, objectDeclared, lowerType);
final List<Type> upperGenerics = lowerType.equals(upperType) ? lowerGenerics
: resolveUpperGenerics(genericsContext, type, objectDeclared, upperType);
return new ConfigPath(
root,
prop.getAccessor().getDeclaringClass(),
lowerType,
// as an example, enum constant type could lead to anonymous class
upperType.isAnonymousClass() ? lowerType : upperType,
lowerGenerics,
upperGenerics,
fullPath(root, prop),
value,
customType,
objectDeclared);
}
/**
* Use jackson serialization api to extract all configuration values with paths from configuration object.
* Always analyze types, even if actual branch is not present at all (null value) in order to always bind
* nulls and avoid "Schrodinger's binding" case. In short, bindings should not depend on configuration values
* (presence).
* <p>
* Still, bindings may vary: for example, bound implementations may differ (best example is dropwizard server type),
* as a consequences, parsed type may be different and so different properties paths could be recognized.
*
* @param config jackson serialization config
* @param content currently parsed paths
* @param type analyzed part type
* @param object analyzed part instance (may be null)
* @return all configuration paths values
*/
@SuppressWarnings("checkstyle:CyclomaticComplexity")
private static List<ConfigPath> resolvePaths(final SerializationConfig config,
final ConfigPath root,
final List<ConfigPath> content,
final Class type,
final Object object,
final GenericsContext genericsContext) {
final BasicBeanDescription description = config.introspect(
config.constructType(type)
);
for (BeanPropertyDefinition prop : description.findProperties()) {
// ignore write-only or groovy special property
if (!prop.couldSerialize() || prop.getName().equals("metaClass")) {
continue;
}
final Object value;
// if configuration doesn't expect serialization and throws error on access
// (like netflix dynamic properties) it should not break app startup
try {
value = readValue(prop.getAccessor(), object);
} catch (Exception ex) {
LOGGER.warn("Can't bind configuration path '{}' due to {}: {}. Enable debug logs to see "
+ "complete stack trace or use @JsonIgnore on property getter.",
fullPath(root, prop), ex.getClass().getSimpleName(), ex.getMessage());
LOGGER.debug("Complete error: ", ex);
continue;
}
final ConfigPath item = createItem(root, prop, value, genericsContext);
content.add(item);
if (root != null) {
root.getChildren().add(item);
}
if (item.isCustomType() && !detectRecursion(item)) {
// build generics context for actual value type (if not null)
final GenericsContext subContext = prop.getGetter() != null
? genericsContext.method(prop.getGetter().getAnnotated()).returnTypeAs(item.getValueType())
: genericsContext.fieldTypeAs(prop.getField().getAnnotated(), item.getValueType());
resolvePaths(config, item, content, item.getValueType(),
item.getValue(), subContext);
}
}
if (root != null) {
// simple properties goes up and composite objects go lower (both groups sorted alphabetically)
root.getChildren().sort(Comparator.comparing(o -> (o.isCustomType() ? 'b' : 'a') + o.getPath()));
}
return content;
}