Spring Boot - IOC(二)

【接前章】

public ConfigurableApplicationContext run(String... args) {
    long startTime = System.nanoTime();
    DefaultBootstrapContext bootstrapContext = createBootstrapContext();
    ConfigurableApplicationContext context = null;
    configureHeadlessProperty();
    SpringApplicationRunListeners listeners = getRunListeners(args);
    listeners.starting(bootstrapContext, this.mainApplicationClass);
    try {
        ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
        ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
        configureIgnoreBeanInfo(environment);
        Banner printedBanner = printBanner(environment);
        context = createApplicationContext();
        context.setApplicationStartup(this.applicationStartup);
        prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
        // 在这里了
        refreshContext(context);
        afterRefresh(context, applicationArguments);
        Duration timeTakenToStartup = Duration.ofNanos(System.nanoTime() - startTime);
        if (this.logStartupInfo) {
            new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), timeTakenToStartup);
        }
        listeners.started(context, timeTakenToStartup);
        callRunners(context, applicationArguments);
    }
    catch (Throwable ex) {
        handleRunFailure(context, ex, listeners);
        throw new IllegalStateException(ex);
    }
    try {
        Duration timeTakenToReady = Duration.ofNanos(System.nanoTime() - startTime);
        listeners.ready(context, timeTakenToReady);
    }
    catch (Throwable ex) {
        handleRunFailure(context, ex, null);
        throw new IllegalStateException(ex);
    }
    return context;
	}

上一篇IOC容器已经准备好了,下面到了IOC容器最核心的部分:refresh

0. refreshContext

private void refreshContext(ConfigurableApplicationContext context) {
    refresh(context);
    if (this.registerShutdownHook) {
        try {
            context.registerShutdownHook();
        }
        catch (AccessControlException ex) {
            // Not allowed in some environments.
        }
    }
}

它直接调了refresh方法(注意此时还是 SpringApplication,没有进到真正的IOC容器),后面又注册了一个关闭的钩子。这个 registerShutdownHook 方法的文档注释:

Register a shutdown hook with the JVM runtime, closing this context on JVM shutdown unless it has already been closed at that time.

向JVM运行时注册一个shutdown的钩子,除非JVM当时已经关闭,否则在JVM关闭时关闭上下文。

可以大概看出来,这个钩子的作用是监听JVM关闭时销毁IOC容器和里面的Bean。这里面有一个很经典的应用:应用停止时释放数据库连接池里面的连接。

下面咱来看这个refresh方法:

protected void refresh(ApplicationContext applicationContext) {
    Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext);
    ((AbstractApplicationContext) applicationContext).refresh();
}

没有什么复杂的逻辑,它会直接强转成 AbstractApplicationContext,调它的refresh方法。之前我们有了解过,AbstractApplicationContext 中的 refresh 是IOC容器启动时的最核心方法:

//最终调到AbstractApplicationContext的refresh方法
public void refresh() throws BeansException, IllegalStateException {
    synchronized (this.startupShutdownMonitor) {
        // Prepare this context for refreshing.
        // 1. 初始化前的预处理
        prepareRefresh();
        
        // Tell the subclass to refresh the internal bean factory.
        // 2. 获取BeanFactory,加载所有bean的定义信息(未实例化)
        ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
        
        // Prepare the bean factory for use in this context.
        // 3. BeanFactory的预处理配置
        prepareBeanFactory(beanFactory);
        
        try {
            // Allows post-processing of the bean factory in context subclasses.
            // 4. 准备BeanFactory完成后进行的后置处理
            postProcessBeanFactory(beanFactory);
            
            // Invoke factory processors registered as beans in the context.
            // 5. 执行BeanFactory创建后的后置处理器
            invokeBeanFactoryPostProcessors(beanFactory);
            
            // Register bean processors that intercept bean creation.
            // 6. 注册Bean的后置处理器
            registerBeanPostProcessors(beanFactory);
            
            // Initialize message source for this context.
            // 7. 初始化MessageSource
            initMessageSource();
            
            // Initialize event multicaster for this context.
            // 8. 初始化事件派发器
            initApplicationEventMulticaster();
            
            // Initialize other special beans in specific context subclasses.
            // 9. 子类的多态onRefresh
            onRefresh();
            
            // Check for listener beans and register them.
            // 10. 注册监听器
            registerListeners();
            
            //到此为止,BeanFactory已创建完成
            
            // Instantiate all remaining (non-lazy-init) singletons.
            // 11. 初始化所有剩下的单例Bean
            finishBeanFactoryInitialization(beanFactory);
            
            // Last step: publish corresponding event.
            // 12. 完成容器的创建工作
            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;
        }
        
        finally {
            // Reset common introspection caches in Spring's core, since we
            // might not ever need metadata for singleton beans anymore...
            // 13. 清除缓存
            resetCommonCaches();
        }
    }
}

这个方法非常长,一共有13个步骤,本篇我们来看前3个步骤:

public void refresh() throws BeansException, IllegalStateException {
    synchronized (this.startupShutdownMonitor) {
        // Prepare this context for refreshing.
        // 1. 初始化前的预处理
        prepareRefresh();
        
        // Tell the subclass to refresh the internal bean factory.
        // 2. 获取BeanFactory,加载所有bean的定义信息(未实例化)
        ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
        
        // Prepare the bean factory for use in this context.
        // 3. BeanFactory的预处理配置
        prepareBeanFactory(beanFactory);

1. prepareRefresh:初始化前的预处理

protected void prepareRefresh() {
    this.startupDate = System.currentTimeMillis(); // 记录启动时间
    this.closed.set(false); // 标记IOC容器的关闭状态为false
    this.active.set(true); // 标记IOC容器已激活
    
    if (logger.isInfoEnabled()) {
        logger.info("Refreshing " + this);
    }
    
    // Initialize any placeholder property sources in the context environment
    // 1.1 初始化属性配置
    initPropertySources();
    
    // Validate that all properties marked as required are resolvable
    // see ConfigurablePropertyResolver#setRequiredProperties
    // 1.2 属性校验
    getEnvironment().validateRequiredProperties();
    
    // Allow for the collection of early ApplicationEvents,
    // to be published once the multicaster is available...
    // 这个集合的作用,是保存容器中的一些事件,以便在合适的时候利用事件广播器来广播这些事件
    // 【配合registerListeners方法中的第三部分使用】
    this.earlyApplicationEvents = new LinkedHashSet<>();
}

最前面先记录启动时间,标记IOC容器状态,之后要开始初始化属性配置:

1.1 initPropertySources:初始化属性配置

protected void initPropertySources() {
    // For subclasses: do nothing by default.
}

这个方法是一个模板方法,留给子类重写,默认不做任何事情。

借助IDEA,发现这个方法在 GenericWebApplicationContext 中有重写,而 AnnotationConfigServletWebServerApplicationContext 恰好继承了它。

protected void initPropertySources() {
    ConfigurableEnvironment env = getEnvironment();
    if (env instanceof ConfigurableWebEnvironment) {
        ((ConfigurableWebEnvironment) env).initPropertySources(this.servletContext, null);
    }
}

它最终又调到 EnvironmentinitPropertySources 中。StandardServletEnvironment 是唯一重写这个方法的:

public void initPropertySources(@Nullable ServletContext servletContext, @Nullable ServletConfig servletConfig) {
    WebApplicationContextUtils.initServletPropertySources(getPropertySources(), servletContext, servletConfig);
}

继续追踪 WebApplicationContextUtils.initServletPropertySources

public static final String SERVLET_CONTEXT_PROPERTY_SOURCE_NAME = "servletContextInitParams";
public static final String SERVLET_CONFIG_PROPERTY_SOURCE_NAME = "servletConfigInitParams";

public static void initServletPropertySources(MutablePropertySources sources,
                                              @Nullable ServletContext servletContext, @Nullable ServletConfig servletConfig) {
    
    Assert.notNull(sources, "'propertySources' must not be null");
    String name = StandardServletEnvironment.SERVLET_CONTEXT_PROPERTY_SOURCE_NAME;
    if (servletContext != null && sources.contains(name) && sources.get(name) instanceof StubPropertySource) {
        sources.replace(name, new ServletContextPropertySource(name, servletContext));
    }
    name = StandardServletEnvironment.SERVLET_CONFIG_PROPERTY_SOURCE_NAME;
    if (servletConfig != null && sources.contains(name) && sources.get(name) instanceof StubPropertySource) {
        sources.replace(name, new ServletConfigPropertySource(name, servletConfig));
    }
}

这个方法的文档注释:

Replace Servlet-based stub property sources with actual instances populated with the given servletContext and servletConfig objects. This method is idempotent with respect to the fact it may be called any number of times but will perform replacement of stub property sources with their corresponding actual property sources once and only once.

将基于Servlet的存根属性源替换为使用给定 ServletContextServletConfig 对象填充的实际实例。

关于此方法可以调用任意次的事实,它是幂等的,但是将用其相应的实际属性源执行一次且仅一次的存根属性源替换。

通过大概的阅读文档注释和内部的两个if,可以大概确定它是把 Servlet 的一些初始化参数放入IOC容器中(类似于 web.xml 中的参数放入IOC容器)。


回到prepareRefresh方法:

initPropertySources();

// Validate that all properties marked as required are resolvable
// see ConfigurablePropertyResolver#setRequiredProperties
// 1.2 属性校验
getEnvironment().validateRequiredProperties();

1.2 validateRequiredProperties:属性校验

// AbstractEnvironment
public void validateRequiredProperties() throws MissingRequiredPropertiesException {
    this.propertyResolver.validateRequiredProperties();
}

// AbstractPropertyResolver
public void validateRequiredProperties() {
    MissingRequiredPropertiesException ex = new MissingRequiredPropertiesException();
    for (String key : this.requiredProperties) {
        if (this.getProperty(key) == null) {
            ex.addMissingRequiredProperty(key);
        }
    }
    if (!ex.getMissingRequiredProperties().isEmpty()) {
        throw ex;
    }
}

从调用的两步来看,它是要检验一些必需的属性是否为空,如果有null的属性会抛出异常。从源码的英文单行注释中可以看到,它与 ConfigurablePropertyResolversetRequiredProperties 方法有关。翻看这个方法的文档注释:

Specify which properties must be present, to be verified by validateRequiredProperties().

指定必须存在哪些属性,以通过 validateRequiredProperties 方法进行验证。

它是说指定了属性,就可以通过 validateRequiredProperties 方法校验。那到底有没有字段校验呢?咱通过Debug来看一眼:

img

。。。。。。根本就没有要校验的。。。那这一步就跳过去吧。。。。。。


回到 prepareRefresh 方法:

getEnvironment().validateRequiredProperties();

// Allow for the collection of early ApplicationEvents,
// to be published once the multicaster is available...
// 这个集合的作用,是保存容器中的一些事件,以便在合适的时候利用事件广播器来广播这些事件
// 【配合registerListeners方法中的第三部分使用】
this.earlyApplicationEvents = new LinkedHashSet<>();

这个早期事件,目前还不好解释,得联系后面的一个组件来解释。

2. obtainFreshBeanFactory:获取BeanFactory,加载所有bean的定义信息

protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
    // 2.1 刷新BeanFactory
    refreshBeanFactory();
    return getBeanFactory();
}

源码非常简单,先刷新后获取。

2.1 refreshBeanFactory

protected abstract void refreshBeanFactory() throws BeansException, IllegalStateException;

发现它是一个抽象方法,留给子类重写。对于XML配置的IOC容器,和注解配置的IOC容器,分别有一种实现。借助IDEA,发现 GenericApplicationContextAbstractRefreshableApplicationContext 重写了它。根据前面的分析,AnnotationConfigServletWebServerApplicationContext 继承了 GenericApplicationContext,故咱来看它的 refreshBeanFactory 方法:

protected final void refreshBeanFactory() throws IllegalStateException {
    if (!this.refreshed.compareAndSet(false, true)) {
        throw new IllegalStateException(
                "GenericApplicationContext does not support multiple refresh attempts: just call 'refresh' once");
    }
    this.beanFactory.setSerializationId(getId());
}

逻辑很简单,只是设置了 BeanFactory 的序列化ID而已。

2.1.1 【扩展】基于XML的refreshBeanFactory

上面看到有两个子类重写了这个方法(XML和注解的),基于XML配置的IOC容器,在这一步要做的事情要更复杂,简单扫一眼:

protected final void refreshBeanFactory() throws BeansException {
    if (hasBeanFactory()) {
        destroyBeans();
        closeBeanFactory();
    }
    try {
        // 创建BeanFactory
        DefaultListableBeanFactory beanFactory = createBeanFactory();
        beanFactory.setSerializationId(getId());
        // 自定义配置BeanFactory
        customizeBeanFactory(beanFactory);
        // 解析、加载XML中定义的BeanDefinition
        loadBeanDefinitions(beanFactory);
        synchronized (this.beanFactoryMonitor) {
            this.beanFactory = beanFactory;
        }
    }
    catch (IOException ex) {
        throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
    }
}

protected DefaultListableBeanFactory createBeanFactory() {
    return new DefaultListableBeanFactory(getInternalParentBeanFactory());
}

// 使用XmlBeanDefinitionReader做bean的装配(即解析xml)
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
    // Create a new XmlBeanDefinitionReader for the given BeanFactory.
    XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
    
    // Configure the bean definition reader with this context's
    // resource loading environment.
    beanDefinitionReader.setEnvironment(this.getEnvironment());
    beanDefinitionReader.setResourceLoader(this);
    beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
    
    // Allow a subclass to provide custom initialization of the reader,
    // then proceed with actually loading the bean definitions.
    initBeanDefinitionReader(beanDefinitionReader);
    loadBeanDefinitions(beanDefinitionReader);
}

