Spring中Java配置类初始化源码分析

日常看看源码系列

Posted by Belin on August 3, 2018

正所谓前人栽树,后人乘凉。

感谢Huxpro提供的博客模板

Spring容器初始化概述

Spring容器初始化的核心是BeanFactory的初始化,本文主要内容是注解的配置方式的BeanFactory的初始化。在容器整个初始化的过程中,无论是以前的xml文件配置还是这里的Java类配置,无非就是从配置源读取数据,然后从数据解析出BeanDefinition,接着注册到BeanFactory中。这个过程必须在容器实例化对象前完成,当然,如果开发者对某个bean的整个生命周期有足够的掌握,任何时候都可以进行这个类的初始化。但是这种情况势必会造成容器bean管理的混乱,所以还是建议按照Spring的Bean管理流程来管理Bean。还有如果是动态类,比如运用某些技术手段在运行时生成类的Bean,这种Bean肯定就是要动态注册到Spring容器,可以是定义成某个动态模块或使用子容器进行管理。

两种BeanDefintion解析方式

有阅读过一些Spring源码的童鞋应该知道,ApplicationContextBeanFactory并没有管理BeanDefintion的能力,BeanDefintion注册能力来自BeanDefinitionRegistry接口,如下的类签名 :

public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory implements ConfigurableListableBeanFactory, BeanDefinitionRegistry, Serializable 
{
	//类实现
}

然而BeanDefinitionRegistry注册Bean定义的接口只接受BeanDefintion对象,并不接受何种来源的bean定义:

void registerBeanDefinition(String beanName, BeanDefinition beanDefinition);

端庄而又优雅,分离了bean的管理和bean的来源,容器就只管理bean生命周期就可以,不用问哪来的。容器的子类中通过委托给xxxBeanDefintionReader对象来完成对bean定义的解析,不同来源的bean定义就使用不同的Reader。
这里说下比较常见的两种bean定义方式:

  • 1、XML文件
  • 2、Java注解

####XML文件配置 对于xml配置的容器子类如:AbstractXmlApplicationContext,向容器注册bean定义是在解析xml的同时注册到容器中,就是说xml中定义的bean这时候就已经解析注册完成,Component-Scan除外,这属于注解的bean。 流程如下:

1.ClassPathXmlApplicationContext:构造器调用refresh();
2.AbstractApplicationContext的refresh()方法调用链如下:
refush()->obtainFreshBeanFactory()->refreshBeanFactory()->loadBeanDefinitions(...)
->reader.loadBeanDefinitions(configResources);

最终是委托给了XmlBeanDefinitionReader持有容器实例 进行加载。 reader对xml文件的解析加载分为两种,一种是默认命名空间,另外一种是特殊命名空间的,如:<tx:xx><context:xx>, 对于特殊标签则是通过处理器的方式进行委托处理:

//默认标签:DefaultBeanDefinitionDocumentReader类
private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
	if (delegate.nodeNameEquals(ele, "import")) {
		importBeanDefinitionResource(ele);
	} else if (delegate.nodeNameEquals(ele, "alias")) {
		processAliasRegistration(ele);
	} else if (delegate.nodeNameEquals(ele, "bean")) {
		processBeanDefinition(ele, delegate);
	} else {
		if (!(delegate.nodeNameEquals(ele, "beans")))
			return;
		doRegisterBeanDefinitions(ele);
	}
}
//特殊标签解析:BeanDefinitionParserDelegate类
public BeanDefinition parseCustomElement(Element ele, BeanDefinition containingBd) {
	String namespaceUri = getNamespaceURI(ele);
	NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver()
		.resolve(namespaceUri);
	if (handler == null) {
		...
		return null;
	}
	return handler.parse(ele, 
		new ParserContext(this.readerContext, this, containingBd));
}

对于不同的标签的处理,采用了策略模式,不同标签自动选择不同的处理器,非常灵活。

Java注解

其实上面xml配置的Component-Scan如果扫描到@Configuation的类也会触发相同的初始化流程,Java类配置的方式xml配置的精简版。
对于Java类配置 如:AnnotationConfigWebApplicationContext 向容器注册bean定义通常都是通过两步完成的:

  • 第一步:向容器注册Java类配置,就是标志了@Configuation的类;
  • 第二步:通过BeanDefinitionRegistryPostProcessor在初始化容器的时候解析注册Java类配置类中定义的Bean; 整个初始化流程的关键就变成容器后处理器的注册和调用了,这部分的容器后处理器注册是在AnnotatedBeanDefinitionReader中完成的,别忘了reader是持有容器对象的,向容器注册啥东西都可以。
