From 5eb10a081765337974bf2906e4477483d2906c66 Mon Sep 17 00:00:00 2001 From: Oliver Gierke Date: Fri, 12 Jan 2018 10:31:44 +0100 Subject: [PATCH] DATACMNS-1206 - Polishing. Moved PropertyDescriptor lookup into dedicated subclass to group functionality around the type and MethodsMetadata instances. Extracted individual stream handling steps into dedicated methods for better understandability. Moved MethodsMetadataReader into classreading package for symmetry with Spring Framework's metadata arrangement. Removed manually declared getters in DefaultMethodsMetadataReader in favor of Lombok getters. Inlined MethodsMetadataReadingVisitor into DefaultMethodsMetadataReader as it's only used there. Original pull request: #263. --- .../DefaultProjectionInformation.java | 255 +++++++++++------- .../data/type/MethodsMetadata.java | 1 + .../DefaultMethodsMetadataReader.java | 133 ++++++--- .../MethodsMetadataReader.java | 5 +- .../MethodsMetadataReaderFactory.java | 1 - .../MethodsMetadataReadingVisitor.java | 107 -------- ...DefaultMethodsMetadataReaderUnitTests.java | 1 - ...MethodsMetadataReaderFactoryUnitTests.java | 3 +- 8 files changed, 260 insertions(+), 246 deletions(-) rename src/main/java/org/springframework/data/type/{ => classreading}/MethodsMetadataReader.java (84%) delete mode 100644 src/main/java/org/springframework/data/type/classreading/MethodsMetadataReadingVisitor.java rename src/test/java/org/springframework/data/type/{ => classreading}/MethodsMetadataReaderFactoryUnitTests.java (94%) diff --git a/src/main/java/org/springframework/data/projection/DefaultProjectionInformation.java b/src/main/java/org/springframework/data/projection/DefaultProjectionInformation.java index 17fe50f30..294717dfc 100644 --- a/src/main/java/org/springframework/data/projection/DefaultProjectionInformation.java +++ b/src/main/java/org/springframework/data/projection/DefaultProjectionInformation.java @@ -15,10 +15,11 @@ */ package org.springframework.data.projection; +import lombok.extern.slf4j.Slf4j; + import java.beans.PropertyDescriptor; import java.io.IOException; import java.lang.reflect.Method; -import java.util.ArrayList; import java.util.Arrays; import java.util.Comparator; import java.util.List; @@ -29,11 +30,11 @@ import java.util.stream.IntStream; import java.util.stream.Stream; import org.springframework.beans.BeanUtils; -import org.springframework.core.type.ClassMetadata; import org.springframework.core.type.MethodMetadata; import org.springframework.data.type.MethodsMetadata; -import org.springframework.data.type.MethodsMetadataReader; +import org.springframework.data.type.classreading.MethodsMetadataReader; import org.springframework.data.type.classreading.MethodsMetadataReaderFactory; +import org.springframework.data.util.StreamUtils; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; @@ -61,7 +62,7 @@ class DefaultProjectionInformation implements ProjectionInformation { Assert.notNull(type, "Projection type must not be null!"); this.projectionType = type; - this.properties = collectDescriptors(type); + this.properties = new PropertyDescriptorSource(type).getDescriptors(); } /* @@ -106,95 +107,6 @@ class DefaultProjectionInformation implements ProjectionInformation { return true; } - /** - * Collects {@link PropertyDescriptor}s for all properties exposed by the given type and all its super interfaces. - * - * @param type must not be {@literal null}. - * @return - */ - private static List collectDescriptors(Class type) { - - List result = new ArrayList<>(); - - Optional metadata = getMetadata(type); - Stream stream = Arrays.stream(BeanUtils.getPropertyDescriptors(type))// - .filter(it -> !hasDefaultGetter(it)); - - Stream streamToUse = metadata.map(DefaultProjectionInformation::getMethodOrder) - .filter(it -> !it.isEmpty()) // - .map(it -> stream.filter(descriptor -> it.containsKey(descriptor.getReadMethod().getName())) - .sorted(Comparator.comparingInt(left -> it.get(left.getReadMethod().getName())))) // - .orElse(stream); - - result.addAll(streamToUse.collect(Collectors.toList())); - - if (metadata.isPresent()) { - - Stream interfaceNames = metadata.map(ClassMetadata::getInterfaceNames) // - .map(Arrays::stream) // - .orElse(Stream.empty()); - - result.addAll(interfaceNames.map(it -> loadClass(it, type.getClassLoader())) // - .map(DefaultProjectionInformation::collectDescriptors) // - .flatMap(List::stream) // - .collect(Collectors.toList())); - } else { - - for (Class interfaze : type.getInterfaces()) { - result.addAll(collectDescriptors(interfaze)); - } - } - - return result.stream().distinct().collect(Collectors.toList()); - } - - private static Class loadClass(String className, ClassLoader classLoader) { - - try { - return ClassUtils.forName(className, classLoader); - } catch (ClassNotFoundException e) { - throw new IllegalArgumentException(String.format("Cannot load class %s", className)); - } - } - - /** - * Returns a {@link Map} containing method name to its positional index according to {@link MethodsMetadata}. - * - * @param metadata - * @return - */ - private static Map getMethodOrder(MethodsMetadata metadata) { - - List methods = metadata.getMethods() // - .stream() // - .map(MethodMetadata::getMethodName) // - .distinct() // - .collect(Collectors.toList()); - - return IntStream.range(0, methods.size()) // - .boxed() // - .collect(Collectors.toMap(methods::get, i -> i)); - } - - /** - * Attempts to obtain {@link MethodsMetadata} from {@link Class}. Returns {@link Optional} containing - * {@link MethodsMetadata} if metadata was read successfully, {@link Optional#empty()} otherwise. - * - * @param type must not be {@literal null}. - * @return the optional {@link MethodsMetadata}. - */ - private static Optional getMetadata(Class type) { - - try { - - MethodsMetadataReaderFactory factory = new MethodsMetadataReaderFactory(type.getClassLoader()); - MethodsMetadataReader metadataReader = factory.getMetadataReader(ClassUtils.getQualifiedName(type)); - return Optional.of(metadataReader.getMethodsMetadata()); - } catch (IOException e) { - return Optional.empty(); - } - } - /** * Returns whether the given {@link PropertyDescriptor} has a getter that is a Java 8 default method. * @@ -204,6 +116,163 @@ class DefaultProjectionInformation implements ProjectionInformation { private static boolean hasDefaultGetter(PropertyDescriptor descriptor) { Method method = descriptor.getReadMethod(); + return method == null ? false : method.isDefault(); } + + /** + * Internal helper to detect {@link PropertyDescriptor} instances for a given type. + * + * @author Mark Paluch + * @author Oliver Gierke + * @since 2.1 + * @soundtrack The Meters - Cissy Strut (Here Comes The Meter Man) + */ + @Slf4j + private static class PropertyDescriptorSource { + + private final Class type; + private final Optional metadata; + + /** + * Creates a new {@link PropertyDescriptorSource} for the given type. + * + * @param type must not be {@literal null}. + */ + public PropertyDescriptorSource(Class type) { + + Assert.notNull(type, "Type must not be null!"); + + this.type = type; + this.metadata = getMetadata(type); + } + + /** + * Returns {@link PropertyDescriptor}s for all properties exposed by the given type and all its super interfaces. + * + * @return + */ + public List getDescriptors() { + return collectDescriptors().distinct().collect(StreamUtils.toUnmodifiableList()); + } + + /** + * Recursively collects {@link PropertyDescriptor}s for all properties exposed by the given type and all its super + * interfaces. + * + * @return + */ + private Stream collectDescriptors() { + + Stream allButDefaultGetters = Arrays.stream(BeanUtils.getPropertyDescriptors(type)) // + .filter(it -> !hasDefaultGetter(it)); + + Stream ownDescriptors = metadata.map(it -> filterAndOrder(allButDefaultGetters, it)) + .orElse(allButDefaultGetters); + + Stream superTypeDescriptors = metadata.map(this::fromMetadata) // + .orElseGet(this::fromType) // + .flatMap(it -> new PropertyDescriptorSource(it).collectDescriptors()); + + return Stream.concat(ownDescriptors, superTypeDescriptors); + } + + /** + * Returns a Stream of {@link PropertyDescriptor} ordered following the given {@link MethodsMetadata} only returning + * methods seen by the given {@link MethodsMetadata}. + * + * @param source must not be {@literal null}. + * @param metadata must not be {@literal null}. + * @return + */ + private static Stream filterAndOrder(Stream source, + MethodsMetadata metadata) { + + Map orderedMethods = getMethodOrder(metadata); + + if (orderedMethods.isEmpty()) { + return source; + } + + return source.filter(descriptor -> orderedMethods.containsKey(descriptor.getReadMethod().getName())) + .sorted(Comparator.comparingInt(left -> orderedMethods.get(left.getReadMethod().getName()))); + } + + /** + * Returns a {@link Stream} of interfaces using the given {@link MethodsMetadata} as primary source for ordering. + * + * @param metadata must not be {@literal null}. + * @return + */ + private Stream> fromMetadata(MethodsMetadata metadata) { + return Arrays.stream(metadata.getInterfaceNames()).map(it -> findType(it, type.getInterfaces())); + } + + /** + * Returns a Stream of interfaces using the given type as primary source for ordering. + * + * @return + */ + private Stream> fromType() { + return Arrays.stream(type.getInterfaces()); + } + + /** + * Attempts to obtain {@link MethodsMetadata} from {@link Class}. Returns {@link Optional} containing + * {@link MethodsMetadata} if metadata was read successfully, {@link Optional#empty()} otherwise. + * + * @param type must not be {@literal null}. + * @return the optional {@link MethodsMetadata}. + */ + private static Optional getMetadata(Class type) { + + try { + + MethodsMetadataReaderFactory factory = new MethodsMetadataReaderFactory(type.getClassLoader()); + MethodsMetadataReader metadataReader = factory.getMetadataReader(ClassUtils.getQualifiedName(type)); + + return Optional.of(metadataReader.getMethodsMetadata()); + + } catch (IOException e) { + + LOG.info("Couldn't read class metadata for {}. Input property calculation might fail!", type); + + return Optional.empty(); + } + } + + /** + * Find the type with the given name in the given array of {@link Class}. + * + * @param name must not be {@literal null} or empty. + * @param types must not be {@literal null}. + * @return + */ + private static Class findType(String name, Class[] types) { + + return Arrays.stream(types) // + .filter(it -> name.equals(it.getName())) // + .findFirst() + .orElseThrow(() -> new IllegalStateException(String.format("Did not find type %s in %s!", name, types))); + } + + /** + * Returns a {@link Map} containing method name to its positional index according to {@link MethodsMetadata}. + * + * @param metadata + * @return + */ + private static Map getMethodOrder(MethodsMetadata metadata) { + + List methods = metadata.getMethods() // + .stream() // + .map(MethodMetadata::getMethodName) // + .distinct() // + .collect(Collectors.toList()); + + return IntStream.range(0, methods.size()) // + .boxed() // + .collect(Collectors.toMap(methods::get, i -> i)); + } + } } diff --git a/src/main/java/org/springframework/data/type/MethodsMetadata.java b/src/main/java/org/springframework/data/type/MethodsMetadata.java index 74fc84988..d9bc3d601 100644 --- a/src/main/java/org/springframework/data/type/MethodsMetadata.java +++ b/src/main/java/org/springframework/data/type/MethodsMetadata.java @@ -19,6 +19,7 @@ import java.util.Set; import org.springframework.core.type.ClassMetadata; import org.springframework.core.type.MethodMetadata; +import org.springframework.data.type.classreading.MethodsMetadataReader; /** * Interface that defines abstract metadata of a specific class, in a form that does not require that class to be loaded diff --git a/src/main/java/org/springframework/data/type/classreading/DefaultMethodsMetadataReader.java b/src/main/java/org/springframework/data/type/classreading/DefaultMethodsMetadataReader.java index d201637a8..39e249693 100644 --- a/src/main/java/org/springframework/data/type/classreading/DefaultMethodsMetadataReader.java +++ b/src/main/java/org/springframework/data/type/classreading/DefaultMethodsMetadataReader.java @@ -15,18 +15,29 @@ */ package org.springframework.data.type.classreading; +import lombok.Getter; + import java.io.BufferedInputStream; import java.io.IOException; import java.io.InputStream; +import java.util.Collections; +import java.util.Set; import org.springframework.asm.ClassReader; +import org.springframework.asm.MethodVisitor; +import org.springframework.asm.Opcodes; +import org.springframework.asm.Type; import org.springframework.core.NestedIOException; import org.springframework.core.io.Resource; import org.springframework.core.type.AnnotationMetadata; import org.springframework.core.type.ClassMetadata; +import org.springframework.core.type.MethodMetadata; +import org.springframework.core.type.classreading.AnnotationMetadataReadingVisitor; +import org.springframework.core.type.classreading.MethodMetadataReadingVisitor; import org.springframework.data.type.MethodsMetadata; -import org.springframework.data.type.MethodsMetadataReader; +import org.springframework.data.util.StreamUtils; import org.springframework.lang.Nullable; +import org.springframework.util.Assert; /** * {@link MethodsMetadataReader} implementation based on an ASM {@link org.springframework.asm.ClassReader}. @@ -34,6 +45,7 @@ import org.springframework.lang.Nullable; * @author Mark Paluch * @since 2.1 */ +@Getter class DefaultMethodsMetadataReader implements MethodsMetadataReader { private final Resource resource; @@ -43,57 +55,96 @@ class DefaultMethodsMetadataReader implements MethodsMetadataReader { DefaultMethodsMetadataReader(Resource resource, @Nullable ClassLoader classLoader) throws IOException { + MethodsMetadataReadingVisitor visitor = new MethodsMetadataReadingVisitor(classLoader); + createClassReader(resource).accept(visitor, ClassReader.SKIP_DEBUG); + this.resource = resource; + this.classMetadata = visitor; + this.annotationMetadata = visitor; + this.methodsMetadata = visitor; + } - ClassReader classReader; + private static ClassReader createClassReader(Resource resource) throws IOException { + + try (InputStream is = new BufferedInputStream(resource.getInputStream())) { + + return new ClassReader(is); - try (InputStream is = new BufferedInputStream(getResource().getInputStream())) { - classReader = new ClassReader(is); } catch (IllegalArgumentException ex) { throw new NestedIOException("ASM ClassReader failed to parse class file - " - + "probably due to a new Java class file version that isn't supported yet: " + getResource(), ex); + + "probably due to a new Java class file version that isn't supported yet: " + resource, ex); + } + } + + /** + * ASM class visitor which looks for the class name and implemented types as well as for the methods defined in the + * class, exposing them through the {@link MethodsMetadata} interface. + * + * @author Mark Paluch + * @since 2.1 + * @see ClassMetadata + * @see MethodMetadata + * @see MethodMetadataReadingVisitor + */ + private static class MethodsMetadataReadingVisitor extends AnnotationMetadataReadingVisitor + implements MethodsMetadata { + + /** + * Construct a new {@link MethodsMetadataReadingVisitor} given {@link ClassLoader}. + * + * @param classLoader may be {@literal null}. + */ + MethodsMetadataReadingVisitor(@Nullable ClassLoader classLoader) { + super(classLoader); } - MethodsMetadataReadingVisitor visitor = new MethodsMetadataReadingVisitor(classLoader); - classReader.accept(visitor, ClassReader.SKIP_DEBUG); + /* + * (non-Javadoc) + * @see org.springframework.core.type.classreading.AnnotationMetadataReadingVisitor#visitMethod(int, java.lang.String, java.lang.String, java.lang.String, java.lang.String[]) + */ + @Override + @SuppressWarnings("null") + public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { - classMetadata = visitor; - annotationMetadata = visitor; - methodsMetadata = visitor; - } + // Skip bridge methods - we're only interested in original user methods. + // On JDK 8, we'd otherwise run into double detection of the same method... + if ((access & Opcodes.ACC_BRIDGE) != 0) { + return super.visitMethod(access, name, desc, signature, exceptions); + } - /* - * (non-Javadoc) - * @see org.springframework.core.type.classreading.MetadataReader#getResource() - */ - @Override - public Resource getResource() { - return resource; - } + // Skip constructors + if (name.equals("")) { + return super.visitMethod(access, name, desc, signature, exceptions); + } - /* - * (non-Javadoc) - * @see org.springframework.core.type.classreading.MetadataReader#getClassMetadata() - */ - @Override - public ClassMetadata getClassMetadata() { - return classMetadata; - } + MethodMetadataReadingVisitor visitor = new MethodMetadataReadingVisitor(name, access, getClassName(), + Type.getReturnType(desc).getClassName(), this.classLoader, this.methodMetadataSet); - /* - * (non-Javadoc) - * @see org.springframework.core.type.classreading.MetadataReader#getAnnotationMetadata() - */ - @Override - public AnnotationMetadata getAnnotationMetadata() { - return annotationMetadata; - } + this.methodMetadataSet.add(visitor); + return visitor; + } - /* (non-Javadoc) - * @see org.springframework.data.util.ClassMetadataReader#getMethodsMetadata() - */ - @Override - public MethodsMetadata getMethodsMetadata() { - return methodsMetadata; + /* + * (non-Javadoc) + * @see org.springframework.data.util.MethodsMetadata#getMethods() + */ + @Override + public Set getMethods() { + return Collections.unmodifiableSet(methodMetadataSet); + } + + /* + * (non-Javadoc) + * @see org.springframework.data.util.MethodsMetadata#getMethods(String) + */ + @Override + public Set getMethods(String name) { + + Assert.hasText(name, "Method name must not be null or empty"); + + return methodMetadataSet.stream() // + .filter(it -> it.getMethodName().equals(name)) // + .collect(StreamUtils.toUnmodifiableSet()); + } } } diff --git a/src/main/java/org/springframework/data/type/MethodsMetadataReader.java b/src/main/java/org/springframework/data/type/classreading/MethodsMetadataReader.java similarity index 84% rename from src/main/java/org/springframework/data/type/MethodsMetadataReader.java rename to src/main/java/org/springframework/data/type/classreading/MethodsMetadataReader.java index f21d64a99..f5797e473 100644 --- a/src/main/java/org/springframework/data/type/MethodsMetadataReader.java +++ b/src/main/java/org/springframework/data/type/classreading/MethodsMetadataReader.java @@ -13,9 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.springframework.data.type; +package org.springframework.data.type.classreading; import org.springframework.core.type.classreading.MetadataReader; +import org.springframework.data.type.MethodsMetadata; /** * Extension to {@link MetadataReader} for accessing class metadata and method metadata as read by an ASM @@ -27,7 +28,7 @@ import org.springframework.core.type.classreading.MetadataReader; public interface MethodsMetadataReader extends MetadataReader { /** - * @return the metadata for methods in the class file. + * @return the {@link MethodsMetadata} for methods in the class file. */ MethodsMetadata getMethodsMetadata(); } diff --git a/src/main/java/org/springframework/data/type/classreading/MethodsMetadataReaderFactory.java b/src/main/java/org/springframework/data/type/classreading/MethodsMetadataReaderFactory.java index 6f4d249e0..d8a4f7921 100644 --- a/src/main/java/org/springframework/data/type/classreading/MethodsMetadataReaderFactory.java +++ b/src/main/java/org/springframework/data/type/classreading/MethodsMetadataReaderFactory.java @@ -21,7 +21,6 @@ import org.springframework.core.io.Resource; import org.springframework.core.io.ResourceLoader; import org.springframework.core.type.classreading.SimpleMetadataReaderFactory; import org.springframework.data.type.MethodsMetadata; -import org.springframework.data.type.MethodsMetadataReader; import org.springframework.lang.Nullable; /** diff --git a/src/main/java/org/springframework/data/type/classreading/MethodsMetadataReadingVisitor.java b/src/main/java/org/springframework/data/type/classreading/MethodsMetadataReadingVisitor.java deleted file mode 100644 index db3826c7c..000000000 --- a/src/main/java/org/springframework/data/type/classreading/MethodsMetadataReadingVisitor.java +++ /dev/null @@ -1,107 +0,0 @@ -/* - * Copyright 2018 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 - * - * http://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.type.classreading; - -import java.util.Collections; -import java.util.LinkedHashSet; -import java.util.Set; - -import org.springframework.asm.MethodVisitor; -import org.springframework.asm.Opcodes; -import org.springframework.asm.Type; -import org.springframework.core.type.ClassMetadata; -import org.springframework.core.type.MethodMetadata; -import org.springframework.core.type.classreading.AnnotationMetadataReadingVisitor; -import org.springframework.core.type.classreading.MethodMetadataReadingVisitor; -import org.springframework.data.type.MethodsMetadata; -import org.springframework.lang.Nullable; -import org.springframework.util.Assert; - -/** - * ASM class visitor which looks for the class name and implemented types as well as for the methods defined in the - * class, exposing them through the {@link MethodsMetadata} interface. - * - * @author Mark Paluch - * @since 2.1 - * @see ClassMetadata - * @see MethodMetadata - * @see MethodMetadataReadingVisitor - */ -class MethodsMetadataReadingVisitor extends AnnotationMetadataReadingVisitor implements MethodsMetadata { - - /** - * Construct a new {@link MethodsMetadataReadingVisitor} given {@link ClassLoader}. - * - * @param classLoader may be {@literal null}. - */ - MethodsMetadataReadingVisitor(@Nullable ClassLoader classLoader) { - super(classLoader); - } - - /* - * (non-Javadoc) - * @see org.springframework.core.type.classreading.AnnotationMetadataReadingVisitor#visitMethod(int, java.lang.String, java.lang.String, java.lang.String, java.lang.String[]) - */ - @Override - public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { - - // Skip bridge methods - we're only interested in original user methods. - // On JDK 8, we'd otherwise run into double detection of the same method... - if ((access & Opcodes.ACC_BRIDGE) != 0) { - return super.visitMethod(access, name, desc, signature, exceptions); - } - - // Skip constructors - if (name.equals("")) { - return super.visitMethod(access, name, desc, signature, exceptions); - } - - MethodMetadataReadingVisitor visitor = new MethodMetadataReadingVisitor(name, access, getClassName(), - Type.getReturnType(desc).getClassName(), this.classLoader, this.methodMetadataSet); - - this.methodMetadataSet.add(visitor); - return visitor; - } - - /* - * (non-Javadoc) - * @see org.springframework.data.util.MethodsMetadata#getMethods() - */ - @Override - public Set getMethods() { - return Collections.unmodifiableSet(methodMetadataSet); - } - - /* - * (non-Javadoc) - * @see org.springframework.data.util.MethodsMetadata#getMethods(String) - */ - @Override - public Set getMethods(String name) { - - Assert.hasText(name, "Method name must not be null or empty"); - - Set result = new LinkedHashSet<>(4); - - for (MethodMetadata metadata : methodMetadataSet) { - if (metadata.getMethodName().equals(name)) { - result.add(metadata); - } - } - - return Collections.unmodifiableSet(result); - } -} diff --git a/src/test/java/org/springframework/data/type/classreading/DefaultMethodsMetadataReaderUnitTests.java b/src/test/java/org/springframework/data/type/classreading/DefaultMethodsMetadataReaderUnitTests.java index 7d0937084..a81ebea6d 100644 --- a/src/test/java/org/springframework/data/type/classreading/DefaultMethodsMetadataReaderUnitTests.java +++ b/src/test/java/org/springframework/data/type/classreading/DefaultMethodsMetadataReaderUnitTests.java @@ -23,7 +23,6 @@ import java.util.Iterator; import org.junit.Test; import org.springframework.core.type.MethodMetadata; import org.springframework.data.type.MethodsMetadata; -import org.springframework.data.type.MethodsMetadataReader; /** * Unit tests for {@link DefaultMethodsMetadataReader}. diff --git a/src/test/java/org/springframework/data/type/MethodsMetadataReaderFactoryUnitTests.java b/src/test/java/org/springframework/data/type/classreading/MethodsMetadataReaderFactoryUnitTests.java similarity index 94% rename from src/test/java/org/springframework/data/type/MethodsMetadataReaderFactoryUnitTests.java rename to src/test/java/org/springframework/data/type/classreading/MethodsMetadataReaderFactoryUnitTests.java index 02d477294..dae9853c7 100644 --- a/src/test/java/org/springframework/data/type/MethodsMetadataReaderFactoryUnitTests.java +++ b/src/test/java/org/springframework/data/type/classreading/MethodsMetadataReaderFactoryUnitTests.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.springframework.data.type; +package org.springframework.data.type.classreading; import static org.assertj.core.api.Assertions.*; @@ -24,6 +24,7 @@ import java.net.URLClassLoader; import org.junit.Test; import org.springframework.core.io.DefaultResourceLoader; +import org.springframework.data.type.classreading.MethodsMetadataReader; import org.springframework.data.type.classreading.MethodsMetadataReaderFactory; /**