Skip to content

Commit

Permalink
Add SimpleAnnotationMeta classes and readers
Browse files Browse the repository at this point in the history
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 spring-projectsgh-22884
  • Loading branch information
philwebb committed May 6, 2019
1 parent 5c00cc3 commit 4e25363
Show file tree
Hide file tree
Showing 18 changed files with 1,198 additions and 30 deletions.
Original file line number Diff line number Diff line change
@@ -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");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -39,6 +39,7 @@
* @author Sam Brannen
* @since 3.1.1
*/
@Deprecated
abstract class AbstractRecursiveAnnotationVisitor extends AnnotationVisitor {

protected final Log logger = LogFactory.getLog(getClass());
Expand Down
Original file line number Diff line number Diff line change
@@ -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");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -42,6 +42,7 @@
* @author Sam Brannen
* @since 3.0
*/
@Deprecated
final class AnnotationAttributesReadingVisitor extends RecursiveAnnotationAttributesVisitor {

private final MultiValueMap<String, AnnotationAttributes> attributesMap;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
* @author Sam Brannen
* @since 2.5
*/
@Deprecated
public class AnnotationMetadataReadingVisitor extends ClassMetadataReadingVisitor implements AnnotationMetadata {

@Nullable
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
* @author Sam Brannen
* @since 4.0
*/
@Deprecated
abstract class AnnotationReadingVisitorUtils {

public static AnnotationAttributes convertClassValues(Object annotatedElement,
Expand Down
Original file line number Diff line number Diff line change
@@ -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");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -43,6 +43,7 @@
* @author Chris Beams
* @since 2.5
*/
@Deprecated
class ClassMetadataReadingVisitor extends ClassVisitor implements ClassMetadata {

private String className = "";
Expand Down
Original file line number Diff line number Diff line change
@@ -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();
}

}

}
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
* @author Phillip Webb
* @since 3.0
*/
@Deprecated
public class MethodMetadataReadingVisitor extends MethodVisitor implements MethodMetadata {

protected final String methodName;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
* @author Juergen Hoeller
* @since 3.1.1
*/
@Deprecated
class RecursiveAnnotationArrayVisitor extends AbstractRecursiveAnnotationVisitor {

private final String attributeName;
Expand Down
Original file line number Diff line number Diff line change
@@ -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");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -28,6 +28,7 @@
* @author Juergen Hoeller
* @since 3.1.1
*/
@Deprecated
class RecursiveAnnotationAttributesVisitor extends AbstractRecursiveAnnotationVisitor {

protected final String annotationType;
Expand Down
Loading

0 comments on commit 4e25363

Please sign in to comment.