//reader 向容器注册容器后处理器,注册时机是reader的构造方法
public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry ..., Environment ...) {
	Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
	...
	//注册处理Java类配置的容器后处理器 
	AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
}

配置类解析过程分析

Java类配置

@Configuration注解标志的Java类都视为Java类配置。该类上还可以添加如下注解:

  • 1、@PropertySource
  • 2、@CompomentScan
  • 3、@Import
  • 4、@ImportResource

以上注解将会按照顺序解析,然后才到当前类的解析。

注册解析Java类配置的容器后处理器

见上文

配置解析入口

解析的入口是容器后处理器ConfigurationClassPostProcessor,如下是注册改容器后处理器的代码:

//org.springframework.context.annotation.AnnotationConfigUtils类
public static Set<BeanDefinitionHolder> registerAnnotationConfigProcessors(
		BeanDefinitionRegistry registry, @Nullable Object source) {
	...
	if (!registry.containsBeanDefinition(...)) {
		//创建容器后处理器bean定义
		RootBeanDefinition def = new RootBeanDefinition(ConfigurationClassPostProcessor.class);
		def.setSource(source);
		//将容器后处理器bean定义注册到容器中
		beanDefs.add(registerPostProcessor(registry, def, CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME));
	}
	...
}

跟进ConfigurationClassPostProcessor
既然是容器后处理器,则先见方法postProcessBeanDefinitionRegistry:

判断该处理器是否被重复执行,然后交给其他方法 这里记录疑问,为什么会重复执行?一个容器中可以有多个registry?

public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
	//1、这里时候防止该容器处理器被重复执行。
	int registryId = System.identityHashCode(registry);
	if (this.registriesPostProcessed.contains(registryId)) {
		throw new IllegalStateException(...);
	}
	if (this.factoriesPostProcessed.contains(registryId)) {
		throw new IllegalStateException(...);
	}
	this.registriesPostProcessed.add(registryId);
	//、执行处理
	processConfigBeanDefinitions(registry);
}

继续跟进processConfigBeanDefinitions,大致流程:

一、遍历目前Spring容器中所有Bean定义,找出Java类配置类,用集合configCandidates临时保存 二、一些解析的初始化工作,包括:bean名称生成策略、environment属性 三、使用ConfigurationClassParser 逐一解析configCandidate,解析结果保存在ConfigurationClassParser对象中 四、从ConfigurationClassParser对象获取解析结果ConfigurationClass集合,并注册到Spring容器中 以上四个步骤只处理了Java类配置类外部的bean定义的解析注册,如果Java类配置类中定义的某些Bean也是Java类配置类,则需要继续处理 五、对当前Java类配置类中定义的Bean进行遍历,如果bean定义中存在Java类配置类,则将类取出并按照上面的加载流程进行加载,直到没找到Java类配置类的bean为止

