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}