diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessor.java b/spring-beans/src/main/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessor.java index cd5ce1ea7db..f711222c0f1 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessor.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/annotation/AutowiredAnnotationBeanPostProcessor.java @@ -83,6 +83,7 @@ import org.springframework.core.annotation.MergedAnnotations; import org.springframework.core.type.AnnotationMetadata; import org.springframework.core.type.MethodMetadata; import org.springframework.core.type.classreading.MetadataReaderFactory; +import org.springframework.core.type.classreading.SimpleMetadataReaderFactory; import org.springframework.javapoet.ClassName; import org.springframework.javapoet.CodeBlock; import org.springframework.lang.Nullable; @@ -283,7 +284,7 @@ public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationA "AutowiredAnnotationBeanPostProcessor requires a ConfigurableListableBeanFactory: " + beanFactory); } this.beanFactory = clbf; - this.metadataReaderFactory = MetadataReaderFactory.create(clbf.getBeanClassLoader()); + this.metadataReaderFactory = new SimpleMetadataReaderFactory(clbf.getBeanClassLoader()); } diff --git a/spring-core/spring-core.gradle b/spring-core/spring-core.gradle index 1ea7bd08c54..a08d0e16672 100644 --- a/spring-core/spring-core.gradle +++ b/spring-core/spring-core.gradle @@ -11,7 +11,7 @@ apply plugin: "kotlin" apply plugin: "kotlinx-serialization" multiRelease { - targetVersions 17, 21, 24 + targetVersions 17, 21 } def javapoetVersion = "1.13.0" @@ -25,10 +25,6 @@ configurations { graalvm } -springFramework { - enableJavaPreviewFeatures = true -} - task javapoetRepackJar(type: ShadowJar) { archiveBaseName = 'spring-javapoet-repack' archiveVersion = javapoetVersion diff --git a/spring-core/src/main/java/org/springframework/core/type/classreading/CachingMetadataReaderFactory.java b/spring-core/src/main/java/org/springframework/core/type/classreading/CachingMetadataReaderFactory.java index b29a7215388..9d9956fcf26 100644 --- a/spring-core/src/main/java/org/springframework/core/type/classreading/CachingMetadataReaderFactory.java +++ b/spring-core/src/main/java/org/springframework/core/type/classreading/CachingMetadataReaderFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2024 the original author or authors. + * Copyright 2002-2022 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. @@ -35,13 +35,11 @@ import org.springframework.lang.Nullable; * @author Costin Leau * @since 2.5 */ -public class CachingMetadataReaderFactory implements MetadataReaderFactory { +public class CachingMetadataReaderFactory extends SimpleMetadataReaderFactory { /** Default maximum number of entries for a local MetadataReader cache: 256. */ public static final int DEFAULT_CACHE_LIMIT = 256; - private final MetadataReaderFactory delegate; - /** MetadataReader cache: either local or shared at the ResourceLoader level. */ @Nullable private Map metadataReaderCache; @@ -52,7 +50,7 @@ public class CachingMetadataReaderFactory implements MetadataReaderFactory { * using a local resource cache. */ public CachingMetadataReaderFactory() { - this.delegate = MetadataReaderFactory.create((ClassLoader) null); + super(); setCacheLimit(DEFAULT_CACHE_LIMIT); } @@ -62,7 +60,7 @@ public class CachingMetadataReaderFactory implements MetadataReaderFactory { * @param classLoader the ClassLoader to use */ public CachingMetadataReaderFactory(@Nullable ClassLoader classLoader) { - this.delegate = MetadataReaderFactory.create(classLoader); + super(classLoader); setCacheLimit(DEFAULT_CACHE_LIMIT); } @@ -74,7 +72,7 @@ public class CachingMetadataReaderFactory implements MetadataReaderFactory { * @see DefaultResourceLoader#getResourceCache */ public CachingMetadataReaderFactory(@Nullable ResourceLoader resourceLoader) { - this.delegate = MetadataReaderFactory.create(resourceLoader); + super(resourceLoader); if (resourceLoader instanceof DefaultResourceLoader defaultResourceLoader) { this.metadataReaderCache = defaultResourceLoader.getResourceCache(MetadataReader.class); } @@ -83,6 +81,7 @@ public class CachingMetadataReaderFactory implements MetadataReaderFactory { } } + /** * Specify the maximum number of entries for the MetadataReader cache. *

Default is 256 for a local cache, whereas a shared cache is @@ -113,10 +112,6 @@ public class CachingMetadataReaderFactory implements MetadataReaderFactory { } } - @Override - public MetadataReader getMetadataReader(String className) throws IOException { - return this.delegate.getMetadataReader(className); - } @Override public MetadataReader getMetadataReader(Resource resource) throws IOException { @@ -124,7 +119,7 @@ public class CachingMetadataReaderFactory implements MetadataReaderFactory { // No synchronization necessary... MetadataReader metadataReader = this.metadataReaderCache.get(resource); if (metadataReader == null) { - metadataReader = this.delegate.getMetadataReader(resource); + metadataReader = super.getMetadataReader(resource); this.metadataReaderCache.put(resource, metadataReader); } return metadataReader; @@ -133,14 +128,14 @@ public class CachingMetadataReaderFactory implements MetadataReaderFactory { synchronized (this.metadataReaderCache) { MetadataReader metadataReader = this.metadataReaderCache.get(resource); if (metadataReader == null) { - metadataReader = this.delegate.getMetadataReader(resource); + metadataReader = super.getMetadataReader(resource); this.metadataReaderCache.put(resource, metadataReader); } return metadataReader; } } else { - return this.delegate.getMetadataReader(resource); + return super.getMetadataReader(resource); } } diff --git a/spring-core/src/main/java/org/springframework/core/type/classreading/MetadataReaderFactory.java b/spring-core/src/main/java/org/springframework/core/type/classreading/MetadataReaderFactory.java index 0748fdafe07..4eddbfa6cd2 100644 --- a/spring-core/src/main/java/org/springframework/core/type/classreading/MetadataReaderFactory.java +++ b/spring-core/src/main/java/org/springframework/core/type/classreading/MetadataReaderFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2024 the original author or authors. + * Copyright 2002-2023 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. @@ -19,15 +19,12 @@ package org.springframework.core.type.classreading; import java.io.IOException; import org.springframework.core.io.Resource; -import org.springframework.core.io.ResourceLoader; -import org.springframework.lang.Nullable; /** * Factory interface for {@link MetadataReader} instances. * Allows for caching a MetadataReader per original resource. * * @author Juergen Hoeller - * @author Brian Clozel * @since 2.5 * @see SimpleMetadataReaderFactory * @see CachingMetadataReaderFactory @@ -52,23 +49,4 @@ public interface MetadataReaderFactory { */ MetadataReader getMetadataReader(Resource resource) throws IOException; - /** - * Create a default {@link MetadataReaderFactory} implementation that's suitable - * for the current JVM. - * @return a new factory instance - * @since 7.0 - */ - static MetadataReaderFactory create(@Nullable ResourceLoader resourceLoader) { - return MetadataReaderFactoryDelegate.create(resourceLoader); - } - - /** - * Create a default {@link MetadataReaderFactory} implementation that's suitable - * for the current JVM. - * @return a new factory instance - * @since 7.0 - */ - static MetadataReaderFactory create(@Nullable ClassLoader classLoader) { - return MetadataReaderFactoryDelegate.create(classLoader); - } } diff --git a/spring-core/src/main/java/org/springframework/core/type/classreading/MetadataReaderFactoryDelegate.java b/spring-core/src/main/java/org/springframework/core/type/classreading/MetadataReaderFactoryDelegate.java deleted file mode 100644 index e24b004b968..00000000000 --- a/spring-core/src/main/java/org/springframework/core/type/classreading/MetadataReaderFactoryDelegate.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright 2002-2024 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.core.type.classreading; - -import org.springframework.core.io.ResourceLoader; -import org.springframework.lang.Nullable; - -/** - * Internal delegate for instantiating {@link MetadataReaderFactory} implementations. - * For JDK < 24, the {@link SimpleMetadataReaderFactory} is being used. - * - * @author Brian Clozel - * @since 7.0 - * @see MetadataReaderFactory - */ -abstract class MetadataReaderFactoryDelegate { - - static MetadataReaderFactory create(@Nullable ResourceLoader resourceLoader) { - return new SimpleMetadataReaderFactory(resourceLoader); - } - - static MetadataReaderFactory create(@Nullable ClassLoader classLoader) { - return new SimpleMetadataReaderFactory(classLoader); - } -} diff --git a/spring-core/src/main/java24/org/springframework/core/type/classreading/ClassFileAnnotationMetadata.java b/spring-core/src/main/java24/org/springframework/core/type/classreading/ClassFileAnnotationMetadata.java deleted file mode 100644 index 7972c9b03f9..00000000000 --- a/spring-core/src/main/java24/org/springframework/core/type/classreading/ClassFileAnnotationMetadata.java +++ /dev/null @@ -1,129 +0,0 @@ -/* - * Copyright 2002-2024 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.core.type.classreading; - - -import java.lang.classfile.Annotation; -import java.lang.classfile.AnnotationElement; -import java.lang.classfile.AnnotationValue; -import java.lang.classfile.attribute.RuntimeVisibleAnnotationsAttribute; -import java.lang.reflect.Array; -import java.util.ArrayList; -import java.util.Collections; -import java.util.LinkedHashMap; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import org.springframework.core.annotation.AnnotationFilter; -import org.springframework.core.annotation.MergedAnnotation; -import org.springframework.core.annotation.MergedAnnotations; -import org.springframework.lang.Nullable; -import org.springframework.util.ClassUtils; - -abstract class ClassFileAnnotationMetadata { - - static MergedAnnotations createMergedAnnotations(String entryName, RuntimeVisibleAnnotationsAttribute annotationAttribute, @Nullable ClassLoader classLoader) { - Set> annotations = new LinkedHashSet<>(4); - annotationAttribute.annotations().forEach(ann -> { - MergedAnnotation mergedAnnotation = createMergedAnnotation(entryName, ann, classLoader); - if (mergedAnnotation != null) { - annotations.add(mergedAnnotation); - } - }); - return MergedAnnotations.of(annotations); - } - - @SuppressWarnings("unchecked") - @Nullable - private static MergedAnnotation createMergedAnnotation(String entryName, Annotation annotation, @Nullable ClassLoader classLoader) { - String typeName = fromTypeDescriptor(annotation.className().stringValue()); - if (AnnotationFilter.PLAIN.matches(typeName)) { - return null; - } - Map attributes = new LinkedHashMap<>(4); - try { - Class annotationType = (Class) ClassUtils.forName(typeName, classLoader); - for (AnnotationElement element : annotation.elements()) { - attributes.put(element.name().stringValue(), readAnnotationValue(element.value(), classLoader)); - } - Map compactedAttributes = (attributes.isEmpty() ? Collections.emptyMap() : attributes); - return MergedAnnotation.of(classLoader, new Source(entryName), annotationType, compactedAttributes); - } - catch (ClassNotFoundException | LinkageError ex) { - return null; - } - } - - private static Object readAnnotationValue(AnnotationValue elementValue, @Nullable ClassLoader classLoader) throws ClassNotFoundException { - switch (elementValue) { - case AnnotationValue.OfArray arrayValue -> { - List rawValues = arrayValue.values(); - List values = new ArrayList<>(rawValues.size()); - for (AnnotationValue arrayEntry : rawValues) { - values.add(readAnnotationValue(arrayEntry, classLoader)); - } - Class elementType = getArrayElementType(values); - return values.toArray((Object[]) Array.newInstance(elementType, rawValues.size())); - } - case AnnotationValue.OfAnnotation annotationValue -> { - return annotationValue.annotation(); - } - case AnnotationValue.OfClass classValue -> { - return fromTypeDescriptor(classValue.className().stringValue()); - } - case AnnotationValue.OfEnum enumValue -> { - return parseEnum(enumValue, classLoader); - } - case AnnotationValue.OfConstant constantValue -> { - return constantValue.resolvedValue(); - } - default -> { - return elementValue; - } - } - } - - private static Class getArrayElementType(List values) { - if (values.isEmpty()) { - return Object.class; - } - Object firstElement = values.getFirst(); - if (firstElement instanceof Enum enumeration) { - return enumeration.getDeclaringClass(); - } - return firstElement.getClass(); - } - - private static String fromTypeDescriptor(String descriptor) { - return descriptor.substring(1, descriptor.length() - 1) - .replace('/', '.'); - } - - @SuppressWarnings("unchecked") - private static > Enum parseEnum(AnnotationValue.OfEnum enumValue, @Nullable ClassLoader classLoader) throws ClassNotFoundException { - String enumClassName = fromTypeDescriptor(enumValue.className().stringValue()); - Class enumClass = (Class) ClassUtils.forName(enumClassName, classLoader); - return Enum.valueOf(enumClass, enumValue.constantName().stringValue()); - } - - record Source(String entryName) { - - } - -} diff --git a/spring-core/src/main/java24/org/springframework/core/type/classreading/ClassFileClassMetadata.java b/spring-core/src/main/java24/org/springframework/core/type/classreading/ClassFileClassMetadata.java deleted file mode 100644 index d5677a34a6f..00000000000 --- a/spring-core/src/main/java24/org/springframework/core/type/classreading/ClassFileClassMetadata.java +++ /dev/null @@ -1,315 +0,0 @@ -/* - * Copyright 2002-2024 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.core.type.classreading; - -import java.lang.classfile.AccessFlags; -import java.lang.classfile.ClassModel; -import java.lang.classfile.Interfaces; -import java.lang.classfile.MethodModel; -import java.lang.classfile.Superclass; -import java.lang.classfile.attribute.InnerClassInfo; -import java.lang.classfile.attribute.InnerClassesAttribute; -import java.lang.classfile.attribute.NestHostAttribute; -import java.lang.classfile.attribute.RuntimeVisibleAnnotationsAttribute; -import java.lang.classfile.constantpool.ClassEntry; -import java.lang.reflect.AccessFlag; -import java.util.Collections; -import java.util.HashSet; -import java.util.LinkedHashSet; -import java.util.Set; - -import org.springframework.core.annotation.MergedAnnotations; -import org.springframework.core.type.AnnotationMetadata; -import org.springframework.core.type.MethodMetadata; -import org.springframework.lang.Nullable; -import org.springframework.util.ClassUtils; -import org.springframework.util.StringUtils; - -/** - * {@link AnnotationMetadata} implementation that leverages - * the {@link java.lang.classfile.ClassFile} API. - * @author Brian Clozel - */ -class ClassFileClassMetadata implements AnnotationMetadata { - - private final String className; - - private final AccessFlags accessFlags; - - @Nullable - private final String enclosingClassName; - - @Nullable - private final String superClassName; - - private final boolean independentInnerClass; - - private final Set interfaceNames; - - private final Set memberClassNames; - - private final Set declaredMethods; - - private final MergedAnnotations mergedAnnotations; - - @Nullable - private Set annotationTypes; - - ClassFileClassMetadata(String className, AccessFlags accessFlags, @Nullable String enclosingClassName, - @Nullable String superClassName, boolean independentInnerClass, Set interfaceNames, - Set memberClassNames, Set declaredMethods, MergedAnnotations mergedAnnotations) { - this.className = className; - this.accessFlags = accessFlags; - this.enclosingClassName = enclosingClassName; - this.superClassName = superClassName; - this.independentInnerClass = independentInnerClass; - this.interfaceNames = interfaceNames; - this.memberClassNames = memberClassNames; - this.declaredMethods = declaredMethods; - this.mergedAnnotations = mergedAnnotations; - } - - @Override - public String getClassName() { - return this.className; - } - - @Override - public boolean isInterface() { - return this.accessFlags.has(AccessFlag.INTERFACE); - } - - @Override - public boolean isAnnotation() { - return this.accessFlags.has(AccessFlag.ANNOTATION); - } - - @Override - public boolean isAbstract() { - return this.accessFlags.has(AccessFlag.ABSTRACT); - } - - @Override - public boolean isFinal() { - return this.accessFlags.has(AccessFlag.FINAL); - } - - @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 StringUtils.toStringArray(this.interfaceNames); - } - - @Override - public String[] getMemberClassNames() { - return StringUtils.toStringArray(this.memberClassNames); - } - - @Override - public MergedAnnotations getAnnotations() { - return this.mergedAnnotations; - } - - @Override - public Set getAnnotationTypes() { - Set annotationTypes = this.annotationTypes; - if (annotationTypes == null) { - annotationTypes = Collections.unmodifiableSet( - AnnotationMetadata.super.getAnnotationTypes()); - this.annotationTypes = annotationTypes; - } - return annotationTypes; - } - - @Override - public Set getAnnotatedMethods(String annotationName) { - Set result = new LinkedHashSet<>(4); - for (MethodMetadata annotatedMethod : this.declaredMethods) { - if (annotatedMethod.isAnnotated(annotationName)) { - result.add(annotatedMethod); - } - } - return Collections.unmodifiableSet(result); - } - - @Override - public Set getDeclaredMethods() { - return Collections.unmodifiableSet(this.declaredMethods); - } - - - @Override - public boolean equals(@Nullable Object other) { - return (this == other || (other instanceof ClassFileClassMetadata that && this.className.equals(that.className))); - } - - @Override - public int hashCode() { - return this.className.hashCode(); - } - - @Override - public String toString() { - return this.className; - } - - - static ClassFileClassMetadata of(ClassModel classModel, @Nullable ClassLoader classLoader) { - Builder builder = new Builder(classLoader); - builder.classEntry(classModel.thisClass()); - String currentClassName = classModel.thisClass().name().stringValue(); - classModel.elementStream().forEach(classElement -> { - switch (classElement) { - case AccessFlags flags -> { - builder.accessFlags(flags); - } - case NestHostAttribute nestHost -> { - builder.enclosingClass(nestHost.nestHost()); - } - case InnerClassesAttribute innerClasses -> { - builder.nestMembers(currentClassName, innerClasses); - } - case RuntimeVisibleAnnotationsAttribute annotationsAttribute -> { - builder.mergedAnnotations(ClassFileAnnotationMetadata.createMergedAnnotations(currentClassName, annotationsAttribute, classLoader)); - } - case Superclass superclass -> { - builder.superClass(superclass); - } - case Interfaces interfaces -> { - builder.interfaces(interfaces); - } - case MethodModel method -> { - builder.method(method); - } - default -> { - // ignore class element - } - } - }); - return builder.build(); - } - - static class Builder { - - private final ClassLoader clasLoader; - - private String className; - - private AccessFlags accessFlags; - - private Set innerAccessFlags; - - @Nullable - private String enclosingClassName; - - @Nullable - private String superClassName; - - private Set interfaceNames = new HashSet<>(); - - private Set memberClassNames = new HashSet<>(); - - private Set declaredMethods = new HashSet<>(); - - private MergedAnnotations mergedAnnotations = MergedAnnotations.of(Collections.emptySet()); - - public Builder(ClassLoader classLoader) { - this.clasLoader = classLoader; - } - - Builder classEntry(ClassEntry classEntry) { - this.className = ClassUtils.convertResourcePathToClassName(classEntry.name().stringValue()); - return this; - } - - Builder accessFlags(AccessFlags accessFlags) { - this.accessFlags = accessFlags; - return this; - } - - Builder innerAccessFlags(Set innerAccessFlags) { - this.innerAccessFlags = innerAccessFlags; - return this; - } - - Builder enclosingClass(ClassEntry enclosingClass) { - this.enclosingClassName = ClassUtils.convertResourcePathToClassName(enclosingClass.name().stringValue()); - return this; - } - - Builder superClass(Superclass superClass) { - this.superClassName = ClassUtils.convertResourcePathToClassName(superClass.superclassEntry().name().stringValue()); - return this; - } - - Builder interfaces(Interfaces interfaces) { - for (ClassEntry entry : interfaces.interfaces()) { - this.interfaceNames.add(ClassUtils.convertResourcePathToClassName(entry.name().stringValue())); - } - return this; - } - - Builder nestMembers(String currentClassName, InnerClassesAttribute innerClasses) { - for (InnerClassInfo classInfo : innerClasses.classes()) { - String innerClassName = classInfo.innerClass().name().stringValue(); - // the current class is an inner class - if (currentClassName.equals(innerClassName)) { - this.innerAccessFlags = classInfo.flags(); - } - // collecting data about actual inner classes - else { - this.memberClassNames.add(ClassUtils.convertResourcePathToClassName(innerClassName)); - } - } - return this; - } - - Builder mergedAnnotations(MergedAnnotations mergedAnnotations) { - this.mergedAnnotations = mergedAnnotations; - return this; - } - - Builder method(MethodModel method) { - this.declaredMethods.add(ClassFileMethodMetadata.of(method, this.clasLoader)); - return this; - } - - ClassFileClassMetadata build() { - boolean independentInnerClass = (this.enclosingClassName != null) && this.innerAccessFlags.contains(AccessFlag.STATIC); - return new ClassFileClassMetadata(this.className, this.accessFlags, this.enclosingClassName, this.superClassName, - independentInnerClass, this.interfaceNames, this.memberClassNames, this.declaredMethods, this.mergedAnnotations); - } - - } - -} diff --git a/spring-core/src/main/java24/org/springframework/core/type/classreading/ClassFileMetadataReader.java b/spring-core/src/main/java24/org/springframework/core/type/classreading/ClassFileMetadataReader.java deleted file mode 100644 index 4205f9ab5d0..00000000000 --- a/spring-core/src/main/java24/org/springframework/core/type/classreading/ClassFileMetadataReader.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright 2002-2024 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.core.type.classreading; - -import java.io.IOException; -import java.io.InputStream; -import java.lang.classfile.ClassFile; -import java.lang.classfile.ClassModel; - -import org.springframework.core.io.Resource; -import org.springframework.core.type.AnnotationMetadata; -import org.springframework.core.type.ClassMetadata; -import org.springframework.lang.Nullable; - -/** - * {@link MetadataReader} implementation based on the {@link ClassFile} API. - * - * @author Brian Clozel - */ -final class ClassFileMetadataReader implements MetadataReader { - - private final Resource resource; - - private final AnnotationMetadata annotationMetadata; - - - ClassFileMetadataReader(Resource resource, @Nullable ClassLoader classLoader) throws IOException { - this.resource = resource; - this.annotationMetadata = ClassFileClassMetadata.of(getClassModel(resource), classLoader); - } - - private static ClassModel getClassModel(Resource resource) throws IOException { - try (InputStream is = resource.getInputStream()) { - byte[] bytes = is.readAllBytes(); - return ClassFile.of().parse(bytes); - } - } - - @Override - public Resource getResource() { - return this.resource; - } - - @Override - public ClassMetadata getClassMetadata() { - return this.annotationMetadata; - } - - @Override - public AnnotationMetadata getAnnotationMetadata() { - return this.annotationMetadata; - } - -} diff --git a/spring-core/src/main/java24/org/springframework/core/type/classreading/ClassFileMetadataReaderFactory.java b/spring-core/src/main/java24/org/springframework/core/type/classreading/ClassFileMetadataReaderFactory.java deleted file mode 100644 index 3d56ebf5806..00000000000 --- a/spring-core/src/main/java24/org/springframework/core/type/classreading/ClassFileMetadataReaderFactory.java +++ /dev/null @@ -1,104 +0,0 @@ -/* - * Copyright 2002-2024 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.core.type.classreading; - -import java.io.FileNotFoundException; -import java.io.IOException; - -import org.springframework.core.io.DefaultResourceLoader; -import org.springframework.core.io.Resource; -import org.springframework.core.io.ResourceLoader; -import org.springframework.lang.Nullable; -import org.springframework.util.ClassUtils; - -/** - * Implementation of the {@link MetadataReaderFactory} interface, - * using the {@link java.lang.classfile.ClassFile} API for parsing the bytecode. - * - * @author Brian Clozel - * @since 7.0 - */ -public class ClassFileMetadataReaderFactory implements MetadataReaderFactory { - - - private final ResourceLoader resourceLoader; - - - /** - * Create a new ClassFileMetadataReaderFactory for the default class loader. - */ - public ClassFileMetadataReaderFactory() { - this.resourceLoader = new DefaultResourceLoader(); - } - - /** - * Create a new ClassFileMetadataReaderFactory for the given resource loader. - * @param resourceLoader the Spring ResourceLoader to use - * (also determines the ClassLoader to use) - */ - public ClassFileMetadataReaderFactory(@Nullable ResourceLoader resourceLoader) { - this.resourceLoader = (resourceLoader != null ? resourceLoader : new DefaultResourceLoader()); - } - - /** - * Create a new ClassFileMetadataReaderFactory for the given class loader. - * @param classLoader the ClassLoader to use - */ - public ClassFileMetadataReaderFactory(@Nullable ClassLoader classLoader) { - this.resourceLoader = - (classLoader != null ? new DefaultResourceLoader(classLoader) : new DefaultResourceLoader()); - } - - /** - * Return the ResourceLoader that this MetadataReaderFactory has been - * constructed with. - */ - public final ResourceLoader getResourceLoader() { - return this.resourceLoader; - } - - @Override - public MetadataReader getMetadataReader(String className) throws IOException { - try { - String resourcePath = ResourceLoader.CLASSPATH_URL_PREFIX + - ClassUtils.convertClassNameToResourcePath(className) + ClassUtils.CLASS_FILE_SUFFIX; - Resource resource = this.resourceLoader.getResource(resourcePath); - return getMetadataReader(resource); - } - catch (FileNotFoundException ex) { - // Maybe an inner class name using the dot name syntax? Need to use the dollar syntax here... - // ClassUtils.forName has an equivalent check for resolution into Class references later on. - int lastDotIndex = className.lastIndexOf('.'); - if (lastDotIndex != -1) { - String innerClassName = - className.substring(0, lastDotIndex) + '$' + className.substring(lastDotIndex + 1); - String innerClassResourcePath = ResourceLoader.CLASSPATH_URL_PREFIX + - ClassUtils.convertClassNameToResourcePath(innerClassName) + ClassUtils.CLASS_FILE_SUFFIX; - Resource innerClassResource = this.resourceLoader.getResource(innerClassResourcePath); - if (innerClassResource.exists()) { - return getMetadataReader(innerClassResource); - } - } - throw ex; - } - } - - @Override - public MetadataReader getMetadataReader(Resource resource) throws IOException { - return new ClassFileMetadataReader(resource, this.resourceLoader.getClassLoader()); - } -} diff --git a/spring-core/src/main/java24/org/springframework/core/type/classreading/ClassFileMethodMetadata.java b/spring-core/src/main/java24/org/springframework/core/type/classreading/ClassFileMethodMetadata.java deleted file mode 100644 index 676c5da1bfe..00000000000 --- a/spring-core/src/main/java24/org/springframework/core/type/classreading/ClassFileMethodMetadata.java +++ /dev/null @@ -1,162 +0,0 @@ -/* - * Copyright 2002-2024 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.core.type.classreading; - -import java.lang.classfile.AccessFlags; -import java.lang.classfile.MethodModel; -import java.lang.classfile.attribute.RuntimeVisibleAnnotationsAttribute; -import java.lang.constant.ClassDesc; -import java.lang.constant.MethodTypeDesc; -import java.lang.reflect.AccessFlag; -import java.util.Collections; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -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} extracted from class bytecode using the - * {@link java.lang.classfile.ClassFile} API. - * @author Brian Clozel - */ -class ClassFileMethodMetadata implements MethodMetadata { - - private final String methodName; - - private final AccessFlags accessFlags; - - @Nullable - private final String declaringClassName; - - private final String returnTypeName; - - // The source implements equals(), hashCode(), and toString() for the underlying method. - private final Object source; - - private final MergedAnnotations annotations; - - ClassFileMethodMetadata(String methodName, AccessFlags accessFlags, String declaringClassName, String returnTypeName, Object source, MergedAnnotations annotations) { - this.methodName = methodName; - this.accessFlags = accessFlags; - this.declaringClassName = declaringClassName; - this.returnTypeName = returnTypeName; - this.source = source; - this.annotations = annotations; - } - - @Override - public String getMethodName() { - return this.methodName; - } - - @Override - @Nullable - public String getDeclaringClassName() { - return this.declaringClassName; - } - - @Override - public String getReturnTypeName() { - return this.returnTypeName; - } - - @Override - public boolean isAbstract() { - return this.accessFlags.has(AccessFlag.ABSTRACT); - } - - @Override - public boolean isStatic() { - return this.accessFlags.has(AccessFlag.STATIC); - } - - @Override - public boolean isFinal() { - return this.accessFlags.has(AccessFlag.FINAL); - } - - @Override - public boolean isOverridable() { - return !isStatic() && !isFinal() && !isPrivate(); - } - - private boolean isPrivate() { - return this.accessFlags.has(AccessFlag.PRIVATE); - } - - @Override - public MergedAnnotations getAnnotations() { - return this.annotations; - } - - - @Override - public boolean equals(@Nullable Object other) { - return (this == other || (other instanceof ClassFileMethodMetadata that && this.source.equals(that.source))); - } - - @Override - public int hashCode() { - return this.source.hashCode(); - } - - @Override - public String toString() { - return this.source.toString(); - } - - static ClassFileMethodMetadata of(MethodModel methodModel, ClassLoader classLoader) { - String methodName = methodModel.methodName().stringValue(); - AccessFlags flags = methodModel.flags(); - String declaringClassName = methodModel.parent().map(parent -> parent.thisClass().name().stringValue()).orElse(null); - String returnTypeName = methodModel.methodTypeSymbol().returnType().displayName(); - Source source = new Source(declaringClassName, methodName, methodModel.methodTypeSymbol()); - MergedAnnotations annotations = methodModel.elementStream() - .filter(element -> element instanceof RuntimeVisibleAnnotationsAttribute) - .findFirst() - .map(element -> ClassFileAnnotationMetadata.createMergedAnnotations(methodName, (RuntimeVisibleAnnotationsAttribute) element, classLoader)) - .orElse(MergedAnnotations.of(Collections.emptyList())); - return new ClassFileMethodMetadata(methodName, flags, declaringClassName, returnTypeName, source, annotations); - } - - /** - * {@link MergedAnnotation} source. - * @param declaringClassName the name of the declaring class - * @param methodName the name of the method - * @param descriptor the bytecode descriptor for this method - */ - record Source(String declaringClassName, String methodName, MethodTypeDesc descriptor) { - - @Override - public String toString() { - StringBuilder builder = new StringBuilder(); - builder.append(this.declaringClassName); - builder.append('.'); - builder.append(this.methodName); - builder.append('('); - builder.append(Stream.of(this.descriptor.parameterArray()) - .map(ClassDesc::displayName) - .collect(Collectors.joining(","))); - builder.append(')'); - return builder.toString(); - } - } - -} diff --git a/spring-core/src/main/java24/org/springframework/core/type/classreading/MetadataReaderFactoryDelegate.java b/spring-core/src/main/java24/org/springframework/core/type/classreading/MetadataReaderFactoryDelegate.java deleted file mode 100644 index 94e33954b41..00000000000 --- a/spring-core/src/main/java24/org/springframework/core/type/classreading/MetadataReaderFactoryDelegate.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright 2002-2024 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.springframework.core.type.classreading; - -import org.springframework.core.io.ResourceLoader; -import org.springframework.lang.Nullable; - -/** - * Internal delegate for instantiating {@link MetadataReaderFactory} implementations. - * For JDK >= 24, the {@link ClassFileMetadataReaderFactory} is being used. - * - * @author Brian Clozel - * @see MetadataReaderFactory - */ -abstract class MetadataReaderFactoryDelegate { - - static MetadataReaderFactory create(@Nullable ResourceLoader resourceLoader) { - return new ClassFileMetadataReaderFactory(resourceLoader); - } - - static MetadataReaderFactory create(@Nullable ClassLoader classLoader) { - return new ClassFileMetadataReaderFactory(classLoader); - } -} diff --git a/spring-core/src/test/java/org/springframework/core/type/AbstractAnnotationMetadataTests.java b/spring-core/src/test/java/org/springframework/core/type/AbstractAnnotationMetadataTests.java index 83d20cd450d..cc966f17995 100644 --- a/spring-core/src/test/java/org/springframework/core/type/AbstractAnnotationMetadataTests.java +++ b/spring-core/src/test/java/org/springframework/core/type/AbstractAnnotationMetadataTests.java @@ -146,8 +146,8 @@ public abstract class AbstractAnnotationMetadataTests { @Test void getSuperClassNameWhenHasNoSuperClassReturnsNull() { assertThat(get(Object.class).getSuperClassName()).isNull(); - assertThat(get(TestInterface.class).getSuperClassName()).isIn(null, "java.lang.Object"); - assertThat(get(TestSubInterface.class).getSuperClassName()).isIn(null, "java.lang.Object"); + assertThat(get(TestInterface.class).getSuperClassName()).isNull(); + assertThat(get(TestSubInterface.class).getSuperClassName()).isNull(); } @Test @@ -210,17 +210,6 @@ public abstract class AbstractAnnotationMetadataTests { assertThat(attributes.get("size")).containsExactlyInAnyOrder(1, 2); } - @Test - void getComplexAttributeTypesReturnsAll() { - MultiValueMap attributes = - get(WithComplexAttributeTypes.class).getAllAnnotationAttributes(ComplexAttributes.class.getName()); - assertThat(attributes).containsOnlyKeys("names", "count", "type"); - assertThat(attributes.get("names")).hasSize(1); - assertThat(attributes.get("names").get(0)).isEqualTo(new String[]{"first", "second"}); - assertThat(attributes.get("count")).containsExactlyInAnyOrder(TestEnum.ONE); - assertThat(attributes.get("type")).containsExactlyInAnyOrder(TestEnum.class); - } - @Test void getAnnotationTypesReturnsDirectAnnotations() { AnnotationMetadata metadata = get(WithDirectAnnotations.class); @@ -418,22 +407,4 @@ public abstract class AbstractAnnotationMetadataTests { } - @ComplexAttributes(names = {"first", "second"}, count = TestEnum.ONE, type = TestEnum.class) - public static class WithComplexAttributeTypes { - } - - @Retention(RetentionPolicy.RUNTIME) - public @interface ComplexAttributes { - - String[] names(); - - TestEnum count(); - - Class type(); - } - - public enum TestEnum { - ONE, TWO, THREE - } - } diff --git a/spring-core/src/test/java/org/springframework/core/type/classreading/SimpleAnnotationMetadataTests.java b/spring-core/src/test/java/org/springframework/core/type/classreading/SimpleAnnotationMetadataTests.java index 8f8e50c40fd..f66c81bc35d 100644 --- a/spring-core/src/test/java/org/springframework/core/type/classreading/SimpleAnnotationMetadataTests.java +++ b/spring-core/src/test/java/org/springframework/core/type/classreading/SimpleAnnotationMetadataTests.java @@ -30,8 +30,9 @@ class SimpleAnnotationMetadataTests extends AbstractAnnotationMetadataTests { @Override protected AnnotationMetadata get(Class source) { try { - return MetadataReaderFactory.create(source.getClassLoader()) - .getMetadataReader(source.getName()).getAnnotationMetadata(); + return new SimpleMetadataReaderFactory( + source.getClassLoader()).getMetadataReader( + source.getName()).getAnnotationMetadata(); } catch (Exception ex) { throw new IllegalStateException(ex);