可以发现逻辑更复杂。简单来看一下吧:

如果已经有 BeanFactory 了,销毁Bean和 BeanFactory 。之后创建一个 BeanFactory,设置序列化ID,执行自定义 BeanFactory 的逻辑,之后加载Bean定义,最后设置到IOC容器中。

这其中有xml的加载和读取,由于 SpringBoot 已经几乎放弃xml配置,全部通过注解和 JavaConfig 来配置应用,故不再深入研究。

2.2 getBeanFactory

public final ConfigurableListableBeanFactory getBeanFactory() {
    return this.beanFactory;
}

更简单了,不必多言。

3. prepareBeanFactory:BeanFactory的预处理配置

protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {
    // Tell the internal bean factory to use the context's class loader etc.
    // 设置BeanFactory的类加载器、表达式解析器等
    beanFactory.setBeanClassLoader(getClassLoader());
    beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader()));
    beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment()));
    
    // Configure the bean factory with context callbacks.
    // 3.1 配置一个可回调注入ApplicationContext的BeanPostProcessor
    beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));
    beanFactory.ignoreDependencyInterface(EnvironmentAware.class);
    beanFactory.ignoreDependencyInterface(EmbeddedValueResolverAware.class);
    beanFactory.ignoreDependencyInterface(ResourceLoaderAware.class);
    beanFactory.ignoreDependencyInterface(ApplicationEventPublisherAware.class);
    beanFactory.ignoreDependencyInterface(MessageSourceAware.class);
    beanFactory.ignoreDependencyInterface(ApplicationContextAware.class);
    
    // BeanFactory interface not registered as resolvable type in a plain factory.
    // MessageSource registered (and found for autowiring) as a bean.
    // 3.2 自动注入的支持
    beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory);
    beanFactory.registerResolvableDependency(ResourceLoader.class, this);
    beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this);
    beanFactory.registerResolvableDependency(ApplicationContext.class, this);
    
    // Register early post-processor for detecting inner beans as ApplicationListeners.
    // 3.3 配置一个可加载所有监听器的组件
    beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(this));
    
    // Detect a LoadTimeWeaver and prepare for weaving, if found.
    if (beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
        beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
        // Set a temporary ClassLoader for type matching.
        beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
    }
    
    // Register default environment beans.
    // 注册了默认的运行时环境、系统配置属性、系统环境的信息
    if (!beanFactory.containsLocalBean(ENVIRONMENT_BEAN_NAME)) {
        beanFactory.registerSingleton(ENVIRONMENT_BEAN_NAME, getEnvironment());
    }
    if (!beanFactory.containsLocalBean(SYSTEM_PROPERTIES_BEAN_NAME)) {
        beanFactory.registerSingleton(SYSTEM_PROPERTIES_BEAN_NAME, getEnvironment().getSystemProperties());
    }
    if (!beanFactory.containsLocalBean(SYSTEM_ENVIRONMENT_BEAN_NAME)) {
        beanFactory.registerSingleton(SYSTEM_ENVIRONMENT_BEAN_NAME, getEnvironment().getSystemEnvironment());
    }
}

在源码中发现了一个组件概念:**BeanPostProcessor**。这个概念非常非常重要,我们先来了解一下它。

【如果小伙伴不是很了解或不了解 **BeanPostProcessor**,请继续往下看;对 BeanPostProcessor 很熟悉的小伙伴可以跳过第3.0节】

3.0 【重要】BeanPostProcessor

它的文档注释原文翻译:

Factory hook that allows for custom modification of new bean instances, e.g. checking for marker interfaces or wrapping them with proxies. ApplicationContexts can autodetect BeanPostProcessor beans in their bean definitions and apply them to any beans subsequently created. Plain bean factories allow for programmatic registration of post-processors, applying to all beans created through this factory. Typically, post-processors that populate beans via marker interfaces or the like will implement postProcessBeforeInitialization, while post-processors that wrap beans with proxies will normally implement postProcessAfterInitialization.

这个接口允许自定义修改新的Bean的实例,例如检查它们的接口或者将他们包装成代理对象等,

ApplicationContexts能自动察觉到我们在 BeanPostProcessor 里对对象作出的改变,并在后来创建该对象时应用其对应的改变。普通的bean工厂允许对后置处理器进行程序化注册,它适用于通过该工厂创建的所有bean。

通常,通过标记接口等填充bean的后处理器将实现 postProcessBeforeInitialization,而使用代理包装bean的后处理器将实现 postProcessAfterInitialization

它通常被称为 “Bean的后置处理器”,它的作用在文档注释中也描述的差不多,它可以在对象实例化但初始化之前,以及初始化之后进行一些后置处理

可以这样简单理解 Bean 的初始化步骤,以及 BeanPostProcessor 的切入时机:

下面用一个实例快速感受 BeanPostProcessor 的作用。

3.0.1 BeanPostProcessor的使用

声明一个 Cat 类:

public class Cat {
    String name;
    
    public Cat(String name) {
        this.name = name;
    }
}

再声明一个 CatBeanPostProcessor:

@Component
public class CatBeanPostProcessor implements BeanPostProcessor {
    
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }
    
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        if (bean instanceof Cat) {
            Cat cat = (Cat) bean;
            cat.name = "dog";
        }
        return bean;
    }
    
}

BeanPostProcessor 可以在前后做一些额外的处理。

接下来,编写一个配置类,并创建这个Cat对象:

@Configuration
@ComponentScan("com.example.demo.postprocessor")
public class ConfigurationDemo {
    
    @Bean
    public Cat cat() {
        return new Cat("cat");
    }
    
}

启动IOC容器,并获取这个Cat,打印它的name,发现打印输出是dog,证明后置处理器已经起作用了。

3.0.2 【执行时机】Bean初始化的顺序及BeanPostProcessor的执行时机

我们在学过 SpringFramework 的时候,知道Bean的几种额外的初始化方法的指定(init-method@PostConstructInitializingBean接口)。那么它们以及构造方法的执行顺序,以及 BeanPostProcessor 的执行时机分别是什么呢?我们修改上面的代码来测试一下:

修改 Cat:

@Component
public class Cat implements InitializingBean {
    
    public Cat(String name) {
        System.out.println("Cat constructor run...");
    }
    
    @PostConstruct
    public void afterInit() {
        System.out.println("Cat PostConstruct run...");
    }
    
    
    @Override
    public void afterPropertiesSet() {
        System.out.println("Cat afterPropertiesSet run...");
    }
}

修改 CatBeanPostProcessor:

@Component
public class CatBeanPostProcessor implements BeanPostProcessor {
    
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        if (bean instanceof Cat) {
            System.out.println("Cat postProcessBeforeInitialization run...");
        }
        return bean;
    }
    
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        if (bean instanceof Cat) {
            System.out.println("Cat postProcessAfterInitialization run...");
        }
        return bean;
    }
    
}

重新启动IOC容器,打印结果如下:

Cat constructor run...
Cat postProcessBeforeInitialization run...
Cat PostConstruct run...
Cat afterPropertiesSet run...
Cat postProcessAfterInitialization run...

由此可得结论:

  • 初始化执行顺序:

    • 构造方法

    • @PostConstruct / init-method

    • InitializingBeanafterPropertiesSet 方法

  • BeanPostProcessor的执行时机

    • before:构造方法之后,@PostConstruct 之前

    • after:afterPropertiesSet 之后

出现这种情况的原理,我们可以先翻看文档注释,等到后面初始化单实例Bean时会有源码解析。

  • @PostConstruct

The PostConstruct annotation is used on a method that needs to be executed after dependency injection is done to perform any initialization. This method MUST be invoked before the class is put into service. This annotation MUST be supported on all classes that support dependency injection.

PostConstruct注解,用于标注在需要依赖注入完成,以执行任何初始化之后需要执行的方法上。在Bean投入使用之前必须调用此方法。所有支持依赖注入的类都必须支持该注解。

  • InitializingBean

Interface to be implemented by beans that need to react once all their properties have been set by a BeanFactory: e.g. to perform custom initialization, or merely to check that all mandatory properties have been set. An alternative to implementing InitializingBean is specifying a custom init method, for example in an XML bean definition.

由 BeanFactory 设置完所有属性后需要作出反应的bean所实现的接口:执行自定义初始化,或仅检查是否已设置所有必填属性。

实现InitializingBean的替代方法是指定自定义 init-method,例如在XML bean定义中。

  • BeanPostProcessor:

    • before

