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.
This commit is contained in:
committed by
Mark Paluch
parent
9e013d3c14
commit
5eb10a0817
@@ -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<PropertyDescriptor> collectDescriptors(Class<?> type) {
|
||||
|
||||
List<PropertyDescriptor> result = new ArrayList<>();
|
||||
|
||||
Optional<MethodsMetadata> metadata = getMetadata(type);
|
||||
Stream<PropertyDescriptor> stream = Arrays.stream(BeanUtils.getPropertyDescriptors(type))//
|
||||
.filter(it -> !hasDefaultGetter(it));
|
||||
|
||||
Stream<PropertyDescriptor> 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<String> 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<String, Integer> getMethodOrder(MethodsMetadata metadata) {
|
||||
|
||||
List<String> 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<MethodsMetadata> 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<MethodsMetadata> 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<PropertyDescriptor> 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<PropertyDescriptor> collectDescriptors() {
|
||||
|
||||
Stream<PropertyDescriptor> allButDefaultGetters = Arrays.stream(BeanUtils.getPropertyDescriptors(type)) //
|
||||
.filter(it -> !hasDefaultGetter(it));
|
||||
|
||||
Stream<PropertyDescriptor> ownDescriptors = metadata.map(it -> filterAndOrder(allButDefaultGetters, it))
|
||||
.orElse(allButDefaultGetters);
|
||||
|
||||
Stream<PropertyDescriptor> 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<PropertyDescriptor> filterAndOrder(Stream<PropertyDescriptor> source,
|
||||
MethodsMetadata metadata) {
|
||||
|
||||
Map<String, Integer> 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<Class<?>> 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<Class<?>> 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<MethodsMetadata> 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<String, Integer> getMethodOrder(MethodsMetadata metadata) {
|
||||
|
||||
List<String> 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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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("<init>")) {
|
||||
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<MethodMetadata> getMethods() {
|
||||
return Collections.unmodifiableSet(methodMetadataSet);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.util.MethodsMetadata#getMethods(String)
|
||||
*/
|
||||
@Override
|
||||
public Set<MethodMetadata> 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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
/**
|
||||
|
||||
@@ -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("<init>")) {
|
||||
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<MethodMetadata> getMethods() {
|
||||
return Collections.unmodifiableSet(methodMetadataSet);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.util.MethodsMetadata#getMethods(String)
|
||||
*/
|
||||
@Override
|
||||
public Set<MethodMetadata> getMethods(String name) {
|
||||
|
||||
Assert.hasText(name, "Method name must not be null or empty");
|
||||
|
||||
Set<MethodMetadata> result = new LinkedHashSet<>(4);
|
||||
|
||||
for (MethodMetadata metadata : methodMetadataSet) {
|
||||
if (metadata.getMethodName().equals(name)) {
|
||||
result.add(metadata);
|
||||
}
|
||||
}
|
||||
|
||||
return Collections.unmodifiableSet(result);
|
||||
}
|
||||
}
|
||||
@@ -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}.
|
||||
|
||||
@@ -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;
|
||||
|
||||
/**
|
||||
Reference in New Issue
Block a user