走自己的路

          路漫漫其修遠(yuǎn)兮,吾將上下而求索

            BlogJava :: 首頁(yè) :: 新隨筆 :: 聯(lián)系 :: 聚合  :: 管理 ::
            50 隨筆 :: 4 文章 :: 118 評(píng)論 :: 0 Trackbacks

           

          最近在完成一個(gè)小小的framework項(xiàng)目,由于項(xiàng)目中不使用spring,guice,自己實(shí)現(xiàn)了一個(gè)簡(jiǎn)易的依賴注入框架。

           

          1. 寫(xiě)一個(gè)xml文件作為配置的實(shí)際例子

           

          <?xml version="1.0" encoding="UTF-8"?>
          <beans xmlns="http://www.ldd600.com/beanIoc" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.ldd600.com/beanIoc beanIoc.xsd">
              
          <bean id="ftpSend" class="com.ldd600.bean.ioc.beans.impl.FTPTransporter">
                  
          <params>
                      
          <param value="/ldd600" key="uploadFolder"></param>
                      
          <param value="com.ldd600.bean.ioc.beans.impl.JugUUIDGenerator" key="generator" converter="com.ldd600.bean.ioc.converters.UUIDGeneratorConverter"></param>
                  
          </params>
              
          </bean>
                  
          <bean id="jmsSend" class="com.ldd600.bean.ioc.beans.impl.JMSTransporter">
                  
          <params>
                      
          <param value="Ldd600Queue" key="destName"></param>
                  
          </params>
              
          </bean>
          </beans>


          2.根據(jù)這個(gè)xml文件,定義一個(gè)schema文件,驗(yàn)證配置文件
          <?xml version="1.0" encoding="UTF-8"?>
          <xsd:schema xmlns:sample="http://www.ldd600.com/beanIoc" xmlns:xsd="http://www.w3.org/2001/XMLSchema" targetNamespace="http://www.ldd600.com/beanIoc" elementFormDefault="qualified" attributeFormDefault="unqualified">
              
          <xsd:complexType name="paramsType">
                  
          <xsd:annotation>
                      
          <xsd:documentation> of java class</xsd:documentation>
                  
          </xsd:annotation>
                  
          <xsd:sequence maxOccurs="unbounded">
                      
          <xsd:element name="param">
                          
          <xsd:complexType>
                              
          <xsd:annotation>
                                  
          <xsd:documentation>key is property name, value is property value</xsd:documentation>
                              
          </xsd:annotation>
                              
          <xsd:simpleContent>
                                  
          <xsd:extension base="sample:leafType">
                                      
          <xsd:attribute name="key" type="sample:notEmptyToken" use="required"/>
                                      
          <xsd:attribute name="value" type="sample:notEmptyToken" use="required"/>
                                      
          <xsd:attribute name="converter" type="sample:classAttributeType" use="optional"/>
                                  
          </xsd:extension>
                              
          </xsd:simpleContent>
                          
          </xsd:complexType>
                      
          </xsd:element>
                  
          </xsd:sequence>
              
          </xsd:complexType>
                  
          <xsd:complexType name="idClassType">
                  
          <xsd:all minOccurs="0">
                      
          <xsd:element name="params" type="sample:paramsType">
                          
          <xsd:key name="outerParamsKey">
                              
          <xsd:selector xpath="sample:param"/>
                              
          <xsd:field xpath="@key"/>
                          
          </xsd:key>
                      
          </xsd:element>
                  
          </xsd:all>
                  
          <xsd:attribute name="id" type="sample:notEmptyToken" use="required"/>
                  
          <xsd:attribute name="class" type="sample:classAttributeType">
                      
          <xsd:annotation>
                          
          <xsd:documentation>class name</xsd:documentation>
                      
          </xsd:annotation>
                  
          </xsd:attribute>
              
          </xsd:complexType>
              
          <xsd:simpleType name="leafType">
                  
          <xsd:restriction base="xsd:string">
                      
          <xsd:pattern value="(\s)*"/>
                  
          </xsd:restriction>
              
          </xsd:simpleType>
              
          <xsd:simpleType name="classAttributeType">
                  
          <xsd:restriction base="sample:notEmptyToken">
                      
          <xsd:pattern value="([a-z|0-9|_]+\.)*[A-Z]([A-Z|a-z|0-9|_])*"/>
                  
          </xsd:restriction>
              
          </xsd:simpleType>
              
          <xsd:simpleType name="notEmptyToken">
                  
          <xsd:restriction base="xsd:token">
                      
          <xsd:pattern value="(\S+\s*)+"/>
                  
          </xsd:restriction>
              
          </xsd:simpleType>
              
          <xsd:element name="beans">
                  
          <xsd:complexType>
                      
          <xsd:sequence maxOccurs="unbounded">
                          
          <xsd:element name="bean" type="sample:idClassType"/>
                      
          </xsd:sequence>
                  
          </xsd:complexType>
                  
          <xsd:key name="beanIdkey">
                      
          <xsd:selector xpath="./sample:bean"></xsd:selector>
                      
          <xsd:field xpath="@id"></xsd:field>
                  
          </xsd:key>
              
          </xsd:element>
          </xsd:schema>


           

          類(lèi)型

          解釋

          paramType

          表示類(lèi)的字段,key是字段名,value字段值,converter如果有用來(lái)將String轉(zhuǎn)換為所需要的實(shí)例。

          idClassType

          用來(lái)表示bean實(shí)例,并且該實(shí)例有唯一的id

          notEmptyToken

          非空token

          leafType

          葉子類(lèi)型比如<a> </a>,中間允許有space,比如空格、換行符等,但是不能出現(xiàn)其他任何字符。

          classAttributeType

          負(fù)責(zé)javaclass全名的string類(lèi)型,比如com.ldd600.BeanClass



           

           

          1. 解析這個(gè)文件,并marshalljava對(duì)象,用xmlbeans實(shí)現(xiàn)

          使用xmlbeans生成schema對(duì)應(yīng)的java類(lèi)和xmlbeans jaxb需要的元數(shù)據(jù)。

          ·         創(chuàng)建xsdConfig文件,內(nèi)容主要是生成的java類(lèi)的package名,類(lèi)simple name的前綴和后綴

          <xb:config
           
          xmlns:xb=
           "http://xml.apache.org/xmlbeans/2004/02/xbean/config"
          >
           
          <xb:namespace uri="http://www.ldd600.com/beanIoc">
            
          <xb:package>com.ldd600.bean.ioc.config.jaxb</xb:package>
          </xb:namespace>
            
          <xb:namespace uriprefix="http://www.ldd600.com/beanIoc">
              
          <xb:prefix>Xml</xb:prefix>
            
          </xb:namespace>
            
          <xb:namespace uriprefix="http://www.ldd600.com/beanIoc">
              
          <xb:suffix>Bean</xb:suffix>
              
          </xb:namespace>
          </xb:config>

           

          ·         創(chuàng)建一個(gè)bat可執(zhí)行文件,不用每次跑都去cmd下敲一堆命令,累啊。bat文件的內(nèi)容:

          scomp -d classes\beanIoc -src src\beanIoc -out beanIoc.jar beanIoc.xsd beanIoc.xsdconfig

          ·         雙擊bat,所需要的java類(lèi)的.java文件,class文件和xmlbeans jaxb需要的元數(shù)據(jù)都橫空出世了,這些元數(shù)據(jù)xmlbeans會(huì)用來(lái)對(duì)xml文件進(jìn)行驗(yàn)證,檢查是否符合schema定義的語(yǔ)義規(guī)則。

          ·         java文件和元數(shù)據(jù)信息都拷貝到eclipse中吧,或者直接把生成的jar包發(fā)布到maven repository中,然后再pomdependency它。


           
          1. 解析的過(guò)程中,需要生成被依賴注入的對(duì)象,并完成屬性的動(dòng)態(tài)設(shè)定。

          4.1   生成依賴注入的對(duì)象

          生成依賴注入的對(duì)象是通過(guò)反射直接生成類(lèi)的實(shí)例,在這里要求有public的參數(shù)為空的構(gòu)造函數(shù)。

              

            Class clazz = Class.forName(sNamclassNamee);

                      
          return clazz.newInstance();

           

           

          4.2  屬性的動(dòng)態(tài)設(shè)定

          Commons-BeanUtils就是專(zhuān)門(mén)免費(fèi)做這件事的好同志,我們可以利用它來(lái)完成,基本類(lèi)型和一些經(jīng)常使用的類(lèi)型,Commons-BeanUtils責(zé)無(wú)旁貸的提供了自動(dòng)轉(zhuǎn)換的功能,beanutils不需要我們提供參數(shù)的類(lèi)型,它可以自動(dòng)完成轉(zhuǎn)換,它是根據(jù)getset方法的類(lèi)型來(lái)決定類(lèi)型的,可參見(jiàn)PropertyDescriptor.getPropertyType()方法。使用方法如下:


          if (!PropertyUtils.isWriteable(object, key)) {
                                  
          throw new ConfigParseException(object.getClass()
                                          
          + " doesn't have the property " + key
                                          
          + "'s setter method!");
                              }

                      String paramVal 
          = paramBean.getValue();
                      BeanUtils.setProperty(object, key, paramVal);


           

          isWriteable方法判斷是否有可用的set方法,如果有就完成屬性的動(dòng)態(tài)設(shè)置。paramBean就是xml文件中定義的那個(gè)param

           

           

          但是Beanutils默認(rèn)幫我們轉(zhuǎn)換的類(lèi)型為基本類(lèi)型和所有它已經(jīng)提供了Converterclass類(lèi)型,如果我們有特殊的類(lèi)需要進(jìn)行動(dòng)態(tài)設(shè)定,必須自己提供converter,注冊(cè)到它的converters map中。這樣beanutils兄弟在動(dòng)態(tài)設(shè)定屬性值的時(shí)候,就會(huì)根據(jù)屬性的類(lèi)型去converter map中把取出該屬性類(lèi)型對(duì)應(yīng)的自定義converter來(lái)轉(zhuǎn)換。因?yàn)樵谶@里配置文件中配置的都是String 所以我們對(duì)Converter接口做了修改:



          public abstract class BeanConverter implements Converter {
              
          public abstract Object convert(Class type, String value) throws ConfigParseException;

              
          public Object convert(Class type, Object value) {
                  
          return this.convert(type, (String)value);
              }

          }



           

          我們強(qiáng)制規(guī)定了convert方法的參數(shù)必須是String,自己提供的converter必須繼承BeanConverter抽象類(lèi)。

          String key = paramBean.getKey();
                              String converterClsName 
          = paramBean.getConverter();
                              
          if (StringUtils.isNotEmpty(converterClsName)) {
                                  Class converterClazz 
          = Class.forName(converterClsName);
                                  
          if (!BeanConverter.class
                                          .isAssignableFrom(converterClazz)) 
          {
                                      
          throw new ConfigParseException(
                                              
          "converter must extend BeanConverter!");
                                  }

                                  
                                  
          if(!this.converters.containsKey(converterClsName)) {
                                      
          this.converters.put(converterClsName, converterClazz);
                                      
          // get property type
                                      Class propertyClazz = PropertyUtils.getPropertyType(
                                              object, key);
                                      
          // register converter
                                      ConvertUtils.register((Converter) converterClazz
                                              .newInstance(), propertyClazz);
                                  }

                              }
           }


          4.3屬性邏輯規(guī)則的檢查

          在設(shè)置好屬性以后,這個(gè)屬性的值并不一定配置的正確,也不一定滿足邏輯規(guī)則,比如希望int值在35之間,比如希望String 不要為空等等。為了在動(dòng)態(tài)設(shè)定完屬性后進(jìn)行邏輯規(guī)則的校驗(yàn),提供了InitializingBean接口



          public interface InitializingBean {
              
          void afterPropertiesSet() throws Exception;
          }



           

          實(shí)現(xiàn)該接口的類(lèi),它們的邏輯規(guī)則需要在afterProperties提供,不符合規(guī)則的請(qǐng)拋異常,并會(huì)在屬性設(shè)定完后檢查


              public void afterPropertiesSet() throws Exception {
                  
          if (StringUtils.isEmpty(uploadFolder)) {
                      
          throw new IllegalArgumentException(
                              
          "upload folder is an empty string!");
                  }

                  
          if (null == generator) {
                      
          throw new IllegalArgumentException("generator is null!");
                  }

              }




          if (object instanceof InitializingBean) {
                      ((InitializingBean) object).afterPropertiesSet();
                  }



            4.4bean注冊(cè)到BeanContext

          String id = idClassTypeBean.getId();
          BeanContextFactory.getBeanContext().setBean(id, object);


           

              4.5清理環(huán)境

          完成屬性的動(dòng)態(tài)注入后,還需要清理環(huán)境


          private void cleanConfig() {
                  ConvertUtils.deregister();
                  
          this.converters = null;
              }

           

           

          5.如何做到基于接口設(shè)計(jì)

          ·         Converter提供了基于接口設(shè)計(jì)的功能:我們可以動(dòng)態(tài)的設(shè)置不同的實(shí)現(xiàn)。

          ·         用了該框架,本身就基于接口,我們可以在配置文件中修改bean的實(shí)現(xiàn)類(lèi)。應(yīng)用程序代碼它不關(guān)心具體的實(shí)現(xiàn)類(lèi),它只關(guān)心id

           

          Transporter transporter =  (Transporter) BeanContextFactory.getBeanContext().getBean(TransporterParser.getTransportName());

           

          不過(guò),這里沒(méi)有象springjuice那么強(qiáng)大的bean factory功能。因?yàn)檫@個(gè)東東只是一個(gè)小項(xiàng)目的一小部分,所以功能上滿足小項(xiàng)目的需求就足夠了。

           

          6. Test

           就簡(jiǎn)單的測(cè)了一下,可以看源代碼。

           

          7.總結(jié)

          主要是項(xiàng)目是基于接口設(shè)計(jì)的,所以一些類(lèi)的實(shí)現(xiàn)需要在配置文件里設(shè)定,實(shí)現(xiàn)類(lèi)的實(shí)例屬性也要是可以擴(kuò)展的,并且提供屬性值的邏輯校驗(yàn),所以就有了這么一個(gè)東東。

          beanIoc源代碼



          posted on 2008-09-25 19:16 叱咤紅人 閱讀(2616) 評(píng)論(4)  編輯  收藏

          評(píng)論

          # re: 利用commons-BeanUtils實(shí)現(xiàn)簡(jiǎn)易的依賴注入框架 2008-09-26 09:10 Jarod Liu
          既然是在仿spring,個(gè)人覺(jué)得沒(méi)必要“不使用spring",只使用spring的ioc就OK了。
          自己做一個(gè)框架,多人項(xiàng)目里別人的學(xué)習(xí)成本更高  回復(fù)  更多評(píng)論
            

          # re: 利用commons-BeanUtils實(shí)現(xiàn)簡(jiǎn)易的依賴注入框架 2008-09-26 09:36 叱咤紅人
          @Jarod Liu
          已有的framework經(jīng)過(guò)評(píng)估,如果可以用,減少開(kāi)發(fā)成本,我覺(jué)得也應(yīng)該利用。
          在這里遇到了一些情況,其實(shí)本來(lái)應(yīng)用的環(huán)境并不象文章中的那樣子,配置文件并不是bean什么的,因?yàn)橄肟偨Y(jié)一下,就改成了bean。原來(lái)的配置文件是有自己語(yǔ)義的,下面是截取的一部分,

          <receiver:app receiverId="gps">
          <receiver:decorators>
          <receiver:decorator class="com.ldd600.frm.mif.receiver.decorator.impl.mf.JAXBXmlBeansMFReceiverDecorator">
          <receiver:params>
          <receiver:param key="xmlDocClassName" value="com.ldd600.frm.mif.test.jaxb.tms.jo.XmlJobOrderDocumentBean"/>
          </receiver:params>
          </receiver:decorator>
          </receiver:decorators>
          <receiver:converter class="com.ldd600.frm.mif.test.receiver.converter.GPSJOVer2_0Converter">
          </receiver:converter>
          <receiver:processor class="com.ldd600.frm.mif.test.receiver.processor.GPSJOProcessor">
          </receiver:processor>
          </receiver:app>

          這個(gè)配置文件是有自己語(yǔ)義的,并不需要bean什么的。雖然spring提供了namespacehandler,但namespacehandler用起來(lái)也有成本的,而且還要自己注冊(cè)到spring的context中。這里只是為了增強(qiáng)一些基于接口的擴(kuò)展功能怕麻煩就簡(jiǎn)單的用了一下beanutils,用spring beans module肯定是可以的。
          而且,項(xiàng)目本來(lái)就有自己的配置文件,所以也不想再增加額外的配置文件。也不可能完全的就把這個(gè)配置整合到spring的applicationContext.xml中。  回復(fù)  更多評(píng)論
            

          # re: 利用commons-BeanUtils實(shí)現(xiàn)簡(jiǎn)易的依賴注入框架 2008-09-28 08:49 隔葉黃鶯
          都做這么復(fù)雜了,為何不用 spring,用 spring 無(wú)疑可以減少學(xué)習(xí)和維護(hù)成本。  回復(fù)  更多評(píng)論
            

          # re: 利用commons-BeanUtils實(shí)現(xiàn)簡(jiǎn)易的依賴注入框架[未登錄](méi) 2008-10-27 13:29 attend
          支持做一些小輪子,自己寫(xiě)的自己清楚,還好維護(hù)點(diǎn)。  回復(fù)  更多評(píng)論
            


          只有注冊(cè)用戶登錄后才能發(fā)表評(píng)論。


          網(wǎng)站導(dǎo)航:
           
          主站蜘蛛池模板: 新巴尔虎左旗| 象山县| 梅河口市| 云和县| 雷波县| 神池县| 汉寿县| 烟台市| 常德市| 大荔县| 华池县| 汽车| 定襄县| 库尔勒市| 司法| 中西区| 定边县| 南昌市| 西贡区| 揭阳市| 承德县| 水城县| 密山市| 大名县| 宜春市| 临沧市| 磐石市| 枝江市| 于田县| 开原市| 梓潼县| 屏东市| 景洪市| 旬邑县| 芷江| 桑日县| 巧家县| 泸溪县| 绥江县| 颍上县| 阳谷县|