Apply this BeanPostProcessor to the given new bean instance before any bean initialization callbacks (like InitializingBean's afterPropertiesSet or a custom init-method). The bean will already be populated with property values. The returned bean instance may be a wrapper around the original.

在任何bean初始化回调(例如 InitializingBean的afterPropertiesSet自定义init-method)之前,将此 BeanPostProcessor 应用于给定的新bean实例。该bean将已经用属性值填充。返回的bean实例可能是原始实例的包装。

    • after

Apply this BeanPostProcessor to the given new bean instance after any bean initialization callbacks (like InitializingBean's afterPropertiesSet or a custom init-method). The bean will already be populated with property values. The returned bean instance may be a wrapper around the original.

在任何bean初始化回调(例如InitializingBean的afterPropertiesSet自定义init-method)之后,将此 BeanPostProcessor 应用于给定的新bean实例。该bean将已经用属性值填充。返回的bean实例可能是原始实例的包装。

了解 BeanPostProcessor 后,来看下面几个片段:

3.1 addBeanPostProcessor(new ApplicationContextAwareProcessor(this));

// Configure the bean factory with context callbacks.
// 3.1 配置一个可回调注入ApplicationContext的BeanPostProcessor
beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));
beanFactory.ignoreDependencyInterface(EnvironmentAware.class);
beanFactory.ignoreDependencyInterface(EmbeddedValueResolverAware.class);
beanFactory.ignoreDependencyInterface(ResourceLoaderAware.class);
beanFactory.ignoreDependencyInterface(ApplicationEventPublisherAware.class);
beanFactory.ignoreDependencyInterface(MessageSourceAware.class);
beanFactory.ignoreDependencyInterface(ApplicationContextAware.class);

它先配置了一个 ApplicationContextAwareProcessor,之后又忽略了下面几个接口。它这么做的原因是什么呢?咱不妨先来看看 ApplicationContextAwareProcessor 是什么。

3.1.1 ApplicationContextAwareProcessor

它的文档注释原文翻译:

BeanPostProcessor implementation that passes the ApplicationContext to beans that implement the EnvironmentAware, EmbeddedValueResolverAware, ResourceLoaderAware, ApplicationEventPublisherAware, MessageSourceAware and/or ApplicationContextAware interfaces. Implemented interfaces are satisfied in order of their mention above. Application contexts will automatically register this with their underlying bean factory. Applications do not use this directly.

BeanPostProcessor 实现,它将 ApplicationContext 传递给实现 EnvironmentAwareEmbeddedValueResolverAwareResourceLoaderAwareApplicationEventPublisherAwareMessageSourceAware 和/或 ApplicationContextAware 接口的bean。

按照上面提到的顺序满足已实现的接口。

IOC容器将自动在其基础bean工厂中注册它。应用程序不直接使用它。

看到这段文档注释,就已经能明白上面的几个ignore的意义了。我们再看一眼源码,便更能理解它的设计了:

public Object postProcessBeforeInitialization(final Object bean, String beanName) throws BeansException {
    AccessControlContext acc = null;
    
    if (System.getSecurityManager() != null &&
        (bean instanceof EnvironmentAware || bean instanceof EmbeddedValueResolverAware ||
         bean instanceof ResourceLoaderAware || bean instanceof ApplicationEventPublisherAware ||
         bean instanceof MessageSourceAware || bean instanceof ApplicationContextAware)) {
        acc = this.applicationContext.getBeanFactory().getAccessControlContext();
    }
    
    if (acc != null) {
        AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
            invokeAwareInterfaces(bean);
            return null;
        }, acc);
    }
    else {
        // 往下调用
        invokeAwareInterfaces(bean);
    }
    
    return bean;
}

private void invokeAwareInterfaces(Object bean) {
    if (bean instanceof Aware) {
        if (bean instanceof EnvironmentAware) {
            ((EnvironmentAware) bean).setEnvironment(this.applicationContext.getEnvironment());
        }
        if (bean instanceof EmbeddedValueResolverAware) {
            ((EmbeddedValueResolverAware) bean).setEmbeddedValueResolver(this.embeddedValueResolver);
        }
        if (bean instanceof ResourceLoaderAware) {
            ((ResourceLoaderAware) bean).setResourceLoader(this.applicationContext);
        }
        if (bean instanceof ApplicationEventPublisherAware) {
            ((ApplicationEventPublisherAware) bean).setApplicationEventPublisher(this.applicationContext);
        }
        if (bean instanceof MessageSourceAware) {
            ((MessageSourceAware) bean).setMessageSource(this.applicationContext);
        }
        if (bean instanceof ApplicationContextAware) {
            ((ApplicationContextAware) bean).setApplicationContext(this.applicationContext);
        }
    }
}

它果然在挨个判断,然后注入。

3.2 registerResolvableDependency:自动注入的支持

// BeanFactory interface not registered as resolvable type in a plain factory.
// MessageSource registered (and found for autowiring) as a bean.
beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory);
beanFactory.registerResolvableDependency(ResourceLoader.class, this);
beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this);
beanFactory.registerResolvableDependency(ApplicationContext.class, this);

上面的单行注释翻译:

BeanFactory 接口未在普通工厂中注册为可解析类型。

MessageSource 注册为Bean(并发现用于自动装配)。

上面一句还能看懂,下面是干什么?讲真我也不是很清楚,咱还是看看这个方法的文档注释吧。

Register a special dependency type with corresponding autowired value. This is intended for factory/context references that are supposed to be autowirable but are not defined as beans in the factory: e.g. a dependency of type ApplicationContext resolved to the ApplicationContext instance that the bean is living in. Note: There are no such default types registered in a plain BeanFactory, not even for the BeanFactory interface itself.

用相应的自动装配值注册一个特殊的依赖类型。

这适用于应该是可自动执行但未在工厂中定义为bean的工厂/上下文引用:类型为 ApplicationContext 的依赖关系已解析为Bean所在的 ApplicationContext 实例。

注意:在普通 BeanFactory 中没有注册这样的默认类型,甚至 BeanFactory 接口本身也没有。

它大概的意思是如果遇到一个特殊的依赖类型,就使用一个特殊的预先准备好的对象装配进去。

它的方法实现(仅在 DefaultListableBeanFactory 中有实现):

/** Map from dependency type to corresponding autowired value. */
private final Map<Class<?>, Object> resolvableDependencies = new ConcurrentHashMap<>(16);

public void registerResolvableDependency(Class<?> dependencyType, @Nullable Object autowiredValue) {
    Assert.notNull(dependencyType, "Dependency type must not be null");
    if (autowiredValue != null) {
        if (!(autowiredValue instanceof ObjectFactory || dependencyType.isInstance(autowiredValue))) {
            throw new IllegalArgumentException("Value [" + autowiredValue +
                    "] does not implement specified dependency type [" + dependencyType.getName() + "]");
        }
        this.resolvableDependencies.put(dependencyType, autowiredValue);
    }
}

前面的判断都不看,底下有一个put操作,key和value分别是依赖的类型自动注入的值

这个 resolvableDependencies 是个Map,它的注释:

从依赖项类型映射到相应的自动装配值。

至此,它的功能已经明确了:它可以支持一些特殊依赖关系的类型,并放到 resolvableDependencies 集合中保存,使得能在任意位置注入上述源码中的组件。

3.3 addBeanPostProcessor(new ApplicationListenerDetector(this))

// Register early post-processor for detecting inner beans as ApplicationListeners.
beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(this));

又注册了一个后置处理器,来看 ApplicationListenerDetector 的文档注释:

BeanPostProcessor that detects beans which implement the ApplicationListener interface. This catches beans that can't reliably be detected by getBeanNamesForType and related operations which only work against top-level beans.

BeanPostProcessor,用于检测实现 ApplicationListener 接口的bean。这将捕获 getBeanNamesForType 和仅对顶级bean有效的相关操作无法可靠检测到的bean。

文档注释还是比较容易理解的,它是来收集 ApplicationListener 的。再来看看它的源码核心部分:

public Object postProcessAfterInitialization(Object bean, String beanName) {
    if (bean instanceof ApplicationListener) {
        // potentially not detected as a listener by getBeanNamesForType retrieval
        Boolean flag = this.singletonNames.get(beanName);
        if (Boolean.TRUE.equals(flag)) {
            // singleton bean (top-level or inner): register on the fly
            this.applicationContext.addApplicationListener((ApplicationListener<?>) bean);
        }
        else if (Boolean.FALSE.equals(flag)) {
            if (logger.isWarnEnabled() && !this.applicationContext.containsBean(beanName)) {
                // inner bean with other scope - can't reliably process events
                logger.warn("Inner bean '" + beanName + "' implements ApplicationListener interface " +
                            "but is not reachable for event multicasting by its containing ApplicationContext " +
                            "because it does not have singleton scope. Only top-level listener beans are allowed " +
                            "to be of non-singleton scope.");
            }
            this.singletonNames.remove(beanName);
        }
    }
    return bean;
}

逻辑还是比较简单的,如果Bean是 ApplicationListener 的实现类,并且是单实例Bean,则会注册到IOC容器中。

4. postProcessBeanFactory:BeanFactory的后置处理

AbstractApplicationContext 中,这个方法又被设置成模板方法了:

protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
}

借助IDEA,发现 AnnotationConfigServletWebServerApplicationContext 重写了这个方法。

// AnnotationConfigServletWebServerApplicationContext
protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
    super.postProcessBeanFactory(beanFactory);
    // 包扫描
    if (this.basePackages != null && this.basePackages.length > 0) {
        this.scanner.scan(this.basePackages);
    }
    if (!this.annotatedClasses.isEmpty()) {
        this.reader.register(ClassUtils.toClassArray(this.annotatedClasses));
    }
}

它首先调了父类 ServletWebServerApplicationContextpostProcessBeanFactory 方法。

4.1 ServletWebServerApplicationContext.postProcessBeanFactory

// ServletWebServerApplicationContext
protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
    // 注册ServletContext注入器
    beanFactory.addBeanPostProcessor(new WebApplicationContextServletContextAwareProcessor(this));
    beanFactory.ignoreDependencyInterface(ServletContextAware.class);
    registerWebApplicationScopes();
}

4.1.1 注册WebApplicationContextServletContextAwareProcessor

它的文档注释原文翻译:

Variant of ServletContextAwareProcessor for use with a ConfigurableWebApplicationContext. Can be used when registering the processor can occur before the ServletContext or ServletConfig have been initialized.

ServletContextAwareProcessor 的扩展,用于 ConfigurableWebApplicationContext 。可以在初始化 ServletContext 或 ServletConfig 之前进行处理器注册时使用。

似乎看不出什么很明显的思路,但它说是 ServletContextAwareProcessor 的扩展,那追到 ServletContextAwareProcessor 的文档注释:

BeanPostProcessor implementation that passes the ServletContext to beans that implement the ServletContextAware interface. Web application contexts will automatically register this with their underlying bean factory. Applications do not use this directly.

ServletContext 传递给实现 ServletContextAware 接口的Bean的 BeanPostProcessor 实现。

Web应用程序上下文将自动将其注册到其底层bean工厂,应用程序不直接使用它。

发现很明白,它是把 ServletContextServletConfig 注入到组件中。它的核心源码:

public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
    if (getServletContext() != null && bean instanceof ServletContextAware) {
        ((ServletContextAware) bean).setServletContext(getServletContext());
    }
    if (getServletConfig() != null && bean instanceof ServletConfigAware) {
        ((ServletConfigAware) bean).setServletConfig(getServletConfig());
    }
    return bean;
}

跟上一篇中的注册几乎是一个套路,不再赘述。

4.1.2 registerWebApplicationScopes

private void registerWebApplicationScopes() {
    ExistingWebApplicationScopes existingScopes = new ExistingWebApplicationScopes(getBeanFactory());
    WebApplicationContextUtils.registerWebApplicationScopes(getBeanFactory());
    existingScopes.restore();
}

这个方法没有任何注释,只能靠里面的源码来试着推测。

4.1.2.1 ExistingWebApplicationScopes

从字面意思上看,它是表示在Web应用上已经存在的作用域。它的部分源码:

public static class ExistingWebApplicationScopes {
    