public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
	List<BeanDefinitionHolder> configCandidates = new ArrayList<>();
	String[] candidateNames = registry.getBeanDefinitionNames();

	// 遍历目前Spring容器中所有Bean定义,找出Java类配置类
	// 这里有一个知识点,Spring识别哪些类为Java类配置类,见附录。
	for (String beanName : candidateNames) {
		BeanDefinition beanDef = registry.getBeanDefinition(beanName);
		if (ConfigurationClassUtils.isFullConfigurationClass(beanDef) ||
				ConfigurationClassUtils.isLiteConfigurationClass(beanDef)) {
			if (logger.isDebugEnabled()) {
				logger.debug("Bean definition has already been processed as a configuration class: " + beanDef);
			}
		}
		else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {
			configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
		}
	}

	// Return immediately if no @Configuration classes were found
	if (configCandidates.isEmpty()) {
		return;
	}

	// Sort by previously determined @Order value, if applicable
	configCandidates.sort((bd1, bd2) -> {
		int i1 = ConfigurationClassUtils.getOrder(bd1.getBeanDefinition());
		int i2 = ConfigurationClassUtils.getOrder(bd2.getBeanDefinition());
		return Integer.compare(i1, i2);
	});

	// bean名称生成策略
	// 这里可以探究一下,这个策略是不是可以自定义
	// Detect any custom bean name generation strategy supplied through the enclosing application context
	SingletonBeanRegistry sbr = null;
	if (registry instanceof SingletonBeanRegistry) {
		sbr = (SingletonBeanRegistry) registry;
		if (!this.localBeanNameGeneratorSet) {
			BeanNameGenerator generator = (BeanNameGenerator) sbr.getSingleton(CONFIGURATION_BEAN_NAME_GENERATOR);
			if (generator != null) {
				this.componentScanBeanNameGenerator = generator;
				this.importBeanNameGenerator = generator;
			}
		}
	}

	if (this.environment == null) {
		this.environment = new StandardEnvironment();
	}

	// 遍历上面找出来的Java类配置类,并进行逐一的解析处理
	// Parse each @Configuration class
	ConfigurationClassParser parser = new ConfigurationClassParser(
			this.metadataReaderFactory, this.problemReporter, this.environment,
			this.resourceLoader, this.componentScanBeanNameGenerator, registry);

	Set<BeanDefinitionHolder> candidates = new LinkedHashSet<>(configCandidates);
	Set<ConfigurationClass> alreadyParsed = new HashSet<>(configCandidates.size());
	do {
		// 1.解析Java类配置类,解析结果保存在ConfigurationClass类的对象中
		parser.parse(candidates);
		parser.validate();

		// 2.获取ConfigurationClass对象
		Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses());
		configClasses.removeAll(alreadyParsed);

		// Read the model and create bean definitions based on its content
		if (this.reader == null) {
			this.reader = new ConfigurationClassBeanDefinitionReader(
					registry, this.sourceExtractor, this.resourceLoader, this.environment,
					this.importBeanNameGenerator, parser.getImportRegistry());
		}
		// 3.将ConfigurationClass对象中的解析结果注册到容器中
		this.reader.loadBeanDefinitions(configClasses);
		alreadyParsed.addAll(configClasses);

		// 4.对当前Java类配置类中定义的Bean进行遍历,如果bean定义中存在Java类配置类,
		// 则将类取出并按照上面的加载流程进行加载。这是Java类配置类内部bean定义的循环判断,直到没找到Java类配置类的bean为止
		candidates.clear();
		if (registry.getBeanDefinitionCount() > candidateNames.length) {
			String[] newCandidateNames = registry.getBeanDefinitionNames();
			Set<String> oldCandidateNames = new HashSet<>(Arrays.asList(candidateNames));
			Set<String> alreadyParsedClasses = new HashSet<>();
			for (ConfigurationClass configurationClass : alreadyParsed) {
				alreadyParsedClasses.add(configurationClass.getMetadata().getClassName());
			}
			for (String candidateName : newCandidateNames) {
				if (!oldCandidateNames.contains(candidateName)) {
					BeanDefinition bd = registry.getBeanDefinition(candidateName);
					if (ConfigurationClassUtils.checkConfigurationClassCandidate(bd, this.metadataReaderFactory) &&
							!alreadyParsedClasses.contains(bd.getBeanClassName())) {
						candidates.add(new BeanDefinitionHolder(bd, candidateName));
					}
				}
			}
			candidateNames = newCandidateNames;
		}
	}
	while (!candidates.isEmpty());

	// Register the ImportRegistry as a bean in order to support ImportAware @Configuration classes
	if (sbr != null && !sbr.containsSingleton(IMPORT_REGISTRY_BEAN_NAME)) {
		sbr.registerSingleton(IMPORT_REGISTRY_BEAN_NAME, parser.getImportRegistry());
	}

	if (this.metadataReaderFactory instanceof CachingMetadataReaderFactory) {
		// Clear cache in externally provided MetadataReaderFactory; this is a no-op
		// for a shared cache since it'll be cleared by the ApplicationContext.
		((CachingMetadataReaderFactory) this.metadataReaderFactory).clearCache();
	}
}

以上是一个大致解析流程,下面具体看解析过程。主要的方法是parser.parse(candidates);
根据代码调用,跳转到核心处理方法:

