/*
* Copyright 2002-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/package org.springframework.context.annotation;import java.io.FileNotFoundException;import java.io.IOException;import java.net.UnknownHostException;import java.util.ArrayDeque;import java.util.ArrayList;import java.util.Collection;import java.util.Collections;import java.util.Comparator;import java.util.Deque;import java.util.HashMap;import java.util.Iterator;import java.util.LinkedHashMap;import java.util.LinkedHashSet;import java.util.LinkedList;import java.util.List;import java.util.Map;import java.util.Set;import org.apache.commons.logging.Log;import org.apache.commons.logging.LogFactory;import org.springframework.beans.BeanUtils;import org.springframework.beans.factory.BeanDefinitionStoreException;import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;import org.springframework.beans.factory.config.BeanDefinition;import org.springframework.beans.factory.config.BeanDefinitionHolder;import org.springframework.beans.factory.parsing.Location;import org.springframework.beans.factory.parsing.Problem;import org.springframework.beans.factory.parsing.ProblemReporter;import org.springframework.beans.factory.support.AbstractBeanDefinition;import org.springframework.beans.factory.support.BeanDefinitionReader;import org.springframework.beans.factory.support.BeanDefinitionRegistry;import org.springframework.beans.factory.support.BeanNameGenerator;import org.springframework.context.annotation.ConfigurationCondition.ConfigurationPhase;import org.springframework.core.NestedIOException;import org.springframework.core.annotation.AnnotationAttributes;import org.springframework.core.annotation.AnnotationAwareOrderComparator;import org.springframework.core.env.CompositePropertySource;import org.springframework.core.env.ConfigurableEnvironment;import org.springframework.core.env.Environment;import org.springframework.core.env.MutablePropertySources;import org.springframework.core.env.PropertySource;import org.springframework.core.io.Resource;import org.springframework.core.io.ResourceLoader;import org.springframework.core.io.support.DefaultPropertySourceFactory;import org.springframework.core.io.support.EncodedResource;import org.springframework.core.io.support.PropertySourceFactory;import org.springframework.core.io.support.ResourcePropertySource;import org.springframework.core.type.AnnotationMetadata;import org.springframework.core.type.MethodMetadata;import org.springframework.core.type.StandardAnnotationMetadata;import org.springframework.core.type.classreading.MetadataReader;import org.springframework.core.type.classreading.MetadataReaderFactory;import org.springframework.core.type.filter.AssignableTypeFilter;import org.springframework.util.Assert;import org.springframework.util.CollectionUtils;import org.springframework.util.LinkedMultiValueMap;import org.springframework.util.MultiValueMap;import org.springframework.util.StringUtils;/**
* Parses a {@link Configuration} class definition, populating a collection of
* {@link ConfigurationClass} objects (parsing a single Configuration class may result in
* any number of ConfigurationClass objects because one Configuration class may import
* another using the {@link Import} annotation).
*
* <p>This class helps separate the concern of parsing the structure of a Configuration
* class from the concern of registering BeanDefinition objects based on the
* content of that model (with the exception of {@code @ComponentScan} annotations which
* need to be registered immediately).
*
* <p>This ASM-based implementation avoids reflection and eager class loading in order to
* interoperate effectively with lazy class loading in a Spring ApplicationContext.
*
* @author Chris Beams
* @author Juergen Hoeller
* @author Phillip Webb
* @author Sam Brannen
* @since 3.0
* @see ConfigurationClassBeanDefinitionReader
*/classConfigurationClassParser{privatestaticfinal PropertySourceFactory DEFAULT_PROPERTY_SOURCE_FACTORY =newDefaultPropertySourceFactory();privatestaticfinal Comparator<DeferredImportSelectorHolder> DEFERRED_IMPORT_COMPARATOR =newComparator<ConfigurationClassParser.DeferredImportSelectorHolder>(){@Overridepublicintcompare(DeferredImportSelectorHolder o1, DeferredImportSelectorHolder o2){return AnnotationAwareOrderComparator.INSTANCE.compare(o1.getImportSelector(), o2.getImportSelector());}};privatefinal Log logger = LogFactory.getLog(getClass());privatefinal MetadataReaderFactory metadataReaderFactory;privatefinal ProblemReporter problemReporter;privatefinal Environment environment;privatefinal ResourceLoader resourceLoader;privatefinal BeanDefinitionRegistry registry;privatefinal ComponentScanAnnotationParser componentScanParser;privatefinal ConditionEvaluator conditionEvaluator;privatefinal Map<ConfigurationClass, ConfigurationClass> configurationClasses =newLinkedHashMap<ConfigurationClass, ConfigurationClass>();privatefinal Map<String, ConfigurationClass> knownSuperclasses =newHashMap<String, ConfigurationClass>();privatefinal List<String> propertySourceNames =newArrayList<String>();privatefinal ImportStack importStack =newImportStack();private List<DeferredImportSelectorHolder> deferredImportSelectors;/**
* Create a new {@link ConfigurationClassParser} instance that will be used
* to populate the set of configuration classes.
*/publicConfigurationClassParser(MetadataReaderFactory metadataReaderFactory,
ProblemReporter problemReporter, Environment environment, ResourceLoader resourceLoader,
BeanNameGenerator componentScanBeanNameGenerator, BeanDefinitionRegistry registry){this.metadataReaderFactory = metadataReaderFactory;this.problemReporter = problemReporter;this.environment = environment;this.resourceLoader = resourceLoader;this.registry = registry;this.componentScanParser =newComponentScanAnnotationParser(
environment, resourceLoader, componentScanBeanNameGenerator, registry);this.conditionEvaluator =newConditionEvaluator(registry, environment, resourceLoader);}publicvoidparse(Set<BeanDefinitionHolder> configCandidates){this.deferredImportSelectors =newLinkedList<DeferredImportSelectorHolder>();for(BeanDefinitionHolder holder : configCandidates){
BeanDefinition bd = holder.getBeanDefinition();try{if(bd instanceofAnnotatedBeanDefinition){parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName());}elseif(bd instanceofAbstractBeanDefinition&&((AbstractBeanDefinition) bd).hasBeanClass()){parse(((AbstractBeanDefinition) bd).getBeanClass(), holder.getBeanName());}else{parse(bd.getBeanClassName(), holder.getBeanName());}}catch(BeanDefinitionStoreException ex){throw ex;}catch(Throwable ex){thrownewBeanDefinitionStoreException("Failed to parse configuration class ["+ bd.getBeanClassName()+"]", ex);}}processDeferredImportSelectors();}protectedfinalvoidparse(String className, String beanName)throws IOException {
MetadataReader reader =this.metadataReaderFactory.getMetadataReader(className);processConfigurationClass(newConfigurationClass(reader, beanName));}protectedfinalvoidparse(Class<?> clazz, String beanName)throws IOException {processConfigurationClass(newConfigurationClass(clazz, beanName));}protectedfinalvoidparse(AnnotationMetadata metadata, String beanName)throws IOException {processConfigurationClass(newConfigurationClass(metadata, beanName));}/**
* Validate each {@link ConfigurationClass} object.
* @see ConfigurationClass#validate
*/publicvoidvalidate(){for(ConfigurationClass configClass :this.configurationClasses.keySet()){
configClass.validate(this.problemReporter);}}public Set<ConfigurationClass>getConfigurationClasses(){returnthis.configurationClasses.keySet();}protectedvoidprocessConfigurationClass(ConfigurationClass configClass)throws IOException {if(this.conditionEvaluator.shouldSkip(configClass.getMetadata(), ConfigurationPhase.PARSE_CONFIGURATION)){return;}
ConfigurationClass existingClass =this.configurationClasses.get(configClass);if(existingClass != null){if(configClass.isImported()){if(existingClass.isImported()){
existingClass.mergeImportedBy(configClass);}// Otherwise ignore new imported config class; existing non-imported class overrides it.return;}else{// Explicit bean definition found, probably replacing an import.// Let's remove the old one and go with the new one.this.configurationClasses.remove(configClass);for(Iterator<ConfigurationClass> it =this.knownSuperclasses.values().iterator(); it.hasNext();){if(configClass.equals(it.next())){
it.remove();}}}}// Recursively process the configuration class and its superclass hierarchy.
SourceClass sourceClass =asSourceClass(configClass);do{
sourceClass =doProcessConfigurationClass(configClass, sourceClass);}while(sourceClass != null);this.configurationClasses.put(configClass, configClass);}/**
* 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
*/protectedfinal SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass)throws IOException {// Recursively process any member (nested) classes firstprocessMemberClasses(configClass, sourceClass);// Process any @PropertySource annotationsfor(AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(
sourceClass.getMetadata(), PropertySources.class,
org.springframework.context.annotation.PropertySource.class)){if(this.environment instanceofConfigurableEnvironment){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 neededfor(BeanDefinitionHolder holder : scannedBeanDefinitions){if(ConfigurationClassUtils.checkConfigurationClassCandidate(
holder.getBeanDefinition(),this.metadataReaderFactory)){parse(holder.getBeanDefinition().getBeanClassName(), holder.getBeanName());}}}}// Process any @Import annotationsprocessImports(configClass, sourceClass,getImports(sourceClass),true);// Process any @ImportResource annotationsif(sourceClass.getMetadata().isAnnotated(ImportResource.class.getName())){
AnnotationAttributes importResource =
AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class);
String[] resources = importResource.getStringArray("locations");
Class<?extendsBeanDefinitionReader> 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 =retrieveBeanMethodMetadata(sourceClass);for(MethodMetadata methodMetadata : beanMethods){
configClass.addBeanMethod(newBeanMethod(methodMetadata, configClass));}// Process default methods on interfacesprocessInterfaces(configClass, sourceClass);// Process superclass, if anyif(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 recursereturn sourceClass.getSuperClass();}}// No superclass -> processing is completereturn null;}/**
* Register member (nested) classes that happen to be configuration classes themselves.
*/privatevoidprocessMemberClasses(ConfigurationClass configClass, SourceClass sourceClass)throws IOException {for(SourceClass memberClass : sourceClass.getMemberClasses()){if(ConfigurationClassUtils.isConfigurationCandidate(memberClass.getMetadata())&&!memberClass.getMetadata().getClassName().equals(configClass.getMetadata().getClassName())){if(this.importStack.contains(configClass)){this.problemReporter.error(newCircularImportProblem(configClass,this.importStack));}else{this.importStack.push(configClass);try{processConfigurationClass(memberClass.asConfigClass(configClass));}finally{this.importStack.pop();}}}}}/**
* Register default methods on interfaces implemented by the configuration class.
*/privatevoidprocessInterfaces(ConfigurationClass configClass, SourceClass sourceClass)throws IOException {for(SourceClass ifc : sourceClass.getInterfaces()){
Set<MethodMetadata> beanMethods =retrieveBeanMethodMetadata(ifc);for(MethodMetadata methodMetadata : beanMethods){if(!methodMetadata.isAbstract()){// A default method or other concrete method on a Java 8+ interface...
configClass.addBeanMethod(newBeanMethod(methodMetadata, configClass));}}processInterfaces(configClass, ifc);}}/**
* Retrieve the metadata for all <code>@Bean</code> methods.
*/private Set<MethodMetadata>retrieveBeanMethodMetadata(SourceClass sourceClass){
AnnotationMetadata original = sourceClass.getMetadata();
Set<MethodMetadata> beanMethods = original.getAnnotatedMethods(Bean.class.getName());if(beanMethods.size()>1&& original instanceofStandardAnnotationMetadata){// Try reading the class file via ASM for deterministic declaration order...// Unfortunately, the JVM's standard reflection returns methods in arbitrary// order, even between different runs of the same application on the same JVM.try{
AnnotationMetadata asm =this.metadataReaderFactory.getMetadataReader(original.getClassName()).getAnnotationMetadata();
Set<MethodMetadata> asmMethods = asm.getAnnotatedMethods(Bean.class.getName());if(asmMethods.size()>= beanMethods.size()){
Set<MethodMetadata> selectedMethods =newLinkedHashSet<MethodMetadata>(asmMethods.size());for(MethodMetadata asmMethod : asmMethods){for(MethodMetadata beanMethod : beanMethods){if(beanMethod.getMethodName().equals(asmMethod.getMethodName())){
selectedMethods.add(beanMethod);break;}}}if(selectedMethods.size()== beanMethods.size()){// All reflection-detected methods found in ASM method set -> proceed
beanMethods = selectedMethods;}}}catch(IOException ex){
logger.debug("Failed to read class file via ASM for determining @Bean method order", ex);// No worries, let's continue with the reflection metadata we started with...}}return beanMethods;}/**
* Process the given <code>@PropertySource</code> annotation metadata.
* @param propertySource metadata for the <code>@PropertySource</code> annotation found
* @throws IOException if loading a property source failed
*/privatevoidprocessPropertySource(AnnotationAttributes propertySource)throws IOException {
String name = propertySource.getString("name");if(!StringUtils.hasLength(name)){
name = null;}
String encoding = propertySource.getString("encoding");if(!StringUtils.hasLength(encoding)){
encoding = null;}
String[] locations = propertySource.getStringArray("value");
Assert.isTrue(locations.length >0,"At least one @PropertySource(value) location is required");boolean ignoreResourceNotFound = propertySource.getBoolean("ignoreResourceNotFound");
Class<?extendsPropertySourceFactory> factoryClass = propertySource.getClass("factory");
PropertySourceFactory factory =(factoryClass == PropertySourceFactory.class?
DEFAULT_PROPERTY_SOURCE_FACTORY : BeanUtils.instantiateClass(factoryClass));for(String location : locations){try{
String resolvedLocation =this.environment.resolveRequiredPlaceholders(location);
Resource resource =this.resourceLoader.getResource(resolvedLocation);addPropertySource(factory.createPropertySource(name,newEncodedResource(resource, encoding)));}catch(IllegalArgumentException ex){// Placeholders not resolvableif(ignoreResourceNotFound){if(logger.isInfoEnabled()){
logger.info("Properties location ["+ location +"] not resolvable: "+ ex.getMessage());}}else{throw ex;}}catch(IOException ex){// Resource not found when trying to open itif(ignoreResourceNotFound &&(ex instanceofFileNotFoundException|| ex instanceofUnknownHostException)){if(logger.isInfoEnabled()){
logger.info("Properties location ["+ location +"] not resolvable: "+ ex.getMessage());}}else{throw ex;}}}}privatevoidaddPropertySource(PropertySource<?> propertySource){
String name = propertySource.getName();
MutablePropertySources propertySources =((ConfigurableEnvironment)this.environment).getPropertySources();if(propertySources.contains(name)&&this.propertySourceNames.contains(name)){// We've already added a version, we need to extend it
PropertySource<?> existing = propertySources.get(name);
PropertySource<?> newSource =(propertySource instanceofResourcePropertySource?((ResourcePropertySource) propertySource).withResourceName(): propertySource);if(existing instanceofCompositePropertySource){((CompositePropertySource) existing).addFirstPropertySource(newSource);}else{if(existing instanceofResourcePropertySource){
existing =((ResourcePropertySource) existing).withResourceName();}
CompositePropertySource composite =newCompositePropertySource(name);
composite.addPropertySource(newSource);
composite.addPropertySource(existing);
propertySources.replace(name, composite);}}else{if(this.propertySourceNames.isEmpty()){
propertySources.addLast(propertySource);}else{
String firstProcessed =this.propertySourceNames.get(this.propertySourceNames.size()-1);
propertySources.addBefore(firstProcessed, propertySource);}}this.propertySourceNames.add(name);}/**
* Returns {@code @Import} class, considering all meta-annotations.
*/private Set<SourceClass>getImports(SourceClass sourceClass)throws IOException {
Set<SourceClass> imports =newLinkedHashSet<SourceClass>();
Set<SourceClass> visited =newLinkedHashSet<SourceClass>();collectImports(sourceClass, imports, visited);return imports;}/**
* Recursively collect all declared {@code @Import} values. Unlike most
* meta-annotations it is valid to have several {@code @Import}s declared with
* different values; the usual process of returning values from the first
* meta-annotation on a class is not sufficient.
* <p>For example, it is common for a {@code @Configuration} class to declare direct
* {@code @Import}s in addition to meta-imports originating from an {@code @Enable}
* annotation.
* @param sourceClass the class to search
* @param imports the imports collected so far
* @param visited used to track visited classes to prevent infinite recursion
* @throws IOException if there is any problem reading metadata from the named class
*/privatevoidcollectImports(SourceClass sourceClass, Set<SourceClass> imports, Set<SourceClass> visited)throws IOException {if(visited.add(sourceClass)){for(SourceClass annotation : sourceClass.getAnnotations()){
String annName = annotation.getMetadata().getClassName();if(!annName.startsWith("java")&&!annName.equals(Import.class.getName())){collectImports(annotation, imports, visited);}}
imports.addAll(sourceClass.getAnnotationAttributes(Import.class.getName(),"value"));}}
⭐️processDeferredImportSelectors
privatevoidprocessDeferredImportSelectors(){
List<DeferredImportSelectorHolder> deferredImports =this.deferredImportSelectors;this.deferredImportSelectors = null;
Collections.sort(deferredImports, DEFERRED_IMPORT_COMPARATOR);for(DeferredImportSelectorHolder deferredImport : deferredImports){
ConfigurationClass configClass = deferredImport.getConfigurationClass();try{//1 SpringBoot中:// deferredImport.getImportSelector() 是 EnableAutoConfigurationImportSelector// selectImports会获取到 spirng.factories中EnableAutoConfiguration下的类
String[] imports = deferredImport.getImportSelector().selectImports(configClass.getMetadata());processImports(configClass,asSourceClass(configClass),asSourceClasses(imports),false);}catch(BeanDefinitionStoreException ex){throw ex;}catch(Throwable ex){thrownewBeanDefinitionStoreException("Failed to process import candidates for configuration class ["+
configClass.getMetadata().getClassName()+"]", ex);}}}
⭐️ processImports
privatevoidprocessImports(ConfigurationClass configClass, SourceClass currentSourceClass,
Collection<SourceClass> importCandidates,boolean checkForCircularImports)throws IOException {if(importCandidates.isEmpty()){return;}if(checkForCircularImports &&isChainedImportOnStack(configClass)){this.problemReporter.error(newCircularImportProblem(configClass,this.importStack));}else{this.importStack.push(configClass);try{// importCandidates 是被导入的类// 1 SpringBoot中:// importCandidates就是 spring.factories中EnableAutoConfiguration下的类for(SourceClass candidate : importCandidates){// candidate是否是ImportSelector的类或其子类或子接口if(candidate.isAssignable(ImportSelector.class)){// Candidate class is an ImportSelector -> delegate to it to determine imports
Class<?> candidateClass = candidate.loadClass();
ImportSelector selector = BeanUtils.instantiateClass(candidateClass, ImportSelector.class);
ParserStrategyUtils.invokeAwareMethods(
selector,this.environment,this.resourceLoader,this.registry);if(this.deferredImportSelectors != null && selector instanceofDeferredImportSelector){this.deferredImportSelectors.add(newDeferredImportSelectorHolder(configClass,(DeferredImportSelector) selector));}else{
String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
Collection<SourceClass> importSourceClasses =asSourceClasses(importClassNames);processImports(configClass, currentSourceClass, importSourceClasses,false);}}elseif(candidate.isAssignable(ImportBeanDefinitionRegistrar.class)){// Candidate class is an ImportBeanDefinitionRegistrar ->// delegate to it to register additional bean definitions
Class<?> candidateClass = candidate.loadClass();
ImportBeanDefinitionRegistrar registrar =
BeanUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class);
ParserStrategyUtils.invokeAwareMethods(
registrar,this.environment,this.resourceLoader,this.registry);
configClass.addImportBeanDefinitionRegistrar(registrar, currentSourceClass.getMetadata());}else{// 处理@Configuration// Candidate class not an ImportSelector or ImportBeanDefinitionRegistrar ->// process it as an @Configuration classthis.importStack.registerImport(
currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());processConfigurationClass(candidate.asConfigClass(configClass));}}}catch(BeanDefinitionStoreException ex){throw ex;}catch(Throwable ex){thrownewBeanDefinitionStoreException("Failed to process import candidates for configuration class ["+
configClass.getMetadata().getClassName()+"]", ex);}finally{this.importStack.pop();}}}privatebooleanisChainedImportOnStack(ConfigurationClass configClass){if(this.importStack.contains(configClass)){
String configClassName = configClass.getMetadata().getClassName();
AnnotationMetadata importingClass =this.importStack.getImportingClassFor(configClassName);while(importingClass != null){if(configClassName.equals(importingClass.getClassName())){returntrue;}
importingClass =this.importStack.getImportingClassFor(importingClass.getClassName());}}returnfalse;}
ImportRegistry getImportRegistry(){returnthis.importStack;}/**
* Factory method to obtain a {@link SourceClass} from a {@link ConfigurationClass}.
*/private SourceClass asSourceClass(ConfigurationClass configurationClass)throws IOException {
AnnotationMetadata metadata = configurationClass.getMetadata();if(metadata instanceofStandardAnnotationMetadata){returnasSourceClass(((StandardAnnotationMetadata) metadata).getIntrospectedClass());}returnasSourceClass(metadata.getClassName());}/**
* Factory method to obtain a {@link SourceClass} from a {@link Class}.
*/
SourceClass asSourceClass(Class<?> classType)throws IOException {try{// Sanity test that we can read annotations, if not fall back to ASM
classType.getAnnotations();returnnewSourceClass(classType);}catch(Throwable ex){// Enforce ASM via class name resolutionreturnasSourceClass(classType.getName());}}/**
* Factory method to obtain {@link SourceClass}s from class names.
*/private Collection<SourceClass>asSourceClasses(String[] classNames)throws IOException {
List<SourceClass> annotatedClasses =newArrayList<SourceClass>(classNames.length);for(String className : classNames){
annotatedClasses.add(asSourceClass(className));}return annotatedClasses;}/**
* Factory method to obtain a {@link SourceClass} from a class name.
*/
SourceClass asSourceClass(String className)throws IOException {if(className.startsWith("java")){// Never use ASM for core java typestry{returnnewSourceClass(this.resourceLoader.getClassLoader().loadClass(className));}catch(ClassNotFoundException ex){thrownewNestedIOException("Failed to load class ["+ className +"]", ex);}}returnnewSourceClass(this.metadataReaderFactory.getMetadataReader(className));}@SuppressWarnings("serial")privatestaticclassImportStackextendsArrayDeque<ConfigurationClass>implementsImportRegistry{privatefinal MultiValueMap<String, AnnotationMetadata> imports =newLinkedMultiValueMap<String, AnnotationMetadata>();publicvoidregisterImport(AnnotationMetadata importingClass, String importedClass){this.imports.add(importedClass, importingClass);}@Overridepublic AnnotationMetadata getImportingClassFor(String importedClass){
List<AnnotationMetadata> list =this.imports.get(importedClass);return(!CollectionUtils.isEmpty(list)? list.get(list.size()-1): null);}@OverridepublicvoidremoveImportingClass(String importingClass){for(List<AnnotationMetadata> list :this.imports.values()){for(Iterator<AnnotationMetadata> iterator = list.iterator(); iterator.hasNext();){if(iterator.next().getClassName().equals(importingClass)){
iterator.remove();break;}}}}/**
* Given a stack containing (in order)
* <ul>
* <li>com.acme.Foo</li>
* <li>com.acme.Bar</li>
* <li>com.acme.Baz</li>
* </ul>
* return "[Foo->Bar->Baz]".
*/@Overridepublic String toString(){
StringBuilder builder =newStringBuilder("[");
Iterator<ConfigurationClass> iterator =iterator();while(iterator.hasNext()){
builder.append(iterator.next().getSimpleName());if(iterator.hasNext()){
builder.append("->");}}return builder.append(']').toString();}}privatestaticclassDeferredImportSelectorHolder{privatefinal ConfigurationClass configurationClass;privatefinal DeferredImportSelector importSelector;publicDeferredImportSelectorHolder(ConfigurationClass configClass, DeferredImportSelector selector){this.configurationClass = configClass;this.importSelector = selector;}public ConfigurationClass getConfigurationClass(){returnthis.configurationClass;}public DeferredImportSelector getImportSelector(){returnthis.importSelector;}}/**
* Simple wrapper that allows annotated source classes to be dealt with
* in a uniform manner, regardless of how they are loaded.
*/privateclassSourceClass{privatefinal Object source;// Class or MetadataReaderprivatefinal AnnotationMetadata metadata;publicSourceClass(Object source){this.source = source;if(source instanceofClass){this.metadata =newStandardAnnotationMetadata((Class<?>) source,true);}else{this.metadata =((MetadataReader) source).getAnnotationMetadata();}}publicfinal AnnotationMetadata getMetadata(){returnthis.metadata;}public Class<?>loadClass()throws ClassNotFoundException {if(this.source instanceofClass){return(Class<?>)this.source;}
String className =((MetadataReader)this.source).getClassMetadata().getClassName();return resourceLoader.getClassLoader().loadClass(className);}publicbooleanisAssignable(Class<?> clazz)throws IOException {if(this.source instanceofClass){return clazz.isAssignableFrom((Class<?>)this.source);}returnnewAssignableTypeFilter(clazz).match((MetadataReader)this.source, metadataReaderFactory);}public ConfigurationClass asConfigClass(ConfigurationClass importedBy)throws IOException {if(this.source instanceofClass){returnnewConfigurationClass((Class<?>)this.source, importedBy);}returnnewConfigurationClass((MetadataReader)this.source, importedBy);}public Collection<SourceClass>getMemberClasses()throws IOException {
Object sourceToProcess =this.source;if(sourceToProcess instanceofClass){
Class<?> sourceClass =(Class<?>) sourceToProcess;try{
Class<?>[] declaredClasses = sourceClass.getDeclaredClasses();
List<SourceClass> members =newArrayList<SourceClass>(declaredClasses.length);for(Class<?> declaredClass : declaredClasses){
members.add(asSourceClass(declaredClass));}return members;}catch(NoClassDefFoundError err){// getDeclaredClasses() failed because of non-resolvable dependencies// -> fall back to ASM below
sourceToProcess = metadataReaderFactory.getMetadataReader(sourceClass.getName());}}// ASM-based resolution - safe for non-resolvable classes as well
MetadataReader sourceReader =(MetadataReader) sourceToProcess;
String[] memberClassNames = sourceReader.getClassMetadata().getMemberClassNames();
List<SourceClass> members =newArrayList<SourceClass>(memberClassNames.length);for(String memberClassName : memberClassNames){try{
members.add(asSourceClass(memberClassName));}catch(IOException ex){// Let's skip it if it's not resolvable - we're just looking for candidatesif(logger.isDebugEnabled()){
logger.debug("Failed to resolve member class ["+ memberClassName +"] - not considering it as a configuration class candidate");}}}return members;}public SourceClass getSuperClass()throws IOException {if(this.source instanceofClass){returnasSourceClass(((Class<?>)this.source).getSuperclass());}returnasSourceClass(((MetadataReader)this.source).getClassMetadata().getSuperClassName());}public Set<SourceClass>getInterfaces()throws IOException {
Set<SourceClass> result =newLinkedHashSet<SourceClass>();if(this.source instanceofClass){
Class<?> sourceClass =(Class<?>)this.source;for(Class<?> ifcClass : sourceClass.getInterfaces()){
result.add(asSourceClass(ifcClass));}}else{for(String className :this.metadata.getInterfaceNames()){
result.add(asSourceClass(className));}}return result;}public Set<SourceClass>getAnnotations()throws IOException {
Set<SourceClass> result =newLinkedHashSet<SourceClass>();for(String className :this.metadata.getAnnotationTypes()){try{
result.add(getRelated(className));}catch(Throwable ex){// An annotation not present on the classpath is being ignored// by the JVM's class loading -> ignore here as well.}}return result;}public Collection<SourceClass>getAnnotationAttributes(String annType, String attribute)throws IOException {
Map<String, Object> annotationAttributes =this.metadata.getAnnotationAttributes(annType,true);if(annotationAttributes == null ||!annotationAttributes.containsKey(attribute)){return Collections.emptySet();}
String[] classNames =(String[]) annotationAttributes.get(attribute);
Set<SourceClass> result =newLinkedHashSet<SourceClass>();for(String className : classNames){
result.add(getRelated(className));}return result;}private SourceClass getRelated(String className)throws IOException {if(this.source instanceofClass){try{
Class<?> clazz =((Class<?>)this.source).getClassLoader().loadClass(className);returnasSourceClass(clazz);}catch(ClassNotFoundException ex){// Ignore -> fall back to ASM next, except for core java types.if(className.startsWith("java")){thrownewNestedIOException("Failed to load class ["+ className +"]", ex);}returnnewSourceClass(metadataReaderFactory.getMetadataReader(className));}}returnasSourceClass(className);}@Overridepublicbooleanequals(Object other){return(this== other ||(other instanceofSourceClass&&this.metadata.getClassName().equals(((SourceClass) other).metadata.getClassName())));}@OverridepublicinthashCode(){returnthis.metadata.getClassName().hashCode();}@Overridepublic String toString(){returnthis.metadata.getClassName();}}/**
* {@link Problem} registered upon detection of a circular {@link Import}.
*/privatestaticclassCircularImportProblemextendsProblem{publicCircularImportProblem(ConfigurationClass attemptedImport, Deque<ConfigurationClass> importStack){super(String.format("A circular @Import has been detected: "+"Illegal attempt by @Configuration class '%s' to import class '%s' as '%s' is "+"already present in the current import stack %s", importStack.peek().getSimpleName(),
attemptedImport.getSimpleName(), attemptedImport.getSimpleName(), importStack),newLocation(importStack.peek().getResource(), attemptedImport.getMetadata()));}}}
/* * Copyright 2002-2017 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http:/