前言:Spring是我們日常Java web開發必不可少的架構,有着很優秀的設計思想,在熟練使用它的基礎上,很有必要了解其源碼,Spring主要有七大子產品,分别為(IOC容器)Spring Core,應用上下文(Spring Context),Spring面向切面程式設計(Spring AOP),JDBC和DAO子產品(Spring DAO),對象實體映射(Spring ORM),Web子產品(Spring Web),MVC子產品(Spring Web MVC)。最核心的子產品為IOC和AOP,也是面試常問及的内容,本文将從比較宏觀的角度分析IOC的源碼,不會太深入一些細節,希望能起到抛磚引玉的作用。
IOC也就是控制反轉,讓對象的建立的權力反轉給IOC容器,具體通過Spring配置來建立對象,它的底層原理包括xml配置檔案,dom4j解析,工廠設計模式,反射等等。使用Spring IOC容器,可以通過使用注解或xml配置的方式,Spring容器在啟動的時候,先會從xml配置或者注解中儲存所有注冊進來的Bean定義資訊。很經典的xml方式配置bean方式如下
<?xml version="1.0" encoding="UTF-8" ?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
<bean id="boss" class="com.baobaotao.simple.Boss"></bean>
</beans>
由于現在普遍是Spring Boot項目,xml配置的方式也漸漸被注解方式取代了,注解方式更加友善,避免了xml檔案一些繁瑣的配置,是以分析Spring源碼過程中也是以注解方式,但是原理是一樣的,配置xml隻是多了一層解析xml檔案罷了。
OK,我們先看看bean的建立。這是主類
public class SpringIOCTest {
@SuppressWarnings("resource")
public static void main(String[] args) {
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(IOCConfig.class);
// 擷取Person bean
Person bean = applicationContext.getBean(Person.class);
System.out.println(bean.toString());
}
}
配置類:
@Configuration //告訴Spring這是一個配置類
public class IOCConfig {
//給容器中注冊一個Bean
@Bean
public Person person(){
return new Person("lisi", 20);
}
}
這就配置好一個Person類 bean,是不是很簡單!AnnotationConfigApplicationContext是注解的Spring上下文類,加載配置類,就可以完成配置,運作主類,就可以從Spring上下文中擷取到Person類對象!下面就分析下整個過程的原理。
進入AnnotationConfigApplicationContext構造方法,可以看到有三個方法,this()是調用無參構造函數,初始化讀取Bean定義資訊的reader以及Bean定義包掃描對象,可以掃描一些@Componet,@Service等注解标注的元件,register方法就是注冊配置類,關鍵是下面refresh()函數
@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// Prepare this context for refreshing.
prepareRefresh();
// Tell the subclass to refresh the internal bean factory.
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// Prepare the bean factory for use in this context.
prepareBeanFactory(beanFactory);
try {
// Allows post-processing of the bean factory in context subclasses.
postProcessBeanFactory(beanFactory);
// Invoke factory processors registered as beans in the context.
invokeBeanFactoryPostProcessors(beanFactory);
// Register bean processors that intercept bean creation.
registerBeanPostProcessors(beanFactory);
// Initialize message source for this context.
initMessageSource();
// Initialize event multicaster for this context.
initApplicationEventMulticaster();
// Initialize other special beans in specific context subclasses.
onRefresh();
// Check for listener beans and register them.
registerListeners();
// Instantiate all remaining (non-lazy-init) singletons.
finishBeanFactoryInitialization(beanFactory);
// Last step: publish corresponding event.
finishRefresh();
}
catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
}
// Destroy already created singletons to avoid dangling resources.
destroyBeans();
// Reset 'active' flag.
cancelRefresh(ex);
// Propagate exception to caller.
throw ex;
}
finally {
// Reset common introspection caches in Spring's core, since we
// might not ever need metadata for singleton beans anymore...
resetCommonCaches();
}
}
}
下面逐一分析下refresh函數裡面做的事情
- prepareRefresh()函數是重新整理前的預處理,裡面做的工作主要是初始化一些屬性設定;子類自定義個性化的屬性設定方法;檢驗屬性的合法等等
- obtainFreshBeanFactory()的作用是擷取BeanFactory,Spring建立Bean可以直接Bean相關參數建立,也可以通過BeanFactory的getObject方法擷取Bean
- prepareBeanFactory(beanFactory);這裡是BeanFactory的預準備工作(BeanFactory進行一些設定),如:
1)設定BeanFactory的類加載器、支援表達式解析器
2)添加部分BeanPostProcessor。BeanPostProcessor的作用是很強大的,它能在Bean初始化的前後做一些工作,例如ApplicationContextAwareProcessor的工作就是在其方法postProcessBeforeInitialization()中對繼承自ApplicationContextAware的bean進行處理,調用其setApplicationContext将Spring上下文注入。是以我們可以簡單寫一個實作ApplicationContextAware接口類重寫setApplicationContext方法就可以得到Spring上下文來做一些事情了。
4. postProcessBeanFactory(beanFactory)是BeanFactory準備工作完成之後進行的設定處理工作。子類可以通過重寫這個方法來在BeanFactory建立并預準備完成以後做進一步的設定操作,如下面的MyBeanFactoryPostProcessor
@Component
public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
System.out.println("MyBeanFactoryPostProcessor...postProcessBeanFactory...");
int count = beanFactory.getBeanDefinitionCount();
String[] names = beanFactory.getBeanDefinitionNames();
System.out.println("目前BeanFactory中有"+count+" 個Bean");
System.out.println(Arrays.asList(names));
}
}
5. invokeBeanFactoryPostProcessor(beanFactory)為執行BeanFactoryPostProcessor的方法。BeanFactoryPostProcessor為BeanFactory的後置處理器,是在BeanFactory初始化之後執行的。
6. registerBeanPostProcessors(beanFactory)這步是注冊BeanPostProcessor,注意這是Bean的後置處理器,差別是BeanFactoryPostProcessor。兩者針對的對象不一樣,前者是針對Bean建立對象的方式,後者則是BeanFactory建立Bean的方式(Spring通過BeanFactory的getObject的方法擷取Bean對象).不同接口類型的BeanPostProcessor;在Bean建立前後的執行時機是不一樣的
1)、擷取所有的 BeanPostProcessor;後置處理器都預設可以通過PriorityOrdered、Ordered接口來執行優先級
2)、先注冊PriorityOrdered優先級接口的BeanPostProcessor;把每一個BeanPostProcessor;添加到BeanFactory中
beanFactory.addBeanPostProcessor(postProcessor);
3)、再注冊Ordered接口的
4)、最後注冊沒有實作任何優先級接口的
5)、最終注冊MergedBeanDefinitionPostProcessor;
6)、注冊一個ApplicationListenerDetector;來在Bean建立完成後檢查是否是ApplicationListener,如果是
applicationContext.addApplicationListener((ApplicationListener<?>) bean)
7. initMessageSource()主要是初始化MessageSource元件,來做國際化功能、消息綁定、消息解析
8. onRefresh()方法是空的,設計的目的主要是給子類重寫,在容器重新整理的時候可以自定義邏輯
9. finishBeanFactoryInitialization(beanFactory):初始化所有剩下的單執行個體Bean 。
具體過程如下:
1、beanFactory.preInstantiateSingletons();初始化後剩下的單執行個體bean
1)、擷取容器中的所有Bean,依次進行初始化和建立對象
2)、擷取Bean的定義資訊;RootBeanDefinition
3)、Bean不是抽象的,是單執行個體的,是懶加載;
1)、判斷是否是FactoryBean;是否是實作FactoryBean接口的Bean;
2)、不是工廠Bean。利用getBean(beanName);建立對象
0、getBean(beanName); ioc.getBean();
1、doGetBean(name, null, null, false);
2、先擷取緩存中儲存的單執行個體Bean。如果能擷取到說明這個Bean之前被建立過(所有建立過的單執行個體Bean都會被緩存起來)
從private final Map<String, Object> singletonObjects = new ConcurrentHashMap<String, Object>(256);擷取的
3、緩存中擷取不到,開始Bean的建立對象流程;
4、标記目前bean已經被建立
5、擷取Bean的定義資訊;
6、【擷取目前Bean依賴的其他Bean;如果有按照getBean()把依賴的Bean先建立出來;】
7、啟動單執行個體Bean的建立流程;
1)、createBean(beanName, mbd, args);
2)、Object bean = resolveBeforeInstantiation(beanName, mbdToUse);讓BeanPostProcessor先攔截傳回代理對象;
【InstantiationAwareBeanPostProcessor】:提前執行;
先觸發:postProcessBeforeInstantiation();
如果有傳回值:觸發postProcessAfterInitialization();
3)、如果前面的InstantiationAwareBeanPostProcessor沒有傳回代理對象;調用4)
4)、Object beanInstance = doCreateBean(beanName, mbdToUse, args);建立Bean
1)、【建立Bean執行個體】;createBeanInstance(beanName, mbd, args);
利用工廠方法或者對象的構造器建立出Bean執行個體;
2)、applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
調用MergedBeanDefinitionPostProcessor的postProcessMergedBeanDefinition(mbd, beanType, beanName);
3)、【Bean屬性指派】populateBean(beanName, mbd, instanceWrapper);
指派之前:
1)、拿到InstantiationAwareBeanPostProcessor後置處理器;
postProcessAfterInstantiation();
2)、拿到InstantiationAwareBeanPostProcessor後置處理器;
postProcessPropertyValues();
=====指派之前:===
3)、應用Bean屬性的值;為屬性利用setter方法等進行指派;
applyPropertyValues(beanName, mbd, bw, pvs);
4)、【Bean初始化】initializeBean(beanName, exposedObject, mbd);
1)、【執行Aware接口方法】invokeAwareMethods(beanName, bean);執行xxxAware接口的方法
BeanNameAware\BeanClassLoaderAware\BeanFactoryAware
2)、【執行後置處理器初始化之前】applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
BeanPostProcessor.postProcessBeforeInitialization();
3)、【執行初始化方法】invokeInitMethods(beanName, wrappedBean, mbd);
1)、是否是InitializingBean接口的實作;執行接口規定的初始化;
2)、是否自定義初始化方法;
4)、【執行後置處理器初始化之後】applyBeanPostProcessorsAfterInitialization
BeanPostProcessor.postProcessAfterInitialization();
5)、注冊Bean的銷毀方法;
5)、将建立的Bean添加到緩存中singletonObjects;
ioc容器就是這些Map;很多的Map裡面儲存了單執行個體Bean,環境資訊。。。。;
所有Bean都利用getBean建立完成以後;
檢查所有的Bean是否是SmartInitializingSingleton接口的;如果是;就執行afterSingletonsInstantiated();
10. finishRefresh()這一步就是完成BeanFactory的初始化建立工作,IOC容器到這裡就建立完成了。
總結:
1. Spring容器在啟動的時候,先會儲存所有注冊進來的Bean定義資訊,Bean定義資訊有兩大來源,一個就是xml配置了,就是本文剛開始提到的Bean配置方式,另一種就是注解注冊Bean!就是平常我們開發常用到的@Service、@Componet、@Bean等等注解
2.Spring容器會在合适的時機建立這些Bean,建立好就儲存在容器中,如果是單例的Bean會緩存下來。最後到了統一建立剩下的Bean的時候,就是前面所提到的方法:finishBeanFactoryInitialization()
3.後置處理器:BeanProcessor非常重要和強大,每一個Bean建立完成,都會使用各種後置處理器進行處理,進而達到增強Bean的功能,Spring另一個強大的子產品AOP面向切面程式設計也依靠到了BeanProcessor來實作
4.上面其實還有某一兩個函數沒有提到,比如initApplicationEventMulticaster(),這個函數涉及到Spring事件驅動模型,來進行事件的監聽,派發。