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.classreading; 018 019import java.util.Collection; 020import java.util.Collections; 021import java.util.LinkedHashMap; 022import java.util.LinkedHashSet; 023import java.util.List; 024import java.util.Map; 025import java.util.Set; 026 027import org.springframework.asm.AnnotationVisitor; 028import org.springframework.asm.MethodVisitor; 029import org.springframework.asm.Opcodes; 030import org.springframework.asm.Type; 031import org.springframework.core.annotation.AnnotationAttributes; 032import org.springframework.core.annotation.AnnotationUtils; 033import org.springframework.core.annotation.MergedAnnotations; 034import org.springframework.core.type.AnnotationMetadata; 035import org.springframework.core.type.MethodMetadata; 036import org.springframework.lang.Nullable; 037import org.springframework.util.LinkedMultiValueMap; 038import org.springframework.util.MultiValueMap; 039 040/** 041 * ASM class visitor which looks for the class name and implemented types as 042 * well as for the annotations defined on the class, exposing them through 043 * the {@link org.springframework.core.type.AnnotationMetadata} interface. 044 * 045 * @author Juergen Hoeller 046 * @author Mark Fisher 047 * @author Costin Leau 048 * @author Phillip Webb 049 * @author Sam Brannen 050 * @since 2.5 051 * @deprecated As of Spring Framework 5.2, this class has been replaced by 052 * {@link SimpleAnnotationMetadataReadingVisitor} for internal use within the 053 * framework, but there is no public replacement for 054 * {@code AnnotationMetadataReadingVisitor}. 055 */ 056@Deprecated 057public class AnnotationMetadataReadingVisitor extends ClassMetadataReadingVisitor implements AnnotationMetadata { 058 059 @Nullable 060 protected final ClassLoader classLoader; 061 062 protected final Set<String> annotationSet = new LinkedHashSet<>(4); 063 064 protected final Map<String, Set<String>> metaAnnotationMap = new LinkedHashMap<>(4); 065 066 /** 067 * Declared as a {@link LinkedMultiValueMap} instead of a {@link MultiValueMap} 068 * to ensure that the hierarchical ordering of the entries is preserved. 069 * @see AnnotationReadingVisitorUtils#getMergedAnnotationAttributes 070 */ 071 protected final LinkedMultiValueMap<String, AnnotationAttributes> attributesMap = new LinkedMultiValueMap<>(4); 072 073 protected final Set<MethodMetadata> methodMetadataSet = new LinkedHashSet<>(4); 074 075 076 public AnnotationMetadataReadingVisitor(@Nullable ClassLoader classLoader) { 077 this.classLoader = classLoader; 078 } 079 080 081 @Override 082 public MergedAnnotations getAnnotations() { 083 throw new UnsupportedOperationException(); 084 } 085 086 @Override 087 public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { 088 // Skip bridge methods - we're only interested in original annotation-defining user methods. 089 // On JDK 8, we'd otherwise run into double detection of the same annotated method... 090 if ((access & Opcodes.ACC_BRIDGE) != 0) { 091 return super.visitMethod(access, name, desc, signature, exceptions); 092 } 093 return new MethodMetadataReadingVisitor(name, access, getClassName(), 094 Type.getReturnType(desc).getClassName(), this.classLoader, this.methodMetadataSet); 095 } 096 097 @Override 098 @Nullable 099 public AnnotationVisitor visitAnnotation(String desc, boolean visible) { 100 if (!visible) { 101 return null; 102 } 103 String className = Type.getType(desc).getClassName(); 104 if (AnnotationUtils.isInJavaLangAnnotationPackage(className)) { 105 return null; 106 } 107 this.annotationSet.add(className); 108 return new AnnotationAttributesReadingVisitor( 109 className, this.attributesMap, this.metaAnnotationMap, this.classLoader); 110 } 111 112 113 @Override 114 public Set<String> getAnnotationTypes() { 115 return this.annotationSet; 116 } 117 118 @Override 119 public Set<String> getMetaAnnotationTypes(String annotationName) { 120 Set<String> metaAnnotationTypes = this.metaAnnotationMap.get(annotationName); 121 return (metaAnnotationTypes != null ? metaAnnotationTypes : Collections.emptySet()); 122 } 123 124 @Override 125 public boolean hasMetaAnnotation(String metaAnnotationType) { 126 if (AnnotationUtils.isInJavaLangAnnotationPackage(metaAnnotationType)) { 127 return false; 128 } 129 Collection<Set<String>> allMetaTypes = this.metaAnnotationMap.values(); 130 for (Set<String> metaTypes : allMetaTypes) { 131 if (metaTypes.contains(metaAnnotationType)) { 132 return true; 133 } 134 } 135 return false; 136 } 137 138 @Override 139 public boolean isAnnotated(String annotationName) { 140 return (!AnnotationUtils.isInJavaLangAnnotationPackage(annotationName) && 141 this.attributesMap.containsKey(annotationName)); 142 } 143 144 @Override 145 public boolean hasAnnotation(String annotationName) { 146 return getAnnotationTypes().contains(annotationName); 147 } 148 149 @Override 150 @Nullable 151 public AnnotationAttributes getAnnotationAttributes(String annotationName, boolean classValuesAsString) { 152 AnnotationAttributes raw = AnnotationReadingVisitorUtils.getMergedAnnotationAttributes( 153 this.attributesMap, this.metaAnnotationMap, annotationName); 154 if (raw == null) { 155 return null; 156 } 157 return AnnotationReadingVisitorUtils.convertClassValues( 158 "class '" + getClassName() + "'", this.classLoader, raw, classValuesAsString); 159 } 160 161 @Override 162 @Nullable 163 public MultiValueMap<String, Object> getAllAnnotationAttributes(String annotationName, boolean classValuesAsString) { 164 MultiValueMap<String, Object> allAttributes = new LinkedMultiValueMap<>(); 165 List<AnnotationAttributes> attributes = this.attributesMap.get(annotationName); 166 if (attributes == null) { 167 return null; 168 } 169 String annotatedElement = "class '" + getClassName() + "'"; 170 for (AnnotationAttributes raw : attributes) { 171 for (Map.Entry<String, Object> entry : AnnotationReadingVisitorUtils.convertClassValues( 172 annotatedElement, this.classLoader, raw, classValuesAsString).entrySet()) { 173 allAttributes.add(entry.getKey(), entry.getValue()); 174 } 175 } 176 return allAttributes; 177 } 178 179 @Override 180 public boolean hasAnnotatedMethods(String annotationName) { 181 for (MethodMetadata methodMetadata : this.methodMetadataSet) { 182 if (methodMetadata.isAnnotated(annotationName)) { 183 return true; 184 } 185 } 186 return false; 187 } 188 189 @Override 190 public Set<MethodMetadata> getAnnotatedMethods(String annotationName) { 191 Set<MethodMetadata> annotatedMethods = new LinkedHashSet<>(4); 192 for (MethodMetadata methodMetadata : this.methodMetadataSet) { 193 if (methodMetadata.isAnnotated(annotationName)) { 194 annotatedMethods.add(methodMetadata); 195 } 196 } 197 return annotatedMethods; 198 } 199 200}