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