    private static final Set<String> SCOPES;
    
    static {
        Set<String> scopes = new LinkedHashSet<>();
        scopes.add(WebApplicationContext.SCOPE_REQUEST);
        scopes.add(WebApplicationContext.SCOPE_SESSION);
        SCOPES = Collections.unmodifiableSet(scopes);
    }
    
    public void restore() {
        this.scopes.forEach((key, value) -> {
            if (logger.isInfoEnabled()) {
                logger.info("Restoring user defined scope " + key);
            }
            this.beanFactory.registerScope(key, value);
        });
    }

发现它确实是缓存了两种scope,分别是 request 域和 session 域。

下面的 restore 方法,是把现在缓存的所有作用域,注册到 BeanFactory 中。

大概猜测这是将Web的request域和session域注册到IOC容器,让IOC容器知道这两种作用域(学过 SpringFramework 都知道Bean的作用域有request 和 session)。

4.1.2.2 WebApplicationContextUtils.registerWebApplicationScopes

public static void registerWebApplicationScopes(ConfigurableListableBeanFactory beanFactory) {
    registerWebApplicationScopes(beanFactory, null);
}

public static void registerWebApplicationScopes(ConfigurableListableBeanFactory beanFactory,
                                                @Nullable ServletContext sc) {
    
    // 注册作用域类型
    beanFactory.registerScope(WebApplicationContext.SCOPE_REQUEST, new RequestScope());
    beanFactory.registerScope(WebApplicationContext.SCOPE_SESSION, new SessionScope());
    if (sc != null) {
        ServletContextScope appScope = new ServletContextScope(sc);
        beanFactory.registerScope(WebApplicationContext.SCOPE_APPLICATION, appScope);
        // Register as ServletContext attribute, for ContextCleanupListener to detect it.
        sc.setAttribute(ServletContextScope.class.getName(), appScope);
    }
    
    // 自动注入的支持
    beanFactory.registerResolvableDependency(ServletRequest.class, new RequestObjectFactory());
    beanFactory.registerResolvableDependency(ServletResponse.class, new ResponseObjectFactory());
    beanFactory.registerResolvableDependency(HttpSession.class, new SessionObjectFactory());
    beanFactory.registerResolvableDependency(WebRequest.class, new WebRequestObjectFactory());
    if (jsfPresent) {
        FacesDependencyRegistrar.registerFacesDependencies(beanFactory);
    }
}

它的文档注释原文翻译:

Register web-specific scopes ("request", "session", "globalSession", "application") with the given BeanFactory, as used by the WebApplicationContext.

使用WebApplicationContext使用的给定BeanFactory注册特定于Web的作用域(“request”,“session”,“globalSession”,“application”)。

注释很清晰,将Web的几种作用域注册到 BeanFactory 中。

源码中注册了 request 、session 、application 域,还注册了几种特定的依赖注入的关系。


回到 AnnotationConfigServletWebServerApplicationContext 中:

    if (this.basePackages != null && this.basePackages.length > 0) {
        this.scanner.scan(this.basePackages);
    }

下一步是进行组件的包扫描。不过注意一点,在这个位置上打断点,Debug运行时发现 basePackages 为null,故此处不进,小伙伴不要觉得之前已经知道了 primarySource 就觉得这个地方 basePackages 就肯定有值了。

咱先了解下这个包扫描,方便后续咱们看到时理解。

4.2 【重要】包扫描

private final AnnotatedBeanDefinitionReader reader;
private final ClassPathBeanDefinitionScanner scanner;

AnnotationConfigServletWebServerApplicationContext 中有声明 注解Bean定义解析器类路径Bean定义扫描器 的类型,可以依此类型来查看原理。

// ClassPathBeanDefinitionScanner
public int scan(String... basePackages) {
    int beanCountAtScanStart = this.registry.getBeanDefinitionCount();

    doScan(basePackages);

    // Register annotation config processors, if necessary.
    if (this.includeAnnotationConfig) {
        AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
    }

    return (this.registry.getBeanDefinitionCount() - beanCountAtScanStart);
}

又出现 scandoScan 了。doScan 方法:

protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
    Assert.notEmpty(basePackages, "At least one base package must be specified");
    Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>();
    // 只有主启动类所在包
    for (String basePackage : basePackages) {
        // 4.2.1 - 4.2.5 扫描包及子包下的组件
        Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
        // 4.3 ......
    }
    return beanDefinitions;
}

4.2.1 findCandidateComponents

来到父类 ClassPathScanningCandidateComponentProviderfindCandidateComponents 方法:

private CandidateComponentsIndex componentsIndex;

public Set<BeanDefinition> findCandidateComponents(String basePackage) {
    if (this.componentsIndex != null && indexSupportsIncludeFilters()) {
        return addCandidateComponentsFromIndex(this.componentsIndex, basePackage);
    }
    else {
        return scanCandidateComponents(basePackage);
    }
}

很明显,包扫描进入的是下面的 scanCandidateComponents

4.2.2 scanCandidateComponents

这个方法中有大量log,精简篇幅如下:

// ResourcePatternResolver
String CLASSPATH_ALL_URL_PREFIX = "classpath*:";

static final String DEFAULT_RESOURCE_PATTERN = "**/*.class";
private String resourcePattern = DEFAULT_RESOURCE_PATTERN;

