IOC的基礎
下面咱們從IOC/AOP開始,它們是Spring平臺實現的核心部分;雖然,咱們一開始大多隻是在這個層面上,作一些配置和外部特性的使用工 做,但對這兩個核心模塊工做原理和運做機制的理解,對深刻理解Spring平臺,倒是相當重要的;由於,它們同時也是Spring其餘模塊實現的基礎。從 Spring要作到的目標,也就是從簡化Java EE開發的出發點來看,簡單的來講,它是經過對POJO開發的支持,來具體實現的;具體的說,Spring經過爲應用開發提供基於POJO的開發模式,把 應用開發和複雜的Java EE服務,實現解耦,並經過提升單元測試的覆蓋率,從而有效的提升整個應用的開發質量。這樣一來,實際上,就須要把爲POJO提供支持的,各類Java EE服務支持抽象到應用平臺中去,去封裝起來;而這種封裝功能的實現,在Spring中,就是由IOC容器以及AOP來具體提供的,這兩個模塊,在很大程 度上,體現了Spring做爲應用開發平臺的核心價值。它們的實現,是Rod.Johnson在他的另外一本著做《Expert One-on-One J2EE Development without EJB》 中,所提到Without EJB設計思想的體現;同時也深入的體現了Spring背後的設計理念。
從更深一點的技術層面上來看,由於Spring是一個基於Java語言的應用平臺,若是咱們可以對Java計算模型,好比像JVM虛擬機實現技術 的基本原理有一些瞭解,會讓咱們對Spring實現的理解,更加的深刻,這些JVM虛擬機的特性使用,包括像反射機制,代理類,字節碼技術等等。它們都是 在Spring實現中,涉及到的一些Java計算環境的底層技術;儘管對應用開發人員來講,可能不會直接去涉及這些JVM虛擬機底層實現的工做,可是瞭解 這些背景知識,或多或少,對咱們瞭解整個Spring平臺的應用背景有很大的幫助;打個比方來講,就像咱們在大學中,學習的那些關於計算機組織和系統方面 的基本知識,好比像數字電路,計算機組成原理,彙編語言,操做系統等等這些基本課程的學習。雖然,坦率的來講,對咱們這些大多數課程的學習者,在之後的工 做中,可能並無太多的機會,直接從事這麼如此底層的技術開發工做;但具有這些知識背景,爲咱們深刻理解基於這些基礎技術構架起來的應用系統,毫無疑問, 是不可缺乏的。隨着JVM虛擬機技術的發展,能夠設想到的是,更多虛擬機級別的基本特性,將會持續的被應用平臺開發者所關注和採用,這也是咱們在學習平臺 實現的過程當中,很是值得注意的一點,由於這些底層技術實現,毫無疑問,會對Spring應用平臺的開發路線,產品策略產生重大的影響。同時,在使用 Spring做爲應用平臺的時候,若是須要更深層次的開發和性能調優,這些底層的知識,也是咱們知識庫中不可缺乏的部分。有了這些底層知識,理解整個系 統,想來就應該障礙不大了。
IOC的一點認識
對Spring IOC的理解離不開對依賴反轉模式的理解,咱們知道,關於如何反轉對依賴的控制,把控制權從具體業務對象手中轉交到平臺或者框架中,是解決面向對象系統設 計複雜性和提升面向對象系統可測試性的一個有效的解決方案。這個問題觸發了IoC設計模式的發展,是IoC容器要解決的核心問題。同時,也是產品化的 IoC容器出現的推進力。而我以爲Spring的IoC容器,就是一個開源的實現依賴反轉模式的產品。
那具體什麼是IoC容器呢?它在Spring框架中到底長什麼樣?說了這麼多,其實對IoC容器的使用者來講,咱們經常接觸到的 BeanFactory和ApplicationContext均可以當作是容器的具體表現形式。這些就是IoC容器,或者說在Spring中提IoC容 器,從實現來講,指的是一個容器系列。這也就是說,咱們一般所說的IoC容器,若是深刻到Spring的實現去看,會發現IoC容器實際上表明着一系列功 能各異的容器產品。只是容器的功能有大有小,有各自的特色。打個比方來講,就像是百貨商店裏出售的商品,咱們舉水桶爲例子,在商店中出售的水桶有大有小; 製做材料也各不相同,有金屬的,有塑料的等等,總之是各式各樣,但只要能裝水,具有水桶的基本特性,那就能夠做爲水桶來出售來讓用戶使用。這在 Spring中也是同樣,它有各式各樣的IoC容器的實現供用戶選擇和使用;使用什麼樣的容器徹底取決於用戶的須要,但在使用以前若是可以瞭解容器的基本 狀況,那會對容器的使用是很是有幫助的;就像咱們在購買商品時進行的對商品的考察和挑選那樣。
咱們從最基本的XmlBeanFactory看起,它是容器系列的最底層實現,這個容器的實現與咱們在Spring應用中用到的那些上下文相比, 有一個很是明顯的特色,它只提供了最基本的IoC容器的功能。從它的名字中能夠看出,這個IoC容器能夠讀取以XML形式定義的 BeanDefinition。理解這一點有助於咱們理解ApplicationContext與基本的BeanFactory之間的區別和聯繫。咱們可 以認爲直接的BeanFactory實現是IoC容器的基本形式,而各類ApplicationContext的實現是IoC容器的高級表現形式。
仔細閱讀XmlBeanFactory的源碼,在一開始的註釋裏面已經對 XmlBeanFactory的功能作了簡要的說明,從代碼的註釋還能夠看到,這是Rod Johnson在2001年就寫下的代碼,可見這個類應該是Spring的元老類了。它是繼承DefaultListableBeanFactory這個 類的,這個DefaultListableBeanFactory就是一個很值得注意的容器!
node
public class XmlBeanFactory extends DefaultListableBeanFactory { 網絡
private final XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(this); 數據結構
public XmlBeanFactory(Resource resource) throws BeansException { 框架
this(resource, null); ide
} 性能
public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException { 單元測試
super(parentBeanFactory); 學習
this.reader.loadBeanDefinitions(resource); 測試
}
}
XmlBeanFactory的功能是創建在DefaultListableBeanFactory這個基本容器的基礎上的,在這個基本容器的基 礎上實現了其餘諸如XML讀取的附加功能。對於這些功能的實現原理,看一看XmlBeanFactory的代碼實現就能很容易地理解。在以下的代碼中能夠 看到,在XmlBeanFactory構造方法中須要獲得Resource對象。對XmlBeanDefinitionReader對象的初始化,以及使 用這個這個對象來完成loadBeanDefinitions的調用,就是這個調用啓動了從Resource中載入BeanDefinitions的過 程,這個loadBeanDefinitions同時也是IoC容器初始化的重要組成部分。
簡單來講,IoC容器的初始化包括BeanDefinition的Resouce定位、載入和註冊這三個基本的過程。我以爲重點是在載入和對 BeanDefinition作解析的這個過程。能夠從DefaultListableBeanFactory來入手看看IoC容器是怎樣完成 BeanDefinition載入的。在refresh調用完成之後,能夠看到loadDefinition的調用:
public abstract class AbstractXmlApplicationContext extends AbstractRefreshableConfigApplicationContext {
public AbstractXmlApplicationContext() {
}
public AbstractXmlApplicationContext(ApplicationContext parent) {
super(parent);
}
//這裏是實現loadBeanDefinitions的地方
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws IOException {
// Create a new XmlBeanDefinitionReader for the given BeanFactory.
// 建立 XmlBeanDefinitionReader,並經過回調設置到 BeanFactory中去,建立BeanFactory的使用的也是 DefaultListableBeanFactory。
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
// Configure the bean definition reader with this context's
// resource loading environment.
// 這裏設置 XmlBeanDefinitionReader, 爲XmlBeanDefinitionReader 配置ResourceLoader,由於DefaultResourceLoader是父類,因此this能夠直接被使用
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.
// 這是啓動Bean定義信息載入的過程
initBeanDefinitionReader(beanDefinitionReader);
loadBeanDefinitions(beanDefinitionReader);
}
protected void initBeanDefinitionReader(XmlBeanDefinitionReader beanDefinitionReader) {
}
這裏使用 XmlBeanDefinitionReader來載入BeanDefinition到容器中,如如下代碼清單所示:
//這裏是調用的入口。
public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
return loadBeanDefinitions(new EncodedResource(resource));
}
//這裏是載入XML形式的BeanDefinition的地方。
public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
Assert.notNull(encodedResource, "EncodedResource must not be null");
if (logger.isInfoEnabled()) {
logger.info("Loading XML bean definitions from " + encodedResource.getResource());
}
Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
if (currentResources == null) {
currentResources = new HashSet<EncodedResource>(4);
this.resourcesCurrentlyBeingLoaded.set(currentResources);
}
if (!currentResources.add(encodedResource)) {
throw new BeanDefinitionStoreException(
"Detected recursive loading of " + encodedResource + " - check your import definitions!");
}
//這裏獲得XML文件,並獲得IO的InputSource準備進行讀取。
try {
InputStream inputStream = encodedResource.getResource().getInputStream();
try {
InputSource inputSource = new InputSource(inputStream);
if (encodedResource.getEncoding() != null) {
inputSource.setEncoding(encodedResource.getEncoding());
}
return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
}
finally {
inputStream.close();
}
}
catch (IOException ex) {
throw new BeanDefinitionStoreException(
"IOException parsing XML document from " + encodedResource.getResource(), ex);
}
finally {
currentResources.remove(encodedResource);
if (currentResources.isEmpty()) {
this.resourcesCurrentlyBeingLoaded.set(null);
}
}
}
//具體的讀取過程能夠在doLoadBeanDefinitions方法中找到:
//這是從特定的XML文件中實際載入BeanDefinition的地方
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
throws BeanDefinitionStoreException {
try {
int validationMode = getValidationModeForResource(resource);
//這裏取得XML文件的Document對象,這個解析過程是由 documentLoader完成的,這個documentLoader是DefaultDocumentLoader,在定義documentLoader的地方建立
Document doc = this.documentLoader.loadDocument(
inputSource, getEntityResolver(), this.errorHandler, validationMode, isNamespaceAware());
//這裏啓動的是對BeanDefinition解析的詳細過程,這個解析會使用到Spring的Bean配置規則,是咱們下面須要詳細關注的地方。
return registerBeanDefinitions(doc, resource);
}
catch (BeanDefinitionStoreException ex) {
throw ex;
}
catch (SAXParseException ex) {
throw new XmlBeanDefinitionStoreException(resource.getDescription(),
"Line " + ex.getLineNumber() + " in XML document from " + resource + " is invalid", ex);
}
catch (SAXException ex) {
throw new XmlBeanDefinitionStoreException(resource.getDescription(),
"XML document from " + resource + " is invalid", ex);
}
catch (ParserConfigurationException ex) {
throw new BeanDefinitionStoreException(resource.getDescription(),
"Parser configuration exception parsing XML from " + resource, ex);
}
catch (IOException ex) {
throw new BeanDefinitionStoreException(resource.getDescription(),
"IOException parsing XML document from " + resource, ex);
}
catch (Throwable ex) {
throw new BeanDefinitionStoreException(resource.getDescription(),
"Unexpected exception parsing XML document from " + resource, ex);
}
}
關於具體的Spring BeanDefinition的解析,是在BeanDefinitionParserDelegate中完成的。這個類裏包含了各類Spring Bean定義規則的處理,感興趣的同窗能夠仔細研究。咱們舉一個例子來分析這個處理過程,好比咱們最熟悉的對Bean元素的處理是怎樣完成的,也就是咱們 在XML定義文件中出現的<bean></bean>這個最多見的元素信息是怎樣被處理的。在這裏,咱們會看到那些熟悉的 BeanDefinition定義的處理,好比id、name、aliase等屬性元素。把這些元素的值從XML文件相應的元素的屬性中讀取出來之後,會 被設置到生成的BeanDefinitionHolder中去。這些屬性的解析仍是比較簡單的。對於其餘元素配置的解析,好比各類Bean的屬性配置,通 過一個較爲複雜的解析過程,這個過程是由parseBeanDefinitionElement來完成的。解析完成之後,會把解析結果放到 BeanDefinition對象中並設置到BeanDefinitionHolder中去,如如下清單所示:
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, BeanDefinition containingBean) {
//這裏取得在<bean>元素中定義的id、name和aliase屬性的值
String id = ele.getAttribute(ID_ATTRIBUTE);
String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);
List<String> aliases = new ArrayList<String>();
if (StringUtils.hasLength(nameAttr)) {
String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, BEAN_NAME_DELIMITERS);
aliases.addAll(Arrays.asList(nameArr));
}
String beanName = id;
if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) {
beanName = aliases.remove(0);
if (logger.isDebugEnabled()) {
logger.debug("No XML 'id' specified - using '" + beanName +
"' as bean name and " + aliases + " as aliases");
}
}
if (containingBean == null) {
checkNameUniqueness(beanName, aliases, ele);
}
//這個方法會引起對bean元素的詳細解析
AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
if (beanDefinition != null) {
if (!StringUtils.hasText(beanName)) {
try {
if (containingBean != null) {
beanName = BeanDefinitionReaderUtils.generateBeanName(
beanDefinition, this.readerContext.getRegistry(), true);
}
else {
beanName = this.readerContext.generateBeanName(beanDefinition);
// Register an alias for the plain bean class name, if still possible,
// if the generator returned the class name plus a suffix.
// This is expected for Spring 1.2/2.0 backwards compatibility.
String beanClassName = beanDefinition.getBeanClassName();
if (beanClassName != null &&
beanName.startsWith(beanClassName) && beanName.length() > beanClassName.length() &&
!this.readerContext.getRegistry().isBeanNameInUse(beanClassName)) {
aliases.add(beanClassName);
}
}
if (logger.isDebugEnabled()) {
logger.debug("Neither XML 'id' nor 'name' specified - " +
"using generated bean name [" + beanName + "]");
}
}
catch (Exception ex) {
error(ex.getMessage(), ele);
return null;
}
}
String[] aliasesArray = StringUtils.toStringArray(aliases);
return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
}
return null;
}
在具體生成BeanDefinition之後。咱們舉一個對property進行解析的例子來完成對整個BeanDefinition載入過程的 分析,仍是在類BeanDefinitionParserDelegate的代碼中,它對BeanDefinition中的定義一層一層地進行解析,好比 從屬性元素集合到具體的每個屬性元素,而後纔是對具體的屬性值的處理。根據解析結果,對這些屬性值的處理會封裝成PropertyValue對象並設置 到BeanDefinition對象中去,如如下代碼清單所示。
/**
* 這裏對指定bean元素的property子元素集合進行解析。
*/
public void parsePropertyElements(Element beanEle, BeanDefinition bd) {
//遍歷全部bean元素下定義的property元素
NodeList nl = beanEle.getChildNodes();
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
if (node instanceof Element && DomUtils.nodeNameEquals(node, PROPERTY_ELEMENT)) {
//在判斷是property元素後對該property元素進行解析的過程
parsePropertyElement((Element) node, bd);
}
}
}
public void parsePropertyElement(Element ele, BeanDefinition bd) {
//這裏取得property的名字
String propertyName = ele.getAttribute(NAME_ATTRIBUTE);
if (!StringUtils.hasLength(propertyName)) {
error("Tag 'property' must have a 'name' attribute", ele);
return;
}
this.parseState.push(new PropertyEntry(propertyName));
try {
//若是同一個bean中已經有同名的存在,則不進行解析,直接返回。也就是說,若是在同一個bean中有同名的property設置,那麼起做用的只是第一個。
if (bd.getPropertyValues().contains(propertyName)) {
error("Multiple 'property' definitions for property '" + propertyName + "'", ele);
return;
}
//這裏是解析property值的地方,返回的對象對應對Bean定義的property屬性設置的解析結果,這個解析結果會封裝到PropertyValue對象中,而後設置到BeanDefinitionHolder中去。
Object val = parsePropertyValue(ele, bd, propertyName);
PropertyValue pv = new PropertyValue(propertyName, val);
parseMetaElements(ele, pv);
pv.setSource(extractSource(ele));
bd.getPropertyValues().addPropertyValue(pv);
}
finally {
this.parseState.pop();
}
}
/**
* 這裏取得property元素的值,也許是一個list或其餘。
*/
public Object parsePropertyValue(Element ele, BeanDefinition bd, String propertyName) {
String elementName = (propertyName != null) ?
"<property> element for property '" + propertyName + "'" :
"<constructor-arg> element";
// Should only have one child element: ref, value, list, etc.
NodeList nl = ele.getChildNodes();
Element subElement = null;
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
if (node instanceof Element && !DomUtils.nodeNameEquals(node, DESCRIPTION_ELEMENT) &&
!DomUtils.nodeNameEquals(node, META_ELEMENT)) {
// Child element is what we're looking for.
if (subElement != null) {
error(elementName + " must not contain more than one sub-element", ele);
}
else {
subElement = (Element) node;
}
}
}
//這裏判斷property的屬性,是ref仍是value,不容許同時是ref和value。
boolean hasRefAttribute = ele.hasAttribute(REF_ATTRIBUTE);
boolean hasValueAttribute = ele.hasAttribute(VALUE_ATTRIBUTE);
if ((hasRefAttribute && hasValueAttribute) ||
((hasRefAttribute || hasValueAttribute) && subElement != null)) {
error(elementName +
" is only allowed to contain either 'ref' attribute OR 'value' attribute OR sub-element", ele);
}
//若是是ref,建立一個ref的數據對象RuntimeBeanReference,這個對象封裝了ref的信息。
if (hasRefAttribute) {
String refName = ele.getAttribute(REF_ATTRIBUTE);
if (!StringUtils.hasText(refName)) {
error(elementName + " contains empty 'ref' attribute", ele);
}
RuntimeBeanReference ref = new RuntimeBeanReference(refName);
ref.setSource(extractSource(ele));
return ref;
} //若是是value,建立一個value的數據對象TypedStringValue ,這個對象封裝了value的信息。
else if (hasValueAttribute) {
TypedStringValue valueHolder = new TypedStringValue(ele.getAttribute(VALUE_ATTRIBUTE));
valueHolder.setSource(extractSource(ele));
return valueHolder;
} //若是還有子元素,觸發對子元素的解析
else if (subElement != null) {
return parsePropertySubElement(subElement, bd);
}
else {
// Neither child element nor "ref" or "value" attribute found.
error(elementName + " must specify a ref or value", ele);
return null;
}
}
好比,再往下看,咱們看到像List這樣的屬性配置是怎樣被解析的,依然在BeanDefinitionParserDelegate中:返回的 是一個List對象,這個List是Spring定義的ManagedList,做爲封裝List這類配置定義的數據封裝,如如下代碼清單所示。
public List parseListElement(Element collectionEle, BeanDefinition bd) {
String defaultElementType = collectionEle.getAttribute(VALUE_TYPE_ATTRIBUTE);
NodeList nl = collectionEle.getChildNodes();
ManagedList<Object> target = new ManagedList<Object>(nl.getLength());
target.setSource(extractSource(collectionEle));
target.setElementTypeName(defaultElementType);
target.setMergeEnabled(parseMergeAttribute(collectionEle));
//具體的List元素的解析過程。
parseCollectionElements(nl, target, bd, defaultElementType);
return target;
}
protected void parseCollectionElements(
NodeList elementNodes, Collection<Object> target, BeanDefinition bd, String defaultElementType) {
//遍歷全部的元素節點,並判斷其類型是否爲Element。
for (int i = 0; i < elementNodes.getLength(); i++) {
Node node = elementNodes.item(i);
if (node instanceof Element && !DomUtils.nodeNameEquals(node, DESCRIPTION_ELEMENT)) {
//加入到target中去,target是一個ManagedList,同時觸發對下一層子元素的解析過程,這是一個遞歸的調用。
target.add(parsePropertySubElement((Element) node, bd, defaultElementType));
}
}
}
通過這樣一層一層的解析,咱們在XML文件中定義的BeanDefinition就被整個給載入到了IoC容器中,並在容器中創建了數據映射。在 IoC容器中創建了對應的數據結構,或者說能夠當作是POJO對象在IoC容器中的映像,這些數據結構能夠以 AbstractBeanDefinition爲入口,讓IoC容器執行索引、查詢和操做。在個人感受中,對核心數據結構的定義和處理應該能夠當作是一個軟件的核心部分了。因此,這裏的BeanDefinition的載入能夠說是IoC容器的核心,若是說IoC容器是Spring的核心,那麼這些BeanDefinition就是Spring的核心的核心了!呵呵,這部分代碼數量不小,但若是掌握這條主線,其餘均可以觸類旁通吧,就像咱們掌握了操做系統啓動的過程,以及在操做系統設計中的核心數據結構 像進程數據結構,文件系統數據結構,網絡協議數據結構的設計和處理同樣,對整個系統的設計原理,包括移植,驅動開發和應用開發,是很是有幫助的!