生活随笔 
收集整理的這篇文章主要介紹了
                                
手写简版spring --5--资源加载器解析文件注册对象 
小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.                        
 
                                
                            一、目標(biāo)  
在完成 Spring 的框架雛形后,現(xiàn)在我們可以通過單元測試進(jìn)行手動操作Bean對象的定義、注冊和屬性填充,以及最終獲取對象調(diào)用方法。但這里會有一個問題,就是如果實(shí)際使用這個 Spring 框架,是不太可能讓用戶通過手動方式創(chuàng)建的,而是最好能通過配置文件的方式簡化創(chuàng)建過程。需要完成如下操作:
 
 如圖中我們需要把步驟:2、3、4 整合到Spring 框架中,通過 Spring 配置文件的方式將 Bean 對象實(shí)例化。 接下來我們就需要在現(xiàn)有的 Spring 框架中,添加能解決 Spring 配置的讀取、解析、注冊Bean 的操作。  
 
二、設(shè)計(jì)  
依照本章節(jié)的需求背景,我們需要在現(xiàn)有的 Spring 框架雛形中添加一個資源解析器,也就是能讀取classpath、本地文件和云文件的配置內(nèi)容。這些配置內(nèi)容就是像使用 Spring 時配置的 Spring.xml 一樣,里面會包括 Bean 對象的描述和屬性信息。 在讀取配置文件信息后,接下來就是對配置文件中的Bean 描述信息解析后進(jìn)行注冊操作,把 Bean 對象注冊到 Spring 容器中。整體設(shè)計(jì)結(jié)構(gòu)如下圖:
 
 資源加載器屬于相對獨(dú)立的部分,它位于 Spring 框架核心包下的IO 實(shí)現(xiàn)內(nèi)容,主要用于處理Class、本地和云環(huán)境中的文件信息。 當(dāng)資源可以加載后,接下來就是解析和注冊 Bean 到 Spring 中的操作,這部分實(shí)現(xiàn)需要和 DefaultListableBeanFactory 核心類結(jié)合起來,因?yàn)槟闼械慕馕龊蟮淖詣幼?#xff0c;都會把 Bean 定義信息放入到這個類中。 那么在實(shí)現(xiàn)的時候就設(shè)計(jì)好接口的實(shí)現(xiàn)層級關(guān)系,包括我們需要定義出 Bean 定義的讀取接口 BeanDefinitionReader 以及做好對應(yīng)的實(shí)現(xiàn)類,在實(shí)現(xiàn)類中完成對 Bean 對象的解析和注冊。 三、實(shí)現(xiàn)  
 工程結(jié)構(gòu)
   類依賴圖
   
該依賴中,不管是文件的來源是什么,加載后都由要經(jīng)過xml解析,才能實(shí)現(xiàn)注入
 
 本章節(jié)為了能把 Bean 的定義、注冊和初始化交給 Spring.xml 配置化處理,那么就需要實(shí)現(xiàn)兩大塊內(nèi)容,分別是:資源加載器、xml 資源處理類,實(shí)現(xiàn)過程主要以對接口 Resource、ResourceLoader的實(shí)現(xiàn),而另外 BeanDefinitionReader 接口則是對資源的具體使用,將配置信息注冊到 Spring 容器中去。 在Resource 的資源加載器的實(shí)現(xiàn)中包括了,ClassPath、系統(tǒng)文件、云配置文件, 這三部分與 Spring源碼中的設(shè)計(jì)和實(shí)現(xiàn)保持一致,最終在 DefaultResourceLoader 中做具體的調(diào)用。 接口:BeanDefinitionReader、抽象類:AbstractBeanDefinitionReader、實(shí)現(xiàn)類:XmlBeanDefinitionReader,這三部分內(nèi)容主要是合理清晰的處理了資源讀取后的注冊 Bean容器操作。接口管定義,抽象類處理非接口功能外的注冊Bean 組件填充,最終實(shí)現(xiàn)類即可只關(guān)心具體的業(yè)務(wù)實(shí)現(xiàn)  
 
另外本章節(jié)還參考 Spring 源碼,做了相應(yīng)接口的集成和實(shí)現(xiàn)的關(guān)系,雖然這些接口目前還并沒有太大的作用,但隨著框架的逐步完善,它們也會發(fā)揮作用
 
 BeanFactory,已經(jīng)存在的 Bean 工廠接口用于獲取 Bean 對象,這次新增加了按照類型獲取 Bean 的方法:<T> T getBean(String name, Class<T> requiredType) ListableBeanFactory,是一個擴(kuò)展 Bean 工廠接口的接口,新增加了getBeansOfType、getBeanDefinitionNames() 方法,在 Spring 源碼中還有其他擴(kuò)展方法。 HierarchicalBeanFactory,在 Spring 源碼中它提供了可以獲取父類 BeanFactory方法,屬于是一種擴(kuò)展工廠的層次子接口。Sub-interface implemented by beanfactories that can be part of a hierarchy. AutowireCapableBeanFactory,是一個自動化處理Bean 工廠配置的接口,目前案例工程中還沒有做相應(yīng)的實(shí)現(xiàn),后續(xù)逐步完善。 ConfigurableBeanFactory,可獲取 BeanPostProcessor、BeanClassLoader 等的一個配置化接口。 ConfigurableListableBeanFactory,提供分析和修改Bean 以及預(yù)先實(shí)例化的操作接口,不過目前只有一個 getBeanDefinition 方法。  
 
四、代碼  
資源加載接口定義和實(shí)現(xiàn)  
public  interface  Resource  { InputStream  getInputStream ( )  throws  IOException ; 
} 
 
分別實(shí)現(xiàn)三種不同的流文件操作:classPath、FileSystem、URL
 
 
public  class  ClassPathResource  implements  Resource  { private  final  String  path
; private  ClassLoader  classLoader
; public  ClassPathResource ( String  path
)  { this ( path
,  ( ClassLoader )  null ) ; } public  ClassPathResource ( String  path
,  ClassLoader  classLoader
)  { Assert . notNull ( path
,  "Path must not be null" ) ; this . path 
=  path
; this . classLoader 
=  ( classLoader 
!=  null  ?  classLoader 
:  ClassUtils . getDefaultClassLoader ( ) ) ; } @Override public  InputStream  getInputStream ( )  throws  IOException  { InputStream  is 
=  classLoader
. getResourceAsStream ( path
) ; if  ( is 
==  null )  { throw  new  FileNotFoundException ( this . path 
+  " cannot be opened because it does not exist" ) ; } return  is
; } 
} 
 
 
public  class  FileSystemResource  implements  Resource  { private  final  File  file
; private  final  String  path
; public  FileSystemResource ( File  file
)  { this . file 
=  file
; this . path 
=  file
. getPath ( ) ; } public  FileSystemResource ( String  path
)  { this . file 
=  new  File ( path
) ; this . path 
=  path
; } @Override public  InputStream  getInputStream ( )  throws  IOException  { return  new  FileInputStream ( this . file
) ; } public  final  String  getPath ( )  { return  this . path
; } 
} 
 
 
public  class  UrlResource  implements  Resource { private  final  URL  url
; public  UrlResource ( URL  url
)  { Assert . notNull ( url
, "URL must not be null" ) ; this . url 
=  url
; } @Override public  InputStream  getInputStream ( )  throws  IOException  { URLConnection  con 
=  this . url
. openConnection ( ) ; try  { return  con
. getInputStream ( ) ; } catch  ( IOException  ex
) { if  ( con 
instanceof  HttpURLConnection ) { ( ( HttpURLConnection )  con
) . disconnect ( ) ; } throw  ex
; } } 
} 
 
包裝資源加載器  
 
public  interface  ResourceLoader  { String  CLASSPATH_URL_PREFIX 
=  "classpath:" ; Resource  getResource ( String  location
) ; 
} 
 
 
public  class  DefaultResourceLoader  implements  ResourceLoader  { @Override public  Resource  getResource ( String  location
)  { Assert . notNull ( location
,  "Location must not be null" ) ; if  ( location
. startsWith ( CLASSPATH_URL_PREFIX
) )  { return  new  ClassPathResource ( location
. substring ( CLASSPATH_URL_PREFIX
. length ( ) ) ) ; } else  { try  { URL  url 
=  new  URL ( location
) ; return  new  UrlResource ( url
) ; }  catch  ( MalformedURLException  e
)  { return  new  FileSystemResource ( location
) ; } } } 
} 
 
 在獲取資源的實(shí)現(xiàn)中,主要是把三種不同類型的資源處理方式進(jìn)行了包裝,分為:判斷是否為ClassPath、URL 以及文件。 雖然 DefaultResourceLoader 類實(shí)現(xiàn)的過程簡單,但這也是設(shè)計(jì)模式約定的具體結(jié)果,像是這里不會讓外部調(diào)用放知道過多的細(xì)節(jié),而是僅關(guān)心具體調(diào)用結(jié)果即可。  
 
 
public  interface  BeanDefinitionReader  { 
BeanDefinitionRegistry  getRegistry ( ) ; 
ResourceLoader  getResourceLoader ( ) ; 
void  loadBeanDefinitions ( Resource  resource
)  throws  BeansException ; 
void  loadBeanDefinitions ( Resource . . .  resources
)  throws  BeansException ; 
void  loadBeanDefinitions ( String  location
)  throws  BeansException ; 
} 
 
 這是一個 Simple interface for bean definition readers. 其實(shí)里面無非定義了幾個方法,包括:getRegistry()、getResourceLoader(),以及三個加載Bean 定義的方法。 這里需要注意 getRegistry()、getResourceLoader(),都是用于提供給后面三個方法的工具,加載和注冊,這兩個方法的實(shí)現(xiàn)會包裝到抽象類中,以免污染具體的接口實(shí)現(xiàn)方法。  
 
 
public  abstract  class  AbstractBeanDefinitionReader  implements  BeanDefinitionReader  { private  final  BeanDefinitionRegistry  registry
; private  ResourceLoader  resourceLoader
; protected  AbstractBeanDefinitionReader ( BeanDefinitionRegistry  registry
)  { this ( registry
,  new  DefaultResourceLoader ( ) ) ; } public  AbstractBeanDefinitionReader ( BeanDefinitionRegistry  registry
,  ResourceLoader  resourceLoader
)     { this . registry 
=  registry
; this . resourceLoader 
=  resourceLoader
; } @Override public  BeanDefinitionRegistry  getRegistry ( )  { return  registry
; } @Override public  ResourceLoader  getResourceLoader ( )  { return  resourceLoader
; } 
} 
 
 抽象類把 BeanDefinitionReader 接口的前兩個方法全部實(shí)現(xiàn)完了,并提供了構(gòu)造函數(shù),讓外部的調(diào)用使用方,把Bean 定義注入類,傳遞進(jìn)來。 這樣在接口 BeanDefinitionReader 的具體實(shí)現(xiàn)類中,就可以把解析后的 XML 文件中的 Bean 信息,注冊到 Spring 容器去了。以前我們是通過單元測試使用,調(diào)用 BeanDefinitionRegistry 完成Bean 的注冊,現(xiàn)在可以放到 XMl 中操作了  
 
 