private Set<BeanDefinition> scanCandidateComponents(String basePackage) {
    Set<BeanDefinition> candidates = new LinkedHashSet<>();
    try {
        // 拼接包扫描路径
        String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
            resolveBasePackage(basePackage) + '/' + this.resourcePattern;
        // 4.2.3,4 包扫描
        Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath);
        for (Resource resource : resources) {
            if (resource.isReadable()) {
                try {
                    MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource);
                    if (isCandidateComponent(metadataReader)) {
                        ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
                        sbd.setResource(resource);
                        sbd.setSource(resource);
                        if (isCandidateComponent(sbd)) {
                            candidates.add(sbd);
                        }
                        // log和catch部分省略
                        return candidates;
}

首先它将要扫描的包和一些前缀进行拼接:前缀是 classpath*: ,后缀默认扫 **/*.class ,中间部分调了一个 resolveBasePackage 方法,这个方法其实不看也能猜出来是把这个包名转换成文件路径(不然怎么拼接到扫描路径呢)。看一眼源码:

// ClassPathScanningCandidateComponentProvider
protected String resolveBasePackage(String basePackage) {
    return ClassUtils.convertClassNameToResourcePath(getEnvironment().resolveRequiredPlaceholders(basePackage));
}

// ClassUtils
private static final char PACKAGE_SEPARATOR = '.';
private static final char PATH_SEPARATOR = '/';

public static String convertClassNameToResourcePath(String className) {
    Assert.notNull(className, "Class name must not be null");
    return className.replace(PACKAGE_SEPARATOR, PATH_SEPARATOR);
}

果然是包名转换,把 . 转换成 /

由此可算得,入门启动程序中的拼接包路径应该是: classpath*:com/example/demo/**/*.class

回到方法中:

String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
    resolveBasePackage(basePackage) + '/' + this.resourcePattern;
Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath);

下面的 getResources 方法最终会拿 ResourcePatternResolver 来获取一组 Resource ,分开来看:

4.2.3 getResourcePatternResolver

private ResourcePatternResolver getResourcePatternResolver() {
    if (this.resourcePatternResolver == null) {
        this.resourcePatternResolver = new PathMatchingResourcePatternResolver();
    }
    return this.resourcePatternResolver;
}

可以发现它用的是 PathMatchingResourcePatternResolver 。那下面的 getResources 方法就是它里面的了:

4.2.4 getResources:包扫描

String CLASSPATH_ALL_URL_PREFIX = "classpath*:";

public Resource[] getResources(String locationPattern) throws IOException {
    Assert.notNull(locationPattern, "Location pattern must not be null");
    if (locationPattern.startsWith(CLASSPATH_ALL_URL_PREFIX)) {
        // a class path resource (multiple resources for same name possible)
        if (getPathMatcher().isPattern(locationPattern.substring(CLASSPATH_ALL_URL_PREFIX.length()))) {
            // a class path resource pattern
            return findPathMatchingResources(locationPattern);
        }
        else {
            // all class path resources with the given name
            return findAllClassPathResources(locationPattern.substring(CLASSPATH_ALL_URL_PREFIX.length()));
        }
    }
    else {
        // Generally only look for a pattern after a prefix here,
        // and on Tomcat only after the "*/" separator for its "war:" protocol.
        int prefixEnd = (locationPattern.startsWith("war:") ? locationPattern.indexOf("*/") + 1 :
                         locationPattern.indexOf(':') + 1);
        if (getPathMatcher().isPattern(locationPattern.substring(prefixEnd))) {
            // a file pattern
            return findPathMatchingResources(locationPattern);
        }
        else {
            // a single resource with the given name
            return new Resource[] {getResourceLoader().getResource(locationPattern)};
        }
    }
}

整个方法的大if结构中,先判断要扫描的包路径是否有 classpath*: ,有则截掉,之后判断路径是否能匹配扫描规则。而这个规则的匹配器,通过IDEA发现 PathMatcher 只有一个实现类: AntPathMatcher ,由此也解释了 SpringFramework 支持的是ant规则声明包。

如果规则匹配,则会进入下面的 findPathMatchingResources 方法:

4.2.4.1 findPathMatchingResources:根据Ant路径进行包扫描

protected Resource[] findPathMatchingResources(String locationPattern) throws IOException {
    // 4.2.4.1.1 截取扫描根路径
    String rootDirPath = determineRootDir(locationPattern);
    // 截取剩下扫描路径(**/*.class)
    String subPattern = locationPattern.substring(rootDirPath.length());
    // 4.2.4.2,3 获取扫描包路径下的所有包
    Resource[] rootDirResources = getResources(rootDirPath);
    Set<Resource> result = new LinkedHashSet<>(16);
    for (Resource rootDirResource : rootDirResources) {
        rootDirResource = resolveRootDirResource(rootDirResource);
        URL rootDirUrl = rootDirResource.getURL();
        if (equinoxResolveMethod != null && rootDirUrl.getProtocol().startsWith("bundle")) {
            URL resolvedUrl = (URL) ReflectionUtils.invokeMethod(equinoxResolveMethod, null, rootDirUrl);
            if (resolvedUrl != null) {
                rootDirUrl = resolvedUrl;
            }
            rootDirResource = new UrlResource(rootDirUrl);
        }
        if (rootDirUrl.getProtocol().startsWith(ResourceUtils.URL_PROTOCOL_VFS)) {
            result.addAll(VfsResourceMatchingDelegate.findMatchingResources(rootDirUrl, subPattern, getPathMatcher()));
        }
        else if (ResourceUtils.isJarURL(rootDirUrl) || isJarResource(rootDirResource)) {
            result.addAll(doFindPathMatchingJarResources(rootDirResource, rootDirUrl, subPattern));
        }
        else {
            result.addAll(doFindPathMatchingFileResources(rootDirResource, subPattern));
        }
    }
    if (logger.isTraceEnabled()) {
        logger.trace("Resolved location pattern [" + locationPattern + "] to resources " + result);
    }
    return result.toArray(new Resource[0]);
}

首先它要截取扫描根路径,这个截取是一个简单算法:

protected String determineRootDir(String location) {
    int prefixEnd = location.indexOf(':') + 1;
    int rootDirEnd = location.length();
    while (rootDirEnd > prefixEnd && getPathMatcher().isPattern(location.substring(prefixEnd, rootDirEnd))) {
        rootDirEnd = location.lastIndexOf('/', rootDirEnd - 2) + 1;
    }
    if (rootDirEnd == 0) {
        rootDirEnd = prefixEnd;
    }
    return location.substring(0, rootDirEnd);
}

通过上面的算法,可以计算得路径是 classpath*:com/example/demo/


回到上面的方法:

// 4.2.4.1.1 截取扫描根路径
String rootDirPath = determineRootDir(locationPattern);
// 截取剩下扫描路径(**/*.class)
String subPattern = locationPattern.substring(rootDirPath.length());
Resource[] rootDirResources = getResources(rootDirPath);

在截取完成后,又把根路径传入了那个 getResources 方法。由于这一次没有后缀了,只有根路径,故进入的分支方法会不一样:

public Resource[] getResources(String locationPattern) throws IOException {
    Assert.notNull(locationPattern, "Location pattern must not be null");
    if (locationPattern.startsWith(CLASSPATH_ALL_URL_PREFIX)) {
        // a class path resource (multiple resources for same name possible)
        if (getPathMatcher().isPattern(locationPattern.substring(CLASSPATH_ALL_URL_PREFIX.length()))) {
            // a class path resource pattern
            return findPathMatchingResources(locationPattern);
        }
        else {
            // ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓
            // all class path resources with the given name
            return findAllClassPathResources(locationPattern.substring(CLASSPATH_ALL_URL_PREFIX.length()));
        }
    }
    // ...

这次路径是 classpath*:com/example/demo/ ,不能匹配了,进入下面的else部分,findAllClassPathResources 方法:

4.2.4.2 findAllClassPathResources

protected Resource[] findAllClassPathResources(String location) throws IOException {
    String path = location;
    if (path.startsWith("/")) {
        path = path.substring(1);
    }
    //                     ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓
    Set<Resource> result = doFindAllClassPathResources(path);
    if (logger.isTraceEnabled()) {
        logger.trace("Resolved classpath location [" + location + "] to resources " + result);
    }
    return result.toArray(new Resource[0]);
}

这里进行真正的扫描获取包的工作:doFindAllClassPathResources

4.2.4.3 doFindAllClassPathResources

protected Set<Resource> doFindAllClassPathResources(String path) throws IOException {
    Set<Resource> result = new LinkedHashSet<>(16);
    ClassLoader cl = getClassLoader();
    Enumeration<URL> resourceUrls = (cl != null ? cl.getResources(path) : ClassLoader.getSystemResources(path));
    while (resourceUrls.hasMoreElements()) {
        URL url = resourceUrls.nextElement();
        result.add(convertClassLoaderURL(url));
    }
    if ("".equals(path)) {
        // The above result is likely to be incomplete, i.e. only containing file system references.
        // We need to have pointers to each of the jar files on the classpath as well...
        addAllClassLoaderJarRoots(cl, result);
    }
    return result;
}

可以发现它在做的工作是使用类加载器,把传入的根包以Resource的形式加载出来,以便后续的文件读取


之后方法返回,回到4.2.4.1的方法中:

4.2.4.4 扫描包

Resource[] rootDirResources = getResources(rootDirPath);
Set<Resource> result = new LinkedHashSet<>(16);
for (Resource rootDirResource : rootDirResources) {
    rootDirResource = resolveRootDirResource(rootDirResource);
    URL rootDirUrl = rootDirResource.getURL();
    if (equinoxResolveMethod != null && rootDirUrl.getProtocol().startsWith("bundle")) {
        URL resolvedUrl = (URL) ReflectionUtils.invokeMethod(equinoxResolveMethod, null, rootDirUrl);
        if (resolvedUrl != null) {
            rootDirUrl = resolvedUrl;
        }
        rootDirResource = new UrlResource(rootDirUrl);
    }
    if (rootDirUrl.getProtocol().startsWith(ResourceUtils.URL_PROTOCOL_VFS)) {
        result.addAll(VfsResourceMatchingDelegate.findMatchingResources(rootDirUrl, subPattern, getPathMatcher()));
    }
    else if (ResourceUtils.isJarURL(rootDirUrl) || isJarResource(rootDirResource)) {
        result.addAll(doFindPathMatchingJarResources(rootDirResource, rootDirUrl, subPattern));
    }
    else {
        // ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓
        result.addAll(doFindPathMatchingFileResources(rootDirResource, subPattern));
    }
    }

它要拿到所有要扫描的包的文件路径,来进行真正的包扫描工作。

首先 resolveRootDirResource 方法在实现中直接把 rootDirResource 返回了(不知道这什么鬼才操作),之后要判断扫描包路径前缀是否为vfs或jar,都不是则进入最底下的方法(默认情况下我们只配置扫描本项目的组件,则扫描最终的扫描包路径前缀为 file:/)。

4.2.4.5 doFindPathMatchingFileResources

这个方法默认在 PathMatchingResourcePatternResolver 中有实现,但通过IDEA发现它被 ServletContextResourcePatternResolver 重写了。通过Debug,走到这一步也发现先进的 ServletContextResourcePatternResolver 中的 doFindPathMatchingFileResources 方法。

protected Set<Resource> doFindPathMatchingFileResources(Resource rootDirResource, String subPattern)
    throws IOException {
    
    if (rootDirResource instanceof ServletContextResource) {
        ServletContextResource scResource = (ServletContextResource) rootDirResource;
        ServletContext sc = scResource.getServletContext();
        String fullPattern = scResource.getPath() + subPattern;
        Set<Resource> result = new LinkedHashSet<>(8);
        doRetrieveMatchingServletContextResources(sc, fullPattern, scResource.getPath(), result);
        return result;
    }
    else {
        return super.doFindPathMatchingFileResources(rootDirResource, subPattern);
    }
}

然而在默认的项目内部包扫描中,与 ServletContextResource 没有关系,故还是要回到 PathMatchingResourcePatternResolver 中。

4.2.4.6 PathMatchingResourcePatternResolver.doFindPathMatchingFileResources

protected Set<Resource> doFindPathMatchingFileResources(Resource rootDirResource, String subPattern)
    throws IOException {
    
    File rootDir;
    try {
        rootDir = rootDirResource.getFile().getAbsoluteFile();
    }
    catch (FileNotFoundException ex) {
        if (logger.isDebugEnabled()) {
            logger.debug("Cannot search for matching files underneath " + rootDirResource +
                         " in the file system: " + ex.getMessage());
        }
        return Collections.emptySet();
    }
    catch (Exception ex) {
        if (logger.isInfoEnabled()) {
            logger.info("Failed to resolve " + rootDirResource + " in the file system: " + ex);
        }
        return Collections.emptySet();
    }
    return doFindMatchingFileSystemResources(rootDir, subPattern);
}

try中先把文件加载出来,之后调了下面的 doFindMatchingFileSystemResources 方法:

protected Set<Resource> doFindMatchingFileSystemResources(File rootDir, String subPattern) throws IOException {
    if (logger.isTraceEnabled()) {
        logger.trace("Looking for matching resources in directory tree [" + rootDir.getPath() + "]");
    }
    //                        ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓
    Set<File> matchingFiles = retrieveMatchingFiles(rootDir, subPattern);
    Set<Resource> result = new LinkedHashSet<>(matchingFiles.size());
    for (File file : matchingFiles) {
        result.add(new FileSystemResource(file));
    }
    return result;
}

源码中很明显只有一句是核心: retrieveMatchingFiles

4.2.4.7 retrieveMatchingFiles

protected Set<File> retrieveMatchingFiles(File rootDir, String pattern) throws IOException {
    // 不存在的检查
    if (!rootDir.exists()) {
        // log
        return Collections.emptySet();
    }
    // 非文件夹检查
    if (!rootDir.isDirectory()) {
        // log
        return Collections.emptySet();
    }
    // 不可读检查
    if (!rootDir.canRead()) {
        // log
        return Collections.emptySet();
    }
    String fullPattern = StringUtils.replace(rootDir.getAbsolutePath(), File.separator, "/");
    if (!pattern.startsWith("/")) {
        fullPattern += "/";
    }
    fullPattern = fullPattern + StringUtils.replace(pattern, File.separator, "/");
    Set<File> result = new LinkedHashSet<>(8);
    doRetrieveMatchingFiles(fullPattern, rootDir, result);
    return result;
}

上面的一些必要的检查之后,它将路径进行整理,最终调用 doRetrieveMatchingFiles 方法:

4.2.4.8 doRetrieveMatchingFiles:递归扫描

protected void doRetrieveMatchingFiles(String fullPattern, File dir, Set<File> result) throws IOException {
    if (logger.isTraceEnabled()) {
        logger.trace("Searching directory [" + dir.getAbsolutePath() +
                     "] for files matching pattern [" + fullPattern + "]");
    }
    for (File content : listDirectory(dir)) {
        String currPath = StringUtils.replace(content.getAbsolutePath(), File.separator, "/");
        if (content.isDirectory() && getPathMatcher().matchStart(fullPattern, currPath + "/")) {
            if (!content.canRead()) {
                if (logger.isDebugEnabled()) {
                    logger.debug("Skipping subdirectory [" + dir.getAbsolutePath() +
                                 "] because the application is not allowed to read the directory");
                }
            }
            else {
                doRetrieveMatchingFiles(fullPattern, content, result);
            }
        }
        if (getPathMatcher().match(fullPattern, currPath)) {
            result.add(content);
        }
    }
}

这个方法是最终进行包扫描的底层,可以发现在16行使用了递归扫描。


扫描完成后, getResources 方法算是彻底执行完成,回到4.2.2 scanCandidateComponents 方法中:

4.2.5 解析Component

Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath);
for (Resource resource : resources) {
    if (traceEnabled) {
        logger.trace("Scanning " + resource);
    }
    if (resource.isReadable()) {
        try {
            MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource);
            if (isCandidateComponent(metadataReader)) {
                ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
                sbd.setResource(resource);
                sbd.setSource(resource);
                if (isCandidateComponent(sbd)) {
                    if (debugEnabled) {
                        logger.debug("Identified candidate component class: " + resource);
                    }
                    candidates.add(sbd);
                }
                    // else ......

下面要遍历每个扫描出来的.class文件(此时还没有进行过滤),来下面的try部分。

它使用了一个 MetadataReader 来解析.class文件,它就可以读取这个class的类定义信息、注解标注信息。之后要用 MetadataReader 来判断这个class是否为一个 Component

4.2.5.1 isCandidateComponent

protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException {
    for (TypeFilter tf : this.excludeFilters) {
        if (tf.match(metadataReader, getMetadataReaderFactory())) {
            return false;
        }
    }
    for (TypeFilter tf : this.includeFilters) {
        if (tf.match(metadataReader, getMetadataReaderFactory())) {
            return isConditionMatch(metadataReader);
        }
    }
    return false;
}

它拿了一组 excludeFilters 和 includeFilters,而这两组过滤器通过Debug可以发现:

它会判断class是否被 @Component / @ManagedBean 标注。至此发现了真正扫描 @Component 的原理。

判定为 Component 后,会将这个class封装为 BeanDefinition,最后返回。

这部分的逻辑非常复杂,咱用一个图来理解这部分过程:


返回到4.2中的doScan中:

4.3 扫描完BeanDefinition后

Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
for (BeanDefinition candidate : candidates) {
    ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
    candidate.setScope(scopeMetadata.getScopeName());
    String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
    if (candidate instanceof AbstractBeanDefinition) {
        postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
    }
    if (candidate instanceof AnnotatedBeanDefinition) {
        AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
    }
    if (checkCandidate(beanName, candidate)) {
        BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
        definitionHolder =
            AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
        beanDefinitions.add(definitionHolder);
        registerBeanDefinition(definitionHolder, this.registry);
    }
    }

它要遍历每个 BeanDefinition,进行一些后置处理。

4.3.1 beanNameGenerator.generateBeanName

之前我们见过它,它的作用到后面加载它的时候咱再看。

4.3.2 postProcessBeanDefinition

protected void postProcessBeanDefinition(AbstractBeanDefinition beanDefinition, String beanName) {
    beanDefinition.applyDefaults(this.beanDefinitionDefaults);
    if (this.autowireCandidatePatterns != null) {
        beanDefinition.setAutowireCandidate(PatternMatchUtils.simpleMatch(this.autowireCandidatePatterns, beanName));
    }
}

发现是设置 BeanDefinition 的一些默认值。

4.3.3 checkCandidate

字面意思是检查候选者,不是很好理解,来看这个方法的文档注释:

Check the given candidate's bean name, determining whether the corresponding bean definition needs to be registered or conflicts with an existing definition.

检查给定候选者的Bean名称,以确定是否需要注册相应的Bean定义或与现有定义冲突。

原来它是检查BeanName是否冲突的。

4.3.4 registerBeanDefinition

既然上面的检查不冲突,就可以进入到if结构体中,最后就可以注册 BeanDefinition

protected void registerBeanDefinition(BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry) {
    BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, registry);
}

遇见了熟悉的方法,这个方法之前已经看过了,不再详细解析。(第10篇4.9.4.6章节)


至此,BeanFactory 的后置处理就结束了,但下面还有一组后置处理器,也是对 BeanFactory 进行处理。

5. invokeBeanFactoryPostProcessors:执行BeanFactory创建后的后置处理器

protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
    // 5.1 执行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));
        beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
    }
}

注意这里又出现了一个新的概念:**BeanFactoryPostProcessor**

5.0 【重要】BeanFactoryPostProcessor

BeanFactoryPostProcessor(BeanFactory后置处理器)是一个接口,它只定义了一个方法:

/**
* Modify the application context's internal bean factory after its standard
* initialization. All bean definitions will have been loaded, but no beans
* will have been instantiated yet. This allows for overriding or adding
* properties even to eager-initializing beans.
* 在应用程序上下文的标准初始化之后修改其内部bean工厂。
* 所有bean定义都已经加载,但是还没有实例化bean。这允许覆盖或添加属性,甚至可以初始化bean。
*/
void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;

实现了这个接口,BeanFactory 标准初始化完毕后,可以对这个 BeanFactory 进行后置处理

这个时机下,所有的 **BeanDefinition** 已经被加载,但没有Bean被实例化

另外,BeanFactoryPostProcessor 还有一个子接口:**BeanDefinitionRegistryPostProcessor**Bean定义注册的后置处理器

它额外定义了一个方法:

/**
* Modify the application context's internal bean definition registry after its
* standard initialization. All regular bean definitions will have been loaded,
* but no beans will have been instantiated yet. This allows for adding further
* bean definitions before the next post-processing phase kicks in.
* 在标准初始化之后修改应用程序上下文的内部bean定义注册表。
* 所有常规bean定义都已加载,但还没有实例化bean。
* 这允许在下一个后期处理阶段开始之前添加进一步的bean定义。
*/
void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException;

它的执行时机是所有Bean的定义信息即将被加载但未实例化时,也就是先于 **BeanFactoryPostProcessor**

规律BeanPostProcessor 是对Bean的后置处理,BeanFactoryPostProcessor 是对 BeanFactory 的后置处理,后续再看到这样的同理。


回到源码:

5.1 PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors:回调后置处理器

这段源码非常长(130行+),为了浏览方便,我直接把关键注释写在源码中了。

public static void invokeBeanFactoryPostProcessors(
    ConfigurableListableBeanFactory beanFactory, List<BeanFactoryPostProcessor> beanFactoryPostProcessors) {
    
    // Invoke BeanDefinitionRegistryPostProcessors first, if any.
    // 首先调用BeanDefinitionRegistryPostProcessor
    Set<String> processedBeans = new HashSet<>();
    
    // 这里要判断BeanFactory的类型,默认SpringBoot创建的BeanFactory是DefaultListableBeanFactory
    // 这个类实现了BeanDefinitionRegistry接口,则此if结构必进
    if (beanFactory instanceof BeanDefinitionRegistry) {
        BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;
        List<BeanFactoryPostProcessor> regularPostProcessors = new ArrayList<>();
        List<BeanDefinitionRegistryPostProcessor> registryProcessors = new ArrayList<>();
        
        // foreach中为了区分不同的后置处理器,并划分到不同的集合中
        // 注意如果是BeanDefinitionRegistryPostProcessor,根据原理描述,还会回调它的后置处理功能
        for (BeanFactoryPostProcessor postProcessor : beanFactoryPostProcessors) {
            if (postProcessor instanceof BeanDefinitionRegistryPostProcessor) {
                BeanDefinitionRegistryPostProcessor registryProcessor =
                    (BeanDefinitionRegistryPostProcessor) postProcessor;
                registryProcessor.postProcessBeanDefinitionRegistry(registry);
                registryProcessors.add(registryProcessor);
            }
            else {
                regularPostProcessors.add(postProcessor);
            }
        }
        
        // Do not initialize FactoryBeans here: We need to leave all regular beans
        // uninitialized to let the bean factory post-processors apply to them!
        // Separate between BeanDefinitionRegistryPostProcessors that implement
        // PriorityOrdered, Ordered, and the rest.
        // 不要在这里初始化BeanFactory:我们需要保留所有未初始化的常规bean,以便让bean工厂后处理器应用到它们!
        // 独立于实现PriorityOrdered、Ordered和其他的BeanDefinitionRegistryPostProcessor之间。
        List<BeanDefinitionRegistryPostProcessor> currentRegistryProcessors = new ArrayList<>();
        // 这部分实际上想表达的意思是,在创建Bean之前,要先执行这些
        // BeanDefinitionRegistryPostProcessor的后置处理方法,并且实现了
        // PriorityOrdered排序接口或实现了Ordered接口的Bean需要优先被加载。
        
        // 下面一段是从BeanFactory中取出所有BeanDefinitionRegistryPostProcessor类型的全限定名(String[]), 
        // 放到下面遍历,还要判断这些类里是否有实现PriorityOrdered接口的,
        // 如果有,存到集合里,之后进行排序、统一回调这些后置处理器
        
        // First, invoke the BeanDefinitionRegistryPostProcessors that implement PriorityOrdered.
        // 首先,调用实现PriorityOrdered接口的BeanDefinitionRegistryPostProcessors。
        String[] postProcessorNames =
            beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
        for (String ppName : postProcessorNames) {
            if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
                currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
                processedBeans.add(ppName);
            }
        }
        sortPostProcessors(currentRegistryProcessors, beanFactory);
        registryProcessors.addAll(currentRegistryProcessors);
        invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
        currentRegistryProcessors.clear();
        
        // Next, invoke the BeanDefinitionRegistryPostProcessors that implement Ordered.
        // 接下来,调用实现Ordered接口的BeanDefinitionRegistryPostProcessors。
        postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
        for (String ppName : postProcessorNames) {
            if (!processedBeans.contains(ppName) && beanFactory.isTypeMatch(ppName, Ordered.class)) {
                currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
                processedBeans.add(ppName);
            }
        }
        sortPostProcessors(currentRegistryProcessors, beanFactory);
        registryProcessors.addAll(currentRegistryProcessors);
        invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
        currentRegistryProcessors.clear();
        
        // Finally, invoke all other BeanDefinitionRegistryPostProcessors until no further ones appear.
        // 最后,调用所有其他BeanDefinitionRegistryPostProcessor
        boolean reiterate = true;
        while (reiterate) {
            reiterate = false;
            postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
            for (String ppName : postProcessorNames) {
                if (!processedBeans.contains(ppName)) {
                    currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
                    processedBeans.add(ppName);
                    reiterate = true;
                }
            }
            sortPostProcessors(currentRegistryProcessors, beanFactory);
            registryProcessors.addAll(currentRegistryProcessors);
            invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
            currentRegistryProcessors.clear();
        }
        
        // Now, invoke the postProcessBeanFactory callback of all processors handled so far.
        // 回调所有BeanFactoryPostProcessor的postProcessBeanFactory方法
        invokeBeanFactoryPostProcessors(registryProcessors, beanFactory);
        invokeBeanFactoryPostProcessors(regularPostProcessors, beanFactory);
        // 先回调BeanDefinitionRegistryPostProcessor的postProcessBeanFactory方法
        // 再调用BeanFactoryPostProcessor的postProcessBeanFactory方法
    }
    
    // 如果BeanFactory没有实现BeanDefinitionRegistry接口,则进入下面的代码流程
    else {
        // Invoke factory processors registered with the context instance.
        // 调用在上下文实例中注册的工厂处理器。
        invokeBeanFactoryPostProcessors(beanFactoryPostProcessors, beanFactory);
    }
    
    // 下面的部分是回调BeanFactoryPostProcessor,思路与上面的几乎一样
    
    // Do not initialize FactoryBeans here: We need to leave all regular beans
    // uninitialized to let the bean factory post-processors apply to them!
    String[] postProcessorNames =
        beanFactory.getBeanNamesForType(BeanFactoryPostProcessor.class, true, false);
    
    // Separate between BeanFactoryPostProcessors that implement PriorityOrdered,
    // Ordered, and the rest.
    List<BeanFactoryPostProcessor> priorityOrderedPostProcessors = new ArrayList<>();
    List<String> orderedPostProcessorNames = new ArrayList<>();
    List<String> nonOrderedPostProcessorNames = new ArrayList<>();
    for (String ppName : postProcessorNames) {
        if (processedBeans.contains(ppName)) {
            // skip - already processed in first phase above
        }
        else if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
            priorityOrderedPostProcessors.add(beanFactory.getBean(ppName, BeanFactoryPostProcessor.class));
        }
        else if (beanFactory.isTypeMatch(ppName, Ordered.class)) {
            orderedPostProcessorNames.add(ppName);
        }
        else {
            nonOrderedPostProcessorNames.add(ppName);
        }
    }
    
    // First, invoke the BeanFactoryPostProcessors that implement PriorityOrdered.
    sortPostProcessors(priorityOrderedPostProcessors, beanFactory);
    invokeBeanFactoryPostProcessors(priorityOrderedPostProcessors, beanFactory);
    
    // Next, invoke the BeanFactoryPostProcessors that implement Ordered.
    List<BeanFactoryPostProcessor> orderedPostProcessors = new ArrayList<>();
    for (String postProcessorName : orderedPostProcessorNames) {
        orderedPostProcessors.add(beanFactory.getBean(postProcessorName, BeanFactoryPostProcessor.class));
    }
    sortPostProcessors(orderedPostProcessors, beanFactory);
    invokeBeanFactoryPostProcessors(orderedPostProcessors, beanFactory);
    
    // Finally, invoke all other BeanFactoryPostProcessors.
    List<BeanFactoryPostProcessor> nonOrderedPostProcessors = new ArrayList<>();
    for (String postProcessorName : nonOrderedPostProcessorNames) {
        nonOrderedPostProcessors.add(beanFactory.getBean(postProcessorName, BeanFactoryPostProcessor.class));
    }
    invokeBeanFactoryPostProcessors(nonOrderedPostProcessors, beanFactory);
    
    // Clear cached merged bean definitions since the post-processors might have
    // modified the original metadata, e.g. replacing placeholders in values...
    // 清理缓存
    beanFactory.clearMetadataCache();
}

