Spring系列之手寫注解與配置文件的解析


目錄

引入

在前面我們已經完成了IOC,DI,AOP的實現,基本的功能都已經完成了,我們的手寫框架也能勉強使用起來。為了讓我們的框架能夠使用起來比較簡單,這一節我們來實現注解和xml的配置。

tips

本章的xml和注解的功能都是為實現bean的創建,其他如aop等功能可仿造實現。

為什么要加注解和xml配置

如果有同學測試過我們寫好的框架,可能會感受到使用起來非常麻煩,在測試的時候我們需要顯示的來定義bean以及運行過程中需要的其他對象。

    public void test() throws Exception {
        DefaultBeanDefinition bd = new DefaultBeanDefinition();
        bd.setClazz(User.class);
        bd.setSingleton(true);
        bd.setBeanFactoryName("TestFactory");
        bd.setCreateBeanMethodName("createMethod");
        bd.setStaticCreateBeanMethodName("staticCreateMethod");

        factory.register(bd, "user");
        bd = new DefaultBeanDefinition();
        bd.setClazz(BeforeAdvice.class);
        factory.register(bd, "myBeforeAdvice");

        AopProxyCreator aapc = new AopProxyCreator();
        aapc.setBeanFactory(factory);
        factory.registerBeanPostProcessor(aapc);
        // 向AdvisorAutoProxyCreator注冊Advisor
        aapc.register(new RegexMatchAdvisor("myBeforeAdvice", "execution(* bean.User.*())", new RegexExpressionPointCutResolver()));
        User user = (User) factory.doGetBean("user");
        user.sayHello();
    }

如上為測試一個AOP的功能,需要定義很多的對象來完成功能,這還只是一個對象的功能增強,在實際使用中肯定會有大量的實例。這樣在使用起來就變得及其麻煩了。參考Spring中,可以通過xml和annotation的方式來簡化類定義或者其他一些處理。

注解和xml的整個處理過程

在實際實現之前,我們先來宏觀的看一些注解和xml是如何來解析的。
xml

基本上在spring中xml就是這樣工作的,同理,注解也差不多是這樣一個過程。
注解

上面的這一過程實際上就是我們需要實現的功能,現在我們就根據以上過程進行實現吧。

XML

定義XML標記

這一小節的內容並不重要,實際就我們的開發中作用並不大,稍微了解即可。沒興趣的可以直接跳到下一節。

定義xml標記的方式有dtd和xsd兩種,假設我想定義一個下面這樣的xml標記:

<?xml version="1.0"?>
<note>
  <to>Tove</to>
  <from>Jani</from>
  <heading>Reminder</heading>
  <body>Don't forget me this weekend!</body>
</note>

那么我們既可以用dtd實現,也可以用xsd實現:

dtd

<!ELEMENT note (to, from, heading, body)>
<!ELEMENT to (#PCDATA)>
<!ELEMENT from (#PCDATA)>
<!ELEMENT heading (#PCDATA)>
<!ELEMENT body (#PCDATA)>

xsd

<?xml version="1.0"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://www.w3schools.com"
xmlns="http://www.w3schools.com"
elementFormDefault="qualified">

<xs:element name="note">
  <xs:complexType>
    <xs:sequence>
      <xs:element name="to" type="xs:string"/>
      <xs:element name="from" type="xs:string"/>
      <xs:element name="heading" type="xs:string"/>
      <xs:element name="body" type="xs:string"/>
    </xs:sequence>
  </xs:complexType>
</xs:element>

</xs:schema>

這節內容只是稍微提下,有興趣的可以搜一些資料學習。

加載xml文件

要來加載文件那么首先我們需要知道這個文件的位置,很明顯這個位置需要由用戶來指定,由用戶來告訴我們需要加載那些配置文件,那么用戶該如何來指定呢?

這里我們需要定義新的類和接口來完成這件事,這個新定義的類用來完成xml文件的加載,解析以及對bean實例的創建和注冊等。同時該類是給用戶使用的,在bean創建后還需要讓用戶能夠取到容器中的實例,即該類還需要具備beanfactory的部分功能。

ApplicationContext

如何加載配置文件

說到加載,首先我們需要明白這些配置文件可能會以什么樣的形式展現,一般來說有着以下的類型:

  • FileSystem:從本地文件加載
  • URL:從網絡獲取
  • ClassPath:在項目中讀取配置

可能還有其他不同的形式,當然這不重要。我們需要明白的是很明顯不同形式的內容加載的方式肯定是不同的,比如通過文件系統加載的是獲得一個文件,而url形式的是從網絡獲取數據。所以我們需要為每一種方式都提供個性的加載方式。

加載的方式不一樣,那么解析呢?

解析xml,我們都是需要獲取到xml的流信息,都是解析xml的InputStream,所以雖然加載方式各不相同,但是解析的方法是可以通用的。

根據上面的分析,我們可以知道,加載的過程實際上就是獲取一個InputStream的過程,很明顯每一種方式獲取配置文件都需要最終返回一個InputStream,這里我們定義一個獲取InputStream的接口。

InputStream

對於配置文件的加載實際上不管通過哪一種方式,實際上都是對文件進行操作,為了方便能夠對解析提供一致的接口,我們需要對不同方式加載進行抽象,使其能接受任意類型的加載方式對象。這里我們定義Resource接口來表示xml資源,不同的實現類表示不同的加載方式。Resource接口具備一部分文件的特性。

Resource

到了現在我們還有一個很重要的問題,就是我們如何來分辨當前需要加載的配置是屬於哪一種類型呢?只有解決了這一問題才能正確的解析。

這里我們使用前綴匹配的方式來分配不同類型的配置文件加載器,比如:

  • FileSystem:使用file:前綴
  • ClassPath:使用classpath:前綴
  • URL:可以使用url:前綴,或者直接使用instanceof來校驗對象

通過不同的前綴來來返回不同的對象,如果看過前面幾節的同學應該馬上就能反應過來這里肯定要用工廠模式了[笑]。

那么加載配置文件這一行為在什么時候進行呢?

考慮到類ApplicationContext是用戶使用框架的入口,該類還包含獲取bean實例的操作,要保證在該類實例化完成后讓ApplicationContext具備獲取實例的能力,那么加載配置文件的功能就需要在ApplicationContext的構造方法中完成。

結合以上分析,對ApplicationContext類圖做出修改。

ResourceFactory

如何解析配置文件

加載已經完成了,就是說我們現在已經取到不同的Resource,那么現在如何對其進行解析呢?

對於xml類型的文件解析我們這里使用dom4j,這個東西沒必要深入研究,要用到的時候查下api就行了。對於xml的解析就是獲取節點,然后對獲取節點的內容。回顧我們之前在做IOC的時候,創建一個bean首先是需要獲取BeanDefinition。xml這里也一樣,根據解析到的內容構造BeanDefinition,通過BeanDefinitionRegistery注冊。在對bean實例化時取用。所以很明顯我們需要定義一個新的接口用於解析Resource,將解析后的內容封裝為BeanDefinition並注冊。

Reader

好了,到這里關於xml的加載和解析基本就完成了,而對於annotation的解析流程基本也是這樣的,用戶指定需要掃描的包,框架遍歷包所在目錄及子目錄,記錄下相應被注解修飾的類,反射生成class對象,獲取class對象數據生成BeanDefinition。類圖在上面也已經體現出來了。后面就需要我們去寫代碼了。

相關的代碼已經托管到github


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM