前言
注解的处理、自动配置类的加载和条件注解的评估
- 注解处理
在springapplication.run方法中,spring容器会解析主类上的注解。
创建ApplicationContext实例
在refreshContext方法中,初始化应用上下文,在这个过程中,会注册配置类。
onRefresh方法会处理@Import注解,从而导入AutoConfigurationImportSelector类
该类的slectImports方法被调用,从而导入自动配置类的列表
- 条件注解(
@ConditionalOnClass
、@ConditionalOnMissingBean
)用于决定某个自动配置类是否应该被激活--ConditionEvaluator
类进行评估
@EnableAutoConfiguration 注解被 Spring 容器感知的过程包括以下几个关键步骤:
- 解析主类注解 :Spring 容器解析主类上的
@SpringBootApplication
注解,进而解析@EnableAutoConfiguration
注解。 - 创建
ApplicationContext
:SpringApplication.run
方法创建ApplicationContext
实例。 - 注册配置类 :
ApplicationContext
的refresh
方法注册配置类。 - 处理
@Import
注解 :@Import
注解导入AutoConfigurationImportSelector
类。 - 调用
selectImports
方法 :AutoConfigurationImportSelector
类的selectImports
方法返回符合条件的自动配置类列表。 - 条件注解的评估 :
ConditionEvaluator
类评估条件注解,决定哪些自动配置类应该被激活。
springApplication构造函数中,会进行一些初始化操作:设置主类、加载初始器、监听器
run方法执行:
在 prepareContext 方法中,会注册主类及其注解
@处理SpringBootApplication注解
@处理EnableAutoConfiguration注解
@Import注解导入AutoConfigrationImportSelector类
- SpringApplication 实例
arduino
public static void main(String[] args)
{ SpringApplication.run(MyApplication.class, args); }
- 创建ApplicationContext实例
scss
public ConfigurableApplicationContext run(String... args) {
// 创建并初始化 SpringApplication 实例
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
Collection<SpringApplicationRunListener> listeners = getRunListeners(args);
for (SpringApplicationRunListener listener : listeners) {
listener.starting();
}
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
configureIgnoreBeanInfo(environment);
Banner printedBanner = printBanner(environment);
context = createApplicationContext(); // 创建 ApplicationContext
configureContext(context);
prepareContext(context, environment, listeners, applicationArguments, printedBanner);
refreshContext(context); // 刷新 ApplicationContext
afterRefresh(context, applicationArguments);
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
}
return context;
} catch (Throwable ex) {
handleRunFailure(context, ex, listeners);
throw new IllegalStateException(ex);
}
}
3. 注册配置类
在 prepareContext
方法中,会注册主类及其注解。
scss
Java
深色版本
private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,
Collection<SpringApplicationRunListener> listeners, ApplicationArguments applicationArguments,
Banner printedBanner) {
context.setEnvironment(environment);
postProcessApplicationContext(context);
applyInitializers(context);
listeners.started(context);
callRunners(context, applicationArguments);
}
4. refresh
方法
在 refreshContext
方法中,会调用 ApplicationContext
的 refresh
方法,初始化应用上下文。
scss
Java
深色版本
private void refreshContext(ConfigurableApplicationContext context) {
refresh(context);
}
5. refresh
方法内部
refresh
方法内部会调用多个子方法来完成应用上下文的初始化。其中,invokeBeanFactoryPostProcessors
方法会处理 BeanFactoryPostProcessor
,包括 ConfigurationClassPostProcessor
。
scss
Java
深色版本
@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// Prepare this context for refreshing.
prepareRefresh();
// Tell the subclass to refresh the internal bean factory.
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// Prepare the bean factory for use in this context.
prepareBeanFactory(beanFactory);
try {
// Allows post-processing of the bean factory in context subclasses.
postProcessBeanFactory(beanFactory);
// Invoke factory processors registered as beans in the context.
invokeBeanFactoryPostProcessors(beanFactory);
// Register bean processors that intercept bean creation.
registerBeanPostProcessors(beanFactory);
// Initialize message source for this context.
initMessageSource();
// Initialize event multicaster for this context.
initApplicationEventMulticaster();
// Initialize other special beans in specific context subclasses.
onRefresh();
// Check for listener beans and register them.
registerListeners();
// Instantiate all remaining (non-lazy-init) singletons.
finishBeanFactoryInitialization(beanFactory);
// Last step: publish corresponding event.
finishRefresh();
} catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
}
// Destroy already created singletons to avoid dangling resources.
destroyBeans();
// Reset 'active' flag.
cancelRefresh(ex);
// Propagate exception to caller.
throw ex;
}
}
}
6. invokeBeanFactoryPostProcessors
方法
invokeBeanFactoryPostProcessors
方法会调用 BeanFactoryPostProcessor
,包括 ConfigurationClassPostProcessor
。
scss
Java
深色版本
protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());
// Detect a LoadTimeWeaver and prepare for weaving, if found in the meantime
// (e.g. through an @Bean method registered by ConfigurationClassPostProcessor)
if (beanFactory.getTempClassLoader() == null && beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
// Re-register post-processor for potential re-ordering
beanFactory.removeBeanDefinition(LOAD_TIME_WEAVER_BEAN_NAME);
registerBeanDefinition(LOAD_TIME_WEAVER_BEAN_NAME, new RootBeanDefinition(LoadTimeWeaverAwareProcessor.class));
}
}
7. ConfigurationClassPostProcessor
处理
ConfigurationClassPostProcessor
是一个特殊的 BeanFactoryPostProcessor
,它负责处理配置类。它会解析带有 @Configuration
注解的类,并处理其中的 @Import
注解。
kotlin
Java
深色版本
public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPostProcessor, PriorityOrdered, Aware {
// ...
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
int registryId = System.identityHashCode(registry);
if (this.registriesPostProcessed.contains(registryId)) {
throw new IllegalStateException("postProcessBeanDefinitionRegistry already called on this post-processor against " + registry);
}
if (this.logger.isDebugEnabled()) {
this.logger.debug("Reading annotations on " + registry.getClass().getName() + " bean definitions.");
}
processConfigBeanDefinitions(registry);
this.registriesPostProcessed.add(registryId);
}
// ...
}
8. processConfigBeanDefinitions
方法
processConfigBeanDefinitions
方法会解析配置类,并处理其中的 @Import
注解。
scss
Java
深色版本
public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
// ...
Set<BeanDefinitionHolder> candidates = new LinkedHashSet<>(16);
String[] candidateNames = registry.getBeanDefinitionNames();
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 parsed as a configuration class: " + beanDef);
}
}
else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {
candidates.add(new BeanDefinitionHolder(beanDef, beanName));
}
}
// Parse each @Configuration class
ConfigurationClassParser parser = new ConfigurationClassParser(
this.metadataReaderFactory, this.problemReporter, this.environment,
this.resourceLoader, this.componentScanParser, registry);
Set<BeanDefinitionHolder> configCandidates = new LinkedHashSet<>(candidates);
configCandidates.addAll(this.deferredImportSelectorHandler.getCandidates());
for (BeanDefinitionHolder holder : configCandidates) {
BeanDefinition bd = holder.getBeanDefinition();
try {
if (bd instanceof AnnotatedBeanDefinition) {
parse(bd, holder.getBeanName());
}
else if (bd instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) bd).hasBeanClass()) {
parse(((AbstractBeanDefinition) bd).getBeanClass(), holder.getBeanName());
}
else {
parse(bd.getBeanClassName(), holder.getBeanName());
}
}
catch (BeanDefinitionStoreException ex) {
throw ex;
}
catch (Throwable ex) {
throw new BeanDefinitionStoreException(
"Failed to parse configuration class [" + bd.getBeanClassName() + "]", ex);
}
}
// Process the parsed configuration classes
Set<ConfigurationClass> candidates = new LinkedHashSet<>(asSet(this.configurationClasses.values()));
candidates.addAll(this.deferredImportSelectorHandler.getCandidates());
this.importRegistry.processDeferQueue();
this.reader.loadBeanDefinitions(candidates);
}
9. parse
方法
parse
方法会解析配置类,并处理其中的 @Import
注解。
scss
Java
深色版本
private void parse(AnnotatedBeanDefinition annotatedDef, String beanName) throws IOException {
AnnotationMetadata annotationMetadata = annotatedDef.getMetadata();
Class<?> beanClass = resolveBeanClass(annotatedDef, beanName);
parse(annotationMetadata, beanClass, beanName);
}
private void parse(Class<?> beanClass, String beanName) throws IOException {
parse(ClassUtils.getMetadata(beanClass), beanClass, beanName);
}
private void parse(AnnotationMetadata metadata, Class<?> beanClass, String beanName) throws IOException {
String className = metadata.getClassName();
if (this.knownSuperclasses.containsKey(className)) {
return;
}
// Handle @Import annotations
for (String importClassName : metadata.getAnnotationTypesByMetaAnnotationType(Import.class.getName())) {
processImports(metadata, null, Collections.singleton(importClassName), false);
}
// Handle @ImportResource annotations
AnnotationAttributes importResourceAnn = AnnotationConfigUtils.attributesFor(metadata, ImportResource.class);
if (importResourceAnn != null) {
String[] resources = importResourceAnn.getStringArray("locations");
Class<? extends BeanDefinitionReader> readerClass = importResourceAnn.getClass("reader");
for (String resource : resources) {
String resolvedResource = this.environment.resolveRequiredPlaceholders(resource);
this.reader.registerBeanDefinition(resolvedResource, beanName, readerClass);
}
}
// Handle @Configuration classes
if (ConfigurationClassUtils.isConfigurationCandidate(metadata) && !this.excludedSuperclasses.contains(className)) {
ConfigurationClass configClass = new ConfigurationClass(metadata, beanClass, beanName);
this.configurationClasses.put(className, configClass);
this.knownSuperclasses.put(className, configClass);
processConfigurationClass(configClass, beanName);
}
}
10. processImports
方法
processImports
方法会处理 @Import
注解,调用 AutoConfigurationImportSelector
的 selectImports
方法。
scss
Java
深色版本
private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass,
Collection<SourceClass> importCandidates, boolean checkForCircularReferences) {
if (importCandidates.isEmpty()) {
return;
}
MultiValueMap<String, SourceClass> candidateImports = new LinkedMultiValueMap<>(importCandidates.size());
for (SourceClass candidate : importCandidates) {
if (candidate.isAssignable(ImportSelector.class)) {
// Candidate class is an ImportSelector -> delegate to it to determine imports
Class<?> candidateClass = candidate.loadClass();
ImportSelector selector = (ImportSelector) instantiateClass(candidateClass);
Predicate<String> predicate = metadataReader -> {
AnnotationMetadata annotationMetadata = AnnotationMetadata.introspect(metadataReader);
return AnnotationUtils.isCandidateClass(annotationMetadata, selector.getImportFilterMarker());
};
String[] importClassNames = selector.selectImports(currentSourceClass.getMetadata());
Set<String> importClasses = new LinkedHashSet<>(Arrays.asList(importClassNames));
if (selector instanceof DeferredImportSelector) {
DeferredImportSelector deferredSelector = (DeferredImportSelector) selector;
importClasses = this.deferredImportSelectorHandler.handle(configClass, deferredSelector, importClasses);
}
for (String importClassName : importClasses) {
if (this.importRegistry.isImportDuplicated(configClass, importClassName)) {
throw new BeanDefinitionStoreException("Detected cyclic processing of @Configuration " +
"class [" + configClass + "]; break the cycle by removing the offending " +
"@Import annotations");
}
if (checkForCircularReferences || AnnotationUtils.isCandidateClass(currentSourceClass.getMetadata(), ImportSelector.class)) {
this.importRegistry.registerImport(configClass, importClassName);
}
if (this.conditionEvaluator.shouldSkip(importClassName, ConfigurationPhase.REGISTER_BEAN)) {
continue;
}
candidateImports.add(importClassName, currentSourceClass);
}
}
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 = (ImportBeanDefinitionRegistrar) instantiateClass(candidateClass);
ConfigurationClassEnhancer enhancer = new ConfigurationClassEnhancer();
if (registrar instanceof ResourceLoaderAware) {
((ResourceLoaderAware) registrar).setResourceLoader(this.resourceLoader);
}
if (registrar instanceof BeanDefinitionRegistryAware) {
((BeanDefinitionRegistryAware) registrar).setBeanDefinitionRegistry(this.registry);
}
if (registrar instanceof EnvironmentAware) {
((EnvironmentAware) registrar).setEnvironment(this.environment);
}
if (registrar instanceof BeanClassLoaderAware) {
((BeanClassLoaderAware) registrar).setBeanClassLoader(this.beanClassLoader);
}
if (registrar instanceof BeanFactoryAware) {
((BeanFactoryAware) registrar).setBeanFactory(this.registry);
}
if (registrar instanceof ImportBeanDefinitionRegistrarAdapter) {
((ImportBeanDefinitionRegistrarAdapter) registrar).setImportMetadata(currentSourceClass.getMetadata());
}
registrar.registerBeanDefinitions(currentSourceClass.getMetadata(), this.registry, enhancer);
}
else {
// Candidate class not an ImportSelector or ImportBeanDefinitionRegistrar ->
// process it as an @Configuration class
SourceClass updatedSourceClass = currentSourceClass;
if (candidate.isAssignable(Configuration.class)) {
updatedSourceClass = this.reader.loadSourceClass(candidate.loadClass(), currentSourceClass);
}
processConfigurationClass(updatedSourceClass.getMetadata(), updatedSourceClass.getMetadata().getClassName());
}
}
if (!candidateImports.isEmpty()) {
// Recursively process any further imports
for (Map.Entry<String, List<SourceClass>> entry : candidateImports.entrySet()) {
String importClassName = entry.getKey();
Class<?> importClass = ClassUtils.resolveClassName(importClassName, this.beanClassLoader);
AnnotationMetadata importMetadata = AnnotationMetadata.introspect(importClass);
for (SourceClass sourceClass : entry.getValue()) {
processImports(configClass, sourceClass, this.metadataReaderFactory, importMetadata, checkForCircularReferences);
}
}
}
}
11. selectImports
方法
AutoConfigurationImportSelector
类的 selectImports
方法会被调用,返回一个包含所有符合条件的自动配置类的列表。
typescript
Java
深色版本
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return NO_IMPORTS;
}
AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);
return (autoConfigurationEntry != null) ? autoConfigurationEntry.getConfigurationClasses() : NO_IMPORTS;
}
总结
- 启动 Spring Boot 应用 :调用
SpringApplication.run
方法。 - 创建
ApplicationContext
:创建并初始化ApplicationContext
。 - 注册配置类:注册主类及其注解。
- 调用
refresh
方法 :刷新ApplicationContext
。 - 处理
BeanFactoryPostProcessor
:调用invokeBeanFactoryPostProcessors
方法。 - 处理
ConfigurationClassPostProcessor
:解析配置类。 - 处理
@Import
注解 :调用processImports
方法。 - 调用
selectImports
方法 :AutoConfigurationImportSelector
类的selectImports
方法返回符合条件的自动配置类列表。