这一部分非常重要。可以简单地这样理解:

img

将这段源码分成几部分来看:

5.1.1 参数中的PostProcessor分类

// foreach中为了区分不同的后置处理器,并划分到不同的集合中
// 注意如果是BeanDefinitionRegistryPostProcessor,根据原理描述,还会回调它的后置处理功能
for (BeanFactoryPostProcessor postProcessor : beanFactoryPostProcessors) {
    if (postProcessor instanceof BeanDefinitionRegistryPostProcessor) {
        BeanDefinitionRegistryPostProcessor registryProcessor =
            (BeanDefinitionRegistryPostProcessor) postProcessor;
        registryProcessor.postProcessBeanDefinitionRegistry(registry);
        registryProcessors.add(registryProcessor);
    }
    else {
        regularPostProcessors.add(postProcessor);
    }
        }

遍历一次后,把同时也是 BeanDefinitionRegistryPostProcessor 的后置处理器单独挑出来,直接回调 postProcessBeanDefinitionRegistry 方法。

通过Debug,发现传入这个方法的参数中,beanFactoryPostProcessors 有3个:

img

这里面同时属于 BeanDefinitionRegistryPostProcessor 有两个:

5.1.2 BeanFactory中取+排序+回调

// First, invoke the BeanDefinitionRegistryPostProcessors that implement PriorityOrdered.
// 首先,调用实现PriorityOrdered接口的BeanDefinitionRegistryPostProcessors。
String[] postProcessorNames =
    beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
