springboot原始碼解析-管中窺豹系列之aware(六)

豐極發表於2021-01-14

一、前言

  • Springboot原始碼解析是一件大工程,逐行逐句的去研究程式碼,會很枯燥,也不容易堅持下去。
  • 我們不追求大而全,而是試著每次去研究一個小知識點,最終聚沙成塔,這就是我們的springboot原始碼管中窺豹系列。

 簡介

二、ApplicationContextAware

  • 假設我們想使用某個bean, 如果是在@Component類下面,直接用@Autowired引用就行了
  • 假設我們想在某個靜態方法裡面用,就不能用上面的方法了
  • 你可能想用new Bean()的方式,new一個,但是這個bean裡面的@Autowired引用用不了
  • 如果有一個靜態的全域性ApplicationContext就好了,用spring的能力獲取bean: ApplicationContext.getBean(clazz)
  • ApplicationContextAware就是這個用處
public interface ApplicationContextAware extends Aware {

	void setApplicationContext(ApplicationContext applicationContext) throws BeansException;

}

public interface Aware {

}

我們寫一個實現類:


import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;

@Component
public class SpringContextUtil implements ApplicationContextAware {

    private static ApplicationContext applicationContext;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        SpringContextUtil.applicationContext = applicationContext;
    }

    private static ApplicationContext getApplicationContext() {
        return applicationContext;
    }

    public static <T> T getBean(Class<T> clazz){
        return getApplicationContext().getBean(clazz);
    }

}
  • 通過setApplicationContext,把applicationContext賦值到本地靜態變數
  • 通過ApplicationContext的getBean就可以在靜態方法中使用任何bean的能力了

三、原始碼分析

我們進入SpringApplication的run方法:

public ConfigurableApplicationContext run(String... args) {

    ...

    try {
        
        ...

        refreshContext(context);
        
        ...
    }
    catch (Throwable ex) {
       
       ...

    }

    ...

    return context;
}

我們進入refreshContext(context)內部:

public void refresh() throws BeansException, IllegalStateException {
    synchronized (this.startupShutdownMonitor) {
        // Prepare this context for refreshing.
        prepareRefresh();

        // Tell the subclass to refresh the internal bean factory.
        ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

        // Prepare the bean factory for use in this context.
        prepareBeanFactory(beanFactory);

        try {
            // Allows post-processing of the bean factory in context subclasses.
            postProcessBeanFactory(beanFactory);

            // Invoke factory processors registered as beans in the context.
            invokeBeanFactoryPostProcessors(beanFactory);

            // Register bean processors that intercept bean creation.
            registerBeanPostProcessors(beanFactory);

            // Initialize message source for this context.
            initMessageSource();

            // Initialize event multicaster for this context.
            initApplicationEventMulticaster();

            // Initialize other special beans in specific context subclasses.
            onRefresh();

            // Check for listener beans and register them.
            registerListeners();

            // Instantiate all remaining (non-lazy-init) singletons.
            finishBeanFactoryInitialization(beanFactory);

            // Last step: publish corresponding event.
            finishRefresh();
        }

        catch (BeansException ex) {
            if (logger.isWarnEnabled()) {
                logger.warn("Exception encountered during context initialization - " +
                        "cancelling refresh attempt: " + ex);
            }

            destroyBeans();

            cancelRefresh(ex);

            throw ex;
        }

        finally {
            resetCommonCaches();
        }
    }
}

這個refresh是spring的核心方法,以後會多次用到,內容太多,我們這次只關注一個方法:

  • prepareBeanFactory(beanFactory);
public void refresh() throws BeansException, IllegalStateException {
    synchronized (this.startupShutdownMonitor) {
        ...

        // Prepare the bean factory for use in this context.
        prepareBeanFactory(beanFactory);

        ...
    }
}

我們先看prepareBeanFactory(beanFactory):

protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {

    ...

    // Configure the bean factory with context callbacks.
    beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));

    ...
    
}

我們看一下這個addBeanPostProcessor方法


private final List<BeanPostProcessor> beanPostProcessors = new CopyOnWriteArrayList<>();

@Override
public void addBeanPostProcessor(BeanPostProcessor beanPostProcessor) {
    Assert.notNull(beanPostProcessor, "BeanPostProcessor must not be null");
    // Remove from old position, if any
    this.beanPostProcessors.remove(beanPostProcessor);
    // Track whether it is instantiation/destruction aware
    if (beanPostProcessor instanceof InstantiationAwareBeanPostProcessor) {
        this.hasInstantiationAwareBeanPostProcessors = true;
    }
    if (beanPostProcessor instanceof DestructionAwareBeanPostProcessor) {
        this.hasDestructionAwareBeanPostProcessors = true;
    }
    // Add to end of list
    this.beanPostProcessors.add(beanPostProcessor);
}
  • 先remove,再add
  • beanPostProcessors是一個執行緒安全的list: CopyOnWriteArrayList
  • 我們往下看看new ApplicationContextAwareProcessor(this),注意:this是ApplicationContext
class ApplicationContextAwareProcessor implements BeanPostProcessor {

	private final ConfigurableApplicationContext applicationContext;

	private final StringValueResolver embeddedValueResolver;


	/**
	 * Create a new ApplicationContextAwareProcessor for the given context.
	 */
	public ApplicationContextAwareProcessor(ConfigurableApplicationContext applicationContext) {
		this.applicationContext = applicationContext;
		this.embeddedValueResolver = new EmbeddedValueResolver(applicationContext.getBeanFactory());
	}


	@Override
	@Nullable
	public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
		if (!(bean instanceof EnvironmentAware || bean instanceof EmbeddedValueResolverAware ||
				bean instanceof ResourceLoaderAware || bean instanceof ApplicationEventPublisherAware ||
				bean instanceof MessageSourceAware || bean instanceof ApplicationContextAware)){
			return bean;
		}

		AccessControlContext acc = null;

		if (System.getSecurityManager() != null) {
			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 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);
		}
	}

}
  • 構造方法,把applicationContext設到本地變數上
  • 實現介面的方法:postProcessBeforeInitialization,回撥的時候會用,主要是校驗許可權
  • 最下面的invokeAwareInterfaces是個私有的核心回撥方法,根據不同型別,有不同回撥

我們看到除了ApplicationContextAware,還有其它的aware, 總共6個

  • EnvironmentAware:環境變數
  • EmbeddedValueResolverAware:值解析器
  • ResourceLoaderAware:資源載入器
  • ApplicationEventPublisherAware:事件釋出器
  • MessageSourceAware:資訊處理器
  • ApplicationContextAware:spring容器

比如我們想用全域性的環境變數,就有EnvironmentAware,想用spring的事件就用ApplicationEventPublisherAware,等等

  • 來源找到了,ApplicationContextAwareProcessor什麼時候執行的呢?
  • 這個比較麻煩,我們後面單開一節再詳細的去看。

 豐極

歡迎關注微信公眾號:豐極,更多技術學習分享。

相關文章