public  class  XmlBeanDefinitionReader  extends  AbstractBeanDefinitionReader  { public  XmlBeanDefinitionReader ( BeanDefinitionRegistry  registry
)  { super ( registry
) ; } public  XmlBeanDefinitionReader ( BeanDefinitionRegistry  registry
,  ResourceLoader  resourceLoader
)  { super ( registry
,  resourceLoader
) ; } @Override public  void  loadBeanDefinitions ( Resource  resource
)  throws  BeansException  { try  { try  ( InputStream  inputStream 
=  resource
. getInputStream ( ) )  { doLoadBeanDefinitions ( inputStream
) ; } }  catch  ( IOException  |  ClassNotFoundException  e
)  { throw  new  BeansException ( "IOException parsing XML document from "  +  resource
,  e
) ; } } @Override public  void  loadBeanDefinitions ( Resource . . .  resources
)  throws  BeansException  { for  ( Resource  resource 
:  resources
)  { loadBeanDefinitions ( resource
) ; } } @Override public  void  loadBeanDefinitions ( String  location
)  throws  BeansException  { ResourceLoader  resourceLoader 
=  getResourceLoader ( ) ; Resource  resource 
=  resourceLoader
. getResource ( location
) ; loadBeanDefinitions ( resource
) ; } protected  void  doLoadBeanDefinitions ( InputStream  inputStream
)  throws  ClassNotFoundException  { Document  doc 
=  XmlUtil . readXML ( inputStream
) ; Element  root 
=  doc
. getDocumentElement ( ) ; NodeList  childNodes 
=  root
. getChildNodes ( ) ; for  ( int  i 
=  0 ;  i 
<  childNodes
. getLength ( ) ;  i
++ )  { if  ( ! ( childNodes
. item ( i
)  instanceof  Element ) )  continue ; if  ( ! "bean" . equals ( childNodes
. item ( i
) . getNodeName ( ) ) )  continue ; Element  bean 
=  ( Element )  childNodes
. item ( i
) ; String  id 
=  bean
. getAttribute ( "id" ) ; String  name 
=  bean
. getAttribute ( "name" ) ; String  className 
=  bean
. getAttribute ( "class" ) ; Class < ? >  clazz 
=  Class . forName ( className
) ; String  beanName 
=  StrUtil . isNotEmpty ( id
)  ?  id 
:  name
; if  ( StrUtil . isEmpty ( beanName
) )  { beanName 
=  StrUtil . lowerFirst ( clazz
. getSimpleName ( ) ) ; } BeanDefinition  beanDefinition 
=  new  BeanDefinition ( clazz
) ; for  ( int  j 
=  0 ;  j 
<  bean
. getChildNodes ( ) . getLength ( ) ;  j
++ )  { if  ( ! ( bean
. getChildNodes ( ) . item ( j
)  instanceof  Element ) )  continue ; if  ( ! "property" . equals ( bean
. getChildNodes ( ) . item ( j
) . getNodeName ( ) ) )  continue ; Element  property 
=  ( Element )  bean
. getChildNodes ( ) . item ( j
) ; String  attrName 
=  property
. getAttribute ( "name" ) ; String  attrValue 
=  property
. getAttribute ( "value" ) ; String  attrRef 
=  property
. getAttribute ( "ref" ) ; Object  value 
=  StrUtil . isNotEmpty ( attrRef
)  ?  new  BeanReference ( attrRef
)  :  attrValue
; PropertyValue  propertyValue 
=  new  PropertyValue ( attrName
,  value
) ; beanDefinition
. getPropertyValues ( ) . addPropertyValue ( propertyValue
) ; } if  ( getRegistry ( ) . containsBeanDefinition ( beanName
) )  { throw  new  BeansException ( "Duplicate beanName["  +  beanName 
+  "] is not allowed" ) ; } getRegistry ( ) . registerBeanDefinition ( beanName
,  beanDefinition
) ; } } 
} 
 
