Add SimpleAnnotationMeta classes and readers
Replace the existing ASM based readers with new implementations that also support MergedAnnotations. The meta-data classes themselves are now immutable, and constructed via separate reader classes. The `SimpleMetadataReader` class has been updated to return the new classes, however the old ones remain since some of them are public and might be being used directly. Closes gh-22884
This commit is contained in:
parent
8c2ccfe6a3
commit
7fbf3f97cd
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2018 the original author or authors.
|
* Copyright 2002-2019 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -39,6 +39,7 @@ import org.springframework.util.ReflectionUtils;
|
||||||
* @author Sam Brannen
|
* @author Sam Brannen
|
||||||
* @since 3.1.1
|
* @since 3.1.1
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
abstract class AbstractRecursiveAnnotationVisitor extends AnnotationVisitor {
|
abstract class AbstractRecursiveAnnotationVisitor extends AnnotationVisitor {
|
||||||
|
|
||||||
protected final Log logger = LogFactory.getLog(getClass());
|
protected final Log logger = LogFactory.getLog(getClass());
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2018 the original author or authors.
|
* Copyright 2002-2019 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -42,6 +42,7 @@ import org.springframework.util.ObjectUtils;
|
||||||
* @author Sam Brannen
|
* @author Sam Brannen
|
||||||
* @since 3.0
|
* @since 3.0
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
final class AnnotationAttributesReadingVisitor extends RecursiveAnnotationAttributesVisitor {
|
final class AnnotationAttributesReadingVisitor extends RecursiveAnnotationAttributesVisitor {
|
||||||
|
|
||||||
private final MultiValueMap<String, AnnotationAttributes> attributesMap;
|
private final MultiValueMap<String, AnnotationAttributes> attributesMap;
|
||||||
|
|
|
@ -49,6 +49,7 @@ import org.springframework.util.MultiValueMap;
|
||||||
* @author Sam Brannen
|
* @author Sam Brannen
|
||||||
* @since 2.5
|
* @since 2.5
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public class AnnotationMetadataReadingVisitor extends ClassMetadataReadingVisitor implements AnnotationMetadata {
|
public class AnnotationMetadataReadingVisitor extends ClassMetadataReadingVisitor implements AnnotationMetadata {
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
|
|
|
@ -42,6 +42,7 @@ import org.springframework.util.ObjectUtils;
|
||||||
* @author Sam Brannen
|
* @author Sam Brannen
|
||||||
* @since 4.0
|
* @since 4.0
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
abstract class AnnotationReadingVisitorUtils {
|
abstract class AnnotationReadingVisitorUtils {
|
||||||
|
|
||||||
public static AnnotationAttributes convertClassValues(Object annotatedElement,
|
public static AnnotationAttributes convertClassValues(Object annotatedElement,
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2017 the original author or authors.
|
* Copyright 2002-2019 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -43,6 +43,7 @@ import org.springframework.util.StringUtils;
|
||||||
* @author Chris Beams
|
* @author Chris Beams
|
||||||
* @since 2.5
|
* @since 2.5
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
class ClassMetadataReadingVisitor extends ClassVisitor implements ClassMetadata {
|
class ClassMetadataReadingVisitor extends ClassVisitor implements ClassMetadata {
|
||||||
|
|
||||||
private String className = "";
|
private String className = "";
|
||||||
|
|
|
@ -0,0 +1,204 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2002-2019 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.core.type.classreading;
|
||||||
|
|
||||||
|
import java.lang.annotation.Annotation;
|
||||||
|
import java.lang.reflect.Array;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
import org.springframework.asm.AnnotationVisitor;
|
||||||
|
import org.springframework.asm.SpringAsmInfo;
|
||||||
|
import org.springframework.asm.Type;
|
||||||
|
import org.springframework.core.annotation.AnnotationFilter;
|
||||||
|
import org.springframework.core.annotation.MergedAnnotation;
|
||||||
|
import org.springframework.lang.Nullable;
|
||||||
|
import org.springframework.util.ClassUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link AnnotationVisitor} that can be used to construct a
|
||||||
|
* {@link MergedAnnotation}.
|
||||||
|
*
|
||||||
|
* @author Phillip Webb
|
||||||
|
* @since 5.2
|
||||||
|
* @param <A> the annotation type
|
||||||
|
*/
|
||||||
|
class MergedAnnotationReadingVisitor<A extends Annotation> extends AnnotationVisitor {
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private final ClassLoader classLoader;
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private final Object source;
|
||||||
|
|
||||||
|
private final Class<A> annotationType;
|
||||||
|
|
||||||
|
private final Consumer<MergedAnnotation<A>> consumer;
|
||||||
|
|
||||||
|
private final Map<String, Object> attributes = new LinkedHashMap<>(4);
|
||||||
|
|
||||||
|
public MergedAnnotationReadingVisitor(ClassLoader classLoader,
|
||||||
|
@Nullable Object source, Class<A> annotationType,
|
||||||
|
Consumer<MergedAnnotation<A>> consumer) {
|
||||||
|
super(SpringAsmInfo.ASM_VERSION);
|
||||||
|
this.classLoader = classLoader;
|
||||||
|
this.source = source;
|
||||||
|
this.annotationType = annotationType;
|
||||||
|
this.consumer = consumer;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(String name, Object value) {
|
||||||
|
if (value instanceof Type) {
|
||||||
|
value = ((Type) value).getClassName();
|
||||||
|
}
|
||||||
|
this.attributes.put(name, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visitEnum(String name, String descriptor, String value) {
|
||||||
|
visitEnum(descriptor, value, enumValue -> this.attributes.put(name, enumValue));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AnnotationVisitor visitAnnotation(String name, String descriptor) {
|
||||||
|
return visitAnnotation(descriptor,
|
||||||
|
annotation -> this.attributes.put(name, annotation));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AnnotationVisitor visitArray(String name) {
|
||||||
|
return new ArrayVisitor(value -> this.attributes.put(name, value));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visitEnd() {
|
||||||
|
MergedAnnotation<A> annotation = MergedAnnotation.of(this.classLoader,
|
||||||
|
this.source, this.annotationType, this.attributes);
|
||||||
|
this.consumer.accept(annotation);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public <E extends Enum<E>> void visitEnum(String descriptor, String value,
|
||||||
|
Consumer<E> consumer) {
|
||||||
|
|
||||||
|
String className = Type.getType(descriptor).getClassName();
|
||||||
|
Class<E> type = (Class<E>) ClassUtils.resolveClassName(className, this.classLoader);
|
||||||
|
E enumValue = Enum.valueOf(type, value);
|
||||||
|
if (enumValue != null) {
|
||||||
|
consumer.accept(enumValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
private <T extends Annotation> AnnotationVisitor visitAnnotation(String descriptor,
|
||||||
|
Consumer<MergedAnnotation<T>> consumer) {
|
||||||
|
|
||||||
|
String className = Type.getType(descriptor).getClassName();
|
||||||
|
if (AnnotationFilter.PLAIN.matches(className)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
Class<T> type = (Class<T>) ClassUtils.resolveClassName(className,
|
||||||
|
this.classLoader);
|
||||||
|
return new MergedAnnotationReadingVisitor<>(this.classLoader, this.source, type,
|
||||||
|
consumer);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
static <A extends Annotation> AnnotationVisitor get(@Nullable ClassLoader classLoader,
|
||||||
|
@Nullable Supplier<Object> sourceSupplier, String descriptor, boolean visible,
|
||||||
|
Consumer<MergedAnnotation<A>> consumer) {
|
||||||
|
if (!visible) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
String typeName = Type.getType(descriptor).getClassName();
|
||||||
|
if (AnnotationFilter.PLAIN.matches(typeName)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
Object source = sourceSupplier != null ? sourceSupplier.get() : null;
|
||||||
|
try {
|
||||||
|
Class<A> annotationType = (Class<A>) ClassUtils.forName(typeName,
|
||||||
|
classLoader);
|
||||||
|
return new MergedAnnotationReadingVisitor<>(classLoader, source,
|
||||||
|
annotationType, consumer);
|
||||||
|
}
|
||||||
|
catch (ClassNotFoundException | LinkageError ex) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link AnnotationVisitor} to deal with array attributes.
|
||||||
|
*/
|
||||||
|
private class ArrayVisitor extends AnnotationVisitor {
|
||||||
|
|
||||||
|
private final List<Object> elements = new ArrayList<>();
|
||||||
|
|
||||||
|
private final Consumer<Object[]> consumer;
|
||||||
|
|
||||||
|
ArrayVisitor(Consumer<Object[]> consumer) {
|
||||||
|
super(SpringAsmInfo.ASM_VERSION);
|
||||||
|
this.consumer = consumer;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(String name, Object value) {
|
||||||
|
if (value instanceof Type) {
|
||||||
|
value = ((Type) value).getClassName();
|
||||||
|
}
|
||||||
|
this.elements.add(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visitEnum(String name, String descriptor, String value) {
|
||||||
|
MergedAnnotationReadingVisitor.this.visitEnum(descriptor, value,
|
||||||
|
enumValue -> this.elements.add(enumValue));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AnnotationVisitor visitAnnotation(String name, String descriptor) {
|
||||||
|
return MergedAnnotationReadingVisitor.this.visitAnnotation(descriptor,
|
||||||
|
annotation -> this.elements.add(annotation));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visitEnd() {
|
||||||
|
Class<?> componentType = getComponentType();
|
||||||
|
Object[] array = (Object[]) Array.newInstance(componentType,
|
||||||
|
this.elements.size());
|
||||||
|
this.consumer.accept(this.elements.toArray(array));
|
||||||
|
}
|
||||||
|
|
||||||
|
private Class<?> getComponentType() {
|
||||||
|
if (this.elements.isEmpty()) {
|
||||||
|
return Object.class;
|
||||||
|
}
|
||||||
|
Object firstElement = this.elements.get(0);
|
||||||
|
if(firstElement instanceof Enum) {
|
||||||
|
return ((Enum<?>) firstElement).getDeclaringClass();
|
||||||
|
}
|
||||||
|
return firstElement.getClass();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -45,6 +45,7 @@ import org.springframework.util.MultiValueMap;
|
||||||
* @author Phillip Webb
|
* @author Phillip Webb
|
||||||
* @since 3.0
|
* @since 3.0
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public class MethodMetadataReadingVisitor extends MethodVisitor implements MethodMetadata {
|
public class MethodMetadataReadingVisitor extends MethodVisitor implements MethodMetadata {
|
||||||
|
|
||||||
protected final String methodName;
|
protected final String methodName;
|
||||||
|
|
|
@ -34,6 +34,7 @@ import org.springframework.util.ObjectUtils;
|
||||||
* @author Juergen Hoeller
|
* @author Juergen Hoeller
|
||||||
* @since 3.1.1
|
* @since 3.1.1
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
class RecursiveAnnotationArrayVisitor extends AbstractRecursiveAnnotationVisitor {
|
class RecursiveAnnotationArrayVisitor extends AbstractRecursiveAnnotationVisitor {
|
||||||
|
|
||||||
private final String attributeName;
|
private final String attributeName;
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2018 the original author or authors.
|
* Copyright 2002-2019 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -28,6 +28,7 @@ import org.springframework.lang.Nullable;
|
||||||
* @author Juergen Hoeller
|
* @author Juergen Hoeller
|
||||||
* @since 3.1.1
|
* @since 3.1.1
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
class RecursiveAnnotationAttributesVisitor extends AbstractRecursiveAnnotationVisitor {
|
class RecursiveAnnotationAttributesVisitor extends AbstractRecursiveAnnotationVisitor {
|
||||||
|
|
||||||
protected final String annotationType;
|
protected final String annotationType;
|
||||||
|
|
|
@ -0,0 +1,157 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2002-2019 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.core.type.classreading;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.LinkedHashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import org.springframework.asm.Opcodes;
|
||||||
|
import org.springframework.core.annotation.MergedAnnotations;
|
||||||
|
import org.springframework.core.type.AnnotationMetadata;
|
||||||
|
import org.springframework.core.type.MethodMetadata;
|
||||||
|
import org.springframework.lang.Nullable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link AnnotationMetadata} created from a
|
||||||
|
* {@link SimpleAnnotationMetadataReadingVistor}.
|
||||||
|
*
|
||||||
|
* @author Phillip Webb
|
||||||
|
* @since 5.2
|
||||||
|
*/
|
||||||
|
final class SimpleAnnotationMetadata implements AnnotationMetadata {
|
||||||
|
|
||||||
|
private final String className;
|
||||||
|
|
||||||
|
private final int access;
|
||||||
|
|
||||||
|
private final String enclosingClassName;
|
||||||
|
|
||||||
|
private final String superClassName;
|
||||||
|
|
||||||
|
private final boolean independentInnerClass;
|
||||||
|
|
||||||
|
private final String[] interfaceNames;
|
||||||
|
|
||||||
|
private final String[] memberClassNames;
|
||||||
|
|
||||||
|
private final MethodMetadata[] annotatedMethods;
|
||||||
|
|
||||||
|
private final MergedAnnotations annotations;
|
||||||
|
|
||||||
|
private Set<String> annotationTypes;
|
||||||
|
|
||||||
|
SimpleAnnotationMetadata(String className, int access, String enclosingClassName,
|
||||||
|
String superClassName, boolean independentInnerClass, String[] interfaceNames,
|
||||||
|
String[] memberClassNames, MethodMetadata[] annotatedMethods,
|
||||||
|
MergedAnnotations annotations) {
|
||||||
|
this.className = className;
|
||||||
|
this.access = access;
|
||||||
|
this.enclosingClassName = enclosingClassName;
|
||||||
|
this.superClassName = superClassName;
|
||||||
|
this.independentInnerClass = independentInnerClass;
|
||||||
|
this.interfaceNames = interfaceNames;
|
||||||
|
this.memberClassNames = memberClassNames;
|
||||||
|
this.annotatedMethods = annotatedMethods;
|
||||||
|
this.annotations = annotations;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getClassName() {
|
||||||
|
return this.className;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isInterface() {
|
||||||
|
return (this.access & Opcodes.ACC_INTERFACE) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isAnnotation() {
|
||||||
|
return (this.access & Opcodes.ACC_ANNOTATION) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isAbstract() {
|
||||||
|
return (this.access & Opcodes.ACC_ABSTRACT) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isFinal() {
|
||||||
|
return (this.access & Opcodes.ACC_FINAL) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isIndependent() {
|
||||||
|
return this.enclosingClassName == null || this.independentInnerClass;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Nullable
|
||||||
|
public String getEnclosingClassName() {
|
||||||
|
return this.enclosingClassName;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Nullable
|
||||||
|
public String getSuperClassName() {
|
||||||
|
return this.superClassName;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String[] getInterfaceNames() {
|
||||||
|
return this.interfaceNames.clone();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String[] getMemberClassNames() {
|
||||||
|
return this.memberClassNames.clone();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<String> getAnnotationTypes() {
|
||||||
|
Set<String> annotationTypes = this.annotationTypes;
|
||||||
|
if (annotationTypes == null) {
|
||||||
|
annotationTypes = Collections.unmodifiableSet(
|
||||||
|
AnnotationMetadata.super.getAnnotationTypes());
|
||||||
|
this.annotationTypes = annotationTypes;
|
||||||
|
}
|
||||||
|
return annotationTypes;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<MethodMetadata> getAnnotatedMethods(String annotationName) {
|
||||||
|
Set<MethodMetadata> annotatedMethods = null;
|
||||||
|
for (int i = 0; i < this.annotatedMethods.length; i++) {
|
||||||
|
if (this.annotatedMethods[i].isAnnotated(annotationName)) {
|
||||||
|
if (annotatedMethods == null) {
|
||||||
|
annotatedMethods = new LinkedHashSet<>(4);
|
||||||
|
}
|
||||||
|
annotatedMethods.add(this.annotatedMethods[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return annotatedMethods != null ? annotatedMethods : Collections.emptySet();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MergedAnnotations getAnnotations() {
|
||||||
|
return this.annotations;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,197 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2002-2019 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.core.type.classreading;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.LinkedHashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import org.springframework.asm.AnnotationVisitor;
|
||||||
|
import org.springframework.asm.ClassVisitor;
|
||||||
|
import org.springframework.asm.MethodVisitor;
|
||||||
|
import org.springframework.asm.Opcodes;
|
||||||
|
import org.springframework.asm.SpringAsmInfo;
|
||||||
|
import org.springframework.core.annotation.MergedAnnotation;
|
||||||
|
import org.springframework.core.annotation.MergedAnnotations;
|
||||||
|
import org.springframework.core.type.MethodMetadata;
|
||||||
|
import org.springframework.lang.Nullable;
|
||||||
|
import org.springframework.util.ClassUtils;
|
||||||
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ASM class visitor to create {@link SimpleAnnotationMetadata}.
|
||||||
|
*
|
||||||
|
* @author Phillip Webb
|
||||||
|
* @since 5.2
|
||||||
|
*/
|
||||||
|
class SimpleAnnotationMetadataReadingVistor extends ClassVisitor {
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private final ClassLoader classLoader;
|
||||||
|
|
||||||
|
private String className = "";
|
||||||
|
|
||||||
|
private int access;
|
||||||
|
|
||||||
|
private String superClassName;
|
||||||
|
|
||||||
|
private String[] interfaceNames;
|
||||||
|
|
||||||
|
private String enclosingClassName;
|
||||||
|
|
||||||
|
private boolean independentInnerClass;
|
||||||
|
|
||||||
|
private Set<String> memberClassNames = new LinkedHashSet<>(4);
|
||||||
|
|
||||||
|
private List<MergedAnnotation<?>> annotations = new ArrayList<>();
|
||||||
|
|
||||||
|
private List<SimpleMethodMetadata> annotatedMethods = new ArrayList<>();
|
||||||
|
|
||||||
|
private SimpleAnnotationMetadata metadata;
|
||||||
|
|
||||||
|
private Source source;
|
||||||
|
|
||||||
|
SimpleAnnotationMetadataReadingVistor(@Nullable ClassLoader classLoader) {
|
||||||
|
super(SpringAsmInfo.ASM_VERSION);
|
||||||
|
this.classLoader = classLoader;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visit(int version, int access, String name, String signature,
|
||||||
|
@Nullable String supername, String[] interfaces) {
|
||||||
|
this.className = toClassName(name);
|
||||||
|
this.access = access;
|
||||||
|
if (supername != null && !isInterface(access)) {
|
||||||
|
this.superClassName = toClassName(supername);
|
||||||
|
}
|
||||||
|
this.interfaceNames = new String[interfaces.length];
|
||||||
|
for (int i = 0; i < interfaces.length; i++) {
|
||||||
|
this.interfaceNames[i] = toClassName(interfaces[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visitOuterClass(String owner, String name, String desc) {
|
||||||
|
this.enclosingClassName = toClassName(owner);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visitInnerClass(String name, @Nullable String outerName, String innerName,
|
||||||
|
int access) {
|
||||||
|
if (outerName != null) {
|
||||||
|
String className = toClassName(name);
|
||||||
|
String outerClassName = toClassName(outerName);
|
||||||
|
if (this.className.equals(className)) {
|
||||||
|
this.enclosingClassName = outerClassName;
|
||||||
|
this.independentInnerClass = ((access & Opcodes.ACC_STATIC) != 0);
|
||||||
|
}
|
||||||
|
else if (this.className.equals(outerClassName)) {
|
||||||
|
this.memberClassNames.add(className);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AnnotationVisitor visitAnnotation(String descriptor, boolean visible) {
|
||||||
|
return MergedAnnotationReadingVisitor.get(this.classLoader, this::getSource,
|
||||||
|
descriptor, visible, this.annotations::add);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MethodVisitor visitMethod(int access, String name, String descriptor,
|
||||||
|
String signature, String[] exceptions) {
|
||||||
|
// Skip bridge methods - we're only interested in original
|
||||||
|
// annotation-defining user methods. On JDK 8, we'd otherwise run into
|
||||||
|
// double detection of the same annotated method...
|
||||||
|
if (isBridge(access)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return new SimpleMethodMetadataReadingVistor(this.classLoader, this.className,
|
||||||
|
access, name, descriptor, this.annotatedMethods::add);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visitEnd() {
|
||||||
|
String[] memberClassNames = StringUtils.toStringArray(this.memberClassNames);
|
||||||
|
MethodMetadata[] annotatedMethods = this.annotatedMethods.toArray(new MethodMetadata[0]);
|
||||||
|
MergedAnnotations annotations = MergedAnnotations.of(this.annotations);
|
||||||
|
this.metadata = new SimpleAnnotationMetadata(this.className, this.access,
|
||||||
|
this.enclosingClassName, this.superClassName, this.independentInnerClass,
|
||||||
|
this.interfaceNames, memberClassNames, annotatedMethods, annotations);
|
||||||
|
}
|
||||||
|
|
||||||
|
public SimpleAnnotationMetadata getMetadata() {
|
||||||
|
return this.metadata;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Source getSource() {
|
||||||
|
Source source = this.source;
|
||||||
|
if (source == null) {
|
||||||
|
source = new Source(this.className);
|
||||||
|
this.source = source;
|
||||||
|
}
|
||||||
|
return source;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String toClassName(String name) {
|
||||||
|
return ClassUtils.convertResourcePathToClassName(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isBridge(int access) {
|
||||||
|
return (access & Opcodes.ACC_BRIDGE) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isInterface(int access) {
|
||||||
|
return (access & Opcodes.ACC_INTERFACE) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link MergedAnnotation} source.
|
||||||
|
*/
|
||||||
|
private static final class Source {
|
||||||
|
|
||||||
|
private final String className;
|
||||||
|
|
||||||
|
Source(String className) {
|
||||||
|
this.className = className;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return this.className.hashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (this == obj) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (obj == null || getClass() != obj.getClass()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return this.className.equals(((Source) obj).className);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return this.className;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2002-2017 the original author or authors.
|
* Copyright 2002-2019 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -31,45 +31,39 @@ import org.springframework.lang.Nullable;
|
||||||
* {@link MetadataReader} implementation based on an ASM
|
* {@link MetadataReader} implementation based on an ASM
|
||||||
* {@link org.springframework.asm.ClassReader}.
|
* {@link org.springframework.asm.ClassReader}.
|
||||||
*
|
*
|
||||||
* <p>Package-visible in order to allow for repackaging the ASM library
|
|
||||||
* without effect on users of the {@code core.type} package.
|
|
||||||
*
|
|
||||||
* @author Juergen Hoeller
|
* @author Juergen Hoeller
|
||||||
* @author Costin Leau
|
* @author Costin Leau
|
||||||
* @since 2.5
|
* @since 2.5
|
||||||
*/
|
*/
|
||||||
final class SimpleMetadataReader implements MetadataReader {
|
final class SimpleMetadataReader implements MetadataReader {
|
||||||
|
|
||||||
private final Resource resource;
|
private static final int PARSING_OPTIONS = ClassReader.SKIP_DEBUG
|
||||||
|
| ClassReader.SKIP_CODE | ClassReader.SKIP_FRAMES;
|
||||||
|
|
||||||
private final ClassMetadata classMetadata;
|
private final Resource resource;
|
||||||
|
|
||||||
private final AnnotationMetadata annotationMetadata;
|
private final AnnotationMetadata annotationMetadata;
|
||||||
|
|
||||||
|
|
||||||
SimpleMetadataReader(Resource resource, @Nullable ClassLoader classLoader) throws IOException {
|
SimpleMetadataReader(Resource resource, @Nullable ClassLoader classLoader) throws IOException {
|
||||||
InputStream is = new BufferedInputStream(resource.getInputStream());
|
SimpleAnnotationMetadataReadingVistor visitor = new SimpleAnnotationMetadataReadingVistor(classLoader);
|
||||||
ClassReader classReader;
|
getClassReader(resource).accept(visitor, PARSING_OPTIONS);
|
||||||
try {
|
|
||||||
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: " + resource, ex);
|
|
||||||
}
|
|
||||||
finally {
|
|
||||||
is.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
AnnotationMetadataReadingVisitor visitor = new AnnotationMetadataReadingVisitor(classLoader);
|
|
||||||
classReader.accept(visitor, ClassReader.SKIP_DEBUG);
|
|
||||||
|
|
||||||
this.annotationMetadata = visitor;
|
|
||||||
// (since AnnotationMetadataReadingVisitor extends ClassMetadataReadingVisitor)
|
|
||||||
this.classMetadata = visitor;
|
|
||||||
this.resource = resource;
|
this.resource = resource;
|
||||||
|
this.annotationMetadata = visitor.getMetadata();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private ClassReader getClassReader(Resource resource)
|
||||||
|
throws IOException, NestedIOException {
|
||||||
|
try (InputStream is = new BufferedInputStream(resource.getInputStream())) {
|
||||||
|
try {
|
||||||
|
return 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: " + resource, ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Resource getResource() {
|
public Resource getResource() {
|
||||||
|
@ -78,7 +72,7 @@ final class SimpleMetadataReader implements MetadataReader {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ClassMetadata getClassMetadata() {
|
public ClassMetadata getClassMetadata() {
|
||||||
return this.classMetadata;
|
return this.annotationMetadata;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -0,0 +1,95 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2002-2019 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.core.type.classreading;
|
||||||
|
|
||||||
|
import org.springframework.asm.Opcodes;
|
||||||
|
import org.springframework.core.annotation.MergedAnnotations;
|
||||||
|
import org.springframework.core.type.MethodMetadata;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link MethodMetadata} created from a
|
||||||
|
* {@link SimpleMethodMetadataReadingVistor}.
|
||||||
|
*
|
||||||
|
* @author Phillip Webb
|
||||||
|
* @since 5.2
|
||||||
|
*/
|
||||||
|
final class SimpleMethodMetadata implements MethodMetadata {
|
||||||
|
|
||||||
|
private final String methodName;
|
||||||
|
|
||||||
|
private final int access;
|
||||||
|
|
||||||
|
private final String declaringClassName;
|
||||||
|
|
||||||
|
private final String returnTypeName;
|
||||||
|
|
||||||
|
private final MergedAnnotations annotations;
|
||||||
|
|
||||||
|
public SimpleMethodMetadata(String methodName, int access, String declaringClassName,
|
||||||
|
String returnTypeName, MergedAnnotations annotations) {
|
||||||
|
this.methodName = methodName;
|
||||||
|
this.access = access;
|
||||||
|
this.declaringClassName = declaringClassName;
|
||||||
|
this.returnTypeName = returnTypeName;
|
||||||
|
this.annotations = annotations;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getMethodName() {
|
||||||
|
return this.methodName;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getDeclaringClassName() {
|
||||||
|
return this.declaringClassName;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getReturnTypeName() {
|
||||||
|
return this.returnTypeName;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isAbstract() {
|
||||||
|
return (this.access & Opcodes.ACC_ABSTRACT) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isStatic() {
|
||||||
|
return (this.access & Opcodes.ACC_STATIC) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isFinal() {
|
||||||
|
return (this.access & Opcodes.ACC_FINAL) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isOverridable() {
|
||||||
|
return !isStatic() && !isFinal() && !isPrivate();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isPrivate() {
|
||||||
|
return (this.access & Opcodes.ACC_PRIVATE) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MergedAnnotations getAnnotations() {
|
||||||
|
return this.annotations;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,162 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2002-2019 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.core.type.classreading;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
import org.springframework.asm.AnnotationVisitor;
|
||||||
|
import org.springframework.asm.MethodVisitor;
|
||||||
|
import org.springframework.asm.SpringAsmInfo;
|
||||||
|
import org.springframework.asm.Type;
|
||||||
|
import org.springframework.core.annotation.MergedAnnotation;
|
||||||
|
import org.springframework.core.annotation.MergedAnnotations;
|
||||||
|
import org.springframework.core.type.MethodMetadata;
|
||||||
|
import org.springframework.lang.Nullable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link MethodMetadata} returned from a {@link SimpleMetadataReader}.
|
||||||
|
*
|
||||||
|
* @author Phillip Webb
|
||||||
|
* @since 5.2
|
||||||
|
*/
|
||||||
|
class SimpleMethodMetadataReadingVistor extends MethodVisitor {
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private final ClassLoader classLoader;
|
||||||
|
|
||||||
|
private final String declaringClassName;
|
||||||
|
|
||||||
|
private final int access;
|
||||||
|
|
||||||
|
private final String name;
|
||||||
|
|
||||||
|
private final String descriptor;
|
||||||
|
|
||||||
|
private final List<MergedAnnotation<?>> annotations = new ArrayList<MergedAnnotation<?>>(
|
||||||
|
4);
|
||||||
|
|
||||||
|
private final Consumer<SimpleMethodMetadata> consumer;
|
||||||
|
|
||||||
|
private Source source;
|
||||||
|
|
||||||
|
SimpleMethodMetadataReadingVistor(@Nullable ClassLoader classLoader,
|
||||||
|
String declaringClassName, int access, String name, String descriptor,
|
||||||
|
Consumer<SimpleMethodMetadata> consumer) {
|
||||||
|
super(SpringAsmInfo.ASM_VERSION);
|
||||||
|
this.classLoader = classLoader;
|
||||||
|
this.declaringClassName = declaringClassName;
|
||||||
|
this.access = access;
|
||||||
|
this.name = name;
|
||||||
|
this.descriptor = descriptor;
|
||||||
|
this.consumer = consumer;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AnnotationVisitor visitAnnotation(String descriptor, boolean visible) {
|
||||||
|
return MergedAnnotationReadingVisitor.get(this.classLoader, this::getSource,
|
||||||
|
descriptor, visible, this.annotations::add);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void visitEnd() {
|
||||||
|
if (!this.annotations.isEmpty()) {
|
||||||
|
String returnTypeName = Type.getReturnType(this.descriptor).getClassName();
|
||||||
|
MergedAnnotations annotations = MergedAnnotations.of(this.annotations);
|
||||||
|
SimpleMethodMetadata metadata = new SimpleMethodMetadata(this.name,
|
||||||
|
this.access, this.declaringClassName, returnTypeName, annotations);
|
||||||
|
this.consumer.accept(metadata);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Object getSource() {
|
||||||
|
Source source = this.source;
|
||||||
|
if (source == null) {
|
||||||
|
source = new Source(this.declaringClassName, this.name, this.descriptor);
|
||||||
|
this.source = source;
|
||||||
|
}
|
||||||
|
return source;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@link MergedAnnotation} source.
|
||||||
|
*/
|
||||||
|
static final class Source {
|
||||||
|
|
||||||
|
private final String declaringClassName;
|
||||||
|
|
||||||
|
private final String name;
|
||||||
|
|
||||||
|
private final String descriptor;
|
||||||
|
|
||||||
|
private String string;
|
||||||
|
|
||||||
|
Source(String declaringClassName, String name, String descriptor) {
|
||||||
|
this.declaringClassName = declaringClassName;
|
||||||
|
this.name = name;
|
||||||
|
this.descriptor = descriptor;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
int result = 1;
|
||||||
|
result = 31 * result + this.declaringClassName.hashCode();
|
||||||
|
result = 31 * result + this.name.hashCode();
|
||||||
|
result = 31 * result + this.descriptor.hashCode();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (this == obj) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (obj == null || getClass() != obj.getClass()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
Source other = (Source) obj;
|
||||||
|
boolean result = true;
|
||||||
|
result = result &= this.declaringClassName.equals(other.declaringClassName);
|
||||||
|
result = result &= this.name.equals(other.name);
|
||||||
|
result = result &= this.descriptor.equals(other.descriptor);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
String string = this.string;
|
||||||
|
if (string == null) {
|
||||||
|
StringBuilder builder = new StringBuilder();
|
||||||
|
builder.append(this.declaringClassName);
|
||||||
|
builder.append(".");
|
||||||
|
builder.append(this.name);
|
||||||
|
Type[] argumentTypes = Type.getArgumentTypes(this.descriptor);
|
||||||
|
builder.append("(");
|
||||||
|
for (Type type : argumentTypes) {
|
||||||
|
builder.append(type.getClassName());
|
||||||
|
}
|
||||||
|
builder.append(")");
|
||||||
|
string = builder.toString();
|
||||||
|
this.string = string;
|
||||||
|
}
|
||||||
|
return string;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -35,6 +35,7 @@ import static org.assertj.core.api.Assertions.*;
|
||||||
*
|
*
|
||||||
* @author Phillip Webb
|
* @author Phillip Webb
|
||||||
*/
|
*/
|
||||||
|
@SuppressWarnings("deprecation")
|
||||||
public class AnnotationMetadataReadingVisitorTests
|
public class AnnotationMetadataReadingVisitorTests
|
||||||
extends AbstractAnnotationMetadataTests {
|
extends AbstractAnnotationMetadataTests {
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,264 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2002-2019 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.core.type.classreading;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import org.springframework.asm.AnnotationVisitor;
|
||||||
|
import org.springframework.asm.ClassReader;
|
||||||
|
import org.springframework.asm.ClassVisitor;
|
||||||
|
import org.springframework.asm.SpringAsmInfo;
|
||||||
|
import org.springframework.core.annotation.MergedAnnotation;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests for {@link MergedAnnotationReadingVisitor}.
|
||||||
|
*
|
||||||
|
* @author Phillip Webb
|
||||||
|
*/
|
||||||
|
public class MergedAnnotationMetadataVisitorTests {
|
||||||
|
|
||||||
|
private MergedAnnotation<?> annotation;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void visitWhenHasSimpleTypesCreatesAnnotation() {
|
||||||
|
loadFrom(WithSimpleTypesAnnotation.class);
|
||||||
|
assertThat(this.annotation.getType()).isEqualTo(SimpleTypesAnnotation.class);
|
||||||
|
assertThat(this.annotation.getValue("stringValue")).contains("string");
|
||||||
|
assertThat(this.annotation.getValue("byteValue")).contains((byte) 1);
|
||||||
|
assertThat(this.annotation.getValue("shortValue")).contains((short) 2);
|
||||||
|
assertThat(this.annotation.getValue("intValue")).contains(3);
|
||||||
|
assertThat(this.annotation.getValue("longValue")).contains(4L);
|
||||||
|
assertThat(this.annotation.getValue("booleanValue")).contains(true);
|
||||||
|
assertThat(this.annotation.getValue("charValue")).contains('c');
|
||||||
|
assertThat(this.annotation.getValue("doubleValue")).contains(5.0);
|
||||||
|
assertThat(this.annotation.getValue("floatValue")).contains(6.0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void visitWhenHasSimpleArrayTypesCreatesAnnotation() {
|
||||||
|
loadFrom(WithSimpleArrayTypesAnnotation.class);
|
||||||
|
assertThat(this.annotation.getType()).isEqualTo(SimpleArrayTypesAnnotation.class);
|
||||||
|
assertThat(this.annotation.getValue("stringValue")).contains(
|
||||||
|
new String[] { "string" });
|
||||||
|
assertThat(this.annotation.getValue("byteValue")).contains(new byte[] { 1 });
|
||||||
|
assertThat(this.annotation.getValue("shortValue")).contains(new short[] { 2 });
|
||||||
|
assertThat(this.annotation.getValue("intValue")).contains(new int[] { 3 });
|
||||||
|
assertThat(this.annotation.getValue("longValue")).contains(new long[] { 4 });
|
||||||
|
assertThat(this.annotation.getValue("booleanValue")).contains(
|
||||||
|
new boolean[] { true });
|
||||||
|
assertThat(this.annotation.getValue("charValue")).contains(new char[] { 'c' });
|
||||||
|
assertThat(this.annotation.getValue("doubleValue")).contains(
|
||||||
|
new double[] { 5.0 });
|
||||||
|
assertThat(this.annotation.getValue("floatValue")).contains(new float[] { 6.0f });
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void visitWhenHasEmptySimpleArrayTypesCreatesAnnotation() {
|
||||||
|
loadFrom(WithSimpleEmptyArrayTypesAnnotation.class);
|
||||||
|
assertThat(this.annotation.getType()).isEqualTo(SimpleArrayTypesAnnotation.class);
|
||||||
|
assertThat(this.annotation.getValue("stringValue")).contains(new String[] {});
|
||||||
|
assertThat(this.annotation.getValue("byteValue")).contains(new byte[] {});
|
||||||
|
assertThat(this.annotation.getValue("shortValue")).contains(new short[] {});
|
||||||
|
assertThat(this.annotation.getValue("intValue")).contains(new int[] {});
|
||||||
|
assertThat(this.annotation.getValue("longValue")).contains(new long[] {});
|
||||||
|
assertThat(this.annotation.getValue("booleanValue")).contains(new boolean[] {});
|
||||||
|
assertThat(this.annotation.getValue("charValue")).contains(new char[] {});
|
||||||
|
assertThat(this.annotation.getValue("doubleValue")).contains(new double[] {});
|
||||||
|
assertThat(this.annotation.getValue("floatValue")).contains(new float[] {});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void visitWhenHasEnumAttributesCreatesAnnotation() {
|
||||||
|
loadFrom(WithEnumAnnotation.class);
|
||||||
|
assertThat(this.annotation.getType()).isEqualTo(EnumAnnotation.class);
|
||||||
|
assertThat(this.annotation.getValue("enumValue")).contains(ExampleEnum.ONE);
|
||||||
|
assertThat(this.annotation.getValue("enumArrayValue")).contains(
|
||||||
|
new ExampleEnum[] { ExampleEnum.ONE, ExampleEnum.TWO });
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void visitWhenHasAnnotationAttributesCreatesAnnotation() {
|
||||||
|
loadFrom(WithAnnotationAnnotation.class);
|
||||||
|
assertThat(this.annotation.getType()).isEqualTo(AnnotationAnnotation.class);
|
||||||
|
MergedAnnotation<NestedAnnotation> value = this.annotation.getAnnotation(
|
||||||
|
"annotationValue", NestedAnnotation.class);
|
||||||
|
assertThat(value.isPresent()).isTrue();
|
||||||
|
assertThat(value.getString(MergedAnnotation.VALUE)).isEqualTo("a");
|
||||||
|
MergedAnnotation<NestedAnnotation>[] arrayValue = this.annotation.getAnnotationArray(
|
||||||
|
"annotationArrayValue", NestedAnnotation.class);
|
||||||
|
assertThat(arrayValue).hasSize(2);
|
||||||
|
assertThat(arrayValue[0].getString(MergedAnnotation.VALUE)).isEqualTo("b");
|
||||||
|
assertThat(arrayValue[1].getString(MergedAnnotation.VALUE)).isEqualTo("c");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void visitWhenHasClassAttributesCreatesAnnotation() {
|
||||||
|
loadFrom(WithClassAnnotation.class);
|
||||||
|
assertThat(this.annotation.getType()).isEqualTo(ClassAnnotation.class);
|
||||||
|
assertThat(this.annotation.getString("classValue")).isEqualTo(InputStream.class.getName());
|
||||||
|
assertThat(this.annotation.getClass("classValue")).isEqualTo(InputStream.class);
|
||||||
|
assertThat(this.annotation.getValue("classValue")).contains(InputStream.class);
|
||||||
|
assertThat(this.annotation.getStringArray("classArrayValue")).containsExactly(OutputStream.class.getName());
|
||||||
|
assertThat(this.annotation.getValue("classArrayValue")).contains(new Class<?>[] {OutputStream.class});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void loadFrom(Class<?> type) {
|
||||||
|
ClassVisitor visitor = new ClassVisitor(SpringAsmInfo.ASM_VERSION) {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AnnotationVisitor visitAnnotation(String descriptor, boolean visible) {
|
||||||
|
return MergedAnnotationReadingVisitor.get(getClass().getClassLoader(),
|
||||||
|
null, descriptor, visible,
|
||||||
|
annotation -> MergedAnnotationMetadataVisitorTests.this.annotation = annotation);
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
try {
|
||||||
|
new ClassReader(type.getName()).accept(visitor, ClassReader.SKIP_DEBUG
|
||||||
|
| ClassReader.SKIP_CODE | ClassReader.SKIP_FRAMES);
|
||||||
|
}
|
||||||
|
catch (IOException ex) {
|
||||||
|
throw new IllegalStateException(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@SimpleTypesAnnotation(stringValue = "string", byteValue = 1, shortValue = 2, intValue = 3, longValue = 4, booleanValue = true, charValue = 'c', doubleValue = 5.0, floatValue = 6.0f)
|
||||||
|
static class WithSimpleTypesAnnotation {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
@interface SimpleTypesAnnotation {
|
||||||
|
|
||||||
|
String stringValue();
|
||||||
|
|
||||||
|
byte byteValue();
|
||||||
|
|
||||||
|
short shortValue();
|
||||||
|
|
||||||
|
int intValue();
|
||||||
|
|
||||||
|
long longValue();
|
||||||
|
|
||||||
|
boolean booleanValue();
|
||||||
|
|
||||||
|
char charValue();
|
||||||
|
|
||||||
|
double doubleValue();
|
||||||
|
|
||||||
|
float floatValue();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@SimpleArrayTypesAnnotation(stringValue = "string", byteValue = 1, shortValue = 2, intValue = 3, longValue = 4, booleanValue = true, charValue = 'c', doubleValue = 5.0, floatValue = 6.0f)
|
||||||
|
static class WithSimpleArrayTypesAnnotation {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@SimpleArrayTypesAnnotation(stringValue = {}, byteValue = {}, shortValue = {}, intValue = {}, longValue = {}, booleanValue = {}, charValue = {}, doubleValue = {}, floatValue = {})
|
||||||
|
static class WithSimpleEmptyArrayTypesAnnotation {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
@interface SimpleArrayTypesAnnotation {
|
||||||
|
|
||||||
|
String[] stringValue();
|
||||||
|
|
||||||
|
byte[] byteValue();
|
||||||
|
|
||||||
|
short[] shortValue();
|
||||||
|
|
||||||
|
int[] intValue();
|
||||||
|
|
||||||
|
long[] longValue();
|
||||||
|
|
||||||
|
boolean[] booleanValue();
|
||||||
|
|
||||||
|
char[] charValue();
|
||||||
|
|
||||||
|
double[] doubleValue();
|
||||||
|
|
||||||
|
float[] floatValue();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@EnumAnnotation(enumValue = ExampleEnum.ONE, enumArrayValue = { ExampleEnum.ONE,
|
||||||
|
ExampleEnum.TWO })
|
||||||
|
static class WithEnumAnnotation {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
@interface EnumAnnotation {
|
||||||
|
|
||||||
|
ExampleEnum enumValue();
|
||||||
|
|
||||||
|
ExampleEnum[] enumArrayValue();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
enum ExampleEnum {
|
||||||
|
ONE, TWO, THREE
|
||||||
|
}
|
||||||
|
|
||||||
|
@AnnotationAnnotation(annotationValue = @NestedAnnotation("a"), annotationArrayValue = {
|
||||||
|
@NestedAnnotation("b"), @NestedAnnotation("c") })
|
||||||
|
static class WithAnnotationAnnotation {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
@interface AnnotationAnnotation {
|
||||||
|
|
||||||
|
NestedAnnotation annotationValue();
|
||||||
|
|
||||||
|
NestedAnnotation[] annotationArrayValue();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
@interface NestedAnnotation {
|
||||||
|
|
||||||
|
String value() default "";
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@ClassAnnotation(classValue = InputStream.class, classArrayValue = OutputStream.class)
|
||||||
|
static class WithClassAnnotation {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
@interface ClassAnnotation {
|
||||||
|
|
||||||
|
Class<?> classValue();
|
||||||
|
|
||||||
|
Class<?>[] classArrayValue();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,43 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2002-2019 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.core.type.classreading;
|
||||||
|
|
||||||
|
import org.springframework.core.type.AbstractAnnotationMetadataTests;
|
||||||
|
import org.springframework.core.type.AnnotationMetadata;
|
||||||
|
import org.springframework.core.type.classreading.SimpleMetadataReaderFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests for {@link SimpleAnnotationMetadata} and
|
||||||
|
* {@link SimpleAnnotationMetadataReadingVistor}.
|
||||||
|
*
|
||||||
|
* @author Phillip Webb
|
||||||
|
*/
|
||||||
|
public class SimpleAnnotationMetadataTests extends AbstractAnnotationMetadataTests {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected AnnotationMetadata get(Class<?> source) {
|
||||||
|
try {
|
||||||
|
return new SimpleMetadataReaderFactory(
|
||||||
|
source.getClassLoader()).getMetadataReader(
|
||||||
|
source.getName()).getAnnotationMetadata();
|
||||||
|
}
|
||||||
|
catch (Exception ex) {
|
||||||
|
throw new IllegalStateException(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,43 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2002-2019 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.core.type.classreading;
|
||||||
|
|
||||||
|
import org.springframework.core.type.AbstractMethodMetadataTests;
|
||||||
|
import org.springframework.core.type.AnnotationMetadata;
|
||||||
|
import org.springframework.core.type.classreading.SimpleMetadataReaderFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests for {@link SimpleMethodMetadata} and
|
||||||
|
* {@link SimpleMethodMetadataReadingVistor}.
|
||||||
|
*
|
||||||
|
* @author Phillip Webb
|
||||||
|
*/
|
||||||
|
public class SimpleMethodMetadataTests extends AbstractMethodMetadataTests {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected AnnotationMetadata get(Class<?> source) {
|
||||||
|
try {
|
||||||
|
return new SimpleMetadataReaderFactory(
|
||||||
|
source.getClassLoader()).getMetadataReader(
|
||||||
|
source.getName()).getAnnotationMetadata();
|
||||||
|
}
|
||||||
|
catch (Exception ex) {
|
||||||
|
throw new IllegalStateException(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue