private void doTestAnnotationInfo(AnnotationMetadata metadata) { assertThat(metadata.getClassName(), is(AnnotatedComponent.class.getName())); assertThat(metadata.isInterface(), is(false)); assertThat(metadata.isAnnotation(), is(false)); assertThat(metadata.isAbstract(), is(false)); assertThat(metadata.isConcrete(), is(true)); assertThat(metadata.hasSuperClass(), is(true)); assertThat(metadata.getSuperClassName(), is(Object.class.getName())); assertThat(metadata.getInterfaceNames().length, is(1)); assertThat(metadata.getInterfaceNames()[0], is(Serializable.class.getName())); assertThat(metadata.hasAnnotation(Component.class.getName()), is(true)); assertThat(metadata.hasAnnotation(Scope.class.getName()), is(true)); assertThat(metadata.hasAnnotation(SpecialAttr.class.getName()), is(true)); assertThat(metadata.getAnnotationTypes().size(), is(6)); assertThat(metadata.getAnnotationTypes().contains(Component.class.getName()), is(true)); assertThat(metadata.getAnnotationTypes().contains(Scope.class.getName()), is(true)); assertThat(metadata.getAnnotationTypes().contains(SpecialAttr.class.getName()), is(true)); AnnotationAttributes compAttrs = (AnnotationAttributes) metadata.getAnnotationAttributes(Component.class.getName()); assertThat(compAttrs.size(), is(1)); assertThat(compAttrs.getString("value"), is("myName")); AnnotationAttributes scopeAttrs = (AnnotationAttributes) metadata.getAnnotationAttributes(Scope.class.getName()); assertThat(scopeAttrs.size(), is(1)); assertThat(scopeAttrs.getString("value"), is("myScope")); Set<MethodMetadata> methods = metadata.getAnnotatedMethods(DirectAnnotation.class.getName()); MethodMetadata method = methods.iterator().next(); assertEquals( "direct", method.getAnnotationAttributes(DirectAnnotation.class.getName()).get("value")); List<Object> allMeta = method.getAllAnnotationAttributes(DirectAnnotation.class.getName()).get("value"); assertThat( new HashSet<>(allMeta), is(equalTo(new HashSet<Object>(Arrays.asList("direct", "meta"))))); allMeta = method.getAllAnnotationAttributes(DirectAnnotation.class.getName()).get("additional"); assertThat(new HashSet<>(allMeta), is(equalTo(new HashSet<Object>(Arrays.asList("direct"))))); assertTrue(metadata.isAnnotated(IsAnnotatedAnnotation.class.getName())); { // perform tests with classValuesAsString = false (the default) AnnotationAttributes specialAttrs = (AnnotationAttributes) metadata.getAnnotationAttributes(SpecialAttr.class.getName()); assertThat(specialAttrs.size(), is(6)); assertTrue(String.class.isAssignableFrom(specialAttrs.getClass("clazz"))); assertTrue(specialAttrs.getEnum("state").equals(Thread.State.NEW)); AnnotationAttributes nestedAnno = specialAttrs.getAnnotation("nestedAnno"); assertThat("na", is(nestedAnno.getString("value"))); assertTrue(nestedAnno.getEnum("anEnum").equals(SomeEnum.LABEL1)); assertArrayEquals(new Class[] {String.class}, (Class[]) nestedAnno.get("classArray")); AnnotationAttributes[] nestedAnnoArray = specialAttrs.getAnnotationArray("nestedAnnoArray"); assertThat(nestedAnnoArray.length, is(2)); assertThat(nestedAnnoArray[0].getString("value"), is("default")); assertTrue(nestedAnnoArray[0].getEnum("anEnum").equals(SomeEnum.DEFAULT)); assertArrayEquals(new Class[] {Void.class}, (Class[]) nestedAnnoArray[0].get("classArray")); assertThat(nestedAnnoArray[1].getString("value"), is("na1")); assertTrue(nestedAnnoArray[1].getEnum("anEnum").equals(SomeEnum.LABEL2)); assertArrayEquals(new Class[] {Number.class}, (Class[]) nestedAnnoArray[1].get("classArray")); assertArrayEquals(new Class[] {Number.class}, nestedAnnoArray[1].getClassArray("classArray")); AnnotationAttributes optional = specialAttrs.getAnnotation("optional"); assertThat(optional.getString("value"), is("optional")); assertTrue(optional.getEnum("anEnum").equals(SomeEnum.DEFAULT)); assertArrayEquals(new Class[] {Void.class}, (Class[]) optional.get("classArray")); assertArrayEquals(new Class[] {Void.class}, optional.getClassArray("classArray")); AnnotationAttributes[] optionalArray = specialAttrs.getAnnotationArray("optionalArray"); assertThat(optionalArray.length, is(1)); assertThat(optionalArray[0].getString("value"), is("optional")); assertTrue(optionalArray[0].getEnum("anEnum").equals(SomeEnum.DEFAULT)); assertArrayEquals(new Class[] {Void.class}, (Class[]) optionalArray[0].get("classArray")); assertArrayEquals(new Class[] {Void.class}, optionalArray[0].getClassArray("classArray")); assertEquals( "direct", metadata.getAnnotationAttributes(DirectAnnotation.class.getName()).get("value")); allMeta = metadata.getAllAnnotationAttributes(DirectAnnotation.class.getName()).get("value"); assertThat( new HashSet<>(allMeta), is(equalTo(new HashSet<Object>(Arrays.asList("direct", "meta"))))); allMeta = metadata.getAllAnnotationAttributes(DirectAnnotation.class.getName()).get("additional"); assertThat(new HashSet<>(allMeta), is(equalTo(new HashSet<Object>(Arrays.asList("direct"))))); } { // perform tests with classValuesAsString = true AnnotationAttributes specialAttrs = (AnnotationAttributes) metadata.getAnnotationAttributes(SpecialAttr.class.getName(), true); assertThat(specialAttrs.size(), is(6)); assertThat(specialAttrs.get("clazz"), is((Object) String.class.getName())); assertThat(specialAttrs.getString("clazz"), is(String.class.getName())); AnnotationAttributes nestedAnno = specialAttrs.getAnnotation("nestedAnno"); assertArrayEquals( new String[] {String.class.getName()}, nestedAnno.getStringArray("classArray")); assertArrayEquals( new String[] {String.class.getName()}, nestedAnno.getStringArray("classArray")); AnnotationAttributes[] nestedAnnoArray = specialAttrs.getAnnotationArray("nestedAnnoArray"); assertArrayEquals( new String[] {Void.class.getName()}, (String[]) nestedAnnoArray[0].get("classArray")); assertArrayEquals( new String[] {Void.class.getName()}, nestedAnnoArray[0].getStringArray("classArray")); assertArrayEquals( new String[] {Number.class.getName()}, (String[]) nestedAnnoArray[1].get("classArray")); assertArrayEquals( new String[] {Number.class.getName()}, nestedAnnoArray[1].getStringArray("classArray")); AnnotationAttributes optional = specialAttrs.getAnnotation("optional"); assertArrayEquals(new String[] {Void.class.getName()}, (String[]) optional.get("classArray")); assertArrayEquals(new String[] {Void.class.getName()}, optional.getStringArray("classArray")); AnnotationAttributes[] optionalArray = specialAttrs.getAnnotationArray("optionalArray"); assertArrayEquals( new String[] {Void.class.getName()}, (String[]) optionalArray[0].get("classArray")); assertArrayEquals( new String[] {Void.class.getName()}, optionalArray[0].getStringArray("classArray")); assertEquals( metadata.getAnnotationAttributes(DirectAnnotation.class.getName()).get("value"), "direct"); allMeta = metadata.getAllAnnotationAttributes(DirectAnnotation.class.getName()).get("value"); assertThat( new HashSet<>(allMeta), is(equalTo(new HashSet<Object>(Arrays.asList("direct", "meta"))))); } }
/** * Apply processing and build a complete {@link ConfigurationClass} by reading the annotations, * members and methods from the source class. This method can be called multiple times as relevant * sources are discovered. * * @param configClass the configuration class being build * @param sourceClass a source class * @return the superclass, or {@code null} if none found or previously processed */ protected final SourceClass doProcessConfigurationClass( ConfigurationClass configClass, SourceClass sourceClass) throws IOException { // Recursively process any member (nested) classes first processMemberClasses(configClass, sourceClass); // Process any @PropertySource annotations for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable( sourceClass.getMetadata(), PropertySources.class, org.springframework.context.annotation.PropertySource.class)) { if (this.environment instanceof ConfigurableEnvironment) { processPropertySource(propertySource); } else { logger.warn( "Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() + "]. Reason: Environment must implement ConfigurableEnvironment"); } } // Process any @ComponentScan annotations Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable( sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class); if (!componentScans.isEmpty() && !this.conditionEvaluator.shouldSkip( sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) { for (AnnotationAttributes componentScan : componentScans) { // The config class is annotated with @ComponentScan -> perform the scan immediately Set<BeanDefinitionHolder> scannedBeanDefinitions = this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName()); // Check the set of scanned definitions for any further config classes and parse recursively // if necessary for (BeanDefinitionHolder holder : scannedBeanDefinitions) { if (ConfigurationClassUtils.checkConfigurationClassCandidate( holder.getBeanDefinition(), this.metadataReaderFactory)) { parse(holder.getBeanDefinition().getBeanClassName(), holder.getBeanName()); } } } } // Process any @Import annotations processImports(configClass, sourceClass, getImports(sourceClass), true); // Process any @ImportResource annotations if (sourceClass.getMetadata().isAnnotated(ImportResource.class.getName())) { AnnotationAttributes importResource = AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class); String[] resources = importResource.getAliasedStringArray("locations", ImportResource.class, sourceClass); Class<? extends BeanDefinitionReader> readerClass = importResource.getClass("reader"); for (String resource : resources) { String resolvedResource = this.environment.resolveRequiredPlaceholders(resource); configClass.addImportedResource(resolvedResource, readerClass); } } // Process individual @Bean methods Set<MethodMetadata> beanMethods = sourceClass.getMetadata().getAnnotatedMethods(Bean.class.getName()); for (MethodMetadata methodMetadata : beanMethods) { configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass)); } // Process default methods on interfaces for (SourceClass ifc : sourceClass.getInterfaces()) { beanMethods = ifc.getMetadata().getAnnotatedMethods(Bean.class.getName()); for (MethodMetadata methodMetadata : beanMethods) { if (!methodMetadata.isAbstract()) { // A default method or other concrete method on a Java 8+ interface... configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass)); } } } // Process superclass, if any if (sourceClass.getMetadata().hasSuperClass()) { String superclass = sourceClass.getMetadata().getSuperClassName(); if (!superclass.startsWith("java") && !this.knownSuperclasses.containsKey(superclass)) { this.knownSuperclasses.put(superclass, configClass); // Superclass found, return its annotation metadata and recurse return sourceClass.getSuperClass(); } } // No superclass -> processing is complete return null; }