1. 讀源碼的方法
java程序員都知道讀源碼的重要性,尤其是spring的源碼,代碼設計不僅優雅,而且功能越來越強大,幾乎可以與很多開源框架整合,讓應用更易於專注業務領域開發。但是能把spring的源碼吃透,不僅需要花費大量時間與精力,更需要需要掌握一些方法。下面結合自己讀源碼與走過的一些彎路,結合網上知名博客專家的建議,整理出以下要點,與讀者共勉。
1.1 重視官方英文文檔
spring的官方文檔寫的非常全面,基本可以認為是spring源碼思想的一手來源,上面有很多例子不僅幫助讀者如何應用,更能幫助我們了解其背后的思想,官方也用大量描述性的文字進行了相關思想的解讀,讓讀者從一個總覽上看大致了解spring的核心功能與特性。截止到目前,官方的最新版本是5.3.6,地址如下:
https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#spring-core。
1.2 寫示例程序debug源碼
通過簡單的示例程序,找到源碼的入口,通過debug而非僅僅看靜態的源碼,只有看到真實跑起來的程序以及運行時的值時,心里才大致清楚源碼做了哪些事情。
1.3 抓大放小
抓住源碼主要流程,而非陷入細節,如果一開始就摳細節,不僅會打消看源碼的積極性,也理不清主要流程,最后只能半途而廢。只有主流程非常熟悉的情況下,並有時間精力,有興趣可以深究一些自己感興趣的細節。
1.4 寫源碼注釋、畫流程圖
源碼的一些重要方法與主要流程可以通過寫注釋、畫流程圖來加深理解。
1.5 思考背后的設計思想
源碼之所以經典,是因為設計思想優秀,spring的源碼在設計模式的靈活應用、類的抽象與封裝、框架的可擴展性都做到了極致,可以把該思想以及實踐應用到自己的項目設計里面。
1.6 螺旋式學習
任何知識都是循序漸進,源碼學習更是如此,因為源碼很容易讓人半途而廢,只有通過刻意重復來逐步提升,每一次都不求甚解,能搞懂多少就搞懂多少,但是每一次都比上一次的理解提升一點,也可參考優質博客系列對比學習,最終將源碼的精髓吃透。
2. xml文件解析
2.1 流程概覽
上圖描述了xml的解析的主要流程,大致分為三個步驟:
step1: 創建BeanFactory
對象;
step2: 解析xml標簽,默認標簽如bean
、import
,自定義標簽<context:component-scan base-package=xxx>
,把標簽封裝成BeanDefinition
對象;
step3: 最后通過注冊機BeanDefinitionRegistry
注冊到BeanFactory
的實現類DefaultListableBeanFactory
。
2.2 源碼分析
AbstractApplicationContext
類中最重要的方法refresh()
,里面調用很多方法,本文先重點看xml解析相關的方法。
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
......
/*
1、創建BeanFactory對象
* 2、xml解析
* 默認標簽解析:bean、import等
* 自定義標簽解析 如:<context:component-scan base-package="com.xxx"/>
* 自定義標簽解析流程:
* a、根據當前解析標簽的頭信息找到對應的namespaceUri
* b、加載spring所以jar中的spring.handlers文件。並建立映射關系
* c、根據namespaceUri從映射關系中找到對應的實現了NamespaceHandler接口的類
* d、調用類的init方法,init方法是注冊了各種自定義標簽的解析類
* e、根據namespaceUri找到對應的解析類,然后調用paser方法完成標簽解析
*
* 3、把解析出來的xml標簽封裝成BeanDefinition對象
* */
// 告訴子類刷新內部beanFactory.
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
......
}
進入obtainFreshBeanFactory()
方法,到refreshBeanFactory()
,該方法是個抽象方法,由具體子類實現。
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
// 刷新beanFactory
refreshBeanFactory();
// 獲取beanFactory
return getBeanFactory();
}
子類AbstractRefreshableApplicationContext
實現refreshBeanFactory()
方法;
protected final void refreshBeanFactory() throws BeansException {
//如果BeanFactory不為空,則清除BeanFactory和里面的實例
if (hasBeanFactory()) {
destroyBeans();
closeBeanFactory();
}
try {
//創建beanFactory
DefaultListableBeanFactory beanFactory = createBeanFactory();
beanFactory.setSerializationId(getId());
customizeBeanFactory(beanFactory);
//傳入beanFactory,並裝載BeanDefinition對象
loadBeanDefinitions(beanFactory);
this.beanFactory = beanFactory;
}
catch (IOException ex) {
throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
}
}
上面方法主要做了兩件事,一是創建beanFactory
,二是beanFactory
作為參數傳入,並負責裝載BeanDefinition對象。接下來進入loadBeanDefinitions(beanFactory)
方法,該方法是個抽象方法,交給子類AbstractXmlApplicationContext
去實現,子類方法如下。
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
//為給定的BeanFactory創建xml的解析器,這里是一個委托模式
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
beanDefinitionReader.setEnvironment(this.getEnvironment());
//這里傳一個this進去,因為ApplicationContext是實現了ResourceLoader接口的
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);
//傳入解析器,裝載BeanDefinitions
loadBeanDefinitions(beanDefinitionReader);
}
上述方法創建xml解析器XmlBeanDefinitionReader
,並交由解析器完成BeanDefinitions
的裝載。再次進入AbstractXmlApplicationContext
類的loadBeanDefinitions
方法。
protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
Resource[] configResources = getConfigResources();
if (configResources != null) {
reader.loadBeanDefinitions(configResources);
}
//獲取需要加載的xml配置文件
String[] configLocations = getConfigLocations();
if (configLocations != null) {
// xml解析器完成裝載
reader.loadBeanDefinitions(configLocations);
}
}
再次進入xml解析器的loadBeanDefinitions
方法,
public int loadBeanDefinitions(String location, @Nullable Set<Resource> actualResources) throws BeanDefinitionStoreException {
......
//把字符串類型的xml文件路徑,轉換成Resource對象
Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
// 傳入Resource對象裝載BeanDefinitions
int count = loadBeanDefinitions(resources);
......
進入上述的loadBeanDefinitions(resources)
方法;
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
throws BeanDefinitionStoreException {
try {
//把inputSource 封裝成Document文件對象
Document doc = doLoadDocument(inputSource, resource);
//根據document對象,去注冊BeanDefinitions
int count = registerBeanDefinitions(doc, resource);
if (logger.isDebugEnabled()) {
logger.debug("Loaded " + count + " bean definitions from " + resource);
}
上述方法一是封裝Document
文件對象,二是用Document
對象去注冊BeanDefinitions
,隨后進入registerBeanDefinitions
方法;
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
//創建BeanDefinitionDocumentReader
BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
int countBefore = getRegistry().getBeanDefinitionCount();
//並委托BeanDefinitionDocumentReader這個類進行document的解析
documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
return getRegistry().getBeanDefinitionCount() - countBefore;
}
該方法創建BeanDefinitionDocumentReader
對象,並委托其解析document,進入registerBeanDefinitions
方法。
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
if (delegate.isDefaultNamespace(root)) {
NodeList nl = root.getChildNodes();
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
if (node instanceof Element) {
Element ele = (Element) node;
if (delegate.isDefaultNamespace(ele)) {
//默認標簽解析
parseDefaultElement(ele, delegate);
}
else {
//自定義標簽解析
delegate.parseCustomElement(ele);
}
}
}
}
else {
delegate.parseCustomElement(root);
}
}
上述方法主要完成默認標簽解析,與自定義標簽解析,默認標簽如同import
、alias
、bean
、beans
,自定義標簽比如<aop:aspectj-autoproxy />
,默認標簽重點分析bean
標簽的解析;
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
//bean簽解析
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
if (bdHolder != null) {
bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
try {
//完成document到BeanDefinition對象轉換后,對BeanDefinition對象進行緩存注冊
BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
......
}
}
上述方法主要是bean
標簽的解析,最后對BeanDefinition
對象進行緩存注冊,先分析解析;
public AbstractBeanDefinition parseBeanDefinitionElement(
Element ele, String beanName, @Nullable BeanDefinition containingBean) {
......
//創建BeanDefinition
AbstractBeanDefinition bd = createBeanDefinition(className, parent);
//解析BeanDefinition里面的屬性
parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));
//解析meta元素
parseMetaElements(ele, bd);
//解析Lookup方法
parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
//解析ReplacedMethod方法
parseReplacedMethodSubElements(ele, bd.getMethodOverrides());
//解析構造參數方法
parseConstructorArgElements(ele, bd);
//解析屬性元素方法
parsePropertyElements(ele, bd);
//解析Qualifier元素方法
parseQualifierElements(ele, bd);
bd.setResource(this.readerContext.getResource());
bd.setSource(extractSource(ele));
// 最后返回beanDefinition
return bd;
再分析注冊BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry())
,進入該方法,最后會進入DefaultListableBeanFactory
類,
@Override
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
throws BeanDefinitionStoreException {
Assert.hasText(beanName, "Bean name must not be empty");
Assert.notNull(beanDefinition, "BeanDefinition must not be null");
if (beanDefinition instanceof AbstractBeanDefinition) {
try {
((AbstractBeanDefinition) beanDefinition).validate();
}
catch (BeanDefinitionValidationException ex) {
throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
"Validation of bean definition failed", ex);
}
}
//先判斷BeanDefinition是否已經注冊
BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName);
if (existingDefinition != null) {
if (!isAllowBeanDefinitionOverriding()) {
throw new BeanDefinitionOverrideException(beanName, beanDefinition, existingDefinition);
}
else if (existingDefinition.getRole() < beanDefinition.getRole()) {
// e.g. was ROLE_APPLICATION, now overriding with ROLE_SUPPORT or ROLE_INFRASTRUCTURE
if (logger.isInfoEnabled()) {
logger.info("Overriding user-defined bean definition for bean '" + beanName +
"' with a framework-generated bean definition: replacing [" +
existingDefinition + "] with [" + beanDefinition + "]");
}
}
else if (!beanDefinition.equals(existingDefinition)) {
if (logger.isDebugEnabled()) {
logger.debug("Overriding bean definition for bean '" + beanName +
"' with a different definition: replacing [" + existingDefinition +
"] with [" + beanDefinition + "]");
}
}
else {
if (logger.isTraceEnabled()) {
logger.trace("Overriding bean definition for bean '" + beanName +
"' with an equivalent definition: replacing [" + existingDefinition +
"] with [" + beanDefinition + "]");
}
}
this.beanDefinitionMap.put(beanName, beanDefinition);
}
else {
if (hasBeanCreationStarted()) {
// Cannot modify startup-time collection elements anymore (for stable iteration)
synchronized (this.beanDefinitionMap) {
this.beanDefinitionMap.put(beanName, beanDefinition);
List<String> updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1);
updatedDefinitions.addAll(this.beanDefinitionNames);
updatedDefinitions.add(beanName);
this.beanDefinitionNames = updatedDefinitions;
removeManualSingletonName(beanName);
}
}
else {
//把beanDefinition緩存到map中
this.beanDefinitionMap.put(beanName, beanDefinition);
//把beanName放到beanDefinitionNames list中
this.beanDefinitionNames.add(beanName);
removeManualSingletonName(beanName);
}
this.frozenBeanDefinitionNames = null;
}
最終看出所謂的注冊到BeanFactory
的容器中的類,無非就是一個定義了ConcurrentHashMap
類型的beanDefinitionMap
。
自定義標簽解析BeanDefinitionParserDelegate
類,執行parseCustomElement
方法;
public BeanDefinition parseCustomElement(Element ele, @Nullable BeanDefinition containingBd) {
// 獲取namespaceURI
String namespaceUri = getNamespaceURI(ele);
if (namespaceUri == null) {
return null;
}
// 解析namespaceURI對應的handler類
NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
if (handler == null) {
error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele);
return null;
}
// 執行handler的解析方法
return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));
}
上述過程主要完成以下步驟:
step1
:獲取namespaceURI;
step2
:解析namespaceURI對應的handler類;
step3
:執行handler方法解析。
其中step2又分為幾個步驟,代碼進入如下方法
public NamespaceHandler resolve(String namespaceUri) {
// 從spring.handler里面獲取映射關系
Map<String, Object> handlerMappings = getHandlerMappings();
// 根據namespaceURI從映射關系map中獲取對應的處理類handler
Object handlerOrClassName = handlerMappings.get(namespaceUri);
if (handlerOrClassName == null) {
return null;
}
else if (handlerOrClassName instanceof NamespaceHandler) {
return (NamespaceHandler) handlerOrClassName;
}
else {
// 通過反射實例化namespaceHandler類
String className = (String) handlerOrClassName;
try {
Class<?> handlerClass = ClassUtils.forName(className, this.classLoader);
if (!NamespaceHandler.class.isAssignableFrom(handlerClass)) {
throw new FatalBeanException("Class [" + className + "] for namespace [" + namespaceUri +
"] does not implement the [" + NamespaceHandler.class.getName() + "] interface");
}
// 實例化namespaceHandler對象
NamespaceHandler namespaceHandler = (NamespaceHandler) BeanUtils.instantiateClass(handlerClass);
// 執行init方法
namespaceHandler.init();
// 把新的namespaceUri與namespaceHandler映射關系組裝到map中
handlerMappings.put(namespaceUri, namespaceHandler);
return namespaceHandler;
}
上述解析namespaceURI對應的handler類,對應步驟又課分為如下幾步,
step1
:從spring.handler里面獲取映射關系;
step2
:根據namespaceURI從映射關系map中獲取對應的處理類handler;
step3
:通過反射獲取handler對象,並執行init方法,完成自定義標簽注冊;
3. 總結
本文主要分析了xml標簽的解析,主要步驟與流程圖上述代碼分析與時序圖,通過調試可以清晰觀察到解析過程,后續將通過示例分析beanDefinition
類的相關屬性。