diff --git a/src/main/java/org/springframework/data/mapping/model/AbstractPersistentProperty.java b/src/main/java/org/springframework/data/mapping/model/AbstractPersistentProperty.java index c8af5bd5a3..616a734750 100644 --- a/src/main/java/org/springframework/data/mapping/model/AbstractPersistentProperty.java +++ b/src/main/java/org/springframework/data/mapping/model/AbstractPersistentProperty.java @@ -49,7 +49,7 @@ public abstract class AbstractPersistentProperty

static { - CAUSE_FIELD = ReflectionUtils.findRequiredField(Throwable.class, "cause"); + CAUSE_FIELD = ReflectionUtils.getRequiredField(Throwable.class, "cause"); ASSOCIATION_TYPE = ReflectionUtils.loadIfPresent("org.jmolecules.ddd.types.Association", AbstractPersistentProperty.class.getClassLoader()); } diff --git a/src/main/java/org/springframework/data/querydsl/binding/QuerydslPredicateBuilder.java b/src/main/java/org/springframework/data/querydsl/binding/QuerydslPredicateBuilder.java index 5e6d1868a7..97dcca56a8 100644 --- a/src/main/java/org/springframework/data/querydsl/binding/QuerydslPredicateBuilder.java +++ b/src/main/java/org/springframework/data/querydsl/binding/QuerydslPredicateBuilder.java @@ -219,7 +219,7 @@ private static TypeDescriptor getTargetTypeDescriptor(PathInformation path) { TypeDescriptor result = descriptor == null // ? TypeDescriptor - .nested(org.springframework.data.util.ReflectionUtils.findRequiredField(owningType, leafProperty), 0) + .nested(org.springframework.data.util.ReflectionUtils.getRequiredField(owningType, leafProperty), 0) : TypeDescriptor .nested(new Property(owningType, descriptor.getReadMethod(), descriptor.getWriteMethod(), leafProperty), 0); diff --git a/src/main/java/org/springframework/data/repository/config/RepositoryComponentProvider.java b/src/main/java/org/springframework/data/repository/config/RepositoryComponentProvider.java index 7999b98268..776b57ab38 100644 --- a/src/main/java/org/springframework/data/repository/config/RepositoryComponentProvider.java +++ b/src/main/java/org/springframework/data/repository/config/RepositoryComponentProvider.java @@ -33,8 +33,8 @@ import org.springframework.data.repository.NoRepositoryBean; import org.springframework.data.repository.Repository; import org.springframework.data.repository.RepositoryDefinition; -import org.springframework.data.repository.util.ClassUtils; import org.springframework.lang.NonNull; +import org.springframework.lang.Nullable; import org.springframework.util.Assert; /** @@ -103,7 +103,7 @@ public void addIncludeFilter(TypeFilter includeFilter) { @Override protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) { - boolean isNonRepositoryInterface = !ClassUtils.isGenericRepositoryInterface(beanDefinition.getBeanClassName()); + boolean isNonRepositoryInterface = !isGenericRepositoryInterface(beanDefinition.getBeanClassName()); boolean isTopLevelType = !beanDefinition.getMetadata().hasEnclosingClass(); boolean isConsiderNestedRepositories = isConsiderNestedRepositoryInterfaces(); @@ -150,6 +150,13 @@ public void setConsiderNestedRepositoryInterfaces(boolean considerNestedReposito this.considerNestedRepositoryInterfaces = considerNestedRepositoryInterfaces; } + /** + * Returns whether the given type name is a {@link Repository} interface name. + */ + private static boolean isGenericRepositoryInterface(@Nullable String interfaceName) { + return Repository.class.getName().equals(interfaceName); + } + /** * {@link org.springframework.core.type.filter.TypeFilter} that only matches interfaces. Thus setting this up makes * only sense providing an interface type as {@code targetType}. @@ -180,21 +187,19 @@ public boolean match(MetadataReader metadataReader, MetadataReaderFactory metada * * @author Oliver Gierke */ - private static class AllTypeFilter implements TypeFilter { - - private final List delegates; + private record AllTypeFilter(List delegates) implements TypeFilter { /** * Creates a new {@link AllTypeFilter} to match if all the given delegates match. * * @param delegates must not be {@literal null}. */ - public AllTypeFilter(List delegates) { + private AllTypeFilter { Assert.notNull(delegates, "TypeFilter deleages must not be null"); - this.delegates = delegates; } + @Override public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException { diff --git a/src/main/java/org/springframework/data/repository/config/RepositoryConfigurationDelegate.java b/src/main/java/org/springframework/data/repository/config/RepositoryConfigurationDelegate.java index 21e4569135..136b9a12ad 100644 --- a/src/main/java/org/springframework/data/repository/config/RepositoryConfigurationDelegate.java +++ b/src/main/java/org/springframework/data/repository/config/RepositoryConfigurationDelegate.java @@ -26,6 +26,7 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; + import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.ConfigurableBeanFactory; import org.springframework.beans.factory.config.DependencyDescriptor; @@ -51,10 +52,9 @@ import org.springframework.data.repository.core.support.AbstractRepositoryMetadata; import org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport; import org.springframework.data.repository.core.support.RepositoryFactorySupport; -import org.springframework.data.util.ReflectionUtils; +import org.springframework.data.util.ClassUtils; import org.springframework.lang.Nullable; import org.springframework.util.Assert; -import org.springframework.util.ClassUtils; import org.springframework.util.StopWatch; /** @@ -123,8 +123,7 @@ private static Environment defaultEnvironment(@Nullable Environment environment, return environment; } - return resourceLoader instanceof EnvironmentCapable capable ? capable.getEnvironment() - : new StandardEnvironment(); + return resourceLoader instanceof EnvironmentCapable capable ? capable.getEnvironment() : new StandardEnvironment(); } /** @@ -321,19 +320,19 @@ private static ApplicationStartup getStartup(BeanDefinitionRegistry registry) { private ResolvableType getRepositoryFactoryBeanType(RepositoryConfiguration configuration) { String interfaceName = configuration.getRepositoryInterface(); - ClassLoader classLoader = resourceLoader.getClassLoader() == null ? ClassUtils.getDefaultClassLoader() + ClassLoader classLoader = resourceLoader.getClassLoader() == null + ? org.springframework.util.ClassUtils.getDefaultClassLoader() : resourceLoader.getClassLoader(); classLoader = classLoader != null ? classLoader : getClass().getClassLoader(); - Class repositoryInterface = ReflectionUtils.loadIfPresent(interfaceName, classLoader); + Class repositoryInterface = ClassUtils.loadIfPresent(interfaceName, classLoader); if (repositoryInterface == null) { return null; } - Class factoryBean = ReflectionUtils.loadIfPresent(configuration.getRepositoryFactoryBeanClassName(), - classLoader); + Class factoryBean = ClassUtils.loadIfPresent(configuration.getRepositoryFactoryBeanClassName(), classLoader); if (factoryBean == null) { return null; diff --git a/src/main/java/org/springframework/data/repository/core/RepositoryInformationSupport.java b/src/main/java/org/springframework/data/repository/core/RepositoryInformationSupport.java index ddc85065ff..a2a2341f29 100644 --- a/src/main/java/org/springframework/data/repository/core/RepositoryInformationSupport.java +++ b/src/main/java/org/springframework/data/repository/core/RepositoryInformationSupport.java @@ -15,8 +15,6 @@ */ package org.springframework.data.repository.core; -import static org.springframework.data.repository.util.ClassUtils.*; - import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.Arrays; @@ -25,9 +23,12 @@ import org.springframework.core.annotation.AnnotationUtils; import org.springframework.data.annotation.QueryAnnotation; +import org.springframework.data.repository.Repository; import org.springframework.data.util.Lazy; import org.springframework.data.util.Streamable; import org.springframework.data.util.TypeInformation; +import org.springframework.lang.Contract; +import org.springframework.lang.Nullable; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; @@ -166,8 +167,8 @@ private final DefaultQueryMethods calculateQueryMethods() { Class repositoryInterface = getRepositoryInterface(); return new DefaultQueryMethods(Streamable.of(Arrays.stream(repositoryInterface.getMethods()) - .map(it -> ClassUtils.getMostSpecificMethod(it, repositoryInterface)) - .filter(this::isQueryMethodCandidate) + .map(it -> ClassUtils.getMostSpecificMethod(it, repositoryInterface)) // + .filter(this::isQueryMethodCandidate) // .toList()), calculateHasCustomMethod(repositoryInterface)); } @@ -187,6 +188,27 @@ private final boolean calculateHasCustomMethod(Class repositoryInterface) { return false; } + /** + * Returns where the given type is the {@link Repository} interface. + * + * @param ifc + * @return + */ + private static boolean isGenericRepositoryInterface(Class ifc) { + return Repository.class.equals(ifc); + } + + /** + * Returns whether the given type name is a repository interface name. + * + * @param interfaceName + * @return + */ + @Contract("null -> false") + public static boolean isGenericRepositoryInterface(@Nullable String interfaceName) { + return Repository.class.getName().equals(interfaceName); + } + /** * Information about query methods to allow canonical computation and reuse of that information. * diff --git a/src/main/java/org/springframework/data/repository/core/support/QueryExecutionResultHandler.java b/src/main/java/org/springframework/data/repository/core/support/QueryExecutionResultHandler.java index 9438a3fc92..966455cf44 100644 --- a/src/main/java/org/springframework/data/repository/core/support/QueryExecutionResultHandler.java +++ b/src/main/java/org/springframework/data/repository/core/support/QueryExecutionResultHandler.java @@ -27,7 +27,6 @@ import org.springframework.core.convert.ConversionService; import org.springframework.core.convert.TypeDescriptor; import org.springframework.core.convert.support.GenericConversionService; -import org.springframework.data.repository.util.ClassUtils; import org.springframework.data.repository.util.QueryExecutionConverters; import org.springframework.data.repository.util.ReactiveWrapperConverters; import org.springframework.data.util.NullableWrapper; @@ -67,7 +66,8 @@ class QueryExecutionResultHandler { public static Class loadIfPresent(String type) { try { - return (Class) org.springframework.util.ClassUtils.forName(type, ClassUtils.class.getClassLoader()); + return (Class) org.springframework.util.ClassUtils.forName(type, + QueryExecutionResultHandler.class.getClassLoader()); } catch (ClassNotFoundException | LinkageError e) { return null; } diff --git a/src/main/java/org/springframework/data/repository/core/support/RepositoryFactorySupport.java b/src/main/java/org/springframework/data/repository/core/support/RepositoryFactorySupport.java index b2b1a728f6..af1c6ad71e 100644 --- a/src/main/java/org/springframework/data/repository/core/support/RepositoryFactorySupport.java +++ b/src/main/java/org/springframework/data/repository/core/support/RepositoryFactorySupport.java @@ -17,6 +17,7 @@ import java.io.Serializable; import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Arrays; @@ -184,7 +185,7 @@ public void setNamedQueries(@Nullable NamedQueries namedQueries) { @Override public void setBeanClassLoader(@Nullable ClassLoader classLoader) { - this.classLoader = classLoader == null ? org.springframework.util.ClassUtils.getDefaultClassLoader() : classLoader; + this.classLoader = classLoader == null ? ClassUtils.getDefaultClassLoader() : classLoader; this.projectionFactory = createProjectionFactory(); } @@ -429,15 +430,15 @@ public T getRepository(Class repositoryInterface, RepositoryFragments fra if (DefaultMethodInvokingMethodInterceptor.hasDefaultMethods(repositoryInterface)) { if (logger.isTraceEnabled()) { - logger.trace(LogMessage.format("Register DefaultMethodInvokingMethodInterceptor for %s…", repositoryInterface.getName())); + logger.trace(LogMessage.format("Register DefaultMethodInvokingMethodInterceptor for %s…", + repositoryInterface.getName())); } result.addAdvice(new DefaultMethodInvokingMethodInterceptor()); } Optional queryLookupStrategy = getQueryLookupStrategy(queryLookupStrategyKey, new ValueExpressionDelegate( - new QueryMethodValueEvaluationContextAccessor(getEnvironment(), evaluationContextProvider), - VALUE_PARSER)); + new QueryMethodValueEvaluationContextAccessor(getEnvironment(), evaluationContextProvider), VALUE_PARSER)); result.addAdvice(new QueryExecutorMethodInterceptor(information, getProjectionFactory(), queryLookupStrategy, namedQueries, queryPostProcessors, methodInvocationListeners)); @@ -529,7 +530,7 @@ private RepositoryInformation getRepositoryInformation(RepositoryMetadata metada return repositoryInformationCache.computeIfAbsent(cacheKey, key -> { - Class baseClass = repositoryBaseClass != null ? repositoryBaseClass : getRepositoryBaseClass(metadata); + Class baseClass = repositoryBaseClass != null ? repositoryBaseClass : getRepositoryBaseClass(metadata); return new DefaultRepositoryInformation(metadata, baseClass, composition); }); @@ -751,11 +752,13 @@ public Object invoke(@SuppressWarnings("null") MethodInvocation invocation) thro try { return composition.invoke(invocationMulticaster, method, arguments); - } catch (Exception e) { - org.springframework.data.repository.util.ClassUtils.unwrapReflectionException(e); - } + } catch (Exception ex) { + if (ex instanceof InvocationTargetException) { + throw ((InvocationTargetException) ex).getTargetException(); + } - throw new IllegalStateException("Should not occur"); + throw ex; + } } } @@ -886,25 +889,24 @@ static class RepositoryValidator { static { - org.springframework.data.repository.util.ClassUtils.ifPresent( - "org.springframework.data.querydsl.QuerydslPredicateExecutor", RepositoryValidator.class.getClassLoader(), - it -> { + org.springframework.data.util.ClassUtils.ifPresent("org.springframework.data.querydsl.QuerydslPredicateExecutor", + RepositoryValidator.class.getClassLoader(), it -> { WELL_KNOWN_EXECUTORS.put(it, "Querydsl"); }); - org.springframework.data.repository.util.ClassUtils.ifPresent( + org.springframework.data.util.ClassUtils.ifPresent( "org.springframework.data.querydsl.ReactiveQuerydslPredicateExecutor", RepositoryValidator.class.getClassLoader(), it -> { WELL_KNOWN_EXECUTORS.put(it, "Reactive Querydsl"); }); - org.springframework.data.repository.util.ClassUtils.ifPresent( + org.springframework.data.util.ClassUtils.ifPresent( "org.springframework.data.repository.query.QueryByExampleExecutor", RepositoryValidator.class.getClassLoader(), it -> { WELL_KNOWN_EXECUTORS.put(it, "Query by Example"); }); - org.springframework.data.repository.util.ClassUtils.ifPresent( + org.springframework.data.util.ClassUtils.ifPresent( "org.springframework.data.repository.query.ReactiveQueryByExampleExecutor", RepositoryValidator.class.getClassLoader(), it -> { WELL_KNOWN_EXECUTORS.put(it, "Reactive Query by Example"); diff --git a/src/main/java/org/springframework/data/repository/query/Parameter.java b/src/main/java/org/springframework/data/repository/query/Parameter.java index 89205e16e0..97eccfbd5f 100644 --- a/src/main/java/org/springframework/data/repository/query/Parameter.java +++ b/src/main/java/org/springframework/data/repository/query/Parameter.java @@ -30,9 +30,9 @@ import org.springframework.data.domain.Pageable; import org.springframework.data.domain.ScrollPosition; import org.springframework.data.domain.Sort; -import org.springframework.data.repository.util.ClassUtils; import org.springframework.data.repository.util.QueryExecutionConverters; import org.springframework.data.repository.util.ReactiveWrapperConverters; +import org.springframework.data.util.ClassUtils; import org.springframework.data.util.Lazy; import org.springframework.data.util.TypeInformation; import org.springframework.util.Assert; diff --git a/src/main/java/org/springframework/data/repository/query/QueryMethod.java b/src/main/java/org/springframework/data/repository/query/QueryMethod.java index 5528c3e351..5a3f887694 100644 --- a/src/main/java/org/springframework/data/repository/query/QueryMethod.java +++ b/src/main/java/org/springframework/data/repository/query/QueryMethod.java @@ -15,8 +15,6 @@ */ package org.springframework.data.repository.query; -import static org.springframework.data.repository.util.ClassUtils.*; - import java.lang.reflect.Method; import java.util.Collections; import java.util.Set; @@ -38,6 +36,7 @@ import org.springframework.data.util.Lazy; import org.springframework.data.util.NullableWrapperConverters; import org.springframework.data.util.ReactiveWrappers; +import org.springframework.data.util.ReflectionUtils; import org.springframework.data.util.TypeInformation; import org.springframework.util.Assert; @@ -77,7 +76,9 @@ public QueryMethod(Method method, RepositoryMetadata metadata, ProjectionFactory Assert.notNull(factory, "ProjectionFactory must not be null"); Parameters.TYPES.stream() // - .filter(type -> getNumberOfOccurrences(method, type) > 1).findFirst().ifPresent(type -> { + .filter(type -> ReflectionUtils.getParameterCount(method, type::equals) > 1) // + .findFirst() // + .ifPresent(type -> { throw new IllegalStateException(String.format( "Method must have only one argument of type %s; Offending method: %s", type.getSimpleName(), method)); }); @@ -107,19 +108,19 @@ private void validate() { QueryMethodValidator.validate(method); - if (hasParameterOfType(method, Pageable.class)) { + if (ReflectionUtils.hasParameterOfType(method, Pageable.class)) { if (!isStreamQuery()) { assertReturnTypeAssignable(method, QueryExecutionConverters.getAllowedPageableTypes()); } - if (hasParameterOfType(method, Sort.class)) { + if (ReflectionUtils.hasParameterOfType(method, Sort.class)) { throw new IllegalStateException(String.format("Method must not have Pageable *and* Sort parameters. " + "Use sorting capabilities on Pageable instead; Offending method: %s", method)); } } - if (hasParameterOfType(method, ScrollPosition.class)) { + if (ReflectionUtils.hasParameterOfType(method, ScrollPosition.class)) { assertReturnTypeAssignable(method, Collections.singleton(Window.class)); } @@ -388,11 +389,12 @@ static void validate(Method method) { static Predicate pageableCannotHaveSortOrLimit = (method) -> { - if (!hasParameterAssignableToType(method, Pageable.class)) { + if (!ReflectionUtils.hasParameterAssignableToType(method, Pageable.class)) { return true; } - if (hasParameterAssignableToType(method, Sort.class) || hasParameterAssignableToType(method, Limit.class)) { + if (ReflectionUtils.hasParameterAssignableToType(method, Sort.class) + || ReflectionUtils.hasParameterAssignableToType(method, Limit.class)) { return false; } diff --git a/src/main/java/org/springframework/data/repository/util/ClassUtils.java b/src/main/java/org/springframework/data/repository/util/ClassUtils.java index 37fd9cad9b..a57502ed24 100644 --- a/src/main/java/org/springframework/data/repository/util/ClassUtils.java +++ b/src/main/java/org/springframework/data/repository/util/ClassUtils.java @@ -19,7 +19,6 @@ import java.lang.reflect.Method; import java.util.Arrays; import java.util.Collection; -import java.util.List; import java.util.function.Consumer; import org.springframework.data.repository.Repository; @@ -36,7 +35,9 @@ * @author Oliver Gierke * @author Mark Paluch * @author Johannes Englmeier + * @deprecated since 3.5, use {@link org.springframework.data.util.ClassUtils} instead. */ +@Deprecated(since = "3.5", forRemoval = true) public abstract class ClassUtils { /** @@ -72,16 +73,7 @@ public static boolean hasProperty(Class type, String property) { * definition for a superclass or interface implemented by the class to be checked here) */ public static void ifPresent(String className, @Nullable ClassLoader classLoader, Consumer> action) { - - try { - Class theClass = org.springframework.util.ClassUtils.forName(className, classLoader); - action.accept(theClass); - } catch (IllegalAccessError err) { - throw new IllegalStateException( - "Readability mismatch in inheritance hierarchy of class [" + className + "]: " + err.getMessage(), err); - } catch (Throwable ex) { - // Typically ClassNotFoundException or NoClassDefFoundError... - } + org.springframework.data.util.ClassUtils.ifPresent(className, classLoader, action); } /** @@ -108,7 +100,6 @@ public static boolean isGenericRepositoryInterface(@Nullable String interfaceNam /** * @deprecated Use {@link #getNumberOfOccurrences(Method, Class)}. */ - @Deprecated public static int getNumberOfOccurences(Method method, Class type) { return getNumberOfOccurrences(method, type); } @@ -124,10 +115,7 @@ public static int getNumberOfOccurences(Method method, Class type) { * @see java.lang.reflect.Method#getParameterTypes() */ public static int getNumberOfOccurrences(@NonNull Method method, @NonNull Class parameterType) { - - return (int) Arrays.stream(method.getParameterTypes()) - .filter(parameterType::equals) - .count(); + return org.springframework.data.util.ReflectionUtils.getParameterCount(method, parameterType::equals); } /** @@ -174,7 +162,7 @@ public static boolean isOfType(@Nullable Object object, Collection> typ * @return */ public static boolean hasParameterOfType(Method method, Class type) { - return Arrays.asList(method.getParameterTypes()).contains(type); + return org.springframework.data.util.ReflectionUtils.hasParameterOfType(method, type); } /** @@ -185,7 +173,7 @@ public static boolean hasParameterOfType(Method method, Class type) { * @return */ public static boolean hasParameterAssignableToType(Method method, Class type) { - return List.of(method.getParameterTypes()).stream().anyMatch(type::isAssignableFrom); + return org.springframework.data.util.ReflectionUtils.hasParameterOfType(method, type); } /** @@ -196,8 +184,8 @@ public static boolean hasParameterAssignableToType(Method method, Class type) */ public static void unwrapReflectionException(Exception ex) throws Throwable { - if (ex instanceof InvocationTargetException) { - throw ((InvocationTargetException) ex).getTargetException(); + if (ex instanceof InvocationTargetException ite) { + ReflectionUtils.handleInvocationTargetException(ite); } throw ex; diff --git a/src/main/java/org/springframework/data/spel/EvaluationContextExtensionInformation.java b/src/main/java/org/springframework/data/spel/EvaluationContextExtensionInformation.java index c7895ebbdf..a4f79ddf23 100644 --- a/src/main/java/org/springframework/data/spel/EvaluationContextExtensionInformation.java +++ b/src/main/java/org/springframework/data/spel/EvaluationContextExtensionInformation.java @@ -74,7 +74,7 @@ public EvaluationContextExtensionInformation(Class rootObjectType = org.springframework.data.util.ReflectionUtils.findRequiredMethod(type, "getRootObject") + Class rootObjectType = org.springframework.data.util.ReflectionUtils.getRequiredMethod(type, "getRootObject") .getReturnType(); this.rootObjectInformation = Optional diff --git a/src/main/java/org/springframework/data/spel/ReactiveExtensionAwareEvaluationContextProvider.java b/src/main/java/org/springframework/data/spel/ReactiveExtensionAwareEvaluationContextProvider.java index bdc68bfb59..65352f6e4f 100644 --- a/src/main/java/org/springframework/data/spel/ReactiveExtensionAwareEvaluationContextProvider.java +++ b/src/main/java/org/springframework/data/spel/ReactiveExtensionAwareEvaluationContextProvider.java @@ -139,7 +139,7 @@ private Mono> getExtensions( private static ResolvableType getExtensionType(ExtensionIdAware extensionCandidate) { return ResolvableType - .forMethodReturnType(ReflectionUtils.findRequiredMethod(extensionCandidate.getClass(), "getExtension")) + .forMethodReturnType(ReflectionUtils.getRequiredMethod(extensionCandidate.getClass(), "getExtension")) .getGeneric(0); } } diff --git a/src/main/java/org/springframework/data/util/BeanLookup.java b/src/main/java/org/springframework/data/util/BeanLookup.java index 3c2470d288..cf602a6ba6 100644 --- a/src/main/java/org/springframework/data/util/BeanLookup.java +++ b/src/main/java/org/springframework/data/util/BeanLookup.java @@ -41,7 +41,7 @@ private BeanLookup() {} * beans of the given type are available in the given {@link BeanFactory}. * * @param type must not be {@literal null}. - * @param beanFactory the {@link BeanFactory} to lookup the bean from. + * @param beanFactory the {@link BeanFactory} to look up the bean from. * @return a {@link Lazy} for the unique bean of the given type or the instance provided by the fallback in case no * bean of the given type can be found. */ diff --git a/src/main/java/org/springframework/data/util/CastUtils.java b/src/main/java/org/springframework/data/util/CastUtils.java index c9df96626c..902eb8a362 100644 --- a/src/main/java/org/springframework/data/util/CastUtils.java +++ b/src/main/java/org/springframework/data/util/CastUtils.java @@ -15,6 +15,10 @@ */ package org.springframework.data.util; +/** + * @deprecated since 3.5 will be removed in a future release. + */ +@Deprecated(since = "3.5", forRemoval = true) public interface CastUtils { @SuppressWarnings("unchecked") diff --git a/src/main/java/org/springframework/data/util/ClassUtils.java b/src/main/java/org/springframework/data/util/ClassUtils.java new file mode 100644 index 0000000000..eecaf4ae2f --- /dev/null +++ b/src/main/java/org/springframework/data/util/ClassUtils.java @@ -0,0 +1,78 @@ +/* + * Copyright 2008-2024 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.util; + +import java.util.function.Consumer; + +import org.springframework.lang.Nullable; + +/** + * Utility class to work with classes. + * + * @author Oliver Gierke + * @author Mark Paluch + * @author Johannes Englmeier + * @since 3.5 + */ +public abstract class ClassUtils { + + /** + * Private constructor to prevent instantiation. + */ + private ClassUtils() {} + + /** + * Determine whether the {@link Class} identified by the supplied {@code className} is present and can be loaded and + * call the {@link Consumer action} if the {@link Class} could be loaded. + * + * @param className the name of the class to check. + * @param classLoader the class loader to use (can be {@literal null}, which indicates the default class loader). + * @param action the action callback to notify. + * @throws IllegalStateException if the corresponding class is resolvable but there was a readability mismatch in the + * inheritance hierarchy of the class (typically a missing dependency declaration in a Jigsaw module + * definition for a superclass or interface implemented by the class to be checked here) + */ + public static void ifPresent(String className, @Nullable ClassLoader classLoader, Consumer> action) { + + try { + Class theClass = org.springframework.util.ClassUtils.forName(className, classLoader); + action.accept(theClass); + } catch (IllegalAccessError err) { + throw new IllegalStateException( + "Readability mismatch in inheritance hierarchy of class [" + className + "]: " + err.getMessage(), err); + } catch (Throwable ex) { + // Typically ClassNotFoundException or NoClassDefFoundError... + } + } + + /** + * Loads the class with the given name using the given {@link ClassLoader}. + * + * @param name the name of the class to be loaded. + * @param classLoader the class loader to use (can be {@literal null}, which indicates the default class loader). + * @return the {@link Class} or {@literal null} in case the class can't be loaded for any reason. + */ + @Nullable + public static Class loadIfPresent(String name, @Nullable ClassLoader classLoader) { + + try { + return org.springframework.util.ClassUtils.forName(name, classLoader); + } catch (Exception o_O) { + return null; + } + } + +} diff --git a/src/main/java/org/springframework/data/util/ReflectionUtils.java b/src/main/java/org/springframework/data/util/ReflectionUtils.java index 2809f2ec39..fbee7fdaf4 100644 --- a/src/main/java/org/springframework/data/util/ReflectionUtils.java +++ b/src/main/java/org/springframework/data/util/ReflectionUtils.java @@ -19,11 +19,13 @@ import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; +import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.List; import java.util.Optional; +import java.util.function.Predicate; import java.util.stream.Collectors; -import java.util.stream.IntStream; import java.util.stream.Stream; import org.springframework.beans.BeanUtils; @@ -52,6 +54,50 @@ private ReflectionUtils() { throw new UnsupportedOperationException("This is a utility class and cannot be instantiated"); } + /** + * Returns whether the given {@link Method} has a parameter of the given type. + * + * @param method the method to check, must not be {@literal null}. + * @param type parameter type to query for, must not be {@literal null}. + * @return {@literal true} the given {@link Method} has a parameter of the given type. + * @since 3.5 + */ + public static boolean hasParameterOfType(Method method, Class type) { + return Arrays.asList(method.getParameterTypes()).contains(type); + } + + /** + * Returns whether the given {@link Method} has a parameter that is assignable to the given type. + * + * @param method the method to check, must not be {@literal null}. + * @param type parameter type to query for, must not be {@literal null}. + * @return {@literal true} the given {@link Method} has a parameter that is assignable to the given type. + * @since 3.5 + */ + public static boolean hasParameterAssignableToType(Method method, Class type) { + + for (Class parameterType : method.getParameterTypes()) { + if (type.isAssignableFrom(parameterType)) { + return true; + } + } + + return false; + } + + /** + * Returns the number of matching parameters {@link Method} for {@link Predicate}. + * + * @param method {@link Method} to evaluate. + * @param predicate the predicate matching {@link Method} + * @return the resulting number of matching parameters. + * @see java.lang.reflect.Method#getParameterTypes() + * @since 3.5 + */ + public static int getParameterCount(Method method, Predicate> predicate) { + return (int) Arrays.stream(method.getParameterTypes()).filter(predicate).count(); + } + /** * Creates an instance of the class with the given fully qualified name or returns the given default instance if the * class cannot be loaded or instantiated. @@ -59,8 +105,10 @@ private ReflectionUtils() { * @param classname the fully qualified class name to create an instance for. * @param defaultInstance the instance to fall back to in case the given class cannot be loaded or instantiated. * @return + * @deprecated since 3.5 as it is not used within the framework anymore. */ @SuppressWarnings("unchecked") + @Deprecated(since = "3.5", forRemoval = true) public static T createInstanceIfPresent(String classname, T defaultInstance) { try { @@ -106,6 +154,7 @@ public interface DescribedFieldFilter extends FieldFilter { * @return */ String getDescription(); + } /** @@ -129,6 +178,7 @@ public boolean matches(Field field) { public String getDescription() { return String.format("Annotation filter for %s", annotationType.getName()); } + } /** @@ -151,6 +201,7 @@ public boolean matches(Field field) { public String getDescription() { return String.format("FieldFilter %s", filter.toString()); } + }, false); } @@ -200,7 +251,7 @@ public static Field findField(Class type, DescribedFieldFilter filter, boolea return field; } - if (foundField != null && enforceUniqueness) { + if (foundField != null) { throw new IllegalStateException(filter.getDescription()); } @@ -218,10 +269,25 @@ public static Field findField(Class type, DescribedFieldFilter filter, boolea * * @param type must not be {@literal null}. * @param name must not be {@literal null} or empty. - * @return + * @return the required field. * @throws IllegalArgumentException in case the field can't be found. + * @deprecated use {@link #getRequiredField(Class, String)} instead. */ + @Deprecated(since = "3.5", forRemoval = true) public static Field findRequiredField(Class type, String name) { + return getRequiredField(type, name); + } + + /** + * Obtains the required field of the given name on the given type or throws {@link IllegalArgumentException} if the + * found could not be found. + * + * @param type must not be {@literal null}. + * @param name must not be {@literal null} or empty. + * @return the required field. + * @throws IllegalArgumentException in case the field can't be found. + */ + public static Field getRequiredField(Class type, String name) { Field result = org.springframework.util.ReflectionUtils.findField(type, name); @@ -251,7 +317,9 @@ public static void setField(Field field, Object target, @Nullable Object value) * @param type must not be {@literal null}. * @param constructorArguments must not be {@literal null}. * @return a {@link Constructor} that is compatible with the given arguments. + * @deprecated since 3.5, return type will change to nullable instead of Optional. */ + @Deprecated public static Optional> findConstructor(Class type, Object... constructorArguments) { Assert.notNull(type, "Target type must not be null"); @@ -271,8 +339,25 @@ public static Optional> findConstructor(Class type, Object... * @param parameterTypes must not be {@literal null}. * @return the method object. * @throws IllegalArgumentException in case the method cannot be resolved. + * @deprecated since 3.5, use {@link #getRequiredMethod(Class, String, Class[])} instead. */ + @Deprecated public static Method findRequiredMethod(Class type, String name, Class... parameterTypes) { + return getRequiredMethod(type, name, parameterTypes); + } + + /** + * Returns the method with the given name of the given class and parameter types. Prefers regular methods over + * {@link Method#isBridge() bridge} and {@link Method#isSynthetic() synthetic} ones. + * + * @param type must not be {@literal null}. + * @param name must not be {@literal null}. + * @param parameterTypes must not be {@literal null}. + * @return the method object. + * @throws IllegalArgumentException in case the method cannot be resolved. + * @since 3.5 + */ + public static Method getRequiredMethod(Class type, String name, Class... parameterTypes) { Assert.notNull(type, "Class must not be null"); Assert.notNull(name, "Method name must not be null"); @@ -313,7 +398,7 @@ private static boolean hasSameParams(Method method, Class[] paramTypes) { * Returns a {@link Stream} of the return and parameters types of the given {@link Method}. * * @param method must not be {@literal null}. - * @return + * @return stream of return and parameter types. * @since 2.0 */ public static Stream> returnTypeAndParameters(Method method) { @@ -332,25 +417,51 @@ public static Stream> returnTypeAndParameters(Method method) { * @param type must not be {@literal null}. * @param name must not be {@literal null} or empty. * @param parameterTypes must not be {@literal null}. - * @return + * @return the optional Method. * @since 2.0 */ + @Deprecated(since = "3.5", forRemoval = true) public static Optional getMethod(Class type, String name, ResolvableType... parameterTypes) { + return Optional.ofNullable(findMethod(type, name, parameterTypes)); + } + + /** + * Returns the {@link Method} with the given name and parameters declared on the given type, if available. + * + * @param type must not be {@literal null}. + * @param name must not be {@literal null} or empty. + * @param parameterTypes must not be {@literal null}. + * @return the required method. + * @since 3.5 + */ + @Nullable + public static Method findMethod(Class type, String name, ResolvableType... parameterTypes) { Assert.notNull(type, "Type must not be null"); Assert.hasText(name, "Name must not be null or empty"); Assert.notNull(parameterTypes, "Parameter types must not be null"); - List> collect = Arrays.stream(parameterTypes)// - .map(ResolvableType::getRawClass)// - .collect(Collectors.toList()); + List> collect = parameterTypes.length == 0 ? Collections.emptyList() + : new ArrayList<>(parameterTypes.length); + for (ResolvableType parameterType : parameterTypes) { + Class rawClass = parameterType.getRawClass(); + collect.add(rawClass); + } + + Method method = org.springframework.util.ReflectionUtils.findMethod(type, name, collect.toArray(new Class[0])); + + if (method != null) { - Method method = org.springframework.util.ReflectionUtils.findMethod(type, name, - collect.toArray(new Class[collect.size()])); + for (int i = 0; i < parameterTypes.length; i++) { + if (!ResolvableType.forMethodParameter(method, i).equals(parameterTypes[i])) { + return null; + } + } + + return method; + } - return Optional.ofNullable(method)// - .filter(it -> IntStream.range(0, it.getParameterCount())// - .allMatch(index -> ResolvableType.forMethodParameter(it, index).equals(parameterTypes[index]))); + return null; } private static boolean argumentsMatch(Class[] parameterTypes, Object[] arguments) { @@ -455,12 +566,9 @@ public static Object getPrimitiveDefault(Class type) { * @since 2.5 */ @Nullable + @Deprecated(since = "3.5", forRemoval = true) public static Class loadIfPresent(String name, ClassLoader classLoader) { - - try { - return ClassUtils.forName(name, classLoader); - } catch (Exception o_O) { - return null; - } + return org.springframework.data.util.ClassUtils.loadIfPresent(name, classLoader); } + } diff --git a/src/test/kotlin/org/springframework/data/repository/kotlin/CoroutineRepositoryMetadataUnitTests.kt b/src/test/kotlin/org/springframework/data/repository/kotlin/CoroutineRepositoryMetadataUnitTests.kt index 729ebb6a0b..2452494806 100644 --- a/src/test/kotlin/org/springframework/data/repository/kotlin/CoroutineRepositoryMetadataUnitTests.kt +++ b/src/test/kotlin/org/springframework/data/repository/kotlin/CoroutineRepositoryMetadataUnitTests.kt @@ -34,7 +34,12 @@ class CoroutineRepositoryMetadataUnitTests { fun shouldDetermineCorrectResultType() { val metadata = DefaultRepositoryMetadata(MyCoRepository::class.java) - val method = ReflectionUtils.findRequiredMethod(MyCoRepository::class.java, "findOne", String::class.java, Continuation::class.java); + val method = ReflectionUtils.getRequiredMethod( + MyCoRepository::class.java, + "findOne", + String::class.java, + Continuation::class.java + ); assertThat(metadata.getReturnedDomainClass(method)).isEqualTo(User::class.java) } @@ -43,7 +48,12 @@ class CoroutineRepositoryMetadataUnitTests { fun shouldDetermineCorrectOptionalResultType() { val metadata = DefaultRepositoryMetadata(MyCoRepository::class.java) - val method = ReflectionUtils.findRequiredMethod(MyCoRepository::class.java, "findOneOptional", String::class.java, Continuation::class.java); + val method = ReflectionUtils.getRequiredMethod( + MyCoRepository::class.java, + "findOneOptional", + String::class.java, + Continuation::class.java + ); assertThat(metadata.getReturnedDomainClass(method)).isEqualTo(User::class.java) } @@ -52,7 +62,11 @@ class CoroutineRepositoryMetadataUnitTests { fun shouldDetermineCorrectFlowResultType() { val metadata = DefaultRepositoryMetadata(MyCoRepository::class.java) - val method = ReflectionUtils.findRequiredMethod(MyCoRepository::class.java, "findMultiple", String::class.java); + val method = ReflectionUtils.getRequiredMethod( + MyCoRepository::class.java, + "findMultiple", + String::class.java + ); assertThat(metadata.getReturnedDomainClass(method)).isEqualTo(User::class.java) } @@ -61,7 +75,12 @@ class CoroutineRepositoryMetadataUnitTests { fun shouldDetermineCorrectSuspendedFlowResultType() { val metadata = DefaultRepositoryMetadata(MyCoRepository::class.java) - val method = ReflectionUtils.findRequiredMethod(MyCoRepository::class.java, "findMultipleSuspended", String::class.java, Continuation::class.java); + val method = ReflectionUtils.getRequiredMethod( + MyCoRepository::class.java, + "findMultipleSuspended", + String::class.java, + Continuation::class.java + ); assertThat(metadata.getReturnedDomainClass(method)).isEqualTo(User::class.java) }