//org.springframework.context.annotation.ConfigurationClassParser#doProcessConfigurationClass
protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass)
		throws IOException {

	// 1.首先处理嵌套的内部类(Java类配置类),处理过程和普通Java类配置类相同,
	// 所以这里是一个递归的过程,进行逐一处理,递归出口就是所有内部类处理完成
	processMemberClasses(configClass, sourceClass);

	// 2.处理@PropertySource,将该注解上的配置文件加载到Environment中,代码中的@value可获取到属性值
	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");
		}
	}

	// 3.处理扫描注解@ComponentScan,这个处理时候直接将bean定义注册到容器中,所以在注册完bean定义后直接递归处理了扫描到的Java类配置类
	// 设计者这么做的目的应该是复用ComponentScan的处理流程,ComponentScan是一种比较特殊的注册bean定义的方法,已经一个比较完整的模块。
	// 当然这也提高了处理的复杂度,这里处理的直接注册的bean定义和处理的Java类配置类,需要和其他注册方式妥善协调,否则会出现重复注册的问题
	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 needed
			// 检查扫描结果
			for (BeanDefinitionHolder holder : scannedBeanDefinitions) {
				if (ConfigurationClassUtils.checkConfigurationClassCandidate(
						holder.getBeanDefinition(), this.metadataReaderFactory)) {
					// 递归调用解析
					parse(holder.getBeanDefinition().getBeanClassName(), holder.getBeanName());
				}
			}
		}
	}

	// 4.处理@Import导入的配置,一般是Java类配置类。SpringBoot自动化配置的关键之一
	processImports(configClass, sourceClass, getImports(sourceClass), true);

	// 5.处理@ImportResource导入的配置,一般是xml或者groovy
	AnnotationAttributes importResource =
			AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class);
	if (importResource != null) {
		String[] resources = importResource.getStringArray("locations");
		Class<? extends BeanDefinitionReader> readerClass = importResource.getClass("reader");
		for (String resource : resources) {
			String resolvedResource = this.environment.resolveRequiredPlaceholders(resource);
			configClass.addImportedResource(resolvedResource, readerClass);
		}
	}

	// 6.处理Java配置类的@Bean注解方法,这里就是bean的定义了
	Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);
	for (MethodMetadata methodMetadata : beanMethods) {
		configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
	}

	// 7.处理接口默认方法,这是JDK1.8以后才有的接口默认方法实现
	processInterfaces(configClass, sourceClass);

	// 8.查找父类,如果父类也有注解,则会循环处理
	if (sourceClass.getMetadata().hasSuperClass()) {
		String superclass = sourceClass.getMetadata().getSuperClassName();
		if (superclass != null && !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;
}

这里重点说下第4步,这里是SpringBoot灵活自动化配置的关键:

private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,
		Collection<SourceClass> importCandidates, boolean checkForCircularImports) {

	if (importCandidates.isEmpty()) {
		return;
	}

	if (checkForCircularImports && isChainedImportOnStack(configClass)) {
		this.problemReporter.error(new CircularImportProblem(configClass, this.importStack));
	}
	else {
		this.importStack.push(configClass);
		try {
			for (SourceClass candidate : importCandidates) {
				// 11111111111111111
				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 instanceof DeferredImportSelector) {
						this.deferredImportSelectors.add(
								new DeferredImportSelectorHolder(configClass, (DeferredImportSelector) selector));
					}
					else {
						String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
						Collection<SourceClass> importSourceClasses = asSourceClasses(importClassNames);
						processImports(configClass, currentSourceClass, importSourceClasses, false);
					}
				}
				// 22222222222
				else if (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());
				}
				// 3333333333333333
				else {
					// Candidate class not an ImportSelector or ImportBeanDefinitionRegistrar ->
					// process it as an @Configuration class
					this.importStack.registerImport(
							currentSourceClass.getMetadata(), candidate.getMetadata().getClassName());
					processConfigurationClass(candidate.asConfigClass(configClass));
				}
			}
		}
		// ...
	}
}

其实可以清晰的看到有三种情况: 第一种导入的类是ImportSelector类型的。利用反射实例化这个类,随后将一些应该注入的注入进去,如果是DeferredImportSelector类型的,那么就放到deferredImportSelectors这个集合里。如果不是这种类型的,就调用selectImports方法得到需要导入的类,随后继续递归此方法,进行导入; deferredImportSelectors调用时机是在parser.parse(candidates);方法中最后,可见延迟加载是延迟到其他所有Java类配置类加载完成后在进行加载; 第二种是ImportBeanDefinitionRegistrar类型,一样利用反射进行初始化,随后注入属性,随后调用了configClass.addImportBeanDefinitionRegistrar添加进来了; 最后一种情况导入的就是一个配置文件Configration类,直接调用processConfigurationClass方法进行处理。 至此,解析的流程就告一段落,解析的数据都放到了ConfigurationClass中。 接着就是ConfigurationClass中bean定义注册,在org.springframework.context.annotation.ConfigurationClassPostProcessor#processConfigBeanDefinitions方法中可以看到

