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 .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()方法之中,會完成初始化,也就是依賴注入的過程
大體上的流程就是這樣,下一篇部落格,再具體地從代碼層面進行介紹。
部落格搬家: 大坤的個人部落格 歡迎評論哦~