1. 程式人生 > 其它 >Spring-IOC(二)

Spring-IOC(二)

refresh

在AnnotationConfigApplicationContext中說完了無參構造和register兩個方法,接下來看最重要的一個方法:refresh,該方法包括了一個bean從例項化到初始化完成所有的流程,該方法在org.springframework.context.support.AbstractApplicationContext#refresh
public void refresh() throws BeansException, IllegalStateException {
	synchronized (this.startupShutdownMonitor) {
		// Prepare this context for refreshing.
		prepareRefresh();

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

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

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

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

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

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

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

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

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

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

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

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

			// Destroy already created singletons to avoid dangling resources.
			destroyBeans();

			// Reset 'active' flag.
			cancelRefresh(ex);

			// Propagate exception to caller.
			throw ex;
		}

		finally {
			// Reset common introspection caches in Spring's core, since we
			// might not ever need metadata for singleton beans anymore...
			resetCommonCaches();
		}
	}
}

這個方法自己沒做任何邏輯,全都是呼叫別的方法來完成bean的建立,先總體說下流程和大體時序圖(即每個方法在哪個類中實現的),然後再分點說明,這個流程說完整個IOC也就說完了

流程步驟

  1. 準備工作,初始化屬性源等
  2. 獲取beanFactory
  3. 準備工作,其實就是對上一步獲取的beanFactory設定一些屬性等
  4. 後置處理BeanFactory
  5. 呼叫BeanFactoryPostProcessor
  6. 註冊BeanPostProcessor
  7. 初始化message
  8. 初始化應用事件廣播器
  9. 呼叫onRefresh
  10. 註冊監聽器
  11. 初始化單例bean
  12. 完成重新整理

時序圖

準備上下文環境

在這裡主要是對上下文環境的設定,比如記錄啟動時間、設定活動標誌等等

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

這個方法是個空實現,留給子類實現