for (String ppName : postProcessorNames) {
    if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
        currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
        processedBeans.add(ppName);
    }
}
sortPostProcessors(currentRegistryProcessors, beanFactory);
registryProcessors.addAll(currentRegistryProcessors);
invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
        currentRegistryProcessors.clear();
// Next, invoke the BeanDefinitionRegistryPostProcessors that implement Ordered.
// 接下来,调用实现Ordered接口的BeanDefinitionRegistryPostProcessors。
postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
for (String ppName : postProcessorNames) {
    if (!processedBeans.contains(ppName) && beanFactory.isTypeMatch(ppName, Ordered.class)) {
        currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
        processedBeans.add(ppName);
    }
}
sortPostProcessors(currentRegistryProcessors, beanFactory);
registryProcessors.addAll(currentRegistryProcessors);
invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
        currentRegistryProcessors.clear();

它把 BeanFactory 中所有的 BeanDefinitionRegistryPostProcessor 分成三部分:实现 PriorityOrdered 接口的、实现 Ordered 接口的,普通的。

上面的两部分源码就是对前两种方式进行回调:筛选,排序,注册,回调,清除。

之后又用同样的逻辑,取所有的 BeanFactoryPostProcessor ,进行同样的操作,不再重复描述。

逻辑不算复杂,下面介绍几个重要的后置处理器。

5.2 【重要扩展】ConfigurationClassPostProcessor

在上述源码的Debug中,第二环节获取所有 BeanDefinitionRegistryPostProcessor 的时候发现了一个后置处理器:ConfigurationClassPostProcessor

它的文档注释原文翻译:

BeanFactoryPostProcessor used for bootstrapping processing of @Configuration classes. Registered by default when using <context:annotation-config/> or <context:component-scan/>. Otherwise, may be declared manually as with any other BeanFactoryPostProcessor. This post processor is priority-ordered as it is important that any Bean methods declared in @Configuration classes have their corresponding bean definitions registered before any other BeanFactoryPostProcessor executes.

BeanFactoryPostProcessor,用于 @Configuration 类的扫描加载处理。 使用<context:annotation-config /><context:component-scan /> 时默认注册。否则,可以像其他任何 BeanFactoryPostProcessor 一样手动声明。 此后处理器按优先级排序,因为在 @Configuration 标注的类中声明的任何Bean方法在执行任何其他 BeanFactoryPostProcessor 之前都要注册其相应的Bean定义,这一点很重要。

那自然我们应该去看它的 postProcessBeanDefinitionRegistry 方法:

public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
    int registryId = System.identityHashCode(registry);
    if (this.registriesPostProcessed.contains(registryId)) {
        throw new IllegalStateException(
                "postProcessBeanDefinitionRegistry already called on this post-processor against " + registry);
    }
    if (this.factoriesPostProcessed.contains(registryId)) {
        throw new IllegalStateException(
                "postProcessBeanFactory already called on this post-processor against " + registry);
    }
    this.registriesPostProcessed.add(registryId);

    processConfigBeanDefinitions(registry);
}

它取出 BeanFactory 的id,并在下面的if结构中判断是否已经被调用过了。确定没有,在被调用过的集合中加上当前 BeanFactory 的id,之后调用 processConfigBeanDefinitions 方法:

public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
    List<BeanDefinitionHolder> configCandidates = new ArrayList<>();
    String[] candidateNames = registry.getBeanDefinitionNames();
    
    // 5.2.1 确定配置类和组件
    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);
    });
    
    // Detect any custom bean name generation strategy supplied through the enclosing application context
    // 5.2.2 加载获取BeanNameGenerator
    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();
    }
    
    // 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 {
        // 5.2.3 解析配置类
        parser.parse(candidates);
        parser.validate();
        
        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());
        }
        // 5.2.4 解析配置类中的内容
        this.reader.loadBeanDefinitions(configClasses);
        alreadyParsed.addAll(configClasses);
        
        candidates.clear();
        // 5.2.5 加载配置类中的被@Bean标注的组件
        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
    // 将ImportRegistry注册为Bean,以支持ImportAware @Configuration类
    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();
    }
}

源码中有几部分是比较复杂且重要的环节,咱们一一来看:

5.2.1 确定配置类和组件

// 5.2.1 确定配置类和组件
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));
    }
    }

这里面需要关注的几个 ConfigurationClassUtils 方法:

  • isFullConfigurationClass:判断一个配置类是否为full类型

  • isLiteConfigurationClass:判断一个配置类是否为lite类型

  • checkConfigurationClassCandidate:检查一个类是否为配置类

上面提到了两个类型,都是可以从源码中看到的。那这个full和lite都是什么呢?

5.2.1.1 full与lite

其实,了解full和lite,只需要到最后一个方法 checkConfigurationClassCandidate 中看一下就知道了:

public static boolean checkConfigurationClassCandidate(
        BeanDefinition beanDef, MetadataReaderFactory metadataReaderFactory) {

    // ......

    if (isFullConfigurationCandidate(metadata)) {
        beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_FULL);
    }
    else if (isLiteConfigurationCandidate(metadata)) {
        beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_LITE);
    }
    else {
        return false;
    }

    // It's a full or lite configuration candidate... Let's determine the order value, if any.
    Integer order = getOrder(metadata);
    if (order != null) {
        beanDef.setAttribute(ORDER_ATTRIBUTE, order);
    }

    return true;
}

前面大段的代码都是校验和获取注解标注信息(已省略),核心的源码在底下的if-else结构中。它会调 isFullConfigurationCandidateisLiteConfigurationCandidate 来校验Bean的类型,而这两个方法的声明:

public static boolean isFullConfigurationCandidate(AnnotationMetadata metadata) {
    return metadata.isAnnotated(Configuration.class.getName());
}


private static final Set<String> candidateIndicators = new HashSet<>(8);
static {
    candidateIndicators.add(Component.class.getName());
    candidateIndicators.add(ComponentScan.class.getName());
    candidateIndicators.add(Import.class.getName());
    candidateIndicators.add(ImportResource.class.getName());
}
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;
    }
}

由这段源码可以得知:

  • full:@Configuration 标注的类

  • lite:有 @Component@ComponentScan@Import@ImportResource 标注的类,以及 @Configuration 中标注 @Bean 的类。

5.2.2 加载获取BeanNameGenerator

// Detect any custom bean name generation strategy supplied through the enclosing application context
// 5.2.2 加载获取BeanNameGenerator
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;
        }
    }
    }

它要在这个地方获取 BeanNameGenerator ,然而通过Debug发现它是null,故先放一边。

5.2.3 解析配置类 与 包扫描的触发时机

do {
    // 5.2.3 解析配置类
    parser.parse(candidates);
    parser.validate();
    
        // ......

这一段第一句就是核心:parse

public void parse(Set<BeanDefinitionHolder> configCandidates) {
    for (BeanDefinitionHolder holder : configCandidates) {
        BeanDefinition bd = holder.getBeanDefinition();
        try {
            if (bd instanceof AnnotatedBeanDefinition) {
                parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName());
            }
            else if (bd instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) bd).hasBeanClass()) {
                parse(((AbstractBeanDefinition) bd).getBeanClass(), holder.getBeanName());
            }
            else {
                parse(bd.getBeanClassName(), holder.getBeanName());
            }
        }
        // catch ......
    }
    
    this.deferredImportSelectorHandler.process();
}

它要遍历每一个 BeanDefinition,并根据类型来决定如何解析。SpringBoot 通常使用注解配置,这里会进入第一个if结构:

protected final void parse(AnnotationMetadata metadata, String beanName) throws IOException {
    processConfigurationClass(new ConfigurationClass(metadata, beanName));
}

protected void processConfigurationClass(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);
            this.knownSuperclasses.values().removeIf(configClass::equals);
        }
    }

    // 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);
}

上面的方法又调到下面,下面的方法中先进行判断。这里的 existingClass 容易被误解,它在这个方法的最后,把当前传入的组件存到一个Map中,每次组件进到这个方法时先校验是否有这个类型的Bean了,如果有,要进行一些处理。如果没有,往下走,进入到do-while结构中,它要执行 doProcessConfigurationClass 方法。

protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass)
    throws IOException {
    
    if (configClass.getMetadata().isAnnotated(Component.class.getName())) {
        // 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);
        }
        // ......
    }
    
    // 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
            // ......
        }
    }
    
    // Process any @Import annotations
    processImports(configClass, sourceClass, getImports(sourceClass), true);
    
    // Process any @ImportResource annotations
    AnnotationAttributes importResource =
        AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class);
    if (importResource != null) {
        // ......
    }
    
    // Process individual @Bean methods
    Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);
    for (MethodMetadata methodMetadata : beanMethods) {
        configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
    }
    
    // Process default methods on interfaces
    processInterfaces(configClass, sourceClass);
    
    // Process superclass, if any
    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;
}

从源码注释中已经看出,它来解析 @PropertySource@ComponentScan@Import@ImportResource@Bean 等注解,并整理成一个 ConfigClass

由此可知,在这一步,一个配置类的所有信息就已经被解析完成了。

咱们以解析 @ComponentScan 为例:

5.2.3.1 解析 @ComponentScan

// 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 needed
        for (BeanDefinitionHolder holder : scannedBeanDefinitions) {
            BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition();
            if (bdCand == null) {
                bdCand = holder.getBeanDefinition();
            }
            if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {
                parse(bdCand.getBeanClassName(), holder.getBeanName());
            }
        }
    }
    }

这部分解析要追踪到 componentScanParser 的parse方法中,它用来真正的做注解解析:

