Resolve package cycle between repository.config and repository.aot.
Closes #2708
This commit is contained in:
committed by
Mark Paluch
parent
0e00d5fd3f
commit
8d424855da
@@ -31,6 +31,8 @@ import org.springframework.beans.factory.support.RegisteredBean;
|
||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.data.domain.ManagedTypes;
|
||||
import org.springframework.data.util.TypeContributor;
|
||||
import org.springframework.data.util.TypeUtils;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
@@ -35,6 +35,7 @@ import org.springframework.beans.factory.support.RegisteredBean;
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.data.domain.ManagedTypes;
|
||||
import org.springframework.data.util.Lazy;
|
||||
import org.springframework.data.util.TypeCollector;
|
||||
import org.springframework.javapoet.ClassName;
|
||||
import org.springframework.javapoet.CodeBlock;
|
||||
import org.springframework.javapoet.MethodSpec.Builder;
|
||||
|
||||
@@ -1,52 +0,0 @@
|
||||
/*
|
||||
* Copyright 2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.aot;
|
||||
|
||||
import java.lang.reflect.Member;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
/**
|
||||
* Abstract utility class containing common, reusable {@link Predicate Predicates}.
|
||||
*
|
||||
* @author John Blum
|
||||
* @see java.util.function.Predicate
|
||||
* @since 3.0
|
||||
*/
|
||||
// TODO: Consider moving to the org.springframework.data.util package.
|
||||
@SuppressWarnings("unused")
|
||||
public abstract class Predicates {
|
||||
|
||||
public static final Predicate<Member> IS_ENUM_MEMBER = member -> member.getDeclaringClass().isEnum();
|
||||
public static final Predicate<Member> IS_HIBERNATE_MEMBER = member -> member.getName().startsWith("$$_hibernate"); // this
|
||||
// should
|
||||
// go
|
||||
// into
|
||||
// JPA
|
||||
public static final Predicate<Member> IS_OBJECT_MEMBER = member -> Object.class.equals(member.getDeclaringClass());
|
||||
public static final Predicate<Member> IS_JAVA = member -> member.getDeclaringClass().getPackageName().startsWith("java.");
|
||||
public static final Predicate<Member> IS_NATIVE = member -> Modifier.isNative(member.getModifiers());
|
||||
public static final Predicate<Member> IS_PRIVATE = member -> Modifier.isPrivate(member.getModifiers());
|
||||
public static final Predicate<Member> IS_PROTECTED = member -> Modifier.isProtected(member.getModifiers());
|
||||
public static final Predicate<Member> IS_PUBLIC = member -> Modifier.isPublic(member.getModifiers());
|
||||
public static final Predicate<Member> IS_SYNTHETIC = Member::isSynthetic;
|
||||
public static final Predicate<Member> IS_STATIC = member -> Modifier.isStatic(member.getModifiers());
|
||||
|
||||
public static final Predicate<Method> IS_BRIDGE_METHOD = Method::isBridge;
|
||||
|
||||
|
||||
}
|
||||
@@ -1,257 +0,0 @@
|
||||
/*
|
||||
* Copyright 2019-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.aot;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Member;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.data.util.Lazy;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
* @author Sebastien Deleuze
|
||||
* @author John Blum
|
||||
*/
|
||||
public class TypeCollector {
|
||||
|
||||
private static final Log logger = LogFactory.getLog(TypeCollector.class);
|
||||
|
||||
static final Set<String> EXCLUDED_DOMAINS = new HashSet<>(Arrays.asList("java", "sun.", "jdk.", "reactor.",
|
||||
"kotlinx.", "kotlin.", "org.springframework.core.", "org.springframework.data.mapping.",
|
||||
"org.springframework.data.repository.", "org.springframework.boot.", "org.springframework.core."));
|
||||
|
||||
private final Predicate<Class<?>> excludedDomainsFilter = type -> {
|
||||
String packageName = type.getPackageName();
|
||||
return EXCLUDED_DOMAINS.stream().noneMatch(packageName::startsWith);
|
||||
};
|
||||
|
||||
private Predicate<Class<?>> typeFilter = excludedDomainsFilter;
|
||||
|
||||
private final Predicate<Method> methodFilter = createMethodFilter();
|
||||
|
||||
private Predicate<Field> fieldFilter = createFieldFilter();
|
||||
|
||||
public TypeCollector filterFields(Predicate<Field> filter) {
|
||||
this.fieldFilter = filter.and(filter);
|
||||
return this;
|
||||
}
|
||||
|
||||
public TypeCollector filterTypes(Predicate<Class<?>> filter) {
|
||||
this.typeFilter = this.typeFilter.and(filter);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inspect the given type and resolve those reachable via fields, methods, generics, ...
|
||||
*
|
||||
* @param types the types to inspect
|
||||
* @return a type model collector for the type
|
||||
*/
|
||||
public static ReachableTypes inspect(Class<?>... types) {
|
||||
return inspect(Arrays.asList(types));
|
||||
}
|
||||
|
||||
public static ReachableTypes inspect(Collection<Class<?>> types) {
|
||||
return new ReachableTypes(new TypeCollector(), types);
|
||||
}
|
||||
|
||||
private void process(Class<?> root, Consumer<ResolvableType> consumer) {
|
||||
processType(ResolvableType.forType(root), new InspectionCache(), consumer);
|
||||
}
|
||||
|
||||
private void processType(ResolvableType type, InspectionCache cache, Consumer<ResolvableType> callback) {
|
||||
|
||||
if (ResolvableType.NONE.equals(type) || cache.contains(type) || type.toClass().isSynthetic()) {
|
||||
return;
|
||||
}
|
||||
|
||||
cache.add(type);
|
||||
|
||||
// continue inspection but only add those matching the filter criteria to the result
|
||||
if (typeFilter.test(type.toClass())) {
|
||||
callback.accept(type);
|
||||
}
|
||||
|
||||
Set<Type> additionalTypes = new LinkedHashSet<>();
|
||||
additionalTypes.addAll(TypeUtils.resolveTypesInSignature(type));
|
||||
additionalTypes.addAll(visitConstructorsOfType(type));
|
||||
additionalTypes.addAll(visitMethodsOfType(type));
|
||||
additionalTypes.addAll(visitFieldsOfType(type));
|
||||
|
||||
if (!ObjectUtils.isEmpty(type.toClass().getDeclaredClasses())) {
|
||||
additionalTypes.addAll(Arrays.asList(type.toClass().getDeclaredClasses()));
|
||||
}
|
||||
|
||||
for (Type discoveredType : additionalTypes) {
|
||||
processType(ResolvableType.forType(discoveredType, type), cache, callback);
|
||||
}
|
||||
}
|
||||
|
||||
Set<Type> visitConstructorsOfType(ResolvableType type) {
|
||||
if (!typeFilter.test(type.toClass())) {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
Set<Type> discoveredTypes = new LinkedHashSet<>();
|
||||
for (Constructor<?> constructor : type.toClass().getDeclaredConstructors()) {
|
||||
for (Class<?> signatureType : TypeUtils.resolveTypesInSignature(type.toClass(), constructor)) {
|
||||
if (typeFilter.test(signatureType)) {
|
||||
discoveredTypes.add(signatureType);
|
||||
}
|
||||
}
|
||||
}
|
||||
return new HashSet<>(discoveredTypes);
|
||||
}
|
||||
|
||||
Set<Type> visitMethodsOfType(ResolvableType type) {
|
||||
if (!typeFilter.test(type.toClass())) {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
|
||||
Set<Type> discoveredTypes = new LinkedHashSet<>();
|
||||
try {
|
||||
ReflectionUtils.doWithLocalMethods(type.toClass(), method -> {
|
||||
if (!methodFilter.test(method)) {
|
||||
return;
|
||||
}
|
||||
for (Class<?> signatureType : TypeUtils.resolveTypesInSignature(type.toClass(), method)) {
|
||||
if (typeFilter.test(signatureType)) {
|
||||
discoveredTypes.add(signatureType);
|
||||
}
|
||||
}
|
||||
});
|
||||
} catch (Exception cause) {
|
||||
logger.warn(cause);
|
||||
}
|
||||
return new HashSet<>(discoveredTypes);
|
||||
}
|
||||
|
||||
Set<Type> visitFieldsOfType(ResolvableType type) {
|
||||
Set<Type> discoveredTypes = new LinkedHashSet<>();
|
||||
ReflectionUtils.doWithLocalFields(type.toClass(), field -> {
|
||||
if (!fieldFilter.test(field)) {
|
||||
return;
|
||||
}
|
||||
for (Class<?> signatureType : TypeUtils.resolveTypesInSignature(ResolvableType.forField(field, type))) {
|
||||
if (typeFilter.test(signatureType)) {
|
||||
discoveredTypes.add(signatureType);
|
||||
}
|
||||
}
|
||||
});
|
||||
return discoveredTypes;
|
||||
}
|
||||
|
||||
private Predicate<Method> createMethodFilter() {
|
||||
|
||||
Predicate<Method> excludedDomainsPredicate = methodToTest -> excludedDomainsFilter
|
||||
.test(methodToTest.getDeclaringClass());
|
||||
|
||||
Predicate<Method> excludedMethodsPredicate = Predicates.IS_BRIDGE_METHOD //
|
||||
.or(Predicates.IS_STATIC) //
|
||||
.or(Predicates.IS_SYNTHETIC) //
|
||||
.or(Predicates.IS_NATIVE) //
|
||||
.or(Predicates.IS_PRIVATE) //
|
||||
.or(Predicates.IS_PROTECTED) //
|
||||
.or(Predicates.IS_OBJECT_MEMBER) //
|
||||
.or(Predicates.IS_HIBERNATE_MEMBER) //
|
||||
.or(Predicates.IS_ENUM_MEMBER) //
|
||||
.or(excludedDomainsPredicate.negate()); //
|
||||
|
||||
return excludedMethodsPredicate.negate();
|
||||
}
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
private Predicate<Field> createFieldFilter() {
|
||||
|
||||
Predicate<Member> excludedFieldPredicate = Predicates.IS_HIBERNATE_MEMBER //
|
||||
.or(Predicates.IS_SYNTHETIC) //
|
||||
.or(Predicates.IS_JAVA);
|
||||
|
||||
return (Predicate) excludedFieldPredicate.negate();
|
||||
}
|
||||
|
||||
public static class ReachableTypes {
|
||||
|
||||
private final Iterable<Class<?>> roots;
|
||||
private final Lazy<List<Class<?>>> reachableTypes = Lazy.of(this::collect);
|
||||
private final TypeCollector typeCollector;
|
||||
|
||||
public ReachableTypes(TypeCollector typeCollector, Iterable<Class<?>> roots) {
|
||||
|
||||
this.typeCollector = typeCollector;
|
||||
this.roots = roots;
|
||||
}
|
||||
|
||||
public void forEach(Consumer<ResolvableType> consumer) {
|
||||
roots.forEach(it -> typeCollector.process(it, consumer));
|
||||
}
|
||||
|
||||
public List<Class<?>> list() {
|
||||
return reachableTypes.get();
|
||||
}
|
||||
|
||||
private List<Class<?>> collect() {
|
||||
List<Class<?>> target = new ArrayList<>();
|
||||
forEach(it -> target.add(it.toClass()));
|
||||
return target;
|
||||
}
|
||||
}
|
||||
|
||||
static class InspectionCache {
|
||||
|
||||
private final Map<String, ResolvableType> mutableCache = new LinkedHashMap<>();
|
||||
|
||||
public void add(ResolvableType resolvableType) {
|
||||
mutableCache.put(resolvableType.toString(), resolvableType);
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
mutableCache.clear();
|
||||
}
|
||||
|
||||
public boolean contains(ResolvableType key) {
|
||||
return mutableCache.containsKey(key.toString());
|
||||
}
|
||||
|
||||
public boolean isEmpty() {
|
||||
return mutableCache.isEmpty();
|
||||
}
|
||||
|
||||
public int size() {
|
||||
return mutableCache.size();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,97 +0,0 @@
|
||||
/*
|
||||
* Copyright 2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.aot;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import org.springframework.aot.generate.GenerationContext;
|
||||
import org.springframework.aot.hint.BindingReflectionHintsRegistrar;
|
||||
import org.springframework.aot.hint.MemberCategory;
|
||||
import org.springframework.core.annotation.MergedAnnotation;
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
* @since 3.0
|
||||
*/
|
||||
public class TypeContributor {
|
||||
|
||||
public static final String DATA_NAMESPACE = "org.springframework.data";
|
||||
public static final BindingReflectionHintsRegistrar REGISTRAR = new BindingReflectionHintsRegistrar();
|
||||
|
||||
/**
|
||||
* Contribute the type with default reflection configuration, skip annotations.
|
||||
*
|
||||
* @param type
|
||||
* @param contribution
|
||||
*/
|
||||
public static void contribute(Class<?> type, GenerationContext contribution) {
|
||||
contribute(type, Collections.emptySet(), contribution);
|
||||
}
|
||||
|
||||
/**
|
||||
* Contribute the type with default reflection configuration and only include matching annotations.
|
||||
*
|
||||
* @param type
|
||||
* @param filter
|
||||
* @param contribution
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static void contribute(Class<?> type, Predicate<Class<? extends Annotation>> filter,
|
||||
GenerationContext contribution) {
|
||||
|
||||
if (type.isPrimitive()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (type.isAnnotation() && filter.test((Class<? extends Annotation>) type)) {
|
||||
|
||||
contribution.getRuntimeHints().reflection().registerType(type,
|
||||
hint -> hint.withMembers(MemberCategory.INTROSPECT_PUBLIC_METHODS));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
REGISTRAR.registerReflectionHints(contribution.getRuntimeHints().reflection(), type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Contribute the type with default reflection configuration and only include annotations from a certain namespace and
|
||||
* those meta annotated with one of them.
|
||||
*
|
||||
* @param type
|
||||
* @param annotationNamespaces
|
||||
* @param contribution
|
||||
*/
|
||||
public static void contribute(Class<?> type, Set<String> annotationNamespaces, GenerationContext contribution) {
|
||||
contribute(type, it -> isPartOfOrMetaAnnotatedWith(it, annotationNamespaces), contribution);
|
||||
}
|
||||
|
||||
public static boolean isPartOf(Class<?> type, Set<String> namespaces) {
|
||||
return namespaces.stream().anyMatch(namespace -> type.getPackageName().startsWith(namespace));
|
||||
}
|
||||
|
||||
public static boolean isPartOfOrMetaAnnotatedWith(Class<? extends Annotation> annotation, Set<String> namespaces) {
|
||||
|
||||
if (isPartOf(annotation, namespaces)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return MergedAnnotation.of(annotation).getMetaTypes().stream().anyMatch(it -> isPartOf(annotation, namespaces));
|
||||
}
|
||||
}
|
||||
@@ -1,258 +0,0 @@
|
||||
/*
|
||||
* Copyright 2019-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.springframework.data.aot;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.AnnotatedElement;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Parameter;
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.core.ResolvableType;
|
||||
import org.springframework.core.annotation.AnnotationFilter;
|
||||
import org.springframework.core.annotation.MergedAnnotation;
|
||||
import org.springframework.core.annotation.MergedAnnotations;
|
||||
import org.springframework.core.annotation.MergedAnnotations.SearchStrategy;
|
||||
import org.springframework.core.annotation.RepeatableContainers;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
/**
|
||||
* @author Christoph Strobl
|
||||
*/
|
||||
// TODO: Consider moving to the org.springframework.data.util package or make this package-private if not used somewhere
|
||||
// else.
|
||||
public class TypeUtils {
|
||||
|
||||
public static String TRANSACTION_MANAGER_CLASS_NAME = "org.springframework.transaction.TransactionManager";
|
||||
|
||||
/**
|
||||
* Resolve ALL annotations present for a given type. Will inspect type, constructors, parameters, methods, fields,...
|
||||
*
|
||||
* @param type
|
||||
* @return never {@literal null}.
|
||||
*/
|
||||
public static Set<MergedAnnotation<Annotation>> resolveUsedAnnotations(Class<?> type) {
|
||||
|
||||
Set<MergedAnnotation<Annotation>> annotations = new LinkedHashSet<>();
|
||||
annotations.addAll(TypeUtils.resolveAnnotationsFor(type).toList());
|
||||
for (Constructor<?> ctor : type.getDeclaredConstructors()) {
|
||||
annotations.addAll(TypeUtils.resolveAnnotationsFor(ctor).toList());
|
||||
for (Parameter parameter : ctor.getParameters()) {
|
||||
annotations.addAll(TypeUtils.resolveAnnotationsFor(parameter).toList());
|
||||
}
|
||||
}
|
||||
for (Field field : type.getDeclaredFields()) {
|
||||
annotations.addAll(TypeUtils.resolveAnnotationsFor(field).toList());
|
||||
}
|
||||
try {
|
||||
for (Method method : type.getDeclaredMethods()) {
|
||||
annotations.addAll(TypeUtils.resolveAnnotationsFor(method).toList());
|
||||
for (Parameter parameter : method.getParameters()) {
|
||||
annotations.addAll(TypeUtils.resolveAnnotationsFor(parameter).toList());
|
||||
}
|
||||
}
|
||||
} catch (NoClassDefFoundError e) {
|
||||
// ignore and move on
|
||||
}
|
||||
return annotations;
|
||||
}
|
||||
|
||||
public static Stream<MergedAnnotation<Annotation>> resolveAnnotationsFor(AnnotatedElement element) {
|
||||
return resolveAnnotationsFor(element, AnnotationFilter.PLAIN);
|
||||
}
|
||||
|
||||
public static Stream<MergedAnnotation<Annotation>> resolveAnnotationsFor(AnnotatedElement element,
|
||||
AnnotationFilter filter) {
|
||||
return MergedAnnotations
|
||||
.from(element, SearchStrategy.TYPE_HIERARCHY, RepeatableContainers.standardRepeatables(), filter).stream();
|
||||
}
|
||||
|
||||
public static Collection<Class<Annotation>> resolveAnnotationTypesFor(AnnotatedElement element,
|
||||
AnnotationFilter filter) {
|
||||
return MergedAnnotations
|
||||
.from(element, SearchStrategy.TYPE_HIERARCHY, RepeatableContainers.standardRepeatables(), filter).stream()
|
||||
.map(MergedAnnotation::getType).toList();
|
||||
}
|
||||
|
||||
public static Collection<Class<Annotation>> resolveAnnotationTypesFor(AnnotatedElement element) {
|
||||
return resolveAnnotationTypesFor(element, AnnotationFilter.PLAIN);
|
||||
}
|
||||
|
||||
public static boolean isAnnotationFromOrMetaAnnotated(Class<? extends Annotation> annotation, String prefix) {
|
||||
if (annotation.getPackage().getName().startsWith(prefix)) {
|
||||
return true;
|
||||
}
|
||||
return TypeUtils.resolveAnnotationsFor(annotation)
|
||||
.anyMatch(it -> it.getType().getPackage().getName().startsWith(prefix));
|
||||
}
|
||||
|
||||
public static boolean hasAnnotatedField(Class<?> type, String annotationName) {
|
||||
|
||||
for (Field field : type.getDeclaredFields()) {
|
||||
MergedAnnotations fieldAnnotations = MergedAnnotations.from(field);
|
||||
boolean hasAnnotation = fieldAnnotations.get(annotationName).isPresent();
|
||||
if (hasAnnotation) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static Set<Field> getAnnotatedField(Class<?> type, String annotationName) {
|
||||
|
||||
Set<Field> fields = new LinkedHashSet<>();
|
||||
for (Field field : type.getDeclaredFields()) {
|
||||
if (MergedAnnotations.from(field).get(annotationName).isPresent()) {
|
||||
fields.add(field);
|
||||
}
|
||||
}
|
||||
return fields;
|
||||
}
|
||||
|
||||
public static Set<Class<?>> resolveTypesInSignature(Class<?> owner, Method method) {
|
||||
Set<Class<?>> signature = new LinkedHashSet<>();
|
||||
signature.addAll(resolveTypesInSignature(ResolvableType.forMethodReturnType(method, owner)));
|
||||
for (Parameter parameter : method.getParameters()) {
|
||||
signature
|
||||
.addAll(resolveTypesInSignature(ResolvableType.forMethodParameter(MethodParameter.forParameter(parameter))));
|
||||
}
|
||||
return signature;
|
||||
}
|
||||
|
||||
public static Set<Class<?>> resolveTypesInSignature(Class<?> owner, Constructor<?> constructor) {
|
||||
Set<Class<?>> signature = new LinkedHashSet<>();
|
||||
for (int i = 0; i < constructor.getParameterCount(); i++) {
|
||||
signature.addAll(resolveTypesInSignature(ResolvableType.forConstructorParameter(constructor, i, owner)));
|
||||
}
|
||||
return signature;
|
||||
}
|
||||
|
||||
public static Set<Class<?>> resolveTypesInSignature(Class<?> root) {
|
||||
Set<Class<?>> signature = new LinkedHashSet<>();
|
||||
resolveTypesInSignature(ResolvableType.forClass(root), signature);
|
||||
return signature;
|
||||
}
|
||||
|
||||
public static Set<Class<?>> resolveTypesInSignature(ResolvableType root) {
|
||||
Set<Class<?>> signature = new LinkedHashSet<>();
|
||||
resolveTypesInSignature(root, signature);
|
||||
return signature;
|
||||
}
|
||||
|
||||
private static void resolveTypesInSignature(ResolvableType current, Set<Class<?>> signatures) {
|
||||
|
||||
if (ResolvableType.NONE.equals(current) || ObjectUtils.nullSafeEquals(Void.TYPE, current.getType())
|
||||
|| ObjectUtils.nullSafeEquals(Object.class, current.getType())) {
|
||||
return;
|
||||
}
|
||||
if (signatures.contains(current.toClass())) {
|
||||
return;
|
||||
}
|
||||
signatures.add(current.toClass());
|
||||
resolveTypesInSignature(current.getSuperType(), signatures);
|
||||
for (ResolvableType type : current.getGenerics()) {
|
||||
resolveTypesInSignature(type, signatures);
|
||||
}
|
||||
for (ResolvableType type : current.getInterfaces()) {
|
||||
resolveTypesInSignature(type, signatures);
|
||||
}
|
||||
}
|
||||
|
||||
public static TypeOps type(Class<?> type) {
|
||||
return new TypeOpsImpl(type);
|
||||
}
|
||||
|
||||
public interface TypeOps {
|
||||
|
||||
Class<?> getType();
|
||||
|
||||
default boolean isPartOf(String... packageNames) {
|
||||
return isPartOf(TypeUtils.PackageFilter.of(packageNames));
|
||||
}
|
||||
|
||||
default boolean isPartOf(PackageFilter... packageFilters) {
|
||||
for (PackageFilter filter : packageFilters) {
|
||||
if (filter.matches(getType().getName())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
default Set<Class<?>> signatureTypes() {
|
||||
return TypeUtils.resolveTypesInSignature(getType());
|
||||
}
|
||||
|
||||
interface PackageFilter {
|
||||
|
||||
default boolean matches(Class<?> type) {
|
||||
return matches(type.getName());
|
||||
}
|
||||
|
||||
boolean matches(String typeName);
|
||||
|
||||
static PackageFilter of(String... packages) {
|
||||
return TypeUtils.PackageFilter.of(packages);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static class TypeOpsImpl implements TypeOps {
|
||||
|
||||
private final Class<?> type;
|
||||
|
||||
TypeOpsImpl(Class<?> type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public Class<?> getType() {
|
||||
return type;
|
||||
}
|
||||
}
|
||||
|
||||
private static class PackageFilter implements TypeOps.PackageFilter {
|
||||
|
||||
Set<String> packageNames;
|
||||
|
||||
PackageFilter(Set<String> packageNames) {
|
||||
this.packageNames = packageNames;
|
||||
}
|
||||
|
||||
static PackageFilter of(String... packageNames) {
|
||||
Set<String> target = new LinkedHashSet<>();
|
||||
for (String pkgName : packageNames) {
|
||||
target.add(pkgName.endsWith(".") ? pkgName : (pkgName + '.'));
|
||||
}
|
||||
return new PackageFilter(target);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean matches(String typeName) {
|
||||
for (String pgkName : packageNames) {
|
||||
if (typeName.startsWith(pgkName)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user