001/*
002 * Copyright 2002-2020 the original author or authors.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *      https://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017package org.springframework.core.type;
018
019import java.lang.annotation.Annotation;
020import java.lang.reflect.Method;
021import java.util.Collections;
022import java.util.LinkedHashSet;
023import java.util.Map;
024import java.util.Set;
025
026import org.springframework.core.annotation.AnnotatedElementUtils;
027import org.springframework.core.annotation.AnnotationUtils;
028import org.springframework.core.annotation.MergedAnnotation;
029import org.springframework.core.annotation.MergedAnnotations;
030import org.springframework.core.annotation.MergedAnnotations.SearchStrategy;
031import org.springframework.core.annotation.RepeatableContainers;
032import org.springframework.lang.Nullable;
033import org.springframework.util.MultiValueMap;
034import org.springframework.util.ReflectionUtils;
035
036/**
037 * {@link AnnotationMetadata} implementation that uses standard reflection
038 * to introspect a given {@link Class}.
039 *
040 * @author Juergen Hoeller
041 * @author Mark Fisher
042 * @author Chris Beams
043 * @author Phillip Webb
044 * @author Sam Brannen
045 * @since 2.5
046 */
047public class StandardAnnotationMetadata extends StandardClassMetadata implements AnnotationMetadata {
048
049        private final MergedAnnotations mergedAnnotations;
050
051        private final boolean nestedAnnotationsAsMap;
052
053        @Nullable
054        private Set<String> annotationTypes;
055
056
057        /**
058         * Create a new {@code StandardAnnotationMetadata} wrapper for the given Class.
059         * @param introspectedClass the Class to introspect
060         * @see #StandardAnnotationMetadata(Class, boolean)
061         * @deprecated since 5.2 in favor of the factory method {@link AnnotationMetadata#introspect(Class)}
062         */
063        @Deprecated
064        public StandardAnnotationMetadata(Class<?> introspectedClass) {
065                this(introspectedClass, false);
066        }
067
068        /**
069         * Create a new {@link StandardAnnotationMetadata} wrapper for the given Class,
070         * providing the option to return any nested annotations or annotation arrays in the
071         * form of {@link org.springframework.core.annotation.AnnotationAttributes} instead
072         * of actual {@link Annotation} instances.
073         * @param introspectedClass the Class to introspect
074         * @param nestedAnnotationsAsMap return nested annotations and annotation arrays as
075         * {@link org.springframework.core.annotation.AnnotationAttributes} for compatibility
076         * with ASM-based {@link AnnotationMetadata} implementations
077         * @since 3.1.1
078         * @deprecated since 5.2 in favor of the factory method {@link AnnotationMetadata#introspect(Class)}.
079         * Use {@link MergedAnnotation#asMap(org.springframework.core.annotation.MergedAnnotation.Adapt...) MergedAnnotation.asMap}
080         * from {@link #getAnnotations()} rather than {@link #getAnnotationAttributes(String)}
081         * if {@code nestedAnnotationsAsMap} is {@code false}
082         */
083        @Deprecated
084        public StandardAnnotationMetadata(Class<?> introspectedClass, boolean nestedAnnotationsAsMap) {
085                super(introspectedClass);
086                this.mergedAnnotations = MergedAnnotations.from(introspectedClass,
087                                SearchStrategy.INHERITED_ANNOTATIONS, RepeatableContainers.none());
088                this.nestedAnnotationsAsMap = nestedAnnotationsAsMap;
089        }
090
091
092        @Override
093        public MergedAnnotations getAnnotations() {
094                return this.mergedAnnotations;
095        }
096
097        @Override
098        public Set<String> getAnnotationTypes() {
099                Set<String> annotationTypes = this.annotationTypes;
100                if (annotationTypes == null) {
101                        annotationTypes = Collections.unmodifiableSet(AnnotationMetadata.super.getAnnotationTypes());
102                        this.annotationTypes = annotationTypes;
103                }
104                return annotationTypes;
105        }
106
107        @Override
108        @Nullable
109        public Map<String, Object> getAnnotationAttributes(String annotationName, boolean classValuesAsString) {
110                if (this.nestedAnnotationsAsMap) {
111                        return AnnotationMetadata.super.getAnnotationAttributes(annotationName, classValuesAsString);
112                }
113                return AnnotatedElementUtils.getMergedAnnotationAttributes(
114                                getIntrospectedClass(), annotationName, classValuesAsString, false);
115        }
116
117        @Override
118        @Nullable
119        public MultiValueMap<String, Object> getAllAnnotationAttributes(String annotationName, boolean classValuesAsString) {
120                if (this.nestedAnnotationsAsMap) {
121                        return AnnotationMetadata.super.getAllAnnotationAttributes(annotationName, classValuesAsString);
122                }
123                return AnnotatedElementUtils.getAllAnnotationAttributes(
124                                getIntrospectedClass(), annotationName, classValuesAsString, false);
125        }
126
127        @Override
128        public boolean hasAnnotatedMethods(String annotationName) {
129                if (AnnotationUtils.isCandidateClass(getIntrospectedClass(), annotationName)) {
130                        try {
131                                Method[] methods = ReflectionUtils.getDeclaredMethods(getIntrospectedClass());
132                                for (Method method : methods) {
133                                        if (isAnnotatedMethod(method, annotationName)) {
134                                                return true;
135                                        }
136                                }
137                        }
138                        catch (Throwable ex) {
139                                throw new IllegalStateException("Failed to introspect annotated methods on " + getIntrospectedClass(), ex);
140                        }
141                }
142                return false;
143        }
144
145        @Override
146        @SuppressWarnings("deprecation")
147        public Set<MethodMetadata> getAnnotatedMethods(String annotationName) {
148                Set<MethodMetadata> annotatedMethods = null;
149                if (AnnotationUtils.isCandidateClass(getIntrospectedClass(), annotationName)) {
150                        try {
151                                Method[] methods = ReflectionUtils.getDeclaredMethods(getIntrospectedClass());
152                                for (Method method : methods) {
153                                        if (isAnnotatedMethod(method, annotationName)) {
154                                                if (annotatedMethods == null) {
155                                                        annotatedMethods = new LinkedHashSet<>(4);
156                                                }
157                                                annotatedMethods.add(new StandardMethodMetadata(method, this.nestedAnnotationsAsMap));
158                                        }
159                                }
160                        }
161                        catch (Throwable ex) {
162                                throw new IllegalStateException("Failed to introspect annotated methods on " + getIntrospectedClass(), ex);
163                        }
164                }
165                return annotatedMethods != null ? annotatedMethods : Collections.emptySet();
166        }
167
168        private boolean isAnnotatedMethod(Method method, String annotationName) {
169                return !method.isBridge() && method.getAnnotations().length > 0 &&
170                                AnnotatedElementUtils.isAnnotated(method, annotationName);
171        }
172
173
174        static AnnotationMetadata from(Class<?> introspectedClass) {
175                return new StandardAnnotationMetadata(introspectedClass, true);
176        }
177
178}