(轉(zhuǎn)貼) jqGrid整理

          原帖地址:
          http://www.cnblogs.com/mycoding/archive/2011/07/07/2099878.html

          一、 jqGrid的加載。

          1.引用相關(guān)頭文件

          引入CSS:

          <link href="Scripts/jquery-ui-1.8.1.custom.css" rel="stylesheet" type="text/css" />

          <link href="Scripts/ui.jqgrid.css" rel="stylesheet" type="text/css" />

          引入JS:

          <script src="Scripts/jquery-1.5.1.js" type="text/javascript"></script>

          <script src="Scripts/jquery-ui.min.js" type="text/javascript"></script>

          <script src="Scripts/grid.locale-en.js" type="text/javascript"></script>

          <script src="Scripts/jquery.jqGrid.min.js" type="text/javascript"></script>

          因為jqGrid3.6及以后的版本集成了jQuery UI,所以,此處需要導(dǎo)入UI相關(guān)js和css。另外grid.locale-en.js這個語言文件必須在jquery.jqGrid.min.js之前加載,否則會出問題。

          2.將jqgrid加入頁面中

          根據(jù)jqGrid的文檔,要想生成一個jqGrid,最直接的方法就是:

          $("#list").jqGrid(options);

          其中l(wèi)ist是頁面上的一個table:<table id="list"></table>

          下面是一個簡單的例子:

          <script type="text/javascript">
           
          $(document).ready(function () {
           
          jQuery("#list").jqGrid({
           
          url: 'Handler.ashx',
           
          datatype: "json",
           
          mtype: 'GET',
           
          colNames: ['SalesReasonID', 'Name', 'ReasonType', 'ModifiedDate'],
           
          colModel: [
           
          { name: 'SalesReasonID', index: 'SalesReasonID', width: 40, align: "left", editable: true },
           
          { name: 'Name', index: 'Name', width: 100, align: "center" },
           
          { name: 'ReasonType', index: 'ReasonType', width: 100, align: "center" },
           
          { name: 'ModifiedDate', index: 'ModifiedDate', width: 150, align: "center", search: false }
           
          ],
           
          rowList: [10, 20, 30],
           
          sortname: 'SalesReasonID',
           
          viewrecords: true,
           
          sortorder: "desc",
           
          jsonReader: {
           
          root: "griddata",
           
          total: "totalpages",
           
          page: "currpage",
           
          records: "totalrecords",
           
          repeatitems: false
           
          },
           
          pager: jQuery('#pager'),
           
          rowNum: 5,
           
          altclass: 'altRowsColour',
           
          //width: 'auto',
           
          width: '500',
           
          height: 'auto',
           
          caption: "DemoGrid"
           
          }).navGrid('#pager', { add: true, edit: true, del: true,search:false,refresh:false }); ;
           
          })

          二、 jqgrid的重要選項

          具體的options參考,可以訪問jqGrid文檔關(guān)于option的章節(jié)(http://www.trirand.com/jqgridwiki/doku.php?id=wiki:options)。其中有幾個是比較常用的,重點(diǎn)介紹一下:

          • url :jqGrid控件通過這個參數(shù)得到需要顯示的數(shù)據(jù),具體的返回值可以使XML也可以是Json。
          • datatype :這個參數(shù)用于設(shè)定將要得到的數(shù)據(jù)類型。類型包括:json 、xml、xmlstring、local、javascript、function。
          • mtype : 定義使用哪種方法發(fā)起請求,GET或者POST。
          • height :Grid的高度,可以接受數(shù)字、%值、auto,默認(rèn)值為150。
          • width :Grid的寬度,如果未設(shè)置,則寬度應(yīng)為所有列寬的之和;如果設(shè)置了寬度,則每列的寬度將會根據(jù)shrinkToFit選項的設(shè)置,進(jìn)行設(shè)置。
          • shrinkToFit :此選項用于根據(jù)width計算每列寬度的算法。默認(rèn)值為true。如果shrinkToFit為true且設(shè)置了width值,則每列寬度會根據(jù) width成比例縮放;如果shrinkToFit為false且設(shè)置了width值,則每列的寬度不會成比例縮放,而是保持原有設(shè)置,而Grid將會有 水平滾動條。
          • autowidth :默認(rèn)值為false。如果設(shè)為true,則Grid的寬度會根據(jù)父容器的寬度自動重算。重算僅發(fā)生在Grid初始化的階段;如果當(dāng)父容器尺寸變化了,同時也需要變化Grid的尺寸的話,則需要在自己的代碼中調(diào)用setGridWidth方法來完成。
          • pager :定義頁碼控制條Page Bar,在上面的例子中是用一個div(<div id=”pager”></div>)來放置的。
          • sortname :指定默認(rèn)的排序列,可以是列名也可以是數(shù)字。此參數(shù)會在被傳遞到Server端。
          • viewrecords :設(shè)置是否在Pager Bar顯示所有記錄的總數(shù)。
          • caption :設(shè)置Grid表格的標(biāo)題,如果未設(shè)置,則標(biāo)題區(qū)域不顯示。
          • rowNum :用于設(shè)置Grid中一次顯示的行數(shù),默認(rèn)值為20。正是這個選項將參數(shù)rows(prmNames中設(shè)置的)通過url選項設(shè)置的鏈接傳遞到Server。注意如果Server返回的數(shù)據(jù)行數(shù)超過了rowNum的設(shè)定,則Grid也只顯示rowNum設(shè)定的行數(shù)。
          • rowList :一個數(shù)組,用于設(shè)置Grid可以接受的rowNum值。例如[10,20,30]。
          • colNames :字符串?dāng)?shù)組,用于指定各列的題頭文本,與列的順序是對應(yīng)的。
          • colModel :最重要的數(shù)組之一,用于設(shè)定各列的參數(shù)。(稍后詳述)
          • prmNames :這是一個數(shù)組,用于設(shè)置jqGrid將要向Server傳遞的參數(shù)名稱。(稍后詳述)
          • jsonReader :這又是一個數(shù)組,用來設(shè)定如何解析從Server端發(fā)回來的json數(shù)據(jù)。(稍后詳述)

          2.1 prmNames選項

          prmNames是jqGrid的一個重要選項,用于設(shè)置jqGrid將要向Server傳遞的參數(shù)名稱。其默認(rèn)值為:

          prmNames : {

          page:"page", // 表示請求頁碼的參數(shù)名稱

          rows:"rows", // 表示請求行數(shù)的參數(shù)名稱

          sort: "sidx", // 表示用于排序的列名的參數(shù)名稱

          order: "sord", // 表示采用的排序方式的參數(shù)名稱

          search:"_search", // 表示是否是搜索請求的參數(shù)名稱

          nd:"nd", // 表示已經(jīng)發(fā)送請求的次數(shù)的參數(shù)名稱

          id:"id", // 表示當(dāng)在編輯數(shù)據(jù)模塊中發(fā)送數(shù)據(jù)時,使用的id的名稱

          oper:"oper", // operation參數(shù)名稱

          editoper:"edit", // 當(dāng)在edit模式中提交數(shù)據(jù)時,操作的名稱

          addoper:"add", // 當(dāng)在add模式中提交數(shù)據(jù)時,操作的名稱

          deloper:"del", // 當(dāng)在delete模式中提交數(shù)據(jù)時,操作的名稱

          subgridid:"id", // 當(dāng)點(diǎn)擊以載入數(shù)據(jù)到子表時,傳遞的數(shù)據(jù)名稱

          npage: null,

          totalrows:"totalrows" // 表示需從Server得到總共多少行數(shù)據(jù)的參數(shù)名稱,參見jqGrid選項中的rowTotal

          }

          2.2 jsonReader選項

          jsonReader是jqGrid的一個重要選項,用于設(shè)置如何解析從Server端發(fā)回來的json數(shù)據(jù),如果Server返回的是xml數(shù)據(jù),則對應(yīng)的使用xmlReader來解析。jsonReader的默認(rèn)值為:

          jsonReader : {

          root: "rows", // json中代表實際模型數(shù)據(jù)的入口

          page: "page", // json中代表當(dāng)前頁碼的數(shù)據(jù)

          total: "total", // json中代表頁碼總數(shù)的數(shù)據(jù)

          records: "records", // json中代表數(shù)據(jù)行總數(shù)的數(shù)據(jù)

          repeatitems: true, // 如果設(shè)為false,則jqGrid在解析json時,會根據(jù)name來搜索對應(yīng)的數(shù)據(jù)元素(即可以json中元素可以不按順序);而所使用的name是來自于colModel中的name設(shè)定。

          cell: "cell",

          id: "id",

          userdata: "userdata",

          subgrid: {

          root:"rows",

          repeatitems: true,

          cell:"cell"

          }

          }

          假如有下面一個json字符串:

          {"totalpages":"3","currpage":"1","totalrecords":"11","griddata": [{"SalesReasonID":"1","Name":"Price","ReasonType":"Other","ModifiedDate":"1998 年6月1日"},{"SalesReasonID":"2","Name":"On Promotion","ReasonType":"Promotion","ModifiedDate":"1998年6月1日"}, {"SalesReasonID":"3","Name":"Magazine Advertisement","ReasonType":"Marketing","ModifiedDate":"1998年6月1日"}, {"SalesReasonID":"4","Name":"Television Advertisement","ReasonType":"Marketing","ModifiedDate":"1998年6月1日"}, {"SalesReasonID":"5","Name":"Manufacturer","ReasonType":"Other","ModifiedDate":"1998 年6月1日"}]}

          其對應(yīng)的jsonReader為:jsonReader: {

          root: "griddata",

          total: "totalpages",

          page: "currpage",

          records: "totalrecords",

          repeatitems: false

          }

          注:cell、id在repeatitems為true時可以用到,即每一個記錄是由一對id和cell組合而成,即可以適用另一種json結(jié)構(gòu)。援引文檔中的例子:

          repeatitems為true時:

          jQuery("#gridid").jqGrid({  

               ...  

               jsonReader : {  

                   root:"invdata",  

                   page: "currpage",  

                   total: "totalpages",  

                   records: "totalrecords"

               },  

               ...  

          });  

          json結(jié)構(gòu)為:

          {   

          "totalpages": "xxx",   

          "currpage": "yyy",  

          "totalrecords": "zzz",  

          "invdata" : [  

                            {"id" :"1", "cell" :["cell11", "cell12", "cell13"]},   // cell中不需要各列的name,只要值就OK了,但是需要保持對應(yīng)

                            {"id" :"2", "cell" :["cell21", "cell22", "cell23"]},  

                            ...  

               ]  

          }  

          repeatitems為false時:

          jQuery("#gridid").jqGrid({  

               ...  

               jsonReader : {  

                   root:"invdata",  

                   page: "currpage",  

                   total: "totalpages",  

                   records: "totalrecords",  

                   repeatitems: false,  

                   id: "0"

               },  

               ...  

          });  

          json結(jié)構(gòu)為:

          {   

          "totalpages" : "xxx",   

          "currpage" : "yyy",  

          "totalrecords" : "zzz",  

          "invdata" : [  

                           {"invid" : "1","invdate":"cell11", "amount" :"cell12", "tax" :"cell13", "total" :"1234", "note" :"somenote"}, // 數(shù)據(jù)中需要各列的name,但是可以不按列的順序

                            {"invid" : "2","invdate":"cell21", "amount" :"cell22", "tax" :"cell23", "total" :"2345", "note" :"some note"},  

                            ...  

               ]  

          }  

          2.3 colModel的重要選項

          colModel也有許多非常重要的選項,在使用搜索、排序等方面都會用到。這里先只說說最基本的。

          • name :為Grid中的每個列設(shè)置唯一的名稱,這是一個必需選項,其中保留字包括subgrid、cb、rn。
          • index :設(shè)置排序時所使用的索引名稱,這個index名稱會作為sidx參數(shù)(prmNames中設(shè)置的)傳遞到Server。
          • label :當(dāng)jqGrid的colNames選項數(shù)組為空時,為各列指定題頭。如果colNames和此項都為空時,則name選項值會成為題頭。
          • width :設(shè)置列的寬度,目前只能接受以px為單位的數(shù)值,默認(rèn)為150。
          • sortable :設(shè)置該列是否可以排序,默認(rèn)為true。
          • search :設(shè)置該列是否可以被列為搜索條件,默認(rèn)為true。
          • resizable :設(shè)置列是否可以變更尺寸,默認(rèn)為true。
          • hidden :設(shè)置此列初始化時是否為隱藏狀態(tài),默認(rèn)為false。
          • formatter :預(yù)設(shè)類型或用來格式化該列的自定義函數(shù)名。常用預(yù)設(shè)格式有:integer、date、currency、number等(具體參見文檔 )。

          三、 注意事項

          1. 動態(tài)改變Add Form或者Edit Form中的select的內(nèi)容,如:改變下圖中的Comparator下拉中的內(nèi)容。

          clip_image002

          $("#list_d").navGrid('#pager_d',{add:true,edit:true,del:true,search:false,refresh:false},

          {

          checkOnSubmit:false, closeAfterEdit: true,recreateForm:true,

          beforeInitData:function(formid){

          initComparator();

          },

          beforeShowForm: function(formid){

          $("#list_d").jqGrid('setColProp', 'Name', { editrules:{required:false},});

          $('#tr_Name', formid).hide();

          }

          },//edit

          {},//add

          {}//del

          beforeInitData, beforeShowForm在每次點(diǎn)擊編輯的時候都會執(zhí)行。initComparator的作用是通過ajax獲取數(shù)據(jù),然后利 用$("#list_d").jqGrid('setColProp', 'Comparator', { editoptions: { value: valueString} });來設(shè)置Comparator下拉中的內(nèi)容。其中valueString的格式如下’ equal to: equal to; not equal to: not equal to’。鍵值之間用冒號隔開,2項之間用分號隔開。注意:把recreateForm設(shè)為true,否則'setColProp'只在第一次調(diào)用時有效。

          2. var rowNum = parseInt($(this).getGridParam("records"), 10); 得到數(shù)據(jù)條數(shù)。

          3. jQuery("#list_d").clearGridData();清空數(shù)據(jù)。

          4. jQuery("#list").getCell(ids,"Key");獲取第ids行的key列。

          5. $("#list").jqGrid('setSelection', "1");選中第一行。放在loadComplete:中在gird加載完成的時候自動選中第一行。 loadComplete:function(data){$("#list").jqGrid('setSelection', "1");

          }

          6. 對于像1中的可編輯的字段,可以設(shè)定rule,參見http://www.trirand.com/jqgridwiki/doku.php?id=wiki:common_rules#editrules

          7. 修改Option,以URL為例

          jQuery("#list_d").jqGrid('setGridParam',{url:"xxx.aspx",page:1}).trigger('reloadGrid');


          復(fù)雜的表格可以參考jquery grid demo網(wǎng)站 :




          posted @ 2011-11-01 14:23 AK47 閱讀(2327) | 評論 (0)編輯 收藏

          (轉(zhuǎn)載)Spring 注解@Component,@Service,@Controller,@Repository

          Spring 2.5 中除了提供 @Component 注釋外,還定義了幾個擁有特殊語義的注釋,它們分別是:@Repository、@Service 和 @Controller。在目前的 Spring 版本中,這 3 個注釋和 @Component 是等效的,但是從注釋類的命名上,很容易看出這 3 個注釋分別和持久層、業(yè)務(wù)層和控制層(Web 層)相對應(yīng)。雖然目前這 3 個注釋和 @Component 相比沒有什么新意,但 Spring 將在以后的版本中為它們添加特殊的功能。所以,如果 Web 應(yīng)用程序采用了經(jīng)典的三層分層結(jié)構(gòu)的話,最好在持久層、業(yè)務(wù)層和控制層分別采用 @Repository、@Service 和 @Controller 對分層中的類進(jìn)行注釋,而用 @Component 對那些比較中立的類進(jìn)行注釋。

          在 一個稍大的項目中,通常會有上百個組件,如果這些組件采用xml的bean定義來配置,顯然會增加配置文件的體積,查找以及維護(hù)起來也不太方便。 Spring2.5為我們引入了組件自動掃描機(jī)制,他可以在類路徑底下尋找標(biāo)注了 @Component,@Service,@Controller,@Repository注解的類,并把這些類納入進(jìn)spring容器中管理。它的作用 和在xml文件中使用bean節(jié)點(diǎn)配置組件時一樣的。要使用自動掃描機(jī)制,我們需要打開以下配置信息: 
          Java代碼

          1. <?xml version="1.0" encoding="UTF-8" ?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans   http://www.springframework.org/schema/beans/spring-beans-2.5.xsd   http://www.springframework.org/schema/context   http://www.springframework.org/schema/context/spring-context-2.5.xsd"  
          2. >  
          3.   
          4. <context:component-scan base-package=”com.eric.spring”>   
          5. </beans>   
             /*其中base-package為需要掃描的包(含所有子包)

               @Service用于標(biāo)注業(yè)務(wù)層組件,

               @Controller用于標(biāo)注控制層組件(如struts中的action),

               @Repository用于標(biāo)注數(shù)據(jù)訪問組件,即DAO組件,

               @Component泛指組件,當(dāng)組件不好歸類的時候,我們可以使用這個注解進(jìn)行標(biāo)注。

              */   


          6. @Service public class VentorServiceImpl implements iVentorService {   
          7. } @Repository public class VentorDaoImpl implements iVentorDao {  
          8. }

          /*getBean的默認(rèn)名稱是類名(頭字母小 寫),如果想自定義,可以@Service(“aaaaa”)這樣來指定,這種bean默認(rèn)是單例的,如果想改變,可以使用 @Service(“beanName”) @Scope(“prototype”)來改變。可以使用以下方式指定初始化方法和銷毀方法(方法名任意): @PostConstruct public void init() {  

          */
          9. }  
          10. @PreDestroy public void destory() {  
          11. } 

          注入方式:

          把 DAO實現(xiàn)類注入到service實現(xiàn)類中,把service的接口(注意不要是service的實現(xiàn)類)注入到action中,注入時不要new 這個注入的類,因為spring會自動注入,如果手動再new的話會出現(xiàn)錯誤,然后屬性加上@Autowired后不需要getter()和 setter()方法,Spring也會自動注入。至于更具體的內(nèi)容,等對注入的方式更加熟練后會做個完整的例子上來。

          注解:

          在 spring的配置文件里面只需要加上<context:annotation-config/> 和<context:component-scan base-package="需要實現(xiàn)注入的類所在包"/>,可以使用base-package="*"表示全部的類。   

          <context:component-scan base-package=”com.eric.spring”> 

          其中base-package為需要掃描的包(含所有子包)

          在接口前面標(biāo)上@Autowired和@Qualifier注釋使得接口可以被容器注入,當(dāng)接口存在兩個實現(xiàn)類的時候必須指定其中一個來注入,使用實現(xiàn)類首字母小寫的字符串來注入,如:

          1.     @Autowired     
          2.   
          3.     @Qualifier("chinese")      
          4.   
          5.     private Man man;   

          否則可以省略,只寫@Autowired   。 

          @Service服務(wù)層組件,用于標(biāo)注業(yè)務(wù)層組件,表示定義一個bean,自動根據(jù)bean的類名實例化一個首寫字母為小寫的bean,例如Chinese實例化為chinese,如果需要自己改名字則:@Service("你自己改的bean名")。   

          @Controller用于標(biāo)注控制層組件(如struts中的action)

          @Repository持久層組件,用于標(biāo)注數(shù)據(jù)訪問組件,即DAO組件

          @Component泛指組件,當(dāng)組件不好歸類的時候,我們可以使用這個注解進(jìn)行標(biāo)注。 


          @Service 
          public class VentorServiceImpl implements iVentorService { 
          }

          @Repository 
          public class VentorDaoImpl implements iVentorDao { 


          getBean 的默認(rèn)名稱是類名(頭字母小寫),如果想自定義,可以@Service(“aaaaa”) 這樣來指定,這種

          bean默認(rèn)是單例的,如果想改變,可以使用@Service(“beanName”) @Scope(“prototype”)來改變。

          可以使用以下方式指定初始化方法和銷毀方法(方法名任意):

          @PostConstruct

          public void init() { 



          @PreDestroy

          public void destory() { 

          }

          posted @ 2011-10-10 16:46 AK47 閱讀(49726) | 評論 (3)編輯 收藏

          (轉(zhuǎn)貼)使用 Spring 2.5 注釋驅(qū)動的 IoC 功能

          原帖地址
          http://www.ibm.com/developerworks/cn/java/j-lo-spring25-ioc/

          概述

          注釋配置相對于 XML 配置具有很多的優(yōu)勢:

          • 它可以充分利用 Java 的反射機(jī)制獲取類結(jié)構(gòu)信息,這些信息可以有效減少配置的工作。如使用 JPA 注釋配置 ORM 映射時,我們就不需要指定 PO 的屬性名、類型等信息,如果關(guān)系表字段和 PO 屬性名、類型都一致,您甚至無需編寫任務(wù)屬性映射信息——因為這些信息都可以通過 Java 反射機(jī)制獲取。
          • 注釋和 Java 代碼位于一個文件中,而 XML 配置采用獨(dú)立的配置文件,大多數(shù)配置信息在程序開發(fā)完成后都不會調(diào)整,如果配置信息和 Java 代碼放在一起,有助于增強(qiáng)程序的內(nèi)聚性。而采用獨(dú)立的 XML 配置文件,程序員在編寫一個功能時,往往需要在程序文件和配置文件中不停切換,這種思維上的不連貫會降低開發(fā)效率。

          因此在很多情況下,注釋配置比 XML 配置更受歡迎,注釋配置有進(jìn)一步流行的趨勢。Spring 2.5 的一大增強(qiáng)就是引入了很多注釋類,現(xiàn)在您已經(jīng)可以使用注釋配置完成大部分 XML 配置的功能。在這篇文章里,我們將向您講述使用注釋進(jìn)行 Bean 定義和依賴注入的內(nèi)容。

           
          原來我們是怎么做的      
          在使用注釋配置之前,先來回顧一下傳統(tǒng)上是如何配置 Bean 并完成 Bean 之間依賴關(guān)系的建立。下面是 3 個類,它們分別是 Office、Car 和 Boss,這 3 個類需要在 Spring 容器中配置為 Bean:    
             
          Office 僅有一個屬性:    
               
          清單 1. Office.java    
                              
          package com.baobaotao;    
          public class Office {    
              private String officeNo =”001”;    
             
              //省略 get/setter    
             
              @Override   
              public String toString() {    
                  return "officeNo:" + officeNo;    
              }    
          }    
                 
          Car 擁有兩個屬性:    
               
          清單 2. Car.java 
                               
          package com.baobaotao;    
             
          public class Car {    
              private String brand;    
              private double price;    
             
              // 省略 get/setter    
             
              @Override   
              public String toString() {    
                  return "brand:" + brand + "," + "price:" + price;    
              }    
          }    
                
          Boss 擁有 Office 和 Car 類型的兩個屬性:    
            
          清單 3. Boss.java    
                              
          package com.baobaotao;    
             
          public class Boss {    
              private Car car;    
              private Office office;    
             
              // 省略 get/setter    
             
              @Override   
              public String toString() {    
                  return "car:" + car + "\n" + "office:" + office;    
              }    
          }    
              
          我們在 Spring 容器中將 Office 和 Car 聲明為 Bean,并注入到 Boss Bean 中:下面是使用傳統(tǒng) XML 完成這個工作的配置文件 beans.xml:    
              
          清單 4. beans.xml 將以上三個類配置成 Bean    
                              
          <?xml version="1.0" encoding="UTF-8" ?>    
          <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-2.5.xsd">    
              <bean id="boss" class="com.baobaotao.Boss">    
                  <property name="car" ref="car"/>    
                  <property name="office" ref="office" />    
              </bean>    
              <bean id="office" class="com.baobaotao.Office">    
                  <property name="officeNo" value="002"/>    
              </bean>    
              <bean id="car" class="com.baobaotao.Car" scope="singleton">    
                  <property name="brand" value=" 紅旗 CA72"/>    
                  <property name="price" value="2000"/>    
              </bean>    
          </beans>    
               
          當(dāng)我們運(yùn)行以下代碼時,控制臺將正確打出 boss 的信息:    
            
          清單 5. 測試類:AnnoIoCTest.java    
                              
          import org.springframework.context.ApplicationContext;    
          import org.springframework.context.support.ClassPathXmlApplicationContext;    
          public class AnnoIoCTest {    
             
              public static void main(String[] args) {    
                  String[] locations = {"beans.xml"};    
                  ApplicationContext ctx =     
                      new ClassPathXmlApplicationContext(locations);    
                  Boss boss = (Boss) ctx.getBean("boss");    
                  System.out.println(boss);    
              }    
          }    
              
          這說明 Spring 容器已經(jīng)正確完成了 Bean 創(chuàng)建和裝配的工作。    
               
          使用 @Autowired 注釋    
             
          Spring 2.5 引入了 @Autowired 注釋,它可以對類成員變量、方法及構(gòu)造函數(shù)進(jìn)行標(biāo)注,完成自動裝配的工作。來看一下使用 @Autowired 進(jìn)行成員變量自動注入的代碼:    
            
          清單 6. 使用 @Autowired 注釋的 Boss.java    
                              
          package com.baobaotao;    
          import org.springframework.beans.factory.annotation.Autowired;    
             
          public class Boss {    
             
              @Autowired   
              private Car car;    
             
              @Autowired   
              private Office office;    
             
              …    
          }    
                 
          Spring 通過一個 BeanPostProcessor 對 @Autowired 進(jìn)行解析,所以要讓 @Autowired 起作用必須事先在 Spring 容器中聲明 AutowiredAnnotationBeanPostProcessor Bean。   

          清單 7. 讓 @Autowired 注釋工作起來    
                              
          <?xml version="1.0" encoding="UTF-8" ?>    
          <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-2.5.xsd">    
             
              <!-- 該 BeanPostProcessor 將自動起作用,對標(biāo)注 @Autowired 的 Bean 進(jìn)行自動注入 -->    
              <bean class="AutowiredAnnotationBeanPostProcessor  
                  org.springframework.beans.factory.annotation.  "/>    
             
              <!-- 移除 boss Bean 的屬性注入配置的信息 -->    
              <bean id="boss" class="com.baobaotao.Boss"/>    
               
              <bean id="office" class="com.baobaotao.Office">    
                  <property name="officeNo" value="001"/>    
              </bean>    
              <bean id="car" class="com.baobaotao.Car" scope="singleton">    
                  <property name="brand" value=" 紅旗 CA72"/>    
                  <property name="price" value="2000"/>    
              </bean>    
          </beans>    
               
              
          這 樣,當(dāng) Spring 容器啟動時,AutowiredAnnotationBeanPostProcessor 將掃描 Spring 容器中所有 Bean,當(dāng)發(fā)現(xiàn) Bean 中擁有 @Autowired 注釋時就找到和其匹配(默認(rèn)按類型匹配)的 Bean,并注入到對應(yīng)的地方中去。    
             
          按 照上面的配置,Spring 將直接采用 Java 反射機(jī)制對 Boss 中的 car 和 office 這兩個私有成員變量進(jìn)行自動注入。所以對成員變量使用 @Autowired 后,您大可將它們的 setter 方法(setCar() 和 setOffice())從 Boss 中刪除。    
             
          當(dāng)然,您也可以通過 @Autowired 對方法或構(gòu)造函數(shù)進(jìn)行標(biāo)注,來看下面的代碼:    
              
          清單 8. 將 @Autowired 注釋標(biāo)注在 Setter 方法上    
                              
          package com.baobaotao;    
             
          public class Boss {    
              private Car car;    
              private Office office;    
             
               @Autowired   
              public void setCar(Car car) {    
                  this.car = car;    
              }    
               
              @Autowired   
              public void setOffice(Office office) {    
                  this.office = office;    
              }    
              …    
          }    
               
          這時,@Autowired 將查找被標(biāo)注的方法的入?yún)㈩愋偷?Bean,并調(diào)用方法自動注入這些 Bean。而下面的使用方法則對構(gòu)造函數(shù)進(jìn)行標(biāo)注:    
              
          清單 9. 將 @Autowired 注釋標(biāo)注在構(gòu)造函數(shù)上    
                              
          package com.baobaotao;    
             
          public class Boss {    
              private Car car;    
              private Office office;    
               
              @Autowired   
              public Boss(Car car ,Office office){    
                  this.car = car;    
                  this.office = office ;    
              }    
               
              …    
          }    
                 
          由于 Boss() 構(gòu)造函數(shù)有兩個入?yún)ⅲ謩e是 car 和 office,@Autowired 將分別尋找和它們類型匹配的 Bean,將它們作為 Boss(Car car ,Office office) 的入?yún)韯?chuàng)建 Boss Bean。    
               
          當(dāng)候選 Bean 數(shù)目不為 1 時的應(yīng)對方法    
             
          在 默認(rèn)情況下使用 @Autowired 注釋進(jìn)行自動注入時,Spring 容器中匹配的候選 Bean 數(shù)目必須有且僅有一個。當(dāng)找不到一個匹配的 Bean 時,Spring 容器將拋出 BeanCreationException 異常,并指出必須至少擁有一個匹配的 Bean。我們可以來做一個實驗:    
             
             
          清單 10. 候選 Bean 數(shù)目為 0 時    
                              
          <?xml version="1.0" encoding="UTF-8" ?>    
          <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-2.5.xsd ">    
               
              <bean class="AutowiredAnnotationBeanPostProcessor  
                  org.springframework.beans.factory.annotation.  "/>     
             
              <bean id="boss" class="com.baobaotao.Boss"/>    
             
              <!-- 將 office Bean 注釋掉 -->    
              <!-- <bean id="office" class="com.baobaotao.Office">    
              <property name="officeNo" value="001"/>    
              </bean>-->    
             
              <bean id="car" class="com.baobaotao.Car" scope="singleton">    
                  <property name="brand" value=" 紅旗 CA72"/>    
                  <property name="price" value="2000"/>    
              </bean>    
          </beans>    
               
          由于 office Bean 被注釋掉了,所以 Spring 容器中將沒有類型為 Office 的 Bean 了,而 Boss 的 office 屬性標(biāo)注了 @Autowired,當(dāng)啟動 Spring 容器時,異常就產(chǎn)生了。    
             
          當(dāng) 不能確定 Spring 容器中一定擁有某個類的 Bean 時,可以在需要自動注入該類 Bean 的地方可以使用 @Autowired(required = false),這等于告訴 Spring:在找不到匹配 Bean 時也不報錯。來看一下具體的例子:    
             
             
          清單 11. 使用 @Autowired(required = false)    
                              
          package com.baobaotao;    
             
          import org.springframework.beans.factory.annotation.Autowired;    
          import org.springframework.beans.factory.annotation.Required;    
             
          public class Boss {    
             
              private Car car;    
              private Office office;    
             
              @Autowired   
              public void setCar(Car car) {    
                  this.car = car;    
              }    
              @Autowired(required = false)    
              public void setOffice(Office office) {    
                  this.office = office;    
              }    
              …    
          }    
              
          當(dāng) 然,一般情況下,使用 @Autowired 的地方都是需要注入 Bean 的,使用了自動注入而又允許不注入的情況一般僅會在開發(fā)期或測試期碰到(如為了快速啟動 Spring 容器,僅引入一些模塊的 Spring 配置文件),所以 @Autowired(required = false) 會很少用到。    
             
          和找不到一個類型匹配 Bean 相反的一個錯誤是:如果 Spring 容器中擁有多個候選 Bean,Spring 容器在啟動時也會拋出 BeanCreationException 異常。來看下面的例子:    
              
          清單 12. 在 beans.xml 中配置兩個 Office 類型的 Bean    
                              
          …     
          <bean id="office" class="com.baobaotao.Office">    
              <property name="officeNo" value="001"/>    
          </bean>    
          <bean id="office2" class="com.baobaotao.Office">    
              <property name="officeNo" value="001"/>    
          </bean>    
          …    
               
          我們在 Spring 容器中配置了兩個類型為 Office 類型的 Bean,當(dāng)對 Boss 的 office 成員變量進(jìn)行自動注入時,Spring 容器將無法確定到底要用哪一個 Bean,因此異常發(fā)生了。    
             
          Spring 允許我們通過 @Qualifier 注釋指定注入 Bean 的名稱,這樣歧義就消除了,可以通過下面的方法解決異常:    
            
          清單 13. 使用 @Qualifier 注釋指定注入 Bean 的名稱    
                              
          @Autowired   
          public void setOffice(@Qualifier("office")Office office) {    
              this.office = office;    
          }    
              
           
          @Qualifier("office") 中的 office 是 Bean 的名稱,所以 @Autowired 和 @Qualifier 結(jié)合使用時,自動注入的策略就從 byType 轉(zhuǎn)變成 byName 了。@Autowired 可以對成員變量、方法以及構(gòu)造函數(shù)進(jìn)行注釋,而 @Qualifier 的標(biāo)注對象是成員變量、方法入?yún)ⅰ?gòu)造函數(shù)入?yún)ⅰU怯捎谧⑨寣ο蟮牟煌?Spring 不將 @Autowired 和 @Qualifier 統(tǒng)一成一個注釋類。下面是對成員變量和構(gòu)造函數(shù)入?yún)⑦M(jìn)行注釋的代碼:    
             
          對成員變量進(jìn)行注釋:    
            
          清單 14. 對成員變量使用 @Qualifier 注釋    
                              
          public class Boss {    
              @Autowired   
              private Car car;    
               
              @Autowired   
              @Qualifier("office")    
              private Office office;    
              …    
          }    
               
              
          對構(gòu)造函數(shù)入?yún)⑦M(jìn)行注釋:    
              
          清單 15. 對構(gòu)造函數(shù)變量使用 @Qualifier 注釋    
                              
          public class Boss {    
              private Car car;    
              private Office office;    
             
              @Autowired   
              public Boss(Car car , @Qualifier("office")Office office){    
                  this.car = car;    
                  this.office = office ;    
              }    
          }    
               
          @Qualifier 只能和 @Autowired 結(jié)合使用,是對 @Autowired 有益的補(bǔ)充。一般來講,@Qualifier 對方法簽名中入?yún)⑦M(jìn)行注釋會降低代碼的可讀性,而對成員變量注釋則相對好一些。    
              
             
          使用 JSR-250 的注釋    
             
          Spring 不但支持自己定義的 @Autowired 的注釋,還支持幾個由 JSR-250 規(guī)范定義的注釋,它們分別是 @Resource、@PostConstruct 以及 @PreDestroy。    
             
          @Resource   
             
          @Resource 的作用相當(dāng)于 @Autowired,只不過 @Autowired 按 byType 自動注入,面 @Resource 默認(rèn)按 byName 自動注入罷了。@Resource 有兩個屬性是比較重要的,分別是 name 和 type,Spring 將 @Resource 注釋的 name 屬性解析為 Bean 的名字,而 type 屬性則解析為 Bean 的類型。所以如果使用 name 屬性,則使用 byName 的自動注入策略,而使用 type 屬性時則使用 byType 自動注入策略。如果既不指定 name 也不指定 type 屬性,這時將通過反射機(jī)制使用 byName 自動注入策略。    
             
          Resource 注釋類位于 Spring 發(fā)布包的 lib/j2ee/common-annotations.jar 類包中,因此在使用之前必須將其加入到項目的類庫中。來看一個使用 @Resource 的例子:    
             
          清單 16. 使用 @Resource 注釋的 Boss.java    
                              
          package com.baobaotao;    
             
          import javax.annotation.Resource;    
             
          public class Boss {    
              // 自動注入類型為 Car 的 Bean    
              @Resource   
              private Car car;    
             
              // 自動注入 bean 名稱為 office 的 Bean    
              @Resource(name = "office")    
              private Office office;    
          }    
               
          一般情況下,我們無需使用類似于 @Resource(type=Car.class) 的注釋方式,因為 Bean 的類型信息可以通過 Java 反射從代碼中獲取。    
             
          要讓 JSR-250 的注釋生效,除了在 Bean 類中標(biāo)注這些注釋外,還需要在 Spring 容器中注冊一個負(fù)責(zé)處理這些注釋的 BeanPostProcessor:    
             
          <bean     
            class="org.springframework.context.annotation.CommonAnnotationBeanPostProcessor"/>    
               
            
          CommonAnnotationBeanPostProcessor 實現(xiàn)了 BeanPostProcessor 接口,它負(fù)責(zé)掃描使用了 JSR-250 注釋的 Bean,并對它們進(jìn)行相應(yīng)的操作。    
             
          @PostConstruct 和 @PreDestroy   
             
          Spring 容器中的 Bean 是有生命周期的,Spring 允許在 Bean 在初始化完成后以及 Bean 銷毀前執(zhí)行特定的操作,您既可以通過實現(xiàn) InitializingBean/DisposableBean 接口來定制初始化之后 / 銷毀之前的操作方法,也可以通過 <bean> 元素的 init-method/destroy-method 屬性指定初始化之后 / 銷毀之前調(diào)用的操作方法。關(guān)于 Spring 的生命周期,筆者在《精通 Spring 2.x—企業(yè)應(yīng)用開發(fā)精解》第 3 章進(jìn)行了詳細(xì)的描述,有興趣的讀者可以查閱。    
             
          JSR-250 為初始化之后/銷毀之前方法的指定定義了兩個注釋類,分別是 @PostConstruct 和 @PreDestroy,這兩個注釋只能應(yīng)用于方法上。標(biāo)注了 @PostConstruct 注釋的方法將在類實例化后調(diào)用,而標(biāo)注了 @PreDestroy 的方法將在類銷毀之前調(diào)用。    
            
          清單 17. 使用 @PostConstruct 和 @PreDestroy 注釋的 Boss.java    
                              
          package com.baobaotao;    
             
          import javax.annotation.Resource;    
          import javax.annotation.PostConstruct;    
          import javax.annotation.PreDestroy;    
             
          public class Boss {    
              @Resource   
              private Car car;    
             
              @Resource(name = "office")    
              private Office office;    
             
              @PostConstruct   
              public void postConstruct1(){    
                  System.out.println("postConstruct1");    
              }    
             
              @PreDestroy   
              public void preDestroy1(){    
                  System.out.println("preDestroy1");     
              }    
              …    
          }    
               
          您只需要在方法前標(biāo)注 @PostConstruct 或 @PreDestroy,這些方法就會在 Bean 初始化后或銷毀之前被 Spring 容器執(zhí)行了。    
             
          我 們知道,不管是通過實現(xiàn) InitializingBean/DisposableBean 接口,還是通過 <bean> 元素的 init-method/destroy-method 屬性進(jìn)行配置,都只能為 Bean 指定一個初始化 / 銷毀的方法。但是使用 @PostConstruct 和 @PreDestroy 注釋卻可以指定多個初始化 / 銷毀方法,那些被標(biāo)注 @PostConstruct 或 @PreDestroy 注釋的方法都會在初始化 / 銷毀時被執(zhí)行。    
             
          通過以下的測試代碼,您將可以看到 Bean 的初始化 / 銷毀方法是如何被執(zhí)行的:    
            
          清單 18. 測試類代碼    
                              
          package com.baobaotao;    
             
          import org.springframework.context.support.ClassPathXmlApplicationContext;    
             
          public class AnnoIoCTest {    
             
              public static void main(String[] args) {    
                  String[] locations = {"beans.xml"};    
                  ClassPathXmlApplicationContext ctx =     
                      new ClassPathXmlApplicationContext(locations);    
                  Boss boss = (Boss) ctx.getBean("boss");    
                  System.out.println(boss);    
                  ctx.destroy();// 關(guān)閉 Spring 容器,以觸發(fā) Bean 銷毀方法的執(zhí)行    
              }    
          }    
               
             
          這 時,您將看到標(biāo)注了 @PostConstruct 的 postConstruct1() 方法將在 Spring 容器啟動時,創(chuàng)建 Boss Bean 的時候被觸發(fā)執(zhí)行,而標(biāo)注了 @PreDestroy 注釋的 preDestroy1() 方法將在 Spring 容器關(guān)閉前銷毀 Boss Bean 的時候被觸發(fā)執(zhí)行。    
                 
          使用 <context:annotation-config/> 簡化配置    
             
          Spring 2.1 添加了一個新的 context 的 Schema 命名空間,該命名空間對注釋驅(qū)動、屬性文件引入、加載期織入等功能提供了便捷的配置。我們知道注釋本身是不會做任何事情的,它僅提供元數(shù)據(jù)信息。要使元數(shù) 據(jù)信息真正起作用,必須讓負(fù)責(zé)處理這些元數(shù)據(jù)的處理器工作起來。     
             
          而我們前面所介紹的 AutowiredAnnotationBeanPostProcessor 和 CommonAnnotationBeanPostProcessor 就是處理這些注釋元數(shù)據(jù)的處理器。但是直接在 Spring 配置文件中定義這些 Bean 顯得比較笨拙。Spring 為我們提供了一種方便的注冊這些 BeanPostProcessor 的方式,這就是 <context:annotation-config/>。請看下面的配置:    
               
          清單 19. 調(diào)整 beans.xml 配置文件    
                              
          <?xml version="1.0" encoding="UTF-8" ?>    
          <beans xmlns="http://www.springframework.org/schema/beans"   
              xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"   
               xmlns:context="http://www.springframework.org/schema/context"   
               xsi:schemaLocation="http://www.springframework.org/schema/beans     
           http://www.springframework.org/schema/beans/spring-beans-2.5.xsd    
           http://www.springframework.org/schema/context     
           http://www.springframework.org/schema/context/spring-context-2.5.xsd">    
               
              <context:annotation-config/>     
             
              <bean id="boss" class="com.baobaotao.Boss"/>    
              <bean id="office" class="com.baobaotao.Office">    
                  <property name="officeNo" value="001"/>    
              </bean>    
              <bean id="car" class="com.baobaotao.Car" scope="singleton">    
                  <property name="brand" value=" 紅旗 CA72"/>    
                  <property name="price" value="2000"/>    
              </bean>    
          </beans>    
                
          <context:annotationconfig/> 將隱式地向 Spring 容器注冊 AutowiredAnnotationBeanPostProcessor、CommonAnnotationBeanPostProcessor、 PersistenceAnnotationBeanPostProcessor 以及 equiredAnnotationBeanPostProcessor 這 4 個 BeanPostProcessor。    
             
          在配置文件中使用 context 命名空間之前,必須在 <beans> 元素中聲明 context 命名空間。    
             
              
          使用 @Component   
             
          雖 然我們可以通過 @Autowired 或 @Resource 在 Bean 類中使用自動注入功能,但是 Bean 還是在 XML 文件中通過 <bean> 進(jìn)行定義 —— 也就是說,在 XML 配置文件中定義 Bean,通過 @Autowired 或 @Resource 為 Bean 的成員變量、方法入?yún)⒒驑?gòu)造函數(shù)入?yún)⑻峁┳詣幼⑷氲墓δ堋D芊褚餐ㄟ^注釋定義 Bean,從 XML 配置文件中完全移除 Bean 定義的配置呢?答案是肯定的,我們通過 Spring 2.5 提供的 @Component 注釋就可以達(dá)到這個目標(biāo)了。    
             
          下面,我們完全使用注釋定義 Bean 并完成 Bean 之間裝配:    
             
             
          清單 20. 使用 @Component 注釋的 Car.java    
                              
          package com.baobaotao;    
             
          import org.springframework.stereotype.Component;    
             
          @Component   
          public class Car {    
              …    
          }    
               
               
          僅需要在類定義處,使用 @Component 注釋就可以將一個類定義了 Spring 容器中的 Bean。下面的代碼將 Office 定義為一個 Bean:    
              
          清單 21. 使用 @Component 注釋的 Office.java    
                              
          package com.baobaotao;    
             
          import org.springframework.stereotype.Component;    
             
          @Component   
          public class Office {    
              private String officeNo = "001";    
              …    
          }    
               
          這樣,我們就可以在 Boss 類中通過 @Autowired 注入前面定義的 Car 和 Office Bean 了。    
             
          清單 22. 使用 @Component 注釋的 Boss.java    
                              
          package com.baobaotao;    
             
          import org.springframework.beans.factory.annotation.Autowired;    
          import org.springframework.beans.factory.annotation.Required;    
          import org.springframework.beans.factory.annotation.Qualifier;    
          import org.springframework.stereotype.Component;    
             
          @Component("boss")    
          public class Boss {    
              @Autowired   
              private Car car;    
             
              @Autowired   
              private Office office;    
              …    
          }    
              
          @Component 有一個可選的入?yún)ⅲ糜谥付?Bean 的名稱,在 Boss 中,我們就將 Bean 名稱定義為“boss”。一般情況下,Bean 都是 singleton 的,需要注入 Bean 的地方僅需要通過 byType 策略就可以自動注入了,所以大可不必指定 Bean 的名稱。    
             
          在使用 @Component 注釋后,Spring 容器必須啟用類掃描機(jī)制以啟用注釋驅(qū)動 Bean 定義和注釋驅(qū)動 Bean 自動注入的策略。Spring 2.5 對 context 命名空間進(jìn)行了擴(kuò)展,提供了這一功能,請看下面的配置:    
              
          清單 23. 簡化版的 beans.xml    
                              
          <?xml version="1.0" encoding="UTF-8" ?>    
          <beans xmlns="http://www.springframework.org/schema/beans"   
              xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"   
              xmlns:context="http://www.springframework.org/schema/context"   
              xsi:schemaLocation="http://www.springframework.org/schema/beans     
           http://www.springframework.org/schema/beans/spring-beans-2.5.xsd    
           http://www.springframework.org/schema/context     
           http://www.springframework.org/schema/context/spring-context-2.5.xsd">    
              <context:component-scan base-package="com.baobaotao"/>    
          </beans>    
                
          這 里,所有通過 <bean> 元素定義 Bean 的配置內(nèi)容已經(jīng)被移除,僅需要添加一行 <context:component-scan/> 配置就解決所有問題了——Spring XML 配置文件得到了極致的簡化(當(dāng)然配置元數(shù)據(jù)還是需要的,只不過以注釋形式存在罷了)。<context:component-scan/> 的 base-package 屬性指定了需要掃描的類包,類包及其遞歸子包中所有的類都會被處理。    
             
          <context:component-scan/> 還允許定義過濾器將基包下的某些類納入或排除。Spring 支持以下 4 種類型的過濾方式,通過下表說明:    
             
          表 1. 掃描過濾方式    
          過濾器類型 說明     
          注釋 假如 com.baobaotao.SomeAnnotation 是一個注釋類,我們可以將使用該注釋的類過濾出來。     
          類名指定 通過全限定類名進(jìn)行過濾,如您可以指定將 com.baobaotao.Boss 納入掃描,而將 com.baobaotao.Car 排除在外。     
          正則表達(dá)式 通過正則表達(dá)式定義過濾的類,如下所示: com\.baobaotao\.Default.*     
          AspectJ 表達(dá)式 通過 AspectJ 表達(dá)式定義過濾的類,如下所示: com. baobaotao..*Service+     
             
          下面是一個簡單的例子:    
             
          <context:component-scan base-package="com.baobaotao">    
              <context:include-filter type="regex"     
                  expression="com\.baobaotao\.service\..*"/>    
              <context:exclude-filter type="aspectj"     
                  expression="com.baobaotao.util..*"/>    
          </context:component-scan>    
                
          值 得注意的是 <context:component-scan/> 配置項不但啟用了對類包進(jìn)行掃描以實施注釋驅(qū)動 Bean 定義的功能,同時還啟用了注釋驅(qū)動自動注入的功能(即還隱式地在內(nèi)部注冊了 AutowiredAnnotationBeanPostProcessor 和 CommonAnnotationBeanPostProcessor),因此當(dāng)使用 <context:component-scan/> 后,就可以將 <context:annotation-config/> 移除了。    
             
          默認(rèn)情況下通過 @Component 定義的 Bean 都是 singleton 的,如果需要使用其它作用范圍的 Bean,可以通過 @Scope 注釋來達(dá)到目標(biāo),如以下代碼所示:    
             
          清單 24. 通過 @Scope 指定 Bean 的作用范圍    
                              
          package com.baobaotao;    
          import org.springframework.context.annotation.Scope;    
          …    
          @Scope("prototype")    
          @Component("boss")    
          public class Boss {    
              …    
          }    
               
          這樣,當(dāng)從 Spring 容器中獲取 boss Bean 時,每次返回的都是新的實例了。    
                
          采用具有特殊語義的注釋    
             
          Spring 2.5 中除了提供 @Component 注釋外,還定義了幾個擁有特殊語義的注釋,它們分別是:@Repository、@Service 和 @Controller。在目前的 Spring 版本中,這 3 個注釋和 @Component 是等效的,但是從注釋類的命名上,很容易看出這 3 個注釋分別和持久層、業(yè)務(wù)層和控制層(Web 層)相對應(yīng)。雖然目前這 3 個注釋和 @Component 相比沒有什么新意,但 Spring 將在以后的版本中為它們添加特殊的功能。所以,如果 Web 應(yīng)用程序采用了經(jīng)典的三層分層結(jié)構(gòu)的話,最好在持久層、業(yè)務(wù)層和控制層分別采用 @Repository、@Service 和 @Controller 對分層中的類進(jìn)行注釋,而用 @Component 對那些比較中立的類進(jìn)行注釋。    
                  
          注釋配置和 XML 配置的適用場合    
             
          是否有了這些 IOC 注釋,我們就可以完全摒除原來 XML 配置的方式呢?答案是否定的。有以下幾點(diǎn)原因:    
             
          注 釋配置不一定在先天上優(yōu)于 XML 配置。如果 Bean 的依賴關(guān)系是固定的,(如 Service 使用了哪幾個 DAO 類),這種配置信息不會在部署時發(fā)生調(diào)整,那么注釋配置優(yōu)于 XML 配置;反之如果這種依賴關(guān)系會在部署時發(fā)生調(diào)整,XML 配置顯然又優(yōu)于注釋配置,因為注釋是對 Java 源代碼的調(diào)整,您需要重新改寫源代碼并重新編譯才可以實施調(diào)整。     
          如果 Bean 不是自己編寫的類(如 JdbcTemplate、SessionFactoryBean 等),注釋配置將無法實施,此時 XML 配置是唯一可用的方式。     
          注釋配置往往是類級別的,而 XML 配置則可以表現(xiàn)得更加靈活。比如相比于 @Transaction 事務(wù)注釋,使用 aop/tx 命名空間的事務(wù)配置更加靈活和簡單。     
          所 以在實現(xiàn)應(yīng)用中,我們往往需要同時使用注釋配置和 XML 配置,對于類級別且不會發(fā)生變動的配置可以優(yōu)先考慮注釋配置;而對于那些第三方類以及容易發(fā)生調(diào)整的配置則應(yīng)優(yōu)先考慮使用 XML 配置。Spring 會在具體實施 Bean 創(chuàng)建和 Bean 注入之前將這兩種配置方式的元信息融合在一起。    
                 
          小結(jié)    
             
          Spring 在 2.1 以后對注釋配置提供了強(qiáng)力的支持,注釋配置功能成為 Spring 2.5 的最大的亮點(diǎn)之一。合理地使用 Spring 2.5 的注釋配置,可以有效減少配置的工作量,提高程序的內(nèi)聚性。但是這并不意味著傳統(tǒng) XML 配置將走向消亡,在第三方類 Bean 的配置,以及那些諸如數(shù)據(jù)源、緩存池、持久層操作模板類、事務(wù)管理等內(nèi)容的配置上,XML 配置依然擁有不可替代的地位。

          posted @ 2011-10-10 15:49 AK47 閱讀(334) | 評論 (0)編輯 收藏

          (轉(zhuǎn)貼)數(shù)據(jù)庫三范式經(jīng)典實例解析

          數(shù)據(jù)庫的設(shè)計范式是數(shù)據(jù)庫設(shè)計所需要滿足的規(guī)范,滿足這些規(guī)范的數(shù)據(jù)庫是簡潔的、結(jié)構(gòu)明晰的,同時,不會發(fā)生插入(insert)、刪除(delete)和更新(update)操作異常。反之則是亂七八糟,不僅給數(shù)據(jù)庫的編程人員制造麻煩,而且面目可憎,可能存儲了大量不需要的冗余信息。
               設(shè)計范式是不是很難懂呢?非也,大學(xué)教材上給我們一堆數(shù)學(xué)公式我們當(dāng)然看不懂,也記不住。所以我們很多人就根本不按照范式來設(shè)計數(shù)據(jù)庫。
               實質(zhì)上,設(shè)計范式用很形象、很簡潔的話語就能說清楚,道明白。本文將對范式進(jìn)行通俗地說明,并以筆者曾經(jīng)設(shè)計的一個簡單論壇的數(shù)據(jù)庫為例來講解怎樣將這些范式應(yīng)用于實際工程。

          范式說明
               第一范式(1NF):數(shù)據(jù)庫表中的字段都是單一屬性的,不可再分。這個單一屬性由基本類型構(gòu)成,包括整型、實數(shù)、字符型、邏輯型、日期型等。
               例如,如下的數(shù)據(jù)庫表是符合第一范式:

          字段1 字段2 字段3 字段4
          ? ? ? ?
           而這樣的數(shù)據(jù)庫表是不符合第一范式的:
          字段1 字段2 字段3 字段4
          ? ? 字段3.1 字段3.2 ?

           

               很顯然,在當(dāng)前的任何關(guān)系數(shù)據(jù)庫管理系統(tǒng)(DBMS)中,傻瓜也不可能做出不符合第一范式的數(shù)據(jù)庫,因為這些DBMS不允許你把數(shù)據(jù)庫表的一列再分成二列或多列。因此,你想在現(xiàn)有的DBMS中設(shè)計出不符合第一范式的數(shù)據(jù)庫都是不可能的。
               第二范式(2NF):數(shù)據(jù)庫表中不存在非關(guān)鍵字段對任一候選關(guān)鍵字段的部分函數(shù)依賴(部分函數(shù)依賴指的是存在組合關(guān)鍵字中的某些字段決定非關(guān)鍵字段的情況),也即所有非關(guān)鍵字段都完全依賴于任意一組候選關(guān)鍵字。
               假定選課關(guān)系表為SelectCourse(學(xué)號, 姓名, 年齡, 課程名稱, 成績, 學(xué)分),關(guān)鍵字為組合關(guān)鍵字(學(xué)號, 課程名稱),因為存在如下決定關(guān)系:
               (學(xué)號, 課程名稱) → (姓名, 年齡, 成績, 學(xué)分)
               這個數(shù)據(jù)庫表不滿足第二范式,因為存在如下決定關(guān)系:
               (課程名稱) → (學(xué)分)
               (學(xué)號) → (姓名, 年齡)
          即存在組合關(guān)鍵字中的字段決定非關(guān)鍵字的情況。
               由于不符合2NF,這個選課關(guān)系表會存在如下問題:
               (1) 數(shù)據(jù)冗余:
               同一門課程由n個學(xué)生選修,"學(xué)分"就重復(fù)n-1次;同一個學(xué)生選修了m門課程,姓名和年齡就重復(fù)了m-1次。
               (2) 更新異常:
               若調(diào)整了某門課程的學(xué)分,數(shù)據(jù)表中所有行的"學(xué)分"值都要更新,否則會出現(xiàn)同一門課程學(xué)分不同的情況。
               (3) 插入異常:
               假設(shè)要開設(shè)一門新的課程,暫時還沒有人選修。這樣,由于還沒有"學(xué)號"關(guān)鍵字,課程名稱和學(xué)分也無法記錄入數(shù)據(jù)庫。
               (4) 刪除異常:
               假設(shè)一批學(xué)生已經(jīng)完成課程的選修,這些選修記錄就應(yīng)該從數(shù)據(jù)庫表中刪除。但是,與此同時,課程名稱和學(xué)分信息也被刪除了。很顯然,這也會導(dǎo)致插入異常。

               把選課關(guān)系表SelectCourse改為如下三個表:
               學(xué)生:Student(學(xué)號, 姓名, 年齡);
               課程:Course(課程名稱, 學(xué)分);
               選課關(guān)系:SelectCourse(學(xué)號, 課程名稱, 成績)。
               這樣的數(shù)據(jù)庫表是符合第二范式的,消除了數(shù)據(jù)冗余、更新異常、插入異常和刪除異常。
               另外,所有單關(guān)鍵字的數(shù)據(jù)庫表都符合第二范式,因為不可能存在組合關(guān)鍵字。
               第三范式(3NF):在第二范式的基礎(chǔ)上,數(shù)據(jù)表中如果不存在非關(guān)鍵字段對任一候選關(guān)鍵字段的傳遞函數(shù)依賴則符合第三范式。所謂傳遞函數(shù)依賴,指的是如果存在"A → B → C"的決定關(guān)系,則C傳遞函數(shù)依賴于A。因此,滿足第三范式的數(shù)據(jù)庫表應(yīng)該不存在如下依賴關(guān)系:
               關(guān)鍵字段 → 非關(guān)鍵字段x → 非關(guān)鍵字段y
               假定學(xué)生關(guān)系表為Student(學(xué)號, 姓名, 年齡, 所在學(xué)院, 學(xué)院地點(diǎn), 學(xué)院電話),關(guān)鍵字為單一關(guān)鍵字"學(xué)號",因為存在如下決定關(guān)系:
               (學(xué)號) → (姓名, 年齡, 所在學(xué)院, 學(xué)院地點(diǎn), 學(xué)院電話)
          這個數(shù)據(jù)庫是符合2NF的,但是不符合3NF,因為存在如下決定關(guān)系:
               (學(xué)號) → (所在學(xué)院) → (學(xué)院地點(diǎn), 學(xué)院電話)
          即存在非關(guān)鍵字段"學(xué)院地點(diǎn)"、"學(xué)院電話"對關(guān)鍵字段"學(xué)號"的傳遞函數(shù)依賴。
               它也會存在數(shù)據(jù)冗余、更新異常、插入異常和刪除異常的情況,讀者可自行分析得知。
               把學(xué)生關(guān)系表分為如下兩個表:
               學(xué)生:(學(xué)號, 姓名, 年齡, 所在學(xué)院);
               學(xué)院:(學(xué)院, 地點(diǎn), 電話)。
          這樣的數(shù)據(jù)庫表是符合第三范式的,消除了數(shù)據(jù)冗余、更新異常、插入異常和刪除異常。
               鮑依斯-科得范式(BCNF):在第三范式的基礎(chǔ)上,數(shù)據(jù)庫表中如果不存在任何字段對任一候選關(guān)鍵字段的傳遞函數(shù)依賴則符合第三范式。
               假設(shè)倉庫管理關(guān)系表為StorehouseManage(倉庫ID, 存儲物品ID, 管理員ID, 數(shù)量),且有一個管理員只在一個倉庫工作;一個倉庫可以存儲多種物品。這個數(shù)據(jù)庫表中存在如下決定關(guān)系:
               (倉庫ID, 存儲物品ID) →(管理員ID, 數(shù)量)
               (管理員ID, 存儲物品ID) → (倉庫ID, 數(shù)量)
               所以,(倉庫ID, 存儲物品ID)和(管理員ID, 存儲物品ID)都是StorehouseManage的候選關(guān)鍵字,表中的唯一非關(guān)鍵字段為數(shù)量,它是符合第三范式的。但是,由于存在如下決定關(guān)系:
               (倉庫ID) → (管理員ID)
               (管理員ID) → (倉庫ID)
          即存在關(guān)鍵字段決定關(guān)鍵字段的情況,所以其不符合 BCNF范式。它會出現(xiàn)如下異常情況:
               (1) 刪除異常:
               當(dāng)倉庫被清空后,所有"存儲物品ID"和"數(shù)量"信息被刪除的同時,"倉庫ID"和"管理員ID"信息也被刪除了。
               (2) 插入異常:
               當(dāng)倉庫沒有存儲任何物品時,無法給倉庫分配管理員。
               (3) 更新異常:
               如果倉庫換了管理員,則表中所有行的管理員ID都要修改。
               把倉庫管理關(guān)系表分解為二個關(guān)系表:
               倉庫管理:StorehouseManage(倉庫ID, 管理員ID);
               倉庫:Storehouse(倉庫ID, 存儲物品ID, 數(shù)量)。
               這樣的數(shù)據(jù)庫表是符合BCNF范式的,消除了刪除異常、插入異常和更新異常。


          原帖地址: http://www.cublog.cn/u/23975/showart.php?id=391210

          posted @ 2011-02-21 14:45 AK47 閱讀(329) | 評論 (0)編輯 收藏

          Hibernate 實體對象的生命周期匯總

          本帖匯總了網(wǎng)上幾篇關(guān)于hibernate的生命周期的帖子。

          轉(zhuǎn)載:

          實體對象的生命周期在Hibernate應(yīng)用中是一個很關(guān)鍵的概念,正確的理解實體對象的生命周期將對我們應(yīng)用Hibernate做持久層設(shè)計起到很大的作用.而所謂的實體對象的生命周期就是指實體對象由產(chǎn)生到被GC回收的一段過程.在這過程中我們需要理解的就是實體對象生命周期中的三種狀態(tài).

          1. 自由狀態(tài)(Transient)
          所謂的Transient狀態(tài),即實體對象在內(nèi)存中自由存在,與數(shù)據(jù)庫中的記錄無關(guān),通常是我們的J2EE中 VO,并沒有被納入Hibernate的實體管理容器.

          1    Test test = new Test();
          2        test.setName("energykk");
          3        //此時的test對象處于Transient(自由狀態(tài))并沒有被Hibernate框架所管理
          4        

          2.持久狀態(tài)(Persistent)
          何謂 Persistent? 即實體對象已經(jīng)處于被Hibernate實體管理容器容器所管理的狀態(tài).這種狀態(tài)下這個實體對象的引用將被納入Hibernate實體管理容器容器所管理.
          處于Persistent狀態(tài)的實體對象,對它的變更也將被固化到數(shù)據(jù)庫中.
          在J2EE中通常指的是一個PO.
          Transaction tr = session.beginTransaction();
                  session.save(test);
                  
          //此時的test對象已經(jīng)處于Persistent(持久狀態(tài))它被Hibernate 納入實體管理容器
                  tr.commit();
                  Transaction tr2 
          = session.beginTransaction();
                  test.setName(
          "xukai");
                  
          //在這個事務(wù)中我們并沒有顯示的調(diào)用save()方法但是由于Persistent狀態(tài)的對象將會自動的固化到
                  
          //數(shù)據(jù)庫中,因此此時正處在Persistent狀態(tài)的test對象的變化也將自動被同步到數(shù)據(jù)庫中
                  tr2.commit();

          處于Persistent狀態(tài)的實體可以簡單的理解為:如果一個實體對象與session發(fā)生了關(guān)聯(lián),并且處于session的有效期內(nèi),那么這個實體對象就處于Persistent狀態(tài).

          3.游離狀態(tài)(Detached)
          處于Persistent狀態(tài)的實體對象,其對應(yīng)的session關(guān)閉以后,那么這個實體就處于 Detached狀態(tài).
          我們可以認(rèn)為session對象就是一個Persistent的宿主,一旦這個宿主失效,那么這個實體就處于 Detached狀態(tài).

          session.close();
                  
          //與test對象關(guān)聯(lián)的session被關(guān)閉,因此此時的test對象進(jìn)入 Detached(游離狀態(tài))
                  
                  session2 
          = HibernateSessionFactory.getSession();
                  Transaction tr3 
          = session2.beginTransaction();
                  session2.update(test);
                  
          //此時正處于Detached狀態(tài)的test對象由于再次借助與session2被納入到Hibernate的實體管理容器所以此時的
                  
          //test對象恢復(fù)到Persistent狀態(tài)
                  test.setName("jjjj");
                  tr3.commit();
                  
                  session2.close();

          既然Transient狀態(tài)的實體與Detached狀態(tài)的實體都與Hibernate的實體管理容器沒有關(guān)系,那他們到底存在哪些差異?
          差異就在于處于Transient狀態(tài)的只有一個Name的屬性.此時的test對象所包含的數(shù)據(jù)信息僅限于此,他與數(shù)據(jù)庫中的記錄沒有任何瓜葛.
          但是處于Detached狀態(tài)的實體已經(jīng)不止包含Name這個屬性,還被賦予了主鍵也就是通常POJO里的id屬性,由于id是主鍵,他可以確定數(shù)據(jù)庫表中的一條
          唯一的記錄,那么自然的處于Detached狀態(tài)的實體就能與數(shù)據(jù)庫表中擁有相同id的記錄相關(guān)聯(lián).
          這就是他們之間所存在的差異, 簡而言之,Transient狀態(tài)的實體缺乏與數(shù)據(jù)庫表記錄之間的聯(lián)系,而Detached狀態(tài)的試題恰恰相反.只不過是脫離了session這個數(shù)據(jù)庫操作平臺而已.
          原帖地址 : http://www.aygfsteel.com/energykk/archive/2007/05/08/115927.html

           生命周期圖:
          原圖地址:http://hi.baidu.com/quest2run/blog/item/39e1d08c7dbd45f4503d9222.html
           

          persistence context



          生命周期特征總結(jié) :
          原帖地址 : http://blog.csdn.net/hgd250/archive/2008/08/06/2775943.aspx
          Transient:

              與數(shù)據(jù)庫中的記錄沒有任何關(guān)系,即沒有與其相關(guān)聯(lián)的數(shù)據(jù)庫記錄.
              與session沒有任何關(guān)系.即沒有通過session對象的實例對其進(jìn)行任何持久化的操作
          Persistent:
              每個persistent狀態(tài)的實體對象都與一個session對象的實例相關(guān)聯(lián)
              處于 Persistent狀態(tài)的實體對象是與數(shù)據(jù)庫中的記錄相關(guān)聯(lián)的.
              Hibernate會依據(jù)persistent狀態(tài)的實體對象的屬性變化而改變數(shù)據(jù)庫中相對應(yīng)的記錄
          .
          Detached:
              游離態(tài)是由持久態(tài)實體對象轉(zhuǎn)變而來的.
              游離態(tài)實體不再與session對象相關(guān)聯(lián).
              游離態(tài)實體對象與數(shù)據(jù)庫中的記錄沒有直接聯(lián)系,對其所做的任何修改將不會影響到到數(shù)據(jù)庫中的數(shù)據(jù).
              游離態(tài)實體對象在數(shù)據(jù)庫有相對應(yīng)的數(shù)據(jù)記錄,如果沒有被其他事務(wù)刪除.

          posted @ 2011-02-14 14:26 AK47 閱讀(339) | 評論 (0)編輯 收藏

          (轉(zhuǎn)貼)BigInteger 和 BigDecimal

          高精度數(shù)字
          Java 提供了兩個類專門用于進(jìn)行高精度運(yùn)算BigInteger 和 BigDecimal ,盡管它們可大致劃分到與封裝器相同的類別里,但兩者都沒有對應(yīng)的主類型;這兩個類都有自己的一系列方法,類似于我們針對主類型執(zhí)行的操作,也就是說能用 int 或float 做的事情,用BigInteger和BigDecimal 一樣可以做,只是必須換用方法調(diào)用,而不是使用運(yùn)算符。此外由于牽涉更多,所以運(yùn)算速度會慢一點(diǎn)總之我們犧牲了速度,但換來了精度。

          高精度浮點(diǎn)數(shù)BigDecimal

          一些非整數(shù)值(如幾美元和幾美分這樣的小數(shù))需要很精確。浮點(diǎn)數(shù)不是精確值,所以使用它們會導(dǎo)致舍入誤差。因此,使用浮點(diǎn)數(shù)來試圖表示象貨幣量這樣的精確數(shù)量不是一個好的想法。使用浮點(diǎn)數(shù)來進(jìn)行美元和美分計算會得到災(zāi)難性的后果。浮點(diǎn)數(shù)最好用來表示象測量值這類數(shù)值,這類值從一開始就不怎么精確。
              從 JDK 1.3 起,Java 開發(fā)人員就有了另一種數(shù)值表示法來表示非整數(shù):BigDecimal。BigDecimal 是標(biāo)準(zhǔn)的類,在編譯器中不需要特殊支持,它可以表示任意精度的小數(shù),并對它們進(jìn)行計算。在內(nèi)部,可以用任意精度任何范圍的值和一個換算因子來表示 BigDecimal,換算因子表示左移小數(shù)點(diǎn)多少位,從而得到所期望范圍內(nèi)的值。因此,用 BigDecimal 表示的數(shù)的形式為 unscaledValue*10-scale。
          用于加、減、乘和除的方法給  BigDecimal 值提供了算術(shù)運(yùn)算。由于 BigDecimal 對象是不可變的,這些方法中的每一個都會產(chǎn)生新的 BigDecimal 對象。因此,因為創(chuàng)建對象的開銷,BigDecimal 不適合于大量的數(shù)學(xué)計算,但設(shè)計它的目的是用來精確地表示小數(shù)。如果您正在尋找一種能精確表示如貨幣量這樣的數(shù)值,則 BigDecimal 可以很好地勝任該任務(wù)。
          如浮點(diǎn)類型一樣,BigDecimal 也有一些令人奇怪的行為。尤其在使用 equals() 方法來檢測數(shù)值之間是否相等時要小心。equals() 方法認(rèn)為,兩個表示同一個數(shù)但換算值不同(例如,100.00 和  100.000)的 BigDecimal 值是不相等的。然而,compareTo() 方法會認(rèn)為這兩個數(shù)是相等的,所以在從數(shù)值上比較兩個  BigDecimal 值時,應(yīng)該使用 compareTo() 而不是 equals()。
          另外還有一些情形,任意精度的小數(shù)運(yùn)算仍不能表示精確結(jié)果。例如,1 除以 9 會產(chǎn)生無限循環(huán)的小數(shù) .111111...。出于這個原因,在進(jìn)行除法運(yùn)算時,BigDecimal 可以讓您顯式地控制舍入。movePointLeft() 方法支持 10 的冪次方的精確除法。
          對于 BigDecimal,有幾個可用的構(gòu)造函數(shù)。其中一個構(gòu)造函數(shù)以雙精度浮點(diǎn)數(shù)作為輸入,另一個以整數(shù)和換算因子作為輸入,還有一個以小數(shù)的 String 表示作為輸入。要小心使用  BigDecimal(double) 構(gòu)造函數(shù), 因為如果不了解它,會在計算過程中產(chǎn)生舍入誤差。請使用基于整數(shù)或 String 的構(gòu)造函數(shù)。
          如果使用 BigDecimal(double) 構(gòu)造函數(shù)不恰當(dāng),在傳遞給 JDBC setBigDecimal() 方法時,會造成似乎很奇怪的 JDBC 驅(qū)動程序中的異常。例如,考慮以下 JDBC 代碼,該代碼希望將數(shù)字 0.01 存儲到小數(shù)字段:
            PreparedStatement ps =connection.prepareStatement("INSERT INTO Foo SET name=?, value=?");
            ps.setString(1, "penny");
            ps.setBigDecimal(2, new BigDecimal(0.01));
            ps.executeUpdate();
               在執(zhí)行這段似乎無害的代碼時會拋出一些令人迷惑不解的異常(這取決于具體的 JDBC 驅(qū)動程序),因為 0.01 的雙精度近似值會導(dǎo)致大的換算值,這可能會使 JDBC 驅(qū)動程序或數(shù)據(jù)庫感到迷惑。JDBC 驅(qū)動程序會產(chǎn)生異常,但可能不會說明代碼實際上錯在哪里,除非意識到二進(jìn)制浮點(diǎn)數(shù)的局限性。相反,使用 BigDecimal("0.01") 或 BigDecimal(1, 2) 構(gòu)造 BigDecimal 來避免這類問題, 因為這兩種方法都可以精確地表示小數(shù)。
           

          code :

          import java.math.BigDecimal;
          /** * *
          * <p>Title: 開源,開放</p>
          * * <p>Description: opeansource</p>
          * * <p>Copyright: Copyright (c) 2004</p>
          * * <p>Company: ?海棠</p>
          * * @author HaiTang Ming
          * * @version 1.0 */
          public class BigDecimalUtil { 
          //默認(rèn)除法運(yùn)算精度,及即保留小數(shù)點(diǎn)多少位 
          private static final int DEF_DIV_SCALE = 2; 
          //這個類不能實例化 
          private BigDecimalUtil (){   } 
          /**   
            * * 提供精確的加法運(yùn)算。   
            * * @param v1 被加數(shù)   
            * * @param v2 加數(shù)   
            * * @return 兩個參數(shù)的和   
            * */ 
          public static double add(double v1,double v2){   
            BigDecimal b1 = new BigDecimal(Double.toString(v1));   
            BigDecimal b2 = new BigDecimal(Double.toString(v2));   
            return (b1.add(b2)).doubleValue(); 

          /**

            *提供精確的減法運(yùn)算。 
            * * @param v1 被減數(shù) 
            * * @param v2 減數(shù) 
            * * @return 兩個參數(shù)的差
            **/ 
          public static double sub(double v1,double v2){   
            BigDecimal b1 = new BigDecimal(Double.toString(v1));   
            BigDecimal b2 = new BigDecimal(Double.toString(v2));   
            return (b1.subtract(b2)).doubleValue(); 

          /**   
            * * 提供精確的乘法運(yùn)算。   
            * * @param v1 被乘數(shù)   
            * * @param v2 乘數(shù)   
            * * @return 兩個參數(shù)的積   
            * */
          public static double mul(double v1,double v2){   
            BigDecimal b1 = new BigDecimal(Double.toString(v1));   
            BigDecimal b2 = new BigDecimal(Double.toString(v2));   
            return (b1.multiply(b2)).doubleValue(); 

          /**   
            * * 提供(相對)精確的除法運(yùn)算,當(dāng)發(fā)生除不盡的情況時,精確到   
            * * 小數(shù)點(diǎn)以后多少位,以后的數(shù)字四舍五入。   
            * * @param v1 被除數(shù)   
            * * @param v2 除數(shù)   
            * * @return 兩個參數(shù)的商   
            * */ 
          public static double div(double v1,double v2){   
            return div(v1,v2,DEF_DIV_SCALE); 

          /**   
            * * 提供(相對)精確的除法運(yùn)算。當(dāng)發(fā)生除不盡的情況時,由scale參數(shù)指   
            * * 定精度,以后的數(shù)字四舍五入。   
            * * @param v1 被除數(shù) 
            * @param v2 除數(shù)   
            * * @param scale 表示表示需要精確到小數(shù)點(diǎn)以后幾位。   
            * * @return 兩個參數(shù)的商   
            * */ 
          public static double div(double v1,double v2,int scale){   
            if(scale<0){     
             throw new IllegalArgumentException("The scale must be a positive integer or zero");   
            }   
            BigDecimal b1 = new BigDecimal(Double.toString(v1));   
            BigDecimal b2 = new BigDecimal(Double.toString(v2));   
            return (b1.divide(b2,scale,BigDecimal.ROUND_HALF_UP)).doubleValue(); 

          /**   
            * * 提供精確的小數(shù)位四舍五入處理。   
            * * @param v 需要四舍五入的數(shù)字   
            * * @param scale 小數(shù)點(diǎn)后保留幾位   
            * * @return 四舍五入后的結(jié)果   
            * */ 
          public static double round(double v,int scale){ 
            if(scale<0){     
             throw new IllegalArgumentException("The scale must be a positive integer or zero");
            }   
            BigDecimal b = new BigDecimal(Double.toString(v));   
            BigDecimal one = new BigDecimal("1");   
            return (b.divide(one,scale,BigDecimal.ROUND_HALF_UP)).doubleValue(); 
          }   
          public static void main(String[] args){   
            System.out.println(add(234.44,534.90));
           
            double a = 123.345678;   
            double d = round(a,2);   
            System.out.println("round("+a+",2)--->"+d); 
          }
          }

          高精度整數(shù)BigInteger

          BigInteger支持任意精度的整數(shù),也就是說我們可精確表示任意大小的整數(shù)值;同時在運(yùn)算過程中不會丟失任何信息;
          在BigInteger類中有所有的基本算術(shù)運(yùn)算方法,如加、減、乘、除,以及可能會用到的位運(yùn)算如或、異或、非、左移、右移等。下面是一些方法的例子:當(dāng)然,如果要有更多的使用方法,可以查閱java api 。

          code :public class BigIntegerTest { 
          public BigIntegerTest() {   } 
          /**   
            * * 測試BigInteger
            * */ 
          public static void testBigInteger() {   
            BigInteger bi = new BigInteger("888");   
            //multiply :乘法   
            BigInteger result = bi.multiply(new BigInteger("2"));   
            System.out.println(result);   
            //divide : 除法   
            result = bi.divide(new BigInteger("2"));   
            System.out.println(result);   
            //add : 加法   
            result = bi.add(new BigInteger("232"));   
            System.out.println(result);   
            //subtract :減法   
            result = bi.subtract(new BigInteger("23122"));   
            System.out.println(result);   
            result = bi.shiftRight(2);   
            System.out.println(result); 
          }   
          public static void main(String[] args) {   
            testBigInteger(); 
          }
          }
          原貼地址http://dev.firnow.com/course/3_program/java/javaxl/2008914 /142796_2.html

          posted @ 2010-12-10 14:16 AK47 閱讀(1038) | 評論 (0)編輯 收藏

          (轉(zhuǎn)貼) 超大整數(shù)相加,超過了long的范圍,你要怎么做!

           

          引用:

           這個只能夠用字符串的形式來處理了,因為計算機(jī)能夠處理的最大是long型,本文以字符串的形式來進(jìn)行超大數(shù)據(jù)的相加,理論上只要你的內(nèi)存允許,相加多大的數(shù)都可以。

          /**

           * 超大整數(shù)相加:

           * 題目要求:如果系統(tǒng)要使用超大整數(shù)(超過long的范圍),請你設(shè)計一個數(shù)據(jù)結(jié)構(gòu)來存儲這種

           * 超大型數(shù)字以及設(shè)計一種算法來實現(xiàn)超大整數(shù)的加法運(yùn)算

           * @author Administrator

           *

           */

          public class VeryBigNumAdd {

           

              /**

               * @param args

               */

              public static void main(String[] args) {

                 // TODO Auto-generated method stub

                 /*

                 String a="1223232";

                 for(int i=a.length()-1;i>=0;i--)

                 {

                     System.out.print(a.charAt(i));

                 }

                 */

                 VeryBigNumAdd vbn=new VeryBigNumAdd();

                 String a="123453243455535634535252345234677576252241234123523453664563634";

                 String b="123453243455535634535252345234677576252241234123523453664563634";

                 String result=vbn.doAdd(a,b);

                 System.out.println("result:"+result);

              }

              /**

               *

               * @param a 加數(shù)字符串1

               * @param b 加數(shù)字符串2

               * @return 結(jié)果字符串

               * 分析:

               * 1、取得兩個字符串的長度

               * 2、把兩個的長度做比較,并得出較長的長度,及較短的長度

               * 3、把長度較短的加數(shù)字符串,在左面補(bǔ)0,使之與較長的字符串一樣長

               * 4、從最高位,一個個數(shù)的取出來相加,當(dāng)然首先得轉(zhuǎn)換為整型

               * 5、設(shè)置進(jìn)位,如果兩個數(shù)相加及加上進(jìn)位大于等于10,并且這不是最左邊一個字符相加,相加結(jié)果等于

               *    (取出1+取出2+進(jìn)位)-10,并把進(jìn)位設(shè)為1;如果沒有大于10,就把進(jìn)位設(shè)為0,如些循環(huán),把

               *    相加的結(jié)果以字符串的形式結(jié)合起來,就得到最后的結(jié)果

               */

              String doAdd(String a,String b)

              {

                 String str="";

                 int lenA=a.length();

                 int lenB=b.length();

                 int maxLen=(lenA>lenB) ? lenA : lenB;

                 int minLen=(lenA<lenB) ? lenA : lenB;

                 String strTmp="";

                 for(int i=maxLen-minLen;i>0;i--)

                 {

                     strTmp+="0";

                 }

                 //把長度調(diào)整到相同

                 if(maxLen==lenA)

                 {

                     b=strTmp+b;

                 }else

                     a=strTmp+a;

                 int JW=0;//進(jìn)位

                 for(int i=maxLen-1;i>=0;i--)

                 {        

                     int tempA=Integer.parseInt(String.valueOf(a.charAt(i)));

                     int tempB=Integer.parseInt(String.valueOf(b.charAt(i)));

                     int temp;

                     if(tempA+tempB+JW>=10 && i!=0)

                     {

                        temp=tempA+tempB+JW-10;

                        JW=1;

                     }

                     else

                     {

                        temp=tempA+tempB+JW;

                        JW=0;

                     }        

                     str=String.valueOf(temp)+str;        

                 }

                 return str;

              }

           

          }

           

          原帖地址: http://blog.csdn.net/fenglibing/archive/2007/08/23/1756773.aspx

              
              其實java 本身也提供了api ,java.math.BigInteger;import java.math.BigDecimal; 也可以實現(xiàn)。

          code :

          package com.kangdy.test;

          import java.math.BigInteger;
          import java.math.BigDecimal;

          public class NumberTest {
           public static void main(String args[]){
            BigInteger b1= new BigInteger("2222222222222222222222222");
            BigInteger b2= new BigInteger("8888888888888888888888888");
            BigDecimal b3 = new BigDecimal("66666666666666666666666666");
            BigDecimal b4 = new BigDecimal("9999999999999999999999999999");
            System.out.println(b1.add(b2).toString());
            System.out.println(b3.add(b4).toString());
           }
          }

          這里只是給出簡單的例子。



          posted @ 2010-12-10 14:06 AK47 閱讀(932) | 評論 (0)編輯 收藏

          (轉(zhuǎn)貼)java回調(diào)函數(shù)

          原帖地址: http://ayzw001.blog.163.com/blog/static/1134114222009420112538726/

          引用:

                 所謂回調(diào),就是客戶程序C調(diào)用服務(wù)程序S中的某個方法a,然后S又在某個時候反過來調(diào)用C中的某個方法b,對于C來說,這個b便叫做回調(diào)函數(shù)。

          一般說來,C不會自己調(diào)用b,C提供b的目的就是讓S來調(diào)用它,而且是C不得不提供。由于S并不知道C提供的b叫甚名誰,所以S會約定b的接口規(guī)范(函數(shù)原型),然后由C提前通過S的一個函數(shù)r告訴S自己將要使用b函數(shù),這個過程稱為回調(diào)函數(shù)的注冊,r稱為注冊函數(shù)。

          下面舉個通俗的例子:

          某天,我打電話向你請教問題,當(dāng)然是個難題,:),你一時想不出解決方法,我又不能拿著電話在那里傻等,于是我們約定:等你想出辦法后打手機(jī)通知我,這樣,我就掛掉電話辦其它事情去了。過了XX分鐘,我的手機(jī)響了,你興高采烈的說問題已經(jīng)搞定,應(yīng)該如此這般處理。故事到此結(jié)束。

          這個例子說明了“異步+回調(diào)”的編程模式。其中,你后來打手機(jī)告訴我結(jié)果便是一個“回調(diào)”過程;我的手機(jī)號碼必須在以前告訴你,這便是注冊回調(diào)函數(shù);我的手機(jī)號碼應(yīng)該有效并且手機(jī)能夠接收到你的呼叫,這是回調(diào)函數(shù)必須符合接口規(guī)范。

           

          如果你還不太清楚看看這段描述合和代碼:

          聲明一個接口,另外一個類有方法里面有個參數(shù)以是這個接口類型的,而后在另外類中實現(xiàn)這個接口(java中多用的是匿名內(nèi)部類),而且以這個匿名的類生成的對象為參數(shù)傳到上面提到類中,而后實現(xiàn)回調(diào).......這種用法可以參考java里面常用到的數(shù)據(jù)庫操作所用到的幾個接口.....

          //聲明一個接口
          public interface ICallBack {
              void postExec();
          }

           

          //另外一個類有方法里面有個參數(shù)以是這個接口類型的
          public class FooBar {
              private ICallBack callBack;
              public void setCallBack(ICallBack callBack) {
                  this.callBack = callBack;
              }
              public void doSth() {
                  callBack.postExec();
              }
          }
          ---------------------------------------
          回調(diào)的實現(xiàn)
          public class Test {
              public static void main(String[] args) {
                  FooBar foo = new FooBar();
                  foo.setCallBack(new ICallBack() {
                      public void postExec() {
                          System.out.println("method executed.");
                      }
                  });
                  foo.doSth();//調(diào)用函數(shù)
              }
          }

          posted @ 2010-12-10 11:22 AK47 閱讀(632) | 評論 (0)編輯 收藏

          (轉(zhuǎn)貼) 真正理解面向接口編程

          面向?qū)ο笤O(shè)計里有一點(diǎn)大家已基本形成共識,就是面向接口編程,我想大多數(shù)人對這個是沒有什么覺得需要懷疑的。

          問題是在實際的項目開發(fā)中我們是怎么體現(xiàn)的呢? 難道就是每一個實現(xiàn)都提供一個接口就了事了?反過來說,你有時候有沒有覺得接口是多余的事? 又或者,你僅僅是覺得現(xiàn)在類似spring這樣的框架已習(xí)慣用接口這種方式而心存當(dāng)然。

          設(shè)計模式解析里提到了面向?qū)ο笤O(shè)計考慮的幾個視角,一個是概念層,一個是規(guī)約層,一個是實現(xiàn)層。我如果沒有猜錯的話,實際上我們大多數(shù)人的眼睛一直是盯著實現(xiàn)層的,而這正是面向?qū)ο笤O(shè)計所極力避免的,即你不要在一開始就關(guān)注這些細(xì)節(jié),你要關(guān)注的是規(guī)約(接口).

          對于實際項目開發(fā)來說,如果我們把實現(xiàn)的過程分為多個階段的話我們不妨這么劃分,第一階段,根據(jù)client端的需要去設(shè)計我們的規(guī)約(interface),在這個階段任何實現(xiàn)都沒有,所有的任務(wù)就是定義接口所需要的職責(zé),以及所需要的一些po,vo;第二階段,實現(xiàn)前面定義的規(guī)約。而以前我是怎么做的呢? 我是交叉作的,即假模假樣的定義一個接口(其實我心里在想這個東西有屁用),然后定義了一個方法,然后就立即去實現(xiàn)這個方法,再然后我又定義一個方法,繼續(xù)去實現(xiàn),我現(xiàn)在終于想通了,這樣好累,效率很低,最重要的是,這不屬于真正的設(shè)計。
          現(xiàn)在我是怎么做的呢?比如一個list.jsp里需要查詢,列表,然后看明細(xì)信息,然后增加信息,我會第一步在接口里定義完(這個過程會有整體設(shè)計的意識),毫不關(guān)心底層實現(xiàn)(數(shù)據(jù)庫、事務(wù)),我的目標(biāo)就是"我想要這個功能,我想要那個功能",至于那個功能怎么實現(xiàn)在第一階段我認(rèn)為那不是我的事情(盡管這個事情最終還是由我來做) .大家看這個過程和前面的過程有什么本質(zhì)的不同呢? 就是分層的概念更加明顯,你的工作更有層次,每次都有先設(shè)計再實現(xiàn)的步驟,而前面那個過程很容易就讓你不知不覺地陷入純實現(xiàn)的陷阱中。

          一點(diǎn)感想,歡迎大家拍磚。

          原帖地址: http://www.aygfsteel.com/alex/archive/2007/03/12/103185.html

          posted @ 2010-11-05 14:09 AK47 閱讀(324) | 評論 (0)編輯 收藏

          數(shù)字金額的中文大小寫轉(zhuǎn)化

          曾經(jīng)去過一家公司面試。筆試題的最后一題是一個數(shù)字金額大小寫轉(zhuǎn)化的問題。
          當(dāng)時沒想那么多,僅僅想到應(yīng)該把數(shù)拆開然后添加單位的一個大致的設(shè)計思路。
          而那個面試官一個勁兒的問我用啥算法。那個題最后也沒答上,回來后比較郁悶,
          在網(wǎng)上搜了一下。這個答案還真不少。不過覺得有一種設(shè)計還比較靠譜。
          大概是這樣:
           * 先將整數(shù)與小數(shù)部分分開,計算小數(shù)部分,角分并保存
           * 整數(shù)部分長度不足12位,前面加0補(bǔ)足。
           * 將整數(shù)部分分割3部分。高4位代表億,中間的是萬,其余分別代表千,百,十,個
           * 定一個方法拼出每一部分串。
           * 最后整數(shù)與小數(shù)部分合成。

          自己實現(xiàn)了一下,以下是代碼。
          code :

          package com.kangdy.test;
          /**
           * 數(shù)字金額轉(zhuǎn)化成大寫
           * 先將整數(shù)與小數(shù)部分分開,計算小數(shù)部分,角分并保存
           * 整數(shù)部分長度不足12位,前面加0補(bǔ)足。
           * 將整數(shù)部分分割3部分。高4位代表億,中間的是萬,其余分別代表千,百,十,個
           * 定一個方法拼出每一部分串。
           * 最后整數(shù)與小數(shù)部分合成。
           * @author dkang
           *
           */
          public class NumberToString {

           String numberStr;

           public static final String unit[] = { "", "十", "百", "千", "萬", "億" };

           public static final String unit2[] = { "元", "角", "分" };

           public static final String numStr[] = { "零", "壹", "貳", "叁", "肆", "伍", "陸",
             "柒", "捌", "玖" };

           /**
            * 字符串長度不足12位用0補(bǔ)足
            *
            * @param str
            * @return
            */
           private String additionalZero(String str) {
            StringBuffer strb = new StringBuffer();
            if (str.length() < 12) {
             int size = 12 - str.length();
             for (int i = 0; i < size; i++) {
              strb.append("0");
             }
            }
            return strb.append(str).toString();
           }

           /**
            * 遞歸拆分?jǐn)?shù)字成字符串
            *
            * @param value
            * @param strBuffer
            * @return
            */
           private String decomposeNumberToString(int value, StringBuffer strBuffer) {
            int quotient = 0;
            quotient = value / 10;
            if (quotient != 0) {
             decomposeNumberToString(quotient, strBuffer);
            }
            int remaider = value % 10;
            strBuffer.append(remaider + ",");
            return strBuffer.toString().substring(0,
              strBuffer.toString().length() - 1);
           }

           /**
            * 使用循環(huán)拆分?jǐn)?shù)字成字符串
            *
            * @param value
            * @return
            */
           private String decomposeNumberToString2(int value) {
            StringBuilder strBuilder = new StringBuilder();
            int quotient = value;
            int remaider = 0;
            while (quotient != 0) {
             remaider = quotient % 10;
             strBuilder.append(remaider + ",");
             quotient = quotient / 10;
            }
            strBuilder.deleteCharAt(strBuilder.lastIndexOf(","));
            return strBuilder.reverse().toString();
           }

           /**
            * 添加單位
            *
            * @param temp
            * @return
            */
           private String addUnits(String temp) {
            StringBuffer sb = new StringBuffer();
            String str[] = temp.split(",");
            String tempStr = temp.replace(",", "");
            if (tempStr.contains("000")) {
             return sb.append(resplaceNumToStr(str[0]) + unit[3]).toString();
            } else if (tempStr.contains("00")) {
             if (tempStr.charAt(3) == '0') {
              return sb.append(resplaceNumToStr(str[0]) + unit[3]).append(
                resplaceNumToStr(str[1]) + unit[2]).toString();
             } else {
              return sb.append(resplaceNumToStr(str[0]) + unit[3]).append(
                numStr[0]).append(resplaceNumToStr(str[3])).toString();
             }
            } else {
             for (int i = 0; i < str.length; i++) {
              sb.append(resplaceNumToStr(str[i]));
              if (!str[i].equals("0")) {
               sb.append(unit[str.length - (i + 1)]);
              }
             }
            }
            return sb.toString();
           }

           /**
            * 數(shù)字替換
            *
            * @param str
            * @return
            */
           private String resplaceNumToStr(String str) {
            try {
             int num = Integer.parseInt(str);
             return numStr[num];
            } catch (Exception e) {
             e.printStackTrace();
            }
            return "";
           }

           /**
            * 把4位長度的數(shù)字轉(zhuǎn)化成字符串
            *
            * @param number
            * @param i
            * @return
            */
           private String transformNumberToString(String number, int i) {
            StringBuffer strBuffer = new StringBuffer();
            StringBuilder strBuilder = new StringBuilder();
            try {
             int num = Integer.parseInt(number);
             if (num != 0) {
              String s1 = decomposeNumberToString(num, strBuffer);
              strBuilder.append(addUnits(s1));
              if (i == 1) {
               strBuilder.append(unit[5]);
              } else if (i == 2)
               strBuilder.append(unit[4]);
             }
            } catch (Exception e) {
             e.printStackTrace();
            }
            return strBuilder.toString();
           }

           /**
            * 得到最終結(jié)果
            *
            * @param str
            * @return
            */
           public String IntegrationResultString(String str) {
            StringBuffer strBuffer = new StringBuffer();
            String numStr[] = null;
            if (str.indexOf(".") != -1) {
             numStr = str.split("\\.");
            } else {
             return strBuffer.append(createIntegerPartsResult(str)).toString();
            }
            String fractionalStr = createFractionalPartsResult(numStr[1]);
            String integerStr = createIntegerPartsResult(numStr[0]);
            return strBuffer.append(integerStr).append(fractionalStr).toString();
           }

           private String createIntegerPartsResult(String integer) {
            StringBuffer strBuffer = new StringBuffer();
            String temp = additionalZero(integer);
            String str1 = temp.substring(0, 4);
            strBuffer.append(transformNumberToString(str1, 1));
            String str2 = temp.substring(4, 8);
            strBuffer.append(transformNumberToString(str2, 2));
            String str3 = temp.substring(8, temp.length());
            strBuffer.append(transformNumberToString(str3, 3) + unit2[0]);
            return strBuffer.toString();
           }

           private String createFractionalPartsResult(String fractionalStr) {
            StringBuilder strB = new StringBuilder();
            String s1 = fractionalStr.substring(0, 1);
            String s2 = fractionalStr.substring(1, fractionalStr.length());
            if (!s1.equals("0")) {
             strB.append(resplaceNumToStr(s1) + unit2[1]);
            }
            if (!s2.equals("0")) {
             strB.append(resplaceNumToStr(s2) + unit2[2]);
            }
            return strB.toString();
           }

           public static void main(String args[]) {
            NumberToString test = new NumberToString();
            String str = "200123004054.11";
            System.out.println(test.IntegrationResultString(str));
           }
          }



          posted @ 2010-11-02 14:59 AK47 閱讀(626) | 評論 (0)編輯 收藏

          僅列出標(biāo)題
          共4頁: 上一頁 1 2 3 4 下一頁 
          <2025年8月>
          272829303112
          3456789
          10111213141516
          17181920212223
          24252627282930
          31123456

          導(dǎo)航

          統(tǒng)計

          常用鏈接

          留言簿

          隨筆分類

          隨筆檔案

          搜索

          最新評論

          閱讀排行榜

          評論排行榜

          主站蜘蛛池模板: 建宁县| 米易县| 台前县| 申扎县| 鄂伦春自治旗| 漳平市| 康乐县| 卢龙县| 莱州市| 日土县| 古田县| 桃江县| 九龙城区| 闻喜县| 南靖县| 商都县| 大兴区| 论坛| 邯郸市| 九寨沟县| 泾阳县| 南部县| 平邑县| 武清区| 乐至县| 余江县| 东丰县| 积石山| 临潭县| 郎溪县| 同仁县| 太仆寺旗| 双峰县| 清水县| 宁都县| 平原县| 凤凰县| 大新县| 桑植县| 绵竹市| 衡山县|