// Read the model and create bean definitions based on its content
if (this.reader == null) {
	this.reader = new ConfigurationClassBeanDefinitionReader(
			registry, this.sourceExtractor, this.resourceLoader, this.environment,
			this.importBeanNameGenerator, parser.getImportRegistry());
}
this.reader.loadBeanDefinitions(configClasses);

这里通过ConfigurationClassBeanDefinitionReader进行委托注册bean:

/**
 * Read {@code configurationModel}, registering bean definitions
 * with the registry based on its contents.
 */
public void loadBeanDefinitions(Set<ConfigurationClass> configurationModel) {
	TrackedConditionEvaluator trackedConditionEvaluator = new TrackedConditionEvaluator();
	for (ConfigurationClass configClass : configurationModel) {
		loadBeanDefinitionsForConfigurationClass(configClass, trackedConditionEvaluator);
	}
}

/**
 * Read a particular {@link ConfigurationClass}, registering bean definitions
 * for the class itself and all of its {@link Bean} methods.
 */
private void loadBeanDefinitionsForConfigurationClass(ConfigurationClass configClass,
		TrackedConditionEvaluator trackedConditionEvaluator) {

	if (trackedConditionEvaluator.shouldSkip(configClass)) {
		String beanName = configClass.getBeanName();
		if (StringUtils.hasLength(beanName) && this.registry.containsBeanDefinition(beanName)) {
			this.registry.removeBeanDefinition(beanName);
		}
		this.importRegistry.removeImportingClass(configClass.getMetadata().getClassName());
		return;
	}

	if (configClass.isImported()) {
		registerBeanDefinitionForImportedConfigurationClass(configClass);
	}
	for (BeanMethod beanMethod : configClass.getBeanMethods()) {
		loadBeanDefinitionsForBeanMethod(beanMethod);
	}
	loadBeanDefinitionsFromImportedResources(configClass.getImportedResources());
	loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());
}

里面的registerBeanDefinitionForImportedConfigurationClassloadBeanDefinitionsForBeanMethodloadBeanDefinitionsFromImportedResourcesloadBeanDefinitionsFromRegistrars分别做的事情时将此Configuration注册成了BeanDefinition,将配置文件里面的使用@Bean注释的方法变成了BeanDefinition,将ImportResource进来的配置文件加载解析,从资源文件加载合适的Bean,最后一个就是调用Config类中ImportBeanDefinitionRegistrar类型的对象的registerBeanDefinitions方法,实现他们内部的注册逻辑。

文章正文到这里吧,大概记录了下流程,方便日后查阅。

附录

1.Spring识别哪些类为Java类配置类 在ConfigurationClassUtils类中有相应的判断方法,如下: a.完整的ConfigurationClass:

// 有@Configuration标记的类
public static boolean isFullConfigurationCandidate(AnnotationMetadata metadata) {
	return metadata.isAnnotated(Configuration.class.getName());
}

b.精简的ConfigurationClass: 首先接口肯定不是; 其次判断当前类是否有被(@Component,@ComponentScan,@Import,@ImportResource)至少一个标志,有的则是ConfigurationClass。; 最后,如果上面都没注解,若当前类的成员方法存在被@Bean标记的,则当前类是ConfigurationClass。

public static boolean isLiteConfigurationCandidate(AnnotationMetadata metadata) {
	// Do not consider an interface or an annotation...
	if (metadata.isInterface()) {
		return false;
	}
	// Any of the typical annotations found?
	for (String indicator : candidateIndicators) {
		if (metadata.isAnnotated(indicator)) {
			return true;
		}
	}
	// Finally, let's look for @Bean methods...
	try {
		return metadata.hasAnnotatedMethods(Bean.class.getName());
	}
	catch (Throwable ex) {
		if (logger.isDebugEnabled()) {
			logger.debug("Failed to introspect @Bean methods on class [" + metadata.getClassName() + "]: " + ex);
		}
		return false;
	}
}

综上,是否是一个Java类配置类,@Configuration并不是必需的。有@Component,@ComponentScan,@Import,@ImportResource,@Bean 或者他们的子类出现即可。