XmlBeanDefinitionReader 類最核心的內(nèi)容就是對 XML 文件的解析,把我們本來在代碼中的操作放到了通過解析 XML 自動注冊的方式。
 
 loadBeanDefinitions 方法,處理資源加載,這里新增加了一個內(nèi)部方法:doLoadBeanDefinitions,它主要負(fù)責(zé)解析 xml 在 doLoadBeanDefinitions 方法中,主要是對xml 的讀取XmlUtil.readXML(inputStream) 和元素 Element 解析。在解析的過程中通過循環(huán)操作,以此獲取 Bean 配置以及配置中的 id、name、class、value、ref信息。 最終把讀取出來的配置信息,創(chuàng)建成 BeanDefinition 以及 PropertyValue,最終把完整的 Bean 定義內(nèi)容注冊到 Bean 容器:getRegistry().registerBeanDefinition(beanName,beanDefinition)  
 
五、測試  
準(zhǔn)備UserDao,UserService public  class  UserDao  { private  static  Map < String ,  String >  hashMap 
=  new  HashMap < > ( ) ; static  { hashMap
. put ( "10001" ,  "小傅哥" ) ; hashMap
. put ( "10002" ,  "八杯水" ) ; hashMap
. put ( "10003" ,  "阿毛" ) ; } public  String  queryUserName ( String  uId
)  { return  hashMap
. get ( uId
) ; } 
} 
 
public  class  UserService  { private  String  uId
; private  UserDao  userDao
; public  String  queryUserInfo ( )  { return  userDao
. queryUserName ( uId
) ; } public  String  getuId ( )  { return  uId
; } public  void  setuId ( String  uId
)  { this . uId 
=  uId
; } public  UserDao  getUserDao ( )  { return  userDao
; } public  void  setUserDao ( UserDao  userDao
)  { this . userDao 
=  userDao
; } 
} 
 
 
<?xml version="1.0" encoding="UTF-8"?> 
< beans> < beanid = " userDao" class = " cn.bugstack.springframework.test.bean.UserDao" /> < beanid = " userService" class = " cn.bugstack.springframework.test.bean.UserService
" > < propertyname = " uId" value = " 10001" /> < propertyname = " userDao" ref = " userDao" /> </ bean> </ beans> @Before public  void  init ( )  { resourceLoader 
=  new  DefaultResourceLoader ( ) ; } @Test public  void  test_classpath ( )  throws  IOException  { Resource  resource 
=  resourceLoader
. getResource ( "classpath:important.properties" ) ; InputStream  inputStream 
=  resource
. getInputStream ( ) ; String  content 
=  IoUtil . readUtf8 ( inputStream
) ; System . out
. println ( content
) ; } @Test public  void  test_file ( )  throws  IOException  { Resource  resource 
=  resourceLoader
. getResource ( "src/test/resources/important.properties" ) ; InputStream  inputStream 
=  resource
. getInputStream ( ) ; String  content 
=  IoUtil . readUtf8 ( inputStream
) ; System . out
. println ( content
) ; } @Test public  void  test_url ( )  throws  IOException  { Resource  resource 
=  resourceLoader
. getResource ( "https://github.com/fuzhengwei/small-spring/important.properties" ) ; InputStream  inputStream 
=  resource
. getInputStream ( ) ; String  content 
=  IoUtil . readUtf8 ( inputStream
) ; System . out
. println ( content
) ; } @Test public  void  test_xml ( )  { DefaultListableBeanFactory  beanFactory 
=  new  DefaultListableBeanFactory ( ) ; XmlBeanDefinitionReader  reader 
=  new  XmlBeanDefinitionReader ( beanFactory
) ; reader
. loadBeanDefinitions ( "classpath:spring.xml" ) ; UserService  userService 
=  beanFactory
. getBean ( "userService" ,  UserService . class ) ; String  result 
=  userService
. queryUserInfo ( ) ; System . out
. println ( "測試結(jié)果:"  +  result
) ; } 
                            總結(jié) 
                            
                                以上是生活随笔 為你收集整理的手写简版spring --5--资源加载器解析文件注册对象 的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
                            
                            
                                如果覺得生活随笔 網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔 推薦給好友。