天天看點

Spring Ioc 源碼分析(一)--Spring Ioc容器的加載

1.目标:熟練使用spring,并分析其源碼,了解其中的思想。這篇主要介紹spring ioc 容器的加載

2.前提條件:會使用debug

3.源碼分析方法:Intellj idea debug 模式下源碼追溯

通過ClassPathXmlApplicationContext 進行xml 件的讀取,從每個堆棧中讀取程式的運作資訊

4.注意:由于Spring的類繼承體系比較複雜,不能全部貼圖,是以隻将分析源碼之後發現的最主要的類繼承結構類圖貼在下方。

5.關于Spring Ioc Demo:我們從demo入手一步步進行代碼追溯。

1.Spring Ioc Demo

1.定義資料通路接口IUserDao.java
public interface IUserDao {  
    public void InsertUser(String username,String password);
}
           

2.定義IUserDao.java實作類IUserDaoImpl.java

public class UserDaoImpl implements IUserDao {    
    @Override    
    public void InsertUser(String username, String password) { 
        System.out.println("----UserDaoImpl --addUser----");    
    }
}
           

3.定義業務邏輯接口UserService.java

public interface UserService {    
    public void addUser(String username,String password);
}
           

4.定義UserService.java實作類UserServiceImpl.java

public class UserServiceImpl implements UserService {    
    private     IUserDao  userDao;    //set方法  
    public void  setUserDao(IUserDao  userDao) {        
        this.userDao = userDao;   
    }    
    @Override    
    public void addUser(String username,String password) { 
        userDao.InsertUser(username,password);    
    }
}
           

bean.xml配置檔案

<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         ">  
 <!--id名字自己取,class表示他代表的類,如果在包裡的話需要加上包名-->    
 <bean id="userService"  class="UserServiceImpl" >      
        <!--property代表是通過set方法注入,ref的值表示注入的内容-->
        <property  name="userDao"  ref="userDao"/>  
 </bean>    
  <bean id="userDao"  class="UserDaoImpl"/>
</beans>
           

2.ApplicationContext 繼承結構:

1.頂層接口:ApplicationContext

2.ClassPathXmlApplicationContext實作類繼承AbstractXmlApplication 抽象類

3.AbstractXmlApplication 繼承AbstractRefreshableConfigApplicationContext

4.AbstractRefreshableConfigApplicationContext抽象類繼承AbstractRefreshableApplicationContext

5.AbstractRefreshableApplicationContext 繼承 AbstractApplicationContext

6.AbstractApplicationContext 實作ConfigurableApplicationContext 接口

7.ConfigurableApplicationContext 接口繼承

ApplicationContext接口

總體來說繼承實作結構較深,内部使用了大量擴充卡模式。

以ClassPathXmlApplicationContext為例,繼承類圖如下圖所示:

Spring Ioc 源碼分析(一)--Spring Ioc容器的加載
Spring ioc .png

3.Spring Ioc容器加載過程源碼詳解

在開始之前,先介紹一個整體的概念。即spring ioc容器的加載,大體上經過以下幾個過程:

資源檔案定位、解析、注冊、執行個體化

1.資源檔案定位

其中資源檔案定位,一般是在ApplicationContext的實作類裡完成的,因為ApplicationContext接口繼承ResourcePatternResolver 接口,ResourcePatternResolver接口繼承ResourceLoader接口,ResourceLoader其中的getResource()方法,可以将外部的資源,讀取為Resource類。

2.解析DefaultBeanDefinitionDocumentReader,

解析主要是在BeanDefinitionReader中完成的,最常用的實作類是XmlBeanDefinitionReader,其中的loadBeanDefinitions()方法,負責讀取Resource,并完成後續的步驟。ApplicationContext完成資源檔案定位之後,是将解析工作委托給XmlBeanDefinitionReader來完成的

解析這裡涉及到很多步驟,最常見的情況,資源檔案來自一個XML配置檔案。首先是BeanDefinitionReader,将XML檔案讀取成w3c的Document文檔。

DefaultBeanDefinitionDocumentReader對Document進行進一步解析。然後DefaultBeanDefinitionDocumentReader又委托給BeanDefinitionParserDelegate進行解析。如果是标準的xml namespace元素,會在Delegate内部完成解析,如果是非标準的xml namespace元素,則會委托合适的NamespaceHandler進行解析最終解析的結果都封裝為BeanDefinitionHolder,至此解析就算完成。

後續會進行細緻講解。

3.注冊

然後bean的注冊是在BeanFactory裡完成的,BeanFactory接口最常見的一個實作類是DefaultListableBeanFactory,它實作了BeanDefinitionRegistry接口,是以其中的registerBeanDefinition()方法,可以對BeanDefinition進行注冊這裡附帶一提,最常見的XmlWebApplicationContext不是自己持有BeanDefinition的,它繼承自AbstractRefreshableApplicationContext,其持有一個DefaultListableBeanFactory的字段,就是用它來儲存BeanDefinition

所謂的注冊,其實就是将BeanDefinition的name和執行個體,儲存到一個Map中。剛才說到,最常用的實作DefaultListableBeanFactory,其中的字段就是beanDefinitionMap,是一個ConcurrentHashMap。

代碼如下:

>1.DefaultListableBeanFactory繼承實作關系

public class DefaultListableBeanFactory
extends 
AbstractAutowireCapableBeanFactory   
implements
ConfigurableListableBeanFactory, 
BeanDefinitionRegistry,
Serializable { 
     // DefaultListableBeanFactory的執行個體中最終儲存了所有注冊的bean    beanDefinitionMap
     /** Map of bean definition objects, keyed by bean name */
     private final Map<String, BeanDefinition> beanDefinitionMap 
     = new ConcurrentHashMap<String, BeanDefinition>(64); 
     //實作BeanDefinitionRegistry中定義的registerBeanDefinition()抽象方法
     public void registerBeanDefinition(String beanName, BeanDefinition    beanDefinition)      throws BeanDefinitionStoreException {
     }
           

>2.BeanDefinitionRegistry接口

public interface BeanDefinitionRegistry extends AliasRegistry {   
    //定義注冊BeanDefinition執行個體的抽象方法
    void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)         throws BeanDefinitionStoreException;
           

4.執行個體化

注冊也完成之後,在BeanFactory的getBean()方法之中,會完成初始化,也就是依賴注入的過程

大體上的流程就是這樣,下一篇部落格,再具體地從代碼層面進行介紹。

部落格搬家: 大坤的個人部落格 歡迎評論哦~