5.2.3.2 ComponentScanAnnotationParser.parse

(这里只记录重要的部分,中间省略的部分小伙伴们可借助IDE查看)

public Set<BeanDefinitionHolder> parse(AnnotationAttributes componentScan, final String declaringClass) {
    ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(this.registry,
                                                                                componentScan.getBoolean("useDefaultFilters"), this.environment, this.resourceLoader);
    
    // ......
    return scanner.doScan(StringUtils.toStringArray(basePackages));
}

先看一眼最后的return:doScan 方法!原来包扫描的触发时机在这里:执行 **ConfigurationClassPostProcessor** **postProcessBeanDefinitionRegistry** 方法,解析 **@ComponentScan** 时触发

除了最后的 doScan,这里面有一个关注的点:

5.2.3.3 new ClassPathBeanDefinitionScanner

这个构造方法中有点细节:

private BeanNameGenerator beanNameGenerator = new AnnotationBeanNameGenerator();
private ScopeMetadataResolver scopeMetadataResolver = new AnnotationScopeMetadataResolver();

public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry, boolean useDefaultFilters,
                                      Environment environment, @Nullable ResourceLoader resourceLoader) {
    
    Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
    this.registry = registry;
    
    if (useDefaultFilters) {
        registerDefaultFilters();
    }
    setEnvironment(environment);
    setResourceLoader(resourceLoader);
}

终于找到这个 BeanNameGenerator 的类型了:AnnotationBeanNameGenerator

5.2.3.4 【扩展】AnnotationBeanNameGenerator 的Bean名称生成规则

public class AnnotationBeanNameGenerator implements BeanNameGenerator {
    
    private static final String COMPONENT_ANNOTATION_CLASSNAME = "org.springframework.stereotype.Component";
    
    
    @Override
    public String generateBeanName(BeanDefinition definition, BeanDefinitionRegistry registry) {
        if (definition instanceof AnnotatedBeanDefinition) {
            String beanName = determineBeanNameFromAnnotation((AnnotatedBeanDefinition) definition);
            if (StringUtils.hasText(beanName)) {
                // Explicit bean name found.
                return beanName;
            }
        }
        // Fallback: generate a unique default bean name.
        return buildDefaultBeanName(definition, registry);
    }
    
    @Nullable
    protected String determineBeanNameFromAnnotation(AnnotatedBeanDefinition annotatedDef) {
        AnnotationMetadata amd = annotatedDef.getMetadata();
        Set<String> types = amd.getAnnotationTypes();
        String beanName = null;
        for (String type : types) {
            AnnotationAttributes attributes = AnnotationConfigUtils.attributesFor(amd, type);
            if (attributes != null && isStereotypeWithNameValue(type, amd.getMetaAnnotationTypes(type), attributes)) {
                Object value = attributes.get("value");
                if (value instanceof String) {
                    String strVal = (String) value;
                    if (StringUtils.hasLength(strVal)) {
                        if (beanName != null && !strVal.equals(beanName)) {
                            throw new IllegalStateException("Stereotype annotations suggest inconsistent " +
                                                            "component names: '" + beanName + "' versus '" + strVal + "'");
                        }
                        beanName = strVal;
                    }
                }
            }
        }
        return beanName;
    }
    
    protected boolean isStereotypeWithNameValue(String annotationType,
                                                Set<String> metaAnnotationTypes, @Nullable Map<String, Object> attributes) {
        
        boolean isStereotype = annotationType.equals(COMPONENT_ANNOTATION_CLASSNAME) ||
            metaAnnotationTypes.contains(COMPONENT_ANNOTATION_CLASSNAME) ||
            annotationType.equals("javax.annotation.ManagedBean") ||
            annotationType.equals("javax.inject.Named");
        
        return (isStereotype && attributes != null && attributes.containsKey("value"));
    }
    
    protected String buildDefaultBeanName(BeanDefinition definition, BeanDefinitionRegistry registry) {
        return buildDefaultBeanName(definition);
    }
    
    protected String buildDefaultBeanName(BeanDefinition definition) {
        String beanClassName = definition.getBeanClassName();
        Assert.state(beanClassName != null, "No bean class name set");
        String shortClassName = ClassUtils.getShortName(beanClassName);
        return Introspector.decapitalize(shortClassName);
    }
    
}

public static String decapitalize(String name) {
    if (name == null || name.length() == 0) {
        return name;
    }
    if (name.length() > 1 && Character.isUpperCase(name.charAt(1)) &&
        Character.isUpperCase(name.charAt(0))){
        return name;
    }
    char chars[] = name.toCharArray();
    chars[0] = Character.toLowerCase(chars[0]);
    return new String(chars);
    }

从重写的方法开始:

先执行下面的 determineBeanNameFromAnnotation 方法,看这些模式注解上是否有显式的声明 value 属性,如果没有,则进入下面的 buildDefaultBeanName 方法,它会取类名的全称,之后调 Introspector.decapitalize 方法将首字母转为小写。

5.2.4 loadBeanDefinitions:解析配置类中的内容

public void loadBeanDefinitions(Set<ConfigurationClass> configurationModel) {
    TrackedConditionEvaluator trackedConditionEvaluator = new TrackedConditionEvaluator();
    for (ConfigurationClass configClass : configurationModel) {
        loadBeanDefinitionsForConfigurationClass(configClass, trackedConditionEvaluator);
    }
}

这里它会循环所有的配置类,去加载配置类里面的Bean定义信息。继续往下看:

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());
}

由于在之前已经解析过这个 configClass 了,所以在这里可以很容易的解析出这里面的 @Import 、标注了 @Bean 的方法、@ImportResource 等,并进行相应处理。

咱们以 读取 @Bean 注解标注的方法为例,看一眼它对Bean的解析和加载:(方法很长,关键注释已标注在源码中)

private void loadBeanDefinitionsForBeanMethod(BeanMethod beanMethod) {
    ConfigurationClass configClass = beanMethod.getConfigurationClass();
    MethodMetadata metadata = beanMethod.getMetadata();
    String methodName = metadata.getMethodName();
    
    // Do we need to mark the bean as skipped by its condition?
    // 判断该Bean是否要被跳过
    if (this.conditionEvaluator.shouldSkip(metadata, ConfigurationPhase.REGISTER_BEAN)) {
        configClass.skippedBeanMethods.add(methodName);
        return;
    }
    if (configClass.skippedBeanMethods.contains(methodName)) {
        return;
    }
    
    // 校验是否标注了@Bean注解
    AnnotationAttributes bean = AnnotationConfigUtils.attributesFor(metadata, Bean.class);
    Assert.state(bean != null, "No @Bean annotation attributes");
    
    // Consider name and any aliases
    // Bean的名称处理规则:如果Bean中标注了name,取第一个;没有标注,取方法名
    List<String> names = new ArrayList<>(Arrays.asList(bean.getStringArray("name")));
    String beanName = (!names.isEmpty() ? names.remove(0) : methodName);
    
    // Register aliases even when overridden
    // 其余声明的name被视为Bean的别名
    for (String alias : names) {
        this.registry.registerAlias(beanName, alias);
    }
    
    // Has this effectively been overridden before (e.g. via XML)?
    // 注解Bean如果覆盖了xml配置的Bean,要看BeanName是否相同,相同则抛出异常
    if (isOverriddenByExistingDefinition(beanMethod, beanName)) {
        if (beanName.equals(beanMethod.getConfigurationClass().getBeanName())) {
            throw new BeanDefinitionStoreException(beanMethod.getConfigurationClass().getResource().getDescription(),
                                                   beanName, "Bean name derived from @Bean method '" + beanMethod.getMetadata().getMethodName() +
                                                   "' clashes with bean name for containing configuration class; please make those names unique!");
        }
        return;
    }
    
    ConfigurationClassBeanDefinition beanDef = new ConfigurationClassBeanDefinition(configClass, metadata);
    beanDef.setResource(configClass.getResource());
    beanDef.setSource(this.sourceExtractor.extractSource(metadata, configClass.getResource()));
    
    // 被@Bean标注的方法是否为一个静态方法
    if (metadata.isStatic()) {
        // static @Bean method
        beanDef.setBeanClassName(configClass.getMetadata().getClassName());
        beanDef.setFactoryMethodName(methodName);
    }
    else {
        // instance @Bean method
        // 实例Bean,设置它的工厂方法为该方法名。这个工厂方法在后续创建Bean时会利用到
        beanDef.setFactoryBeanName(configClass.getBeanName());
        beanDef.setUniqueFactoryMethodName(methodName);
    }
    beanDef.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_CONSTRUCTOR);
    beanDef.setAttribute(org.springframework.beans.factory.annotation.RequiredAnnotationBeanPostProcessor.
                         SKIP_REQUIRED_CHECK_ATTRIBUTE, Boolean.TRUE);
    
    AnnotationConfigUtils.processCommonDefinitionAnnotations(beanDef, metadata);
    
    Autowire autowire = bean.getEnum("autowire");
    if (autowire.isAutowire()) {
        beanDef.setAutowireMode(autowire.value());
    }
    
    // 是否需要自动注入
    boolean autowireCandidate = bean.getBoolean("autowireCandidate");
    if (!autowireCandidate) {
        beanDef.setAutowireCandidate(false);
    }
    
    // 初始化方法
    String initMethodName = bean.getString("initMethod");
    if (StringUtils.hasText(initMethodName)) {
        beanDef.setInitMethodName(initMethodName);
    }
    
    // 销毁方法
    String destroyMethodName = bean.getString("destroyMethod");
    beanDef.setDestroyMethodName(destroyMethodName);
    
    // Consider scoping
    ScopedProxyMode proxyMode = ScopedProxyMode.NO;
    AnnotationAttributes attributes = AnnotationConfigUtils.attributesFor(metadata, Scope.class);
    if (attributes != null) {
        beanDef.setScope(attributes.getString("value"));
        proxyMode = attributes.getEnum("proxyMode");
        if (proxyMode == ScopedProxyMode.DEFAULT) {
            proxyMode = ScopedProxyMode.NO;
        }
    }
    
    // Replace the original bean definition with the target one, if necessary
    // 如果有必要,将原始bean定义替换为目标bean定义
    BeanDefinition beanDefToRegister = beanDef;
    if (proxyMode != ScopedProxyMode.NO) {
        BeanDefinitionHolder proxyDef = ScopedProxyCreator.createScopedProxy(
            new BeanDefinitionHolder(beanDef, beanName), this.registry,
            proxyMode == ScopedProxyMode.TARGET_CLASS);
        beanDefToRegister = new ConfigurationClassBeanDefinition(
            (RootBeanDefinition) proxyDef.getBeanDefinition(), configClass, metadata);
    }
    
    if (logger.isTraceEnabled()) {
        logger.trace(String.format("Registering bean definition for @Bean method %s.%s()",
                                   configClass.getMetadata().getClassName(), beanName));
    }
    // 注册Bean定义信息
    this.registry.registerBeanDefinition(beanName, beanDefToRegister);
}

5.2.5 加载配置类中的未加载完成的被@Bean标注的组件

// 5.2.4 加载配置类中的被@Bean标注的组件
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;
        }

这部分的判断比较有趣:在上面的配置类都加载完成后,它要比对 BeanDefinition 的个数,以及被处理过的数量。只要数量不对应,就会展开那些配置类继续加载。这部分的源码与上面比较类似,只是检测逻辑的不同,小册不再详细展开,有兴趣的小伙伴可以自行Debug看一下效果。

最后更新于