Spring-IOC(二)
refresh
在AnnotationConfigApplicationContext中說完了無參構造和register兩個方法,接下來看最重要的一個方法:refresh,該方法包括了一個bean從例項化到初始化完成所有的流程,該方法在org.springframework.context.support.AbstractApplicationContext#refreshpublic void refresh() throws BeansException, IllegalStateException { synchronized (this.startupShutdownMonitor) { // Prepare this context for refreshing. prepareRefresh(); // Tell the subclass to refresh the internal bean factory. ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); // Prepare the bean factory for use in this context. prepareBeanFactory(beanFactory); try { // Allows post-processing of the bean factory in context subclasses. postProcessBeanFactory(beanFactory); // Invoke factory processors registered as beans in the context. invokeBeanFactoryPostProcessors(beanFactory); // Register bean processors that intercept bean creation. registerBeanPostProcessors(beanFactory); // Initialize message source for this context. initMessageSource(); // Initialize event multicaster for this context. initApplicationEventMulticaster(); // Initialize other special beans in specific context subclasses. onRefresh(); // Check for listener beans and register them. registerListeners(); // Instantiate all remaining (non-lazy-init) singletons. finishBeanFactoryInitialization(beanFactory); // Last step: publish corresponding event. finishRefresh(); } catch (BeansException ex) { if (logger.isWarnEnabled()) { logger.warn("Exception encountered during context initialization - " + "cancelling refresh attempt: " + ex); } // Destroy already created singletons to avoid dangling resources. destroyBeans(); // Reset 'active' flag. cancelRefresh(ex); // Propagate exception to caller. throw ex; } finally { // Reset common introspection caches in Spring's core, since we // might not ever need metadata for singleton beans anymore... resetCommonCaches(); } } }
這個方法自己沒做任何邏輯,全都是呼叫別的方法來完成bean的建立,先總體說下流程和大體時序圖(即每個方法在哪個類中實現的),然後再分點說明,這個流程說完整個IOC也就說完了
流程步驟
- 準備工作,初始化屬性源等
- 獲取beanFactory
- 準備工作,其實就是對上一步獲取的beanFactory設定一些屬性等
- 後置處理BeanFactory
- 呼叫BeanFactoryPostProcessor
- 註冊BeanPostProcessor
- 初始化message
- 初始化應用事件廣播器
- 呼叫onRefresh
- 註冊監聽器
- 初始化單例bean
- 完成重新整理
時序圖
準備上下文環境
在這裡主要是對上下文環境的設定,比如記錄啟動時間、設定活動標誌等等
protected void prepareRefresh() { // Switch to active. this.startupDate = System.currentTimeMillis(); // 設定關閉標誌 this.closed.set(false); // 設定活動標誌 this.active.set(true); if (logger.isDebugEnabled()) { if (logger.isTraceEnabled()) { logger.trace("Refreshing " + this); } else { logger.debug("Refreshing " + getDisplayName()); } } // Initialize any placeholder property sources in the context environment. // 初始化資源,這是一個空方法,留給子類實現 initPropertySources(); // Validate that all properties marked as required are resolvable: // see ConfigurablePropertyResolver#setRequiredProperties // 獲取環境變數並校驗所需的屬性 getEnvironment().validateRequiredProperties(); // Store pre-refresh ApplicationListeners... // 判斷早期事件監聽是否為空,如果為空建立一個集合用來存放 if (this.earlyApplicationListeners == null) { this.earlyApplicationListeners = new LinkedHashSet<>(this.applicationListeners); } else { // Reset local application listeners to pre-refresh state. this.applicationListeners.clear(); this.applicationListeners.addAll(this.earlyApplicationListeners); } // Allow for the collection of early ApplicationEvents, // to be published once the multicaster is available... // 存放早期應用事件 this.earlyApplicationEvents = new LinkedHashSet<>(); }
獲取beanFactory
呼叫obtainFreshBeanFactory方法獲取beanFactory其實就是獲取DefaultListableBeanFactory,在上一篇文章中說到DefaultListableBeanFactory這個類的建立是在GenericApplicationContext的無參構造方法中new出來的,所以這裡獲取的beanFactory就是從GenericApplicationContext中獲取的
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
/**
* 1.如果程式是以xml形式啟動的(ClassPathXmlApplicationContext方式),那麼這一步會進入{@link AbstractRefreshableApplicationContext}
* xml 方式為什麼是這個類:因為 ClassPathXmlApplicationContext 這個類繼承了 AbstractRefreshableApplicationContext
*
* 2.如果程式是以註解形式啟動的(AnnotationConfigApplicationContext方式),那麼這一步會進入{@link GenericApplicationContext}
*/
refreshBeanFactory();
return getBeanFactory();
}
這是一個標準的模板方法,refreshBeanFactory和getBeanFactory都是抽象方法,具體實現由子類實現。我們都知道spring的配置有兩種,一種是xml配置,一種是註解配置,兩種配置所對應的處理邏輯是不同的,所以這裡獲取beanFactory的方式也不同,如果是xml的話,這裡獲取beanFactory是在AbstractRefreshableApplicationContext中,如果是註解的話獲取beanFactory是在GenericApplicationContext中。顯然這裡是通過GenericApplicationContext獲取beanFactory。
@Override
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());
}
@Override
public final ConfigurableListableBeanFactory getBeanFactory() {
return this.beanFactory;
}
準備beanFactory
這個方法主要是對beanFactory做一些屬性上的配置,比如設定幾個忽略自動裝配的介面、新增後置處理器等等
protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {
// Tell the internal bean factory to use the context's class loader etc.
// 設定beanFactory的classLoader為當前context的classloader
beanFactory.setBeanClassLoader(getClassLoader());
// 設定beanFactory的表示式語言處理器
beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader()));
beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment()));
// Configure the bean factory with context callbacks.
// 新增這個BeanPostProcessor 的實現類是為了解析程式設計師寫的類實現 ApplicationContextAware 介面
beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));
// 設定幾個忽略自動裝配的介面,這幾個介面都在ApplicationContextAwareProcessor中操作,所以這裡需要忽略
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.
// 設定幾個自動裝配的特殊規則,如果注入的型別是前面介面型別,那麼在注入的時候會自動注入後面的實現,這個稍後會做個試驗
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.
// 新增一個BeanPostProcessor:ApplicationListenerDetector,這個processor主要是處理spring事件監聽器的
beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(this));
// Detect a LoadTimeWeaver and prepare for weaving, if found.
// 增加對AspectJ的支援
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.
// 註冊預設環境變數 bean
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());
}
}
首先看下如果注入的是BeanFactory、ResourceLoader、ApplicationEventPublisher、ApplicationContext四種類型的話,因為這四個介面的實現太多,如果注入的話不知道到底應該是哪個具體的實現類,所以spring這裡就直接替我們選擇好了。如果是BeanFactory的話,那麼注入的實際型別是DefaultListableBeanFactory,如果注入的是ResourceLoader,那麼注入的實際型別是AnnotationConfigApplicationContext,接下來用一段程式碼來證實這個結論:
@Component
public class SpringAutoRegister {
@Autowired
private BeanFactory beanFactory;
@Autowired
private ResourceLoader resourceLoader;
@Autowired
private ApplicationEventPublisher applicationEventPublisher;
@Autowired
private ApplicationContext applicationContext;
@Override
public String toString() {
return "SpringAutoRegister{" +
"beanFactory=" + beanFactory +
", resourceLoader=" + resourceLoader +
", applicationEventPublisher=" + applicationEventPublisher +
", applicationContext=" + applicationContext +
'}';
}
}
public class Main {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
SpringAutoRegister aspectService = context.getBean("springAutoRegister", SpringAutoRegister.class);
System.out.println(aspectService);
}
}
執行結果如下:
SpringAutoRegister{beanFactory=org.springframework.beans.factory.support.DefaultListableBeanFactory@45283ce2: defining beans [org.springframework.context.annotation.internalConfigurationAnnotationProcessor,org.springframework.context.annotation.internalAutowiredAnnotationProcessor,org.springframework.context.annotation.internalCommonAnnotationProcessor,org.springframework.context.event.internalEventListenerProcessor,org.springframework.context.event.internalEventListenerFactory,appConfig,conServiceA,conServiceB,serviceImpl,springAutoRegister,org.springframework.aop.config.internalAutoProxyCreator]; root of factory hierarchy, resourceLoader=org.springframework.context.annotation.AnnotationConfigApplicationContext@30f39991, started on Wed Jul 21 17:59:32 CST 2021, applicationEventPublisher=org.springframework.context.annotation.AnnotationConfigApplicationContext@30f39991, started on Wed Jul 21 17:59:32 CST 2021, applicationContext=org.springframework.context.annotation.AnnotationConfigApplicationContext@30f39991, started on Wed Jul 21 17:59:32 CST 2021}
看執行結果是符合我們的預期,那麼我們在工作中也會遇到一個介面有多個實現類,那麼在注入的時候應該注入哪種實際型別呢?
接下來再看一個例子:
public interface MultiService {
}
@Service
public class ServiceImpl {
@Autowired
private MultiService multiService;
@Override
public String toString() {
return "ServiceImpl{" +
"service=" + multiService +
'}';
}
@Component("conServiceA")
public static class ConServiceA implements MultiService{}
@Component("conServiceB")
public static class ConServiceB implements MultiService{}
}
public class RunTest {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
System.out.println(context.getBean("serviceImpl"));
}
}
上面這段程式中MultiService介面有兩個實現類ConServiceA和ConServiceB,在ServiceImpl類中注入了MultiService,那麼應該是注入的哪個?這段程式的執行結果是什麼?
執行結果如下:
expected single matching bean but found 2: conServiceA,conServiceB
很顯然spring無法識別我們需要注入哪個,直接給我們報錯了,那又如何解決呢?
改造一
可以引數spring的方式使用registerResolvableDependency方法來指定使用哪個實際型別,做法如下(先不用管為何實現BeanFactoryPostProcessor,後續會著重介紹這個):
@Component
public class MultiBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
ServiceImpl.ConServiceA conServiceA = new ServiceImpl.ConServiceA();
beanFactory.registerResolvableDependency(MultiService.class,conServiceA);
}
}
增加這麼一個類,然後再執行剛才的程式,結果如下:
ServiceImpl{service=com.xxx.spring.multiimpl.ServiceImpl$ConServiceA@4563e9ab}
程式正常運行了,這裡是引數spring的方法來做的,但在實際工作中畢竟這種方式不多,所以可以使用第二種方式:
改造二
將Autowired註解改為Resource註解並使用name指明使用哪個
@Service
public class ServiceImpl {
@Resource(name = "conServiceA")
private MultiService multiService;
@Override
public String toString() {
return "ServiceImpl{" +
"service=" + multiService +
'}';
}
@Component("conServiceA")
public static class ConServiceA implements MultiService{}
@Component("conServiceB")
public static class ConServiceB implements MultiService{}
}
這樣執行的結果如下:
ServiceImpl{service=com.xxx.spring.multiimpl.ServiceImpl$ConServiceA@511baa65}
程式也正常執行起來了,這兩種方式比較顯然第二種方式更符合我們業務開發的思維,但這也從側面說明了spring的可擴充套件性的強大。
這僅僅是一點,到後續的例項化、屬性注入和初始化階段時更能體現spring擴充套件性的強大。
postProcessBeanFactory
這個方法是個空實現,留給子類實現