• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            C++ Jounior

            once setback,once inspiration,once self-awareness
            重要的是這個磨練過程,而不是結(jié)果,要的是你粗壯的腿,而不是你身上背的那袋鹽巴

             

            面向?qū)ο蟮娜齻€基本特征(講解)

            面向?qū)ο蟮娜齻€基本特征(講解)

            面向?qū)ο蟮娜齻€基本特征是:封裝、繼承、多態(tài)。

            o_OOBase.gif

            封裝

            封裝最好理解了。封裝是面向?qū)ο蟮奶卣髦唬菍ο蠛皖惛拍畹闹饕匦浴?/span>

            封裝,也就是把客觀事物封裝成抽象的類,并且類可以把自己的數(shù)據(jù)和方法只讓可信的類或者對象操作,對不可信的進(jìn)行信息隱藏。

            繼承

            面向?qū)ο缶幊?/span> (OOP) 語言的一個主要功能就是 繼承 。繼承是指這樣一種能力:它可以使用現(xiàn)有類的所有功能,并在無需重新編寫原來的類的情況下對這些功能進(jìn)行擴(kuò)展。

            通過繼承創(chuàng)建的新類稱為“子類”或“派生類”。

            被繼承的類稱為 基類 父類 超類

            繼承的過程,就是從一般到特殊的過程。

            要實現(xiàn)繼承,可以通過 繼承 Inheritance )和 組合 Composition )來實現(xiàn)。

            在某些 OOP 語言中,一個子類可以繼承多個基類。但是一般情況下,一個子類只能有一個基類,要實現(xiàn)多重繼承,可以通過多級繼承來實現(xiàn)。

            ?

            繼承概念的實現(xiàn)方式有三類:實現(xiàn)繼承、接口繼承和可視繼承。

            ????????? 實現(xiàn)繼承是指使用基類的屬性和方法而無需額外編碼的能力;

            ????????? 接口繼承是指僅使用屬性和方法的名稱、但是子類必須提供實現(xiàn)的能力;

            ????????? 可視繼承是指子窗體(類)使用基窗體(類)的外觀和實現(xiàn)代碼的能力。

            在考慮使用繼承時,有一點需要注意,那就是兩個類之間的關(guān)系應(yīng)該是 屬于 關(guān)系。例如, Employee 是一個人, Manager 也是一個人,因此這兩個類都可以繼承 Person 類。但是 Leg 類卻不能繼承 Person 類,因為腿并不是一個人。

            抽象類僅定義將由子類創(chuàng)建的一般屬性和方法,創(chuàng)建抽象類時,請使用關(guān)鍵字 Interface 而不是 Class

            OO 開發(fā)范式大致為:劃分對象 抽象類 將類組織成為層次化結(jié)構(gòu) ( 繼承和合成 ) → 用類與實例進(jìn)行設(shè)計和實現(xiàn)幾個階段。

            ?

            多態(tài)

            多態(tài)性( polymorphisn )是允許你將父對象設(shè)置成為和一個或更多的他的子對象相等的技術(shù),賦值之后,父對象就可以根據(jù)當(dāng)前賦值給它的子對象的特性以不同的方式運作。簡單的說,就是一句話:允許將子類類型的指針賦值給父類類型的指針。

            實現(xiàn)多態(tài),有二種方式,覆蓋,重載。

            覆蓋,是指子類重新定義父類的虛函數(shù)的做法。

            重載,是指允許存在多個同名函數(shù),而這些函數(shù)的參數(shù)表不同(或許參數(shù)個數(shù)不同,或許參數(shù)類型不同,或許兩者都不同)。

            其實,重載的概念并不屬于 面向?qū)ο缶幊?/span> ,重載的實現(xiàn)是:編譯器根據(jù)函數(shù)不同的參數(shù)表,對同名函數(shù)的名稱做修飾,然后這些同名函數(shù)就成了不同的函數(shù)(至少對于編譯器來說是這樣的)。如,有兩個同名函數(shù): function func(p:integer):integer; function func(p:string):integer; 。那么編譯器做過修飾后的函數(shù)名稱可能是這樣的: int_func str_func 。對于這兩個函數(shù)的調(diào)用,在編譯器間就已經(jīng)確定了,是靜態(tài)的(記住:是靜態(tài))。也就是說,它們的地址在編譯期就綁定了(早綁定),因此,重載和多態(tài)無關(guān)!真正和多態(tài)相關(guān)的是 覆蓋 。當(dāng)子類重新定義了父類的虛函數(shù)后,父類指針根據(jù)賦給它的不同的子類指針,動態(tài)(記住:是動態(tài)!)的調(diào)用屬于子類的該函數(shù),這樣的函數(shù)調(diào)用在編譯期間是無法確定的(調(diào)用的子類的虛函數(shù)的地址無法給出)。因此,這樣的函數(shù)地址是在運行期綁定的(晚邦定)。結(jié)論就是:重載只是一種語言特性,與多態(tài)無關(guān),與面向?qū)ο笠矡o關(guān)!引用一句 Bruce Eckel 的話: 不要犯傻,如果它不是晚邦定,它就不是多態(tài)。

            那么,多態(tài)的作用是什么呢?我們知道,封裝可以隱藏實現(xiàn)細(xì)節(jié),使得代碼模塊化;繼承可以擴(kuò)展已存在的代碼模塊(類);它們的目的都是為了 —— 代碼重用。而多態(tài)則是為了實現(xiàn)另一個目的 —— 接口重用!多態(tài)的作用,就是為了類在繼承和派生的時候,保證使用 家譜 中任一類的實例的某一屬性時的正確調(diào)用。

            ?

            概念講解

            泛化( Generalization

            o_Generalization.gif

            圖表 1 泛化

            在上圖中,空心的三角表示繼承關(guān)系(類繼承),在 UML 的術(shù)語中,這種關(guān)系被稱為泛化( Generalization )。 Person( ) 是基類, Teacher( 教師 ) Student( 學(xué)生 ) Guest( 來賓 ) 是子類。

            若在邏輯上 B A 的“一種”,并且 A 的所有功能和屬性對 B 而言都有意義,則允許 B 繼承 A 的功能和屬性。

            例如, 教師是人, Teacher Person 的“一種”( a kind of )。 那么類 Teacher 可以從類 Person 派生(繼承)。

            如果 A 是基類, B A 的派生類,那么 B 將繼承 A 的數(shù)據(jù)和函數(shù)。

            如果類 A 和類 B 毫不相關(guān),不可以為了使 B 的功能更多些而讓 B 繼承 A 的功能和屬性。

            若在邏輯上 B A 的“一種”( a kind of ),則允許 B 繼承 A 的功能和屬性。

            ?

            聚合(組合)

            o_aggregationBase.gif

            圖表 2 組合

            若在邏輯上 A B 的“一部分”( a part of ),則不允許 B A 派生,而是要用 A 和其它東西組合出 B

            例如,眼( Eye )、鼻( Nose )、口( Mouth )、耳( Ear )是頭( Head )的一部分,所以類 Head 應(yīng)該由類 Eye Nose Mouth Ear 組合而成,不是派生(繼承)而成。

            ?

            聚合的類型分為無、共享 ( 聚合 ) 、復(fù)合 ( 組合 ) 三類。

            ?

            聚合( aggregation

            ?

            o_aggregation.gif

            圖表 3 共享

            上面圖中,有一個菱形(空心)表示聚合( aggregation )(聚合類型為共享),聚合的意義表示 has-a 關(guān)系。聚合是一種相對松散的關(guān)系,聚合類 B 不需要對被聚合的類 A 負(fù)責(zé)。

            ?

            組合( composition

            o_composition.gif

            圖表 4 復(fù)合

            這幅圖與上面的唯一區(qū)別是菱形為實心的,它代表了一種更為堅固的關(guān)系 —— 組合( composition )(聚合類型為復(fù)合)。組合表示的關(guān)系也是 has-a ,不過在這里, A 的生命期受 B 控制。即 A 會隨著 B 的創(chuàng)建而創(chuàng)建,隨 B 的消亡而消亡。

            ?

            依賴 (Dependency)

            o_Dependency.gif

            圖表 5 依賴

            這里 B A 的關(guān)系只是一種依賴 (Dependency) 關(guān)系,這種關(guān)系表明,如果類 A 被修改,那么類 B 會受到影響。

            ==================接口與抽象類有什么區(qū)別==================
            abstract class和interface是Java語言中對于抽象類定義進(jìn)行支持的兩種機(jī)制,正是由于這兩種機(jī)制的存在,才賦予了Java強(qiáng)大的面向?qū)ο竽芰Αbstract class和interface之間在對于抽象類定義的支持方面具有很大的相似性,甚至可以相互替換,因此很多開發(fā)者在進(jìn)行抽象類定義時對于abstract class和interface的選擇顯得比較隨意。其實,兩者之間還是有很大的區(qū)別的,對于它們的選擇甚至反映出對于問題領(lǐng)域本質(zhì)的理解、對于設(shè)計意圖的理解是否正確、合理。本文將對它們之間的區(qū)別進(jìn)行一番剖析,試圖給開發(fā)者提供一個在二者之間進(jìn)行選擇的依據(jù)。

            理解抽象類

            abstract class和interface在Java語言中都是用來進(jìn)行抽象類(本文中的抽象類并非從abstract class翻譯而來,它表示的是一個抽象體,而abstract class為Java語言中用于定義抽象類的一種方法,請讀者注意區(qū)分)定義的,那么什么是抽象類,使用抽象類能為我們帶來什么好處呢?

            ?

            ?

            在面向?qū)ο蟮母拍钪校覀冎浪械膶ο蠖际峭ㄟ^類來描繪的,但是反過來卻不是這樣。并不是所有的類都是用來描繪對象的,如果一個類中沒有包含足夠的信息來描繪一個具體的對象,這樣的類就是抽象類。抽象類往往用來表征我們在對問題領(lǐng)域進(jìn)行分析、設(shè)計中得出的抽象概念,是對一系列看上去不同,但是本質(zhì)上相同的具體概念的抽象。比如:如果我們進(jìn)行一個圖形編輯軟件的開發(fā),就會發(fā)現(xiàn)問題領(lǐng)域存在著圓、三角形這樣一些具體概念,它們是不同的,但是它們又都屬于形狀這樣一個概念,形狀這個概念在問題領(lǐng)域是不存在的,它就是一個抽象概念。正是因為抽象的概念在問題領(lǐng)域沒有對應(yīng)的具體概念,所以用以表征抽象概念的抽象類是不能夠?qū)嵗摹?

            在面向?qū)ο箢I(lǐng)域,抽象類主要用來進(jìn)行類型隱藏。我們可以構(gòu)造出一個固定的一組行為的抽象描述,但是這組行為卻能夠有任意個可能的具體實現(xiàn)方式。這個抽象描述就是抽象類,而這一組任意個可能的具體實現(xiàn)則表現(xiàn)為所有可能的派生類。模塊可以操作一個抽象體。由于模塊依賴于一個固定的抽象體,因此它可以是不允許修改的;同時,通過從這個抽象體派生,也可擴(kuò)展此模塊的行為功能。熟悉OCP的讀者一定知道,為了能夠?qū)崿F(xiàn)面向?qū)ο笤O(shè)計的一個最核心的原則OCP(Open-Closed Principle),抽象類是其中的關(guān)鍵所在。


            從語法定義層面看abstract class和interface

            在語法層面,Java語言對于abstract class和interface給出了不同的定義方式,下面以定義一個名為Demo的抽象類為例來說明這種不同。

            使用abstract class的方式定義Demo抽象類的方式如下:

            abstract class Demo {
            abstract void method1();
            abstract void method2();

            使用interface的方式定義Demo抽象類的方式如下:

            interface Demo {
            void method1();
            void method2();

            }

            在abstract class方式中,Demo可以有自己的數(shù)據(jù)成員,也可以有非abstarct的成員方法,而在interface方式的實現(xiàn)中,Demo只能夠有靜態(tài)的不能被修改的數(shù)據(jù)成員(也就是必須是static final的,不過在interface中一般不定義數(shù)據(jù)成員),所有的成員方法都是abstract的。從某種意義上說,interface是一種特殊形式的abstract class。

            從編程的角度來看,abstract class和interface都可以用來實現(xiàn)"design by contract"的思想。但是在具體的使用上面還是有一些區(qū)別的。

            首先,abstract class在Java語言中表示的是一種繼承關(guān)系,一個類只能使用一次繼承關(guān)系。但是,一個類卻可以實現(xiàn)多個interface。也許,這是Java語言的設(shè)計者在考慮Java對于多重繼承的支持方面的一種折中考慮吧。

            其次,在abstract class的定義中,我們可以賦予方法的默認(rèn)行為。但是在interface的定義中,方法卻不能擁有默認(rèn)行為,為了繞過這個限制,必須使用委托,但是這會 增加一些復(fù)雜性,有時會造成很大的麻煩。

            在抽象類中不能定義默認(rèn)行為還存在另一個比較嚴(yán)重的問題,那就是可能會造成維護(hù)上的麻煩。因為如果后來想修改類的界面(一般通過abstract class或者interface來表示)以適應(yīng)新的情況(比如,添加新的方法或者給已用的方法中添加新的參數(shù))時,就會非常的麻煩,可能要花費很多的時間(對于派生類很多的情況,尤為如此)。但是如果界面是通過abstract class來實現(xiàn)的,那么可能就只需要修改定義在abstract class中的默認(rèn)行為就可以了。

            同樣,如果不能在抽象類中定義默認(rèn)行為,就會導(dǎo)致同樣的方法實現(xiàn)出現(xiàn)在該抽象類的每一個派生類中,違反了"one rule,one place"原則,造成代碼重復(fù),同樣不利于以后的維護(hù)。因此,在abstract class和interface間進(jìn)行選擇時要非常的小心。


            從設(shè)計理念層面看abstract class和interface

            上面主要從語法定義和編程的角度論述了abstract class和interface的區(qū)別,這些層面的區(qū)別是比較低層次的、非本質(zhì)的。本小節(jié)將從另一個層面:abstract class和interface所反映出的設(shè)計理念,來分析一下二者的區(qū)別。作者認(rèn)為,從這個層面進(jìn)行分析才能理解二者概念的本質(zhì)所在。

            前面已經(jīng)提到過,abstarct class在Java語言中體現(xiàn)了一種繼承關(guān)系,要想使得繼承關(guān)系合理,父類和派生類之間必須存在"is a"關(guān)系,即父類和派生類在概念本質(zhì)上應(yīng)該是相同的(參考文獻(xiàn)〔3〕中有關(guān)于"is a"關(guān)系的大篇幅深入的論述,有興趣的讀者可以參考)。對于interface 來說則不然,并不要求interface的實現(xiàn)者和interface定義在概念本質(zhì)上是一致的,僅僅是實現(xiàn)了interface定義的契約而已。為了使論述便于理解,下面將通過一個簡單的實例進(jìn)行說明。

            考慮這樣一個例子,假設(shè)在我們的問題領(lǐng)域中有一個關(guān)于Door的抽象概念,該Door具有執(zhí)行兩個動作open和close,此時我們可以通過abstract class或者interface來定義一個表示該抽象概念的類型,定義方式分別如下所示:

            使用abstract class方式定義Door:

            abstract class Door {
            abstract void open();
            abstract void close();
            }


            使用interface方式定義Door:


            interface Door {
            void open();
            void close();
            }


            其他具體的Door類型可以extends使用abstract class方式定義的Door或者implements使用interface方式定義的Door。看起來好像使用abstract class和interface沒有大的區(qū)別。

            如果現(xiàn)在要求Door還要具有報警的功能。我們該如何設(shè)計針對該例子的類結(jié)構(gòu)呢(在本例中,主要是為了展示abstract class和interface反映在設(shè)計理念上的區(qū)別,其他方面無關(guān)的問題都做了簡化或者忽略)?下面將羅列出可能的解決方案,并從設(shè)計理念層面對這些不同的方案進(jìn)行分析。

            解決方案一:

            簡單的在Door的定義中增加一個alarm方法,如下:

            abstract class Door {
            abstract void open();
            abstract void close();
            abstract void alarm();
            }


            或者

            interface Door {
            void open();
            void close();
            void alarm();
            }


            那么具有報警功能的AlarmDoor的定義方式如下:

            class AlarmDoor extends Door {
            void open() { … }
            void close() { … }
            void alarm() { … }
            }


            或者

            class AlarmDoor implements Door {
            void open() { … }
            void close() { … }
            void alarm() { … }

            這種方法違反了面向?qū)ο笤O(shè)計中的一個核心原則ISP(Interface Segregation Priciple),在Door的定義中把Door概念本身固有的行為方法和另外一個概念"報警器"的行為方法混在了一起。這樣引起的一個問題是那些僅僅依賴于Door這個概念的模塊會因為"報警器"這個概念的改變(比如:修改alarm方法的參數(shù))而改變,反之依然。

            解決方案二:

            既然open、close和alarm屬于兩個不同的概念,根據(jù)ISP原則應(yīng)該把它們分別定義在代表這兩個概念的抽象類中。定義方式有:這兩個概念都使用abstract class方式定義;兩個概念都使用interface方式定義;一個概念使用abstract class方式定義,另一個概念使用interface方式定義。

            顯然,由于Java語言不支持多重繼承,所以兩個概念都使用abstract class方式定義是不可行的。后面兩種方式都是可行的,但是對于它們的選擇卻反映出對于問題領(lǐng)域中的概念本質(zhì)的理解、對于設(shè)計意圖的反映是否正確、合理。我們一一來分析、說明。

            如果兩個概念都使用interface方式來定義,那么就反映出兩個問題:1、我們可能沒有理解清楚問題領(lǐng)域,AlarmDoor在概念本質(zhì)上到底是Door還是報警器?2、如果我們對于問題領(lǐng)域的理解沒有問題,比如:我們通過對于問題領(lǐng)域的分析發(fā)現(xiàn)AlarmDoor在概念本質(zhì)上和Door是一致的,那么我們在實現(xiàn)時就沒有能夠正確的揭示我們的設(shè)計意圖,因為在這兩個概念的定義上(均使用interface方式定義)反映不出上述含義。

            如果我們對于問題領(lǐng)域的理解是:AlarmDoor在概念本質(zhì)上是Door,同時它有具有報警的功能。我們該如何來設(shè)計、實現(xiàn)來明確的反映出我們的意思呢?前面已經(jīng)說過,abstract class在Java語言中表示一種繼承關(guān)系,而繼承關(guān)系在本質(zhì)上是"is a"關(guān)系。所以對于Door這個概念,我們應(yīng)該使用abstarct class方式來定義。另外,AlarmDoor又具有報警功能,說明它又能夠完成報警概念中定義的行為,所以報警概念可以通過interface方式定義。如下所示:

            abstract class Door {
            abstract void open();
            abstract void close();
            }
            interface Alarm {
            void alarm();
            }
            class AlarmDoor extends Door implements Alarm {
            void open() { … }
            void close() { … }
            void alarm() { … }
            }


            這種實現(xiàn)方式基本上能夠明確的反映出我們對于問題領(lǐng)域的理解,正確的揭示我們的設(shè)計意圖。其實abstract class表示的是"is a"關(guān)系,interface表示的是"like a"關(guān)系,大家在選擇時可以作為一個依據(jù),當(dāng)然這是建立在對問題領(lǐng)域的理解上的,比如:如果我們認(rèn)為AlarmDoor在概念本質(zhì)上是報警器,同時又具有Door的功能,那么上述的定義方式就要反過來了。


            ==================游標(biāo),可不可以嵌套,可以用什么辦法替換使用游標(biāo)==================
            For standard SQL, it is surely that cursors can be nested, just like LOOPs can be nested.
            Below is an example written in PL/SQL:

            Declare
            ?? Cursor_H IS CURSOR AS SELECT * FROM customer_header;
            ?? Cursor_L (p_cust_id) IS CURSOR AS SELECT * FROM customer_detail WHERE customer_id = p_cust_id;
            Begin
            ?? For H in Cursor_H
            ?? Loop
            ???????? Print customer_header;
            ???????? For L in Cursor_L (H.customer_id)
            ???????? Loop
            ??????????????? Print_customer_detail;
            ???????? End loop;
            ?? End loop;
            End;


            In PL/SQL, you can use Direct-Loop instead of cursor;
            For example:

            Begin
            ?? For H in (SELECT * FROM customer_header)?????? -- instead of Cursor_H
            ?? Loop
            ???????? Print customer_header;
            ???????? For L in (SELECT * FROM customer_detail WHERE customer_id = H.customer_id)????? -- instead of Cursor_L
            ???????? Loop
            ??????????????? Print_customer_detail;
            ???????? End loop;
            ?? End loop;
            End;
            =======================
            MVC是一種目前廣泛流行的軟件設(shè)計模式,早在70年代,IBM就推出了Sanfronscisico項目計劃,其實就是MVC設(shè)計模式的研究。近來,隨著J2EE的成熟,它正在成為在J2EE平臺上推薦的一種設(shè)計模型,也是廣大Java開發(fā)者非常感興趣的設(shè)計模型。MVC模式也逐漸在PHP和ColdFusion開發(fā)者中運用,并有增長趨勢。隨著網(wǎng)絡(luò)應(yīng)用的快速增加,MVC模式對于Web應(yīng)用的開發(fā)無疑是一種非常先進(jìn)的設(shè)計思想,無論你選擇哪種語言,無論應(yīng)用多復(fù)雜,它都能為你理解分析應(yīng)用模型時提供最基本的分析方法,為你構(gòu)造產(chǎn)品提供清晰的設(shè)計框架,為你的軟件工程提供規(guī)范的依據(jù)。  

            MVC設(shè)計思想

            MVC英文即Model-View-Controller,即把一個應(yīng)用的輸入、處理、輸出流程按照Model、View、Controller的方式進(jìn)行分離,這樣一個應(yīng)用被分成三個層——模型層、視圖層、控制層。

            視圖(View)代表用戶交互界面,對于Web應(yīng)用來說,可以概括為HTML界面,但有可能為XHTML、XML和Applet。隨著應(yīng)用的復(fù)雜性和規(guī)模性,界面的處理也變得具有挑戰(zhàn)性。一個應(yīng)用可能有很多不同的視圖,MVC設(shè)計模式對于視圖的處理僅限于視圖上數(shù)據(jù)的采集和處理,以及用戶的請求,而不包括在視圖上的業(yè)務(wù)流程的處理。業(yè)務(wù)流程的處理交予模型(Model)處理。比如一個訂單的視圖只接受來自模型的數(shù)據(jù)并顯示給用戶,以及將用戶界面的輸入數(shù)據(jù)和請求傳遞給控制和模型。

            模型(Model):就是業(yè)務(wù)流程/狀態(tài)的處理以及業(yè)務(wù)規(guī)則的制定。業(yè)務(wù)流程的處理過程對其它層來說是黑箱操作,模型接受視圖請求的數(shù)據(jù),并返回最終的處理結(jié)果。業(yè)務(wù)模型的設(shè)計可以說是MVC最主要的核心。目前流行的EJB模型就是一個典型的應(yīng)用例子,它從應(yīng)用技術(shù)實現(xiàn)的角度對模型做了進(jìn)一步的劃分,以便充分利用現(xiàn)有的組件,但它不能作為應(yīng)用設(shè)計模型的框架。它僅僅告訴你按這種模型設(shè)計就可以利用某些技術(shù)組件,從而減少了技術(shù)上的困難。對一個開發(fā)者來說,就可以專注于業(yè)務(wù)模型的設(shè)計。MVC設(shè)計模式告訴我們,把應(yīng)用的模型按一定的規(guī)則抽取出來,抽取的層次很重要,這也是判斷開發(fā)人員是否優(yōu)秀的設(shè)計依據(jù)。抽象與具體不能隔得太遠(yuǎn),也不能太近。MVC并沒有提供模型的設(shè)計方法,而只告訴你應(yīng)該組織管理這些模型,以便于模型的重構(gòu)和提高重用性。我們可以用對象編程來做比喻,MVC定義了一個頂級類,告訴它的子類你只能做這些,但沒法限制你能做這些。這點對編程的開發(fā)人員非常重要。

            業(yè)務(wù)模型還有一個很重要的模型那就是數(shù)據(jù)模型。數(shù)據(jù)模型主要指實體對象的數(shù)據(jù)保存(持續(xù)化)。比如將一張訂單保存到數(shù)據(jù)庫,從數(shù)據(jù)庫獲取訂單。我們可以將這個模型單獨列出,所有有關(guān)數(shù)據(jù)庫的操作只限制在該模型中。

            控制(Controller)可以理解為從用戶接收請求, 將模型與視圖匹配在一起,共同完成用戶的請求。劃分控制層的作用也很明顯,它清楚地告訴你,它就是一個分發(fā)器,選擇什么樣的模型,選擇什么樣的視圖,可以完成什么樣的用戶請求。控制層并不做任何的數(shù)據(jù)處理。例如,用戶點擊一個連接,控制層接受請求后, 并不處理業(yè)務(wù)信息,它只把用戶的信息傳遞給模型,告訴模型做什么,選擇符合要求的視圖返回給用戶。因此,一個模型可能對應(yīng)多個視圖,一個視圖可能對應(yīng)多個模型。

            MVC的優(yōu)點

            大部分用過程語言比如ASP、PHP開發(fā)出來的Web應(yīng)用,初始的開發(fā)模板就是混合層的數(shù)據(jù)編程。例如,直接向數(shù)據(jù)庫發(fā)送請求并用HTML顯示,開發(fā)速度往往比較快,但由于數(shù)據(jù)頁面的分離不是很直接,因而很難體現(xiàn)出業(yè)務(wù)模型的樣子或者模型的重用性。產(chǎn)品設(shè)計彈性力度很小,很難滿足用戶的變化性需求。MVC要求對應(yīng)用分層,雖然要花費額外的工作,但產(chǎn)品的結(jié)構(gòu)清晰,產(chǎn)品的應(yīng)用通過模型可以得到更好地體現(xiàn)。

            首先,最重要的是應(yīng)該有多個視圖對應(yīng)一個模型的能力。在目前用戶需求的快速變化下,可能有多種方式訪問應(yīng)用的要求。例如,訂單模型可能有本系統(tǒng)的訂單,也有網(wǎng)上訂單,或者其他系統(tǒng)的訂單,但對于訂單的處理都是一樣,也就是說訂單的處理是一致的。按MVC設(shè)計模式,一個訂單模型以及多個視圖即可解決問題。這樣減少了代碼的復(fù)制,即減少了代碼的維護(hù)量,一旦模型發(fā)生改變,也易于維護(hù)。



            MVC設(shè)計模型



            其次,由于模型返回的數(shù)據(jù)不帶任何顯示格式,因而這些模型也可直接應(yīng)用于接口的使用。

            再次,由于一個應(yīng)用被分離為三層,因此有時改變其中的一層就能滿足應(yīng)用的改變。一個應(yīng)用的業(yè)務(wù)流程或者業(yè)務(wù)規(guī)則的改變只需改動MVC的模型層。

            控制層的概念也很有效,由于它把不同的模型和不同的視圖組合在一起完成不同的請求,因此,控制層可以說是包含了用戶請求權(quán)限的概念。

            最后,它還有利于軟件工程化管理。由于不同的層各司其職,每一層不同的應(yīng)用具有某些相同的特征,有利于通過工程化、工具化產(chǎn)生管理程序代碼。

            MVC的缺點

            MVC的設(shè)計實現(xiàn)并不十分容易, 理解起來比較容易,但對開發(fā)人員的要求比較高。MVC只是一種基本的設(shè)計思想,還需要詳細(xì)的設(shè)計規(guī)劃。

            模型和視圖的嚴(yán)格分離可能使得調(diào)試?yán)щy一些,但比較容易發(fā)現(xiàn)錯誤。

            經(jīng)驗表明,MVC由于將應(yīng)用分為三層,意味著代碼文件增多,因此,對于文件的管理需要費點心思。

            綜合上述,MVC是構(gòu)筑軟件非常好的基本模式,至少將業(yè)務(wù)處理與顯示分離,強(qiáng)迫將應(yīng)用分為模型、視圖以及控制層, 使得你會認(rèn)真考慮應(yīng)用的額外復(fù)雜性,把這些想法融進(jìn)到架構(gòu)中,增加了應(yīng)用的可拓展性。如果能把握到這一點,MVC模式會使得你的應(yīng)用更加強(qiáng)壯,更加有彈性,更加個性化。
            ??? 引言: MVC如何工作 MVC是一個設(shè)計模式,它強(qiáng)制性的使應(yīng)用程序的輸入、處理和輸出分開。?


            ?·網(wǎng)絡(luò)上md5的java實現(xiàn).??? ?顯示摘要???? 摘要:無意間發(fā)現(xiàn)的.不過章章師兄研究的密碼學(xué)里md5好象已經(jīng)用c++實現(xiàn)了.package com.sinosoftgroup.msd.util; /** * * <p>title: </p> * <p>description: </p> * <p>copyright: copyright (c) 2003</p> * <p...... ·jie server開發(fā)指南??? ?顯示摘要???? 摘要: 一個簡單的action,返回hello world  package org.jie.demo; import org.jie.server.action.standardaction;import javax.servlet.http.httpservletrequest; class testaction extends standardaction { publi......

            MVC設(shè)計模式帶來更好的軟件結(jié)構(gòu)和代碼重用
            mvc如何工作

            mvc是一個設(shè)計模式,它強(qiáng)制性的使應(yīng)用程序的輸入.處理與輸出分開.使用mvc應(yīng)用程序被分成三個核心部件:模型.視圖.控制器.它們各自處理自己的任務(wù).

            如何處理應(yīng)用程序的界面變得越來越有挑戰(zhàn)性.mvc一個大的好處是它能為你的應(yīng)用程序處理很多不同的視圖.在視圖中其實沒有真正的處理發(fā)生,不管這些數(shù)據(jù)是聯(lián)機(jī)存儲的還是一個雇員列表,作為視圖來講,它只是作為一種輸出數(shù)據(jù)并允許用戶操縱的方式. 【程序編程相關(guān):Hibernate middlegen中】

            視圖
            視圖是用戶看到并與之交互的界面.對老式的web應(yīng)用程序來說,視圖就是由html元素組成的界面,在新式的web應(yīng)用程序中,html依舊在視圖中扮演著重要的角色,但一些新的技術(shù)已層出不窮,它們包括macromedia flash與象xhtml,xml/xsl,wml等一些標(biāo)識語言與web services. 【推薦閱讀:JAVA文件加密器(收藏)】

            控制器
            控制器接受用戶的輸入并調(diào)用模型與視圖去完成用戶的需求.所以當(dāng)單擊web頁面中的超鏈接與發(fā)送html表單時,控制器本身不輸出任何東西與做任何處理.它只是接收請求并決定調(diào)用哪個模型構(gòu)件去處理請求,然后用確定用哪個視圖來顯示模型處理返回的數(shù)據(jù). 【擴(kuò)展信息:CruiseControl簡介】

            模型
            模型表示企業(yè)數(shù)據(jù)與業(yè)務(wù)規(guī)則.在mvc的三個部件中,模型擁有最多的處理任務(wù).例如它可能用象ejbs與coldfusion components這樣的構(gòu)件對象來處理數(shù)據(jù)庫.被模型返回的數(shù)據(jù)是中立的,就是說模型與數(shù)據(jù)格式無關(guān),這樣一個模型能為多個視圖提供數(shù)據(jù).由于應(yīng)用于模型的代碼只需寫一次就可以被多個視圖重用,所以減少了代碼的重復(fù)性

            -----------------
            強(qiáng)命名程序集(Strong Name Assembly)的概念

            ? ?因為不同的公司可能會開發(fā)出有相同名字的程序集來,如果這些程序集都被復(fù)制到同一 個相同的目錄下,最后一個安裝的程序集將會代替前面的程序集。這就是著名的Windows “DLL Hell”出現(xiàn)的原因。
            ??
            ? ?很明顯,簡單的用文件名來區(qū)分程序集是不夠的,CLR需要支持某種機(jī)制來唯一的標(biāo)識一個程序集。這就是所謂的強(qiáng)命名程序集。
            ??
            ? ?一個強(qiáng)命名程序集包含四個唯一標(biāo)志程序集的特性:文件名(沒有擴(kuò)展名),版本號,語言文化信息(如果有的話),公有秘鑰。
            ??
            ? ?這些信息存儲在程序集的清單(manifest)中。清單包含了程序集的元數(shù)據(jù),并嵌入在程序集的某個文件中。
            ??
            ? ?下面的字符串標(biāo)識了四個不同的程序集文件:
            ??
            ? ?“MyType, Version=1.0.1.0,
            ? ?Culture=neutral, PublicKeyToken=bf5779af662fc055”
            ??
            ? ?“MyType, Version=1.0.1.0,
            ? ?Culture=en-us, PublicKeyToken=bf5779af662fc055”
            ??
            ? ?“MyType, Version=1.0.2.0,
            ? ?Culture=neturl, PublicKeyToken=bf5779af662fc055”
            ??
            ? ?“MyType, Version=1.0.2.0,
            ? ?Culture=neutral, PublicKeyToken=dbe4120289f9fd8a”
            ??
            ? ?如果一個公司想唯一的標(biāo)識它的程序集,那么它必須首先獲取一個公鑰/私鑰對,然后將共有秘鑰和程序集相關(guān)聯(lián)。不存在兩個兩個公司有同樣的公鑰/私鑰對的情況,正是這種區(qū)分使得我們可以創(chuàng)建有著相同名稱,版本和語言文化信息的程序集,而不引起任何沖突。
            ??
            ? ?與強(qiáng)命名程序集對應(yīng)的就是所謂的弱命名程序集。(其實就是普通的沒有被強(qiáng)命名的程序集)。兩種程序集在結(jié)構(gòu)上是相同的。都使用相同的PE文件格式,PE表頭,CLR表頭,元數(shù)據(jù),以及清單(manifest)。二者之間真正的區(qū)別在于:強(qiáng)命名程序集有一個發(fā)布者的公鑰/私鑰對簽名,其中的公鑰/私鑰對唯一的標(biāo)識了程序集的發(fā)布者。利用公鑰/私鑰對,我們可以對程序集進(jìn)行唯一性識別、實施安全策略和版本控制策略,這種唯一標(biāo)識程序集的能力使得應(yīng)用程序在試圖綁定一個強(qiáng)命名程序集時,CLR能夠?qū)嵤┠承耙汛_知安全”的策略(比如只信任某個公司的程序集)。
            請說明在.net中常用的幾種頁面間傳遞參數(shù)的方法,并說出他們的優(yōu)缺點。
            session(viewstate) 簡單,但易丟失
            application 全局
            cookie 簡單,但可能不支持,可能被偽造
            input type="hidden" 簡單,可能被偽造
            url參數(shù) 簡單,顯示于地址欄,長度有限
            數(shù)據(jù)庫 穩(wěn)定,安全,但性能相對弱
            ===============================asp.net 頁面生命周期

            錯誤:
            ??? 每個頁面的生命周期為用戶的訪問開始到結(jié)束,也就是說程序中的全局變量同時生存到用戶的訪問結(jié)束.

            正確:
            ??? 每個頁面的生命周期為用戶的每一次訪問,也就是說每一次客戶端與服務(wù)器之間的一個往返過程.全局變量的生命周期在此之間.

            例:
            string gb_string;

            void Page_Load()
            {
            ??? if(!IsPostBack)
            ??? {
            ????? //第一次被訪問的代碼
            ????? gb_string ="Test!";???
            ??? }
            }


            void btnSubmit_Click(Object sender, EventArgs e)
            {
            //在這里gb_string 為空,是這一次postback新建的字符串

            //跟第一次那個被賦值的字符串不是同一對象.
            }

            ASP.NET 頁面生命周期
            1. Page_Init();
            2. Load ViewState and Postback data;
            3. Page_Load();
            4. Handle control events;
            5. Page_PreRender();
            6. Page_Render();
            7. Unload event;
            8. Dispose method called;

            總結(jié):

            在Page_Load()中執(zhí)行的初始化工作


            ???? 1.一般都是初始化web控件,因為這些控件的狀態(tài)(值),由系統(tǒng)自動保存(__VIEWSTATE),
            下次返回時,自動賦值,這是ASP.NET保存"web控件"狀態(tài)的特性.
            ??

            ?? 2.但是初始的如果是程序中用到的全局變量,就應(yīng)該注意他的生命周期的問題.如果函數(shù)都在一個生命周期中,全局變量的值當(dāng)然是一致的.但是如果在"由客戶激發(fā)"的事件中,這就不同了,每個客戶事件的激發(fā),都會導(dǎo)致新的頁面生命周期的產(chǎn)生,全局變量的值會丟失,因為它們不在同一頁面生命周期(就像上面出現(xiàn)的錯誤1).


            *********************************************************

            asp.net2.0 頁面生命周期方法

            方法 活動

            Constructor

            Always

            Construct

            Always

            TestDeviceFilter

            Always

            AddParsedSubObject

            Always

            DeterminePostBackMode

            Always

            OnPreInit

            Always

            LoadPersonalizationData

            Always

            InitializeThemes

            Always

            OnInit

            Always

            ApplyControlSkin

            Always

            ApplyPersonalization

            Always

            OnInitComplete

            Always

            LoadPageStateFromPersistenceMedium

            Always

            LoadControlState

            Always

            LoadViewState

            Always

            ProcessPostData1

            Always

            OnPreLoad

            Always

            OnLoad

            Always

            ProcessPostData2

            Always

            RaiseChangedEvents

            Always

            RaisePostBackEvent

            Always

            OnLoadComplete

            Always

            OnPreRender

            Always

            OnPreRenderComplete

            Always

            SavePersonalizationData

            Always

            SaveControlState

            Always

            SaveViewState

            Always

            SavePageStateToPersistenceMedium

            Always

            Render

            Always

            OnUnload

            Always

            查看頁面生命周期的底層細(xì)節(jié),我們可以看到 ASP.NET 2.0 中提供的許多功能(例如主題和個性化)將在什么地方容易實現(xiàn)。例如,主題在 IntializeThemes 事件中處理,而個性化數(shù)據(jù)將在 LoadPersonalizationData 中加載并稍后用于 ApplyPersonalization 方法。請注意,就哪一個 UI 元素將決定 Web 應(yīng)用程序的最終外觀和感覺而言,方法的順序非常重要。


            在以前寫個一篇關(guān)于ASP.NET頁面生命周期的草稿,最近又看了看ASP.NET,做個補(bǔ)充,看看頁面初始過程到底是怎么樣的
            下面是ASP.NET頁面初始的過程:
            1. Page_Init();
            2. Load ViewState;
            3. Load Postback data;
            4. Page_Load();
            5. Handle control events;
            6. Page_PreRender();
            7. Page_Render();
            8. Unload event;
            9. Dispose method called;

            下面對其中的一些過程作下描述:
            1. Page_Init();
            這個過程主要是初始化控件,每次頁面載入執(zhí)行這個初始過程,包括第一次和以后的Postback(這里說下Postback,其實就可以簡單理解成用戶點
            擊SUBMIT按鈕之類的,把表單<Form>提交給服務(wù)器,這就是一次postback),在這里面可以訪問控件,但是這里面的控件值不是我們期待的控件里面 的值,他只是一個控件的初始值(默認(rèn)值),舉例: 比如一個TextBox1,我們填入了"哈哈",在點擊SUBMIT提交了頁面后,在Page_Init()里面,我們訪 問到的TextBox1.Text不是我們的"哈哈",而是開始的""空字符串,如果TextBox1在我們設(shè)計的時候提供了默認(rèn)值,這里訪問到的也就是提供的默 認(rèn)值,為什么呢,這就要看下一個過程了.

            對應(yīng)的事件Page.Init

            2. Load ViewState
            這個過程是載入VIEWSTATE和Postback數(shù)據(jù),比如我們上面的TextBox1,這時就賦了"哈哈",所以,在Post_Init()對控件賦值是無意義的,它都會
            在這個過程里被改寫,當(dāng)然第一次頁面載入例外,因為沒有VIEWSTATE數(shù)據(jù)。

            沒有對應(yīng)的事件

            3.Load Postback data;
            上面說了,Postback可以理解成用戶提交表單數(shù)據(jù),所以這里就是處理表單數(shù)據(jù),當(dāng)然這里要設(shè)計到控件的設(shè)計,一般情況不會要我們自己處理這
            個過程,我們暫且略過. (在以前那篇關(guān)于ASP.NET頁面生命周期的簡單描述中,把這個過程和Load ViewState放在了一起,其實那是微軟提供的生命周期過程,這里單獨提出來是為 了讓大家明白這是一個單獨的過程)
            ?
            沒有對應(yīng)的事件
            4. Page_Load();
            這個過程也是每次頁面載入時一定會執(zhí)行的,但是注意和Page_Init的區(qū)別,上面已經(jīng)涉及了,這里注意的是一般都會用到Page.IsPostBack,該
            值指示該頁是否正為響應(yīng)客戶端回發(fā)而加載,或者它是否正被首次加載和訪問。
            private void Page_Load(object sender, System.EventArgs e)
            {
            ? if(!Page.IsPostBack)
            ? {
            ???//第一次執(zhí)行的CODE HERE
            ? }
            ? else
            ? {
            ??? //用戶提交FORM(即Postback)CODE HERE
            ? }

            ? //每次這里的都回執(zhí)行CODE HERE
            }

            對應(yīng)的事件Page.Load

            5. Handle control events;
            這個過程里,相應(yīng)具體的控件事件,比如private void ListBox1_SelectedIndexChanged(object sender, System.EventArgs e)事件等等

            沒有對應(yīng)的事件(我們自己的事件函數(shù)都包括在這個過程里比如上面的ListBox1_SelectedIndexChanged)

            6. Page_
            預(yù)先呈遞對象,這里是在向用戶程序呈現(xiàn)數(shù)據(jù)的倒數(shù)第二步,我估計提供這個過程的意義,也就是在這里能對控件屬性等等要呈現(xiàn)給用戶的數(shù)據(jù)進(jìn)
            行修改,這也是最后的修改,以前的修改(比如在Page_Init里面)都可能被覆蓋.做完這了還會進(jìn)行一個操作就是保存狀態(tài),即SaveViewState.

            對應(yīng)的事件時Page.PreRender

            7. Page_Render();
            大家可以在瀏纜器里View->Source查看到,每個頁面都有一個隱藏的<input>,這里面的"__VIEWSTATE"就是我們服務(wù)器寫回來的頁面狀態(tài)信息,
            在這個之前,服務(wù)器要呈現(xiàn)頁面(也就是構(gòu)造HTML格式的文件),就是從這個"__VIEWSTATE"里面獲取的數(shù)據(jù),當(dāng)然大家也注意到了,這里有個Page.Render事件,我們可以添加自己的處理代碼,也就是說我們又可以更改數(shù)據(jù),不過這里推薦不要在這里修改,既然提供了PreRender,就應(yīng)該在里面做最后的修改,當(dāng)然這不是必須的,只是推薦!

            對應(yīng)的事件Page.Render

            8. Unload event;
            大家應(yīng)該明白,當(dāng)想服務(wù)器請求一個對象的時候,就會在內(nèi)存里生成一個繼承頁面對象,也就是頁面的類,它繼承自System.Web.UI.Page.
            當(dāng)頁面對象從內(nèi)存中卸載時發(fā)生,將觸發(fā)該事件.

            對應(yīng)的事件Page.Unload

            9. Dispose method called;
            銷毀所有的對象.當(dāng)從內(nèi)存釋放Page時發(fā)生,這是生存期的最后階段。可能第8和9似乎有些模糊,不過我也沒怎么搞清楚,待研究!

            對應(yīng)的事件Dispose

            以上就是ASP.NET頁面周期的描述。

            注意上面灰色背景的文字,如果一個過程中有對應(yīng)的事件,我們可以自己定義一個函數(shù)(當(dāng)然先在MSDN中找到函數(shù)原型),然后在
            InitializeComponent中向事件的鏈表上添加上去,像下面:
            private void InitializeComponent()
            {???
            ? this.Unload += new System.EventHandler(this.MainWebForm_Unload);
            ? this.Load += new System.EventHandler(this.Page_Load);
            ? this.Init += new System.EventHandler(this.Page_Init);
            ? this.PreRender += new System.EventHandler(this.My_PreRender);
            }

            對于幾個沒有對應(yīng)事件的過程,比如2.Load ViewState,我們可以重載Page的虛函數(shù)protected override void LoadViewState(object savedState);來添加自己的控制代碼,不過切忌掉用基類的對應(yīng)方法,比如:
            protected override void LoadViewState(object savedState)
            {
            ? //自己處理VIEWSTATE
            ? base.LoadViewState (savedState);
            }

            初學(xué)ASP.NET,請各位不吝賜教!???
            ====================\

            Asp.net2.0頁面的生命周期

            表格 1. ASP.NET 頁面生存周期中的關(guān)鍵事件

            階段

            頁面事件

            可重寫方法

            頁面初始化

            Init

            加載視圖狀態(tài)

            LoadViewState

            處理回發(fā)數(shù)據(jù)

            實現(xiàn) IPostBackDataHandler 接口的任何控件中的 LoadPostData 方法

            加載頁面

            Load

            回發(fā)更改通知

            實現(xiàn) IPostBackDataHandler 接口的任何控件中的 RaisePostDataChangedEvent 方法

            處理回發(fā)事件

            控件所定義的任何回發(fā)事件

            實現(xiàn)了 IPostBackEventHandler 接口的任何控件的 RaisePostBackEvent 方法

            頁面呈現(xiàn)前階段

            PreRender

            保存視圖狀態(tài)

            SaveViewState

            呈現(xiàn)頁面

            Render

            卸載頁面

            Unload

            ?

            ?

            ?

            當(dāng)一個獲取網(wǎng)頁的請求(可能是通過用戶提交完成的,也可能是通過超鏈接完成的)被發(fā)送到Web服務(wù)器后,這個頁面就會接著運行從創(chuàng)建到處理完成的一系列事件。在我們試圖建立Asp.net頁面的時候,這個執(zhí)行周期是不必去考慮的,那樣只會自討苦吃。然而,如果被正確的操縱,一個頁面的執(zhí)行周期將是一道有效而且功能強(qiáng)大的工具。許多開發(fā)者在編寫 Asp.net的頁面以及用戶控件的時候發(fā)現(xiàn),如果知道整個過程中發(fā)生了什么以及在什么時候發(fā)生將對完成整個任務(wù)起到很重要的幫助作用。下面我就向大家介紹一下一個Asp.net頁面從創(chuàng)建到處理完成過程中的十個事件。同時,也向大家展示如何在這些事件中添加自己的代碼以達(dá)到預(yù)定的效果。

            一.預(yù)初始化對象(OnPreInit)

            ?

            protected ? override ? void ?OnPreInit(EventArgs?e)???????
            {
            ????
            // ?custom?code????????????

            ???? base .OnPreInit(e);
            }


            注意,我們只能在PreInit()事件中動態(tài)的設(shè)置themes

            使用母版頁時的特例
            我們先要了解一個非常重要的知識點——母版頁被處理的過程就相當(dāng)于內(nèi)容頁中的一個控件。

            所以如果一個頁有其相關(guān)聯(lián)的母版頁的話,那么在PreInit()事件里頁中的所有控件都不會被初始化。而只有在Init()事件開始之后,你才能直接訪問這些控件。為什么?

            這 個原因就是內(nèi)容頁中的所有控件都包含在“ContentPlaceholder”里,而“ContentPlaceholder”其實就是母版頁的一個子 控件。現(xiàn)在母版頁被處理的過程就相當(dāng)于內(nèi)容頁中的一個控件,我們早先提到過,除了Init()和Unload()之外的所有事件都是從最外面到最里面被激 發(fā)的。雖然頁的PreInit()是第一個被觸發(fā)的事件,但是用戶控件和母版頁是沒有這個事件的,所以在頁的Page_PreInit()方法中,母版頁 和用戶控件都不會被初始化,而是在Init()事件之后

            接下來讓我們來看一下Page_Init()事件之后控件的層次結(jié)構(gòu)

            在這個頁面級的事件中,所有在設(shè)計時創(chuàng)建的控件都將被用默認(rèn)值做初始化。例如,如果你有一個Text屬性值為“Hello”的TextBox控件,則此時這個屬性被設(shè)置。我們也可以在這里動態(tài)的創(chuàng)建控件。

            這個事件僅僅發(fā)生在頁級別的類中,用戶控件和母版頁沒有這個事件

            下面的代碼示例了如何重寫這個方法以增加你的自定義代碼

            二.初始化對象(OnInit)

            一個頁面的控件(以及頁面本身)最初應(yīng)被正確的初始化。通過在你的C#文件的構(gòu)造函數(shù) 中聲名所有對象,頁面就知道要創(chuàng)建多少對象以及它們的類型。一旦你在你的構(gòu)造函數(shù)中聲名了所有的對象,你就可以通過繼承類、方法、事件或是屬性訪問它們。 然而,如果你的一些對象是在Aspx文件中指定的一些控件,那么這些控件就沒有屬性可言了。同時,通過代碼訪問它們會產(chǎn)生一些意外的錯誤,因為這些控件實 例是沒有一個確定的創(chuàng)建順序的(如果它們是被一起創(chuàng)建的)。還有,你可以通過OnInit來重載初始化事件.

            在這個事件里,我們能讀出控件的屬性(在設(shè)計模式中設(shè)置的)。但是我們不能讀出用戶設(shè)置的值,因為得到用戶設(shè)置的值是在LoadPostData()事件被激發(fā)之后。不過在這個事件中我們可以得到POST數(shù)據(jù),如下

            string ?selectedValue? = ?Request.Form[controlID].ToString();

            三.完成初始化(OnInitComplete)

            完成初始化頁面OnInit事件后觸發(fā)。
            ?

            四.導(dǎo)入Viewstate數(shù)據(jù)(LoadViewState)

            在初始化事件后,所有控件只可以通過它們的ID被引用訪問(因為還沒有相應(yīng)的DOM可使用)。在 LoadViewState這個事件中,所有的控件將獲得它們的第一個屬性:Viewstate屬性。這個屬性最終將被返回給服務(wù)器以判斷這個頁面是已經(jīng) 被用戶訪問完畢還是仍然在被用戶所訪問。Viewstate屬性以“名稱/值”對的字符串方式被保存,它包含了控件的文本以及值等信息。該屬性被存儲在一 個隱藏的<input>控件的值屬性里,在請求頁面時被傳遞。這種方式比起Asp3.0的維持、判斷頁面狀態(tài)的方式有了很大的進(jìn)步啊。還有, 你可以重載LoadViewState事件函數(shù)來對相應(yīng)的控件進(jìn)行值設(shè)定。下圖是一個例子:

            ?

            五.用LoadPostData處理Postback數(shù)據(jù)(LoadPostData)

            在頁面創(chuàng)建的這個階段,服務(wù)器對頁面上的控件提交的表單數(shù)據(jù)(在Asp.net中稱postback數(shù)據(jù))進(jìn)行處理。當(dāng)一個頁面提交一個表單時,框 架就在每個提交了數(shù)據(jù)的控件上執(zhí)行一個IPostBackDataHandler接口操作。然后頁面執(zhí)行LoadPostData事件,解析頁面,找到每 個執(zhí)行了IpostBackDataHandler接口操作的控件,并用恰當(dāng)?shù)膒ostback數(shù)據(jù)更新這些控件狀態(tài)。Asp.net是通過用 NameValue集中的“名稱/值”對和每個控件的唯一的ID匹配來實現(xiàn)這一操作的。所以,在Asp.net的頁面上每個控件必須有一個唯一的ID,不 可以出現(xiàn)幾個控件共有ID的情況。即使是用戶自定義的一些控件,框架也會賦予它們各自唯一的ID的。在LoadPostData事件后,就要執(zhí)行下面的 RaisePostDataChanged事件了。

            ?

            六.OnPreLoad

            在加載頁面OnLoad事件前觸發(fā)。可以在頁面里面通過Page_OnPreLoad事件綁定

            七.導(dǎo)入對象(OnLoad)

            Page_Load是事件綁定得方法 ?
            ? page.load ? + ? = ? new ? eventhandler(Page_Load)???
            OnLoad()是引發(fā)Load事件主要是判斷事件是否為空,如果不為空就執(zhí)行事件下綁定得方法

            在Load事件中,對象都實例化了。所有的對象第一次被布置在DOM頁面(在Asp.net中稱控件樹)里了并且可以通 過代碼或是相關(guān)的位置被引用。這樣,對象就可以很容易的從客戶端獲得諸如寬度、高度、值、可見性等在Html中的屬性值。在Load事件中,當(dāng)然還有像設(shè) 置控件屬性等操作的發(fā)生。這個過程是整個生命周期中最重要、最主要的,你可以通過調(diào)用OnLoad來重載Load事件,圖示如下:

            ?

            八.RaisePostBackChanged事件(RaisePostDataChangedEvent)

            就像在上面提到的那樣,這個事件是發(fā)生在所有的控件執(zhí)行了IPostBackDataHandler接口操作并被正確的 postback數(shù)據(jù)更新后的。在這個過程中,每個控件都被賦予一個布爾值來標(biāo)志該控件有沒有被更新。然后,Asp.net就在整個頁面上尋找任何已被更 新過的控件并執(zhí)行RaisePostDataChanged事件操作。不過,這個事件是要在所有的控件都被更新了以及Load事件完成后才進(jìn)行的。這樣就 保證了一個控件在被postback數(shù)據(jù)更新前,別的控件在RaisePostDataChanged事件中是不會被手動改變的。

            九.處理客戶端PostBack事件(RaisePostBackEvent)

            當(dāng)由postback數(shù)據(jù)在服務(wù)器端引起的事件都完成后,產(chǎn)生postback數(shù)據(jù)的對象就執(zhí)行 RaisePostBackEvent事件操作。可是會有這種情況,由于一個控件狀態(tài)的改變使得它將表單返回給服務(wù)器或是用戶點擊了提交按鈕使得表單返回 給服務(wù)器。在這種情況下應(yīng)該有相應(yīng)的處理代碼來體現(xiàn)事件驅(qū)動這一面向?qū)ο螅∣OP)編程原則。由于要滿足呈現(xiàn)給瀏覽器的數(shù)據(jù)的精確性要求,在一系列 postback事件中RaisePostBackEvent事件是最后發(fā)生的。

            在postback過程中改變的控件不應(yīng)在執(zhí)行功能函數(shù)被調(diào)用后更新。也就是說,任何由于一個預(yù)期的事件而改變的數(shù)據(jù)應(yīng)該在最終的頁面上被反映出來。你可以通過修改RaisePostBackEvent函數(shù)來滿足你的要求,圖示如下:

            ?

            十.Page_OnLoadComplete

            完成頁面加載OnLoad事件后觸發(fā)。

            十一.預(yù)先呈遞對象

            可以改變對象并將改變保存的最后時刻就是這一步――預(yù)先呈遞對象。這樣,你可以在這一步對控件的屬性、控件樹結(jié)構(gòu)等作出最后的修改。同時還不用考慮 Asp.net對其作出任何改變,因為此時已經(jīng)脫離了數(shù)據(jù)庫調(diào)用以及viewstate更新了。在這一步之后,對對象的所有修改將最終被確定,不能被保存 到頁面的viewstate中了。你可以通過OnPreRender來重載這一步。

            十二.完成預(yù)呈現(xiàn)(OnPreRenderComplete)

            在完成預(yù)呈現(xiàn)OnPreRender事件后觸發(fā)。這是完成頁面呈現(xiàn)的最后一道關(guān)卡,在此之后,頁面將無法再進(jìn)行任何呈現(xiàn)上的改動。

            十三.保存ControlState(SaveControlState)
            ?
            保 存控件狀態(tài)ControlState。ControlState是ASP.NET2.0控件新增的一個屬性,類似ViewState作用,但它們區(qū)別在于 ControlState用于保存更加重要的控件狀態(tài)信息,以保證在禁用ViewState的情況下還可以對控件狀態(tài)進(jìn)行讀寫操作。

            八.保存ViewState(SaveViewState)

            所有對頁面控件的修改完成后viewstate就被保存了。對像的狀態(tài)數(shù)據(jù)還是保留在隱藏的<input> 控件里面,呈現(xiàn)給Html的對象狀態(tài)數(shù)據(jù)也是從這里取得的。在SaveViewState事件中,其值能被保存到viewstate對象,然而這時在頁面 上控件的修改卻不能了。你可以用SaveViewState來重載這一步,圖示如下:

            ?

            九.呈遞給Html(Render)

            運用Html創(chuàng)建給瀏覽器輸出的頁面的時候Render事件就發(fā)生了。在Render事件過程中,頁面調(diào)用其中的對象將 它們呈遞給Html。然后,頁面就可以以Html的形式被用戶的瀏覽器訪問了。當(dāng)Render事件被重載時,開發(fā)者可以編寫自定義的Html代碼使得原先 生成的Html都無效而按照新的Html來組織頁面。Render方法將一個HtmlTextWriter對象作為參數(shù)并用它將Html在瀏覽器上以網(wǎng)頁 的形式顯示。這時仍然可以做一些修改動作,不過它們只是客戶端的一些變化而已了。你可以重載Render事件,圖示如下:

            ?

            十.銷毀對象(Page_UnLoad)

            在呈遞給Html完成后,所有的對象都應(yīng)被銷毀。在Dispose事件中,你應(yīng)該銷毀所有在建立這個頁面時創(chuàng)建的對象。這時,所有的處理已經(jīng)完畢,所以銷毀任何剩下的對象都是不會產(chǎn)生錯誤的,包括頁面對象。你可以重載Dispose事件。

            全文總結(jié)

            以上就是Asp.net頁面生命周期中的幾個主要事件。每次我們請求一個Asp.net頁面時,我們都經(jīng)歷著同樣的過程:從初始化對象到銷毀對象。 通過了解Asp.net頁面的內(nèi)部運行機(jī)制,我相信大家在編寫、調(diào)試代碼的時候會更加游刃有余的。不過整個頁面的生命周期的方法如下:

            方法 活動

            Constructor

            Always

            Construct

            Always

            TestDeviceFilter

            Always

            AddParsedSubObject

            Always

            DeterminePostBackMode

            Always

            OnPreInit

            Always

            LoadPersonalizationData

            Always

            InitializeThemes

            Always

            OnInit

            Always

            ApplyControlSkin

            Always

            ApplyPersonalization

            Always

            OnInitComplete

            Always

            LoadPageStateFromPersistenceMedium

            Always

            LoadControlState

            Always

            LoadViewState

            Always

            ProcessPostData1

            Always

            OnPreLoad

            Always

            OnLoad

            Always

            ProcessPostData2

            Always

            RaiseChangedEvents

            Always

            RaisePostBackEvent

            Always

            OnLoadComplete

            Always

            OnPreRender

            Always

            OnPreRenderComplete

            Always

            SavePersonalizationData

            Always

            SaveControlState

            Always

            SaveViewState

            Always

            SavePageStateToPersistenceMedium

            Always

            Render

            Always

            OnUnload

            Always

            查看頁面生命周期的底層細(xì)節(jié),我們可以看到 ASP.NET 2.0 中提供的許多功能(例如主題和個性化)將在什么地方容易實現(xiàn)。例如,主題在 IntializeThemes 事件中處理,而個性化數(shù)據(jù)將在 LoadPersonalizationData 中加載并稍后用于 ApplyPersonalization 方法。請注意,就哪一個 UI 元素將決定 Web 應(yīng)用程序的最終外觀和感覺而言,方法的順序非常重要。

            AspNet2.0頁面生命周期 ?

            頁面框架通過如下過程處理aspx文件請求:
            ??? 1:解析aspx文件,并創(chuàng)建一個控件樹;
            ??? 2:使用控件樹動態(tài)實現(xiàn)一個繼承自Page類的類或者控件 ;
            ??? 3:動態(tài)編譯類;
            ??? 4:緩存編譯類,以備后用;
            ??? 5:動態(tài)創(chuàng)建一個編譯類的實例。頁面開始啟動生命期,在這個過程中,頁面將通過生命周期的不同階段;=========================================================
            頁面經(jīng)歷了如下階段【其中一些階段標(biāo)記為僅僅回傳,是說只有在被回傳到服務(wù)器時,頁面才經(jīng)歷這些階段】:

            01:頁面首先從QueryString或者Request對象的Form集合中獲得回傳數(shù)據(jù)。

            02:頁面檢測回傳數(shù)據(jù)集合(NameValueCollection,Form或者QueryString)是否包含一個鍵為_CallBackId的項。如? 果存在,那么設(shè)置其Boolean屬性IsCallBack為True,以便通過AspNet客戶端回調(diào)機(jī)制,標(biāo)明頁面已經(jīng)回傳到服務(wù)器。

            03:預(yù)初始化(PreInit):
            ? 在頁面生命周期的預(yù)初始化階段執(zhí)行如下操作:
            ?? a:調(diào)用OnPreInit方法引發(fā)PreInit事件。
            ?? b:利用App_Themes目錄中的內(nèi)容初始化主題,以動態(tài)實現(xiàn)一個PageTheme類型的類,
            ??? 編譯該類,并創(chuàng)建一個編譯類的實例,接著將實例賦值給它的PageTheme屬性值
            ?? c:應(yīng)用母版頁

            04:初始化(Init):
            ? 在頁面生命周期的初始化階段執(zhí)行以下操作
            ?? a:遞歸初始化Controls集合中的控件。初始化包括設(shè)置這些控件的屬性,
            ??? 例如:Page,Id和NameContainer等
            ?? b:遞歸應(yīng)用控件皮膚
            ?? c:調(diào)用OnInit方法以引發(fā)自身的Init事件,接著遞歸調(diào)用子控件的OnInit方法來引發(fā)它們的Init事件
            ?? d:調(diào)用自身的TrackViewState方法來啟動自身的視圖狀態(tài)跟蹤,接著遞歸調(diào)用子控件
            ??? 的TrackViewState方法來啟動它們的視圖狀態(tài)跟蹤。

            05:完成初始化(InitComplete):
            ? 頁面調(diào)用OnInitComplete方法來引發(fā)InitComplete事件。該事件標(biāo)明了初始化階段的結(jié)束。
            此時,頁面Controls集合的所有控件都被初始化了。


            06:加載控件狀態(tài)(Load Control State)[PostBack Only]:
            ? 頁面遞歸調(diào)用Control集合中控件的LoadControlState方法,這些控件已經(jīng)調(diào)用了Page類
            的RegisterRequiresControlState方法,以使用它們的控件狀態(tài)。

            07:加載視圖狀態(tài)(Load View State)[PostBack Only]:
            ? 頁面首先調(diào)用自身的LoadViewState方法,接著遞歸調(diào)用Controls集合控件的LoadViewState方法,以允許它們加載它們的已經(jīng)保存的視圖狀態(tài)。

            08:加載回傳數(shù)據(jù)(Load Post Data)[PostBack Only]:
            ? 頁面調(diào)用實現(xiàn)IPostBackDataHandler接口的控件的LoadPostData方法,并將回傳數(shù)據(jù)傳遞給該方法。每個控件的LoadPostDate方法都必須訪問回傳數(shù)據(jù),并據(jù)此更新相應(yīng)的控件屬性。
            例如:TextBox控件的LoadPostData方法將文本框的新值設(shè)置為TextBox控件的Text屬性值。

            09:預(yù)加載(PreLoad):
            ? 頁面調(diào)用OnPreLoad方法以引發(fā)PreLoad事件。該事件表示頁面生命周期將進(jìn)入加載階段。

            10:加載(Load):
            ? 頁面首先調(diào)用自身的OnLoad方法以引發(fā)自身的Load事件,接著遞歸調(diào)用Controls集合中控件的OnLoad方法以引發(fā)它們的Load事件。頁面開發(fā)人員可以為Load事件注冊回調(diào),那么就可以通過編程將子控件添加到頁面的Controls集合中。

            11:加載回傳數(shù)據(jù)(Load Post Data)[PostBack Only Second Try]:
            ? 頁面調(diào)用控件的LoadPostBack方法。這些控件如果實現(xiàn)了IPostBackDataHandler接口,那么在加載階段,它們已通過編程添加到Controls集合中。

            12:引發(fā)回傳數(shù)據(jù)修改事件(Raise Post Data Changed Event)[PostBack Only]:
            ? 頁面調(diào)用控件的RaisePostDataChangeEvent方法,這些控件的LoadPostData方法返回true。
            RaisePostDataChangeEvent方法引發(fā)了回傳數(shù)據(jù)修改事件。例如:當(dāng)文本框的新值與舊值
            不同時,那么TextBox控件將引發(fā)該事件。

            13:引發(fā)回傳事件(Raise PostBack Event)[PostBack Only]:
            ? 頁面調(diào)用控件的RaisePostEvent方法,這些控件相關(guān)的Html元素用于提交表單。例如,Button控件的相關(guān)Html元素將頁面回傳到服務(wù)器。控件的RaisePostBackEvent方法必須將回傳事件映射到一個或多個服務(wù)器事件。例如,Button控件的 RaisePostBackEvent方法將事件映射到了服務(wù)器端事件Command和Click上。

            14:完成加載(Load Complete):
            ? 頁面調(diào)用OnLoadComplete方法來引發(fā)LoadComplete事件,這表示所有加載活動,包括加載回傳數(shù)據(jù),以及引發(fā)回傳數(shù)據(jù)修改事件,并以更新控件自身的活動都完成了。

            15:引發(fā)回調(diào)事件(Raise CallBack Event)(PostBack And CallBack Only):
            ? 頁面調(diào)用控件的RaiseCallBackEvent方法。該控件可使用AspNet客戶端回調(diào)機(jī)制來允許客戶端方法(例如JavaScript函數(shù))調(diào)用服務(wù)器端方法,而無需將整個頁面回傳給服務(wù)器。
            RaiseCallBackEvent方法必須調(diào)用服務(wù)器端方法。如果頁面的回傳使用了客戶端回調(diào)機(jī)制,那么頁面將不會執(zhí)行剩余的頁面生命周期階段。

            16:預(yù)呈現(xiàn)(PreRender):
            ? 在頁面生命周期這個階段執(zhí)行一下操作。
            ?? a:調(diào)用EnsureChildControls方法來確保在頁面進(jìn)入呈現(xiàn)階段之前,創(chuàng)建其子控件。
            ?? b:調(diào)用自身的OnPreRender方法來引發(fā)PreRender事件。
            ?? c:遞歸調(diào)用Controls集合中控件的OnPreRender方法,以引發(fā)它們的PreRender事件。

            17:預(yù)呈現(xiàn)完成(PreRender Complete):
            ? 頁面調(diào)用OnPrerenderComplete方法來引發(fā)PreRenderComplete事件,這表示所有預(yù)呈現(xiàn)活動完成了。

            18:保存控件狀態(tài)(Save Control State):
            ? 頁面遞歸調(diào)用Controls集合中控件的SaveControlState方法。這些控件已經(jīng)調(diào)用了Page類的RegisterRequiresControlState方法來保存它們的控件狀態(tài)。

            19:保存視圖狀態(tài)(Save View State):
            ? 頁面首先調(diào)用自身的SaveViewState方法,然后調(diào)用Controls集合中的SaveViewState方法,以允許它們來保存其視圖狀態(tài)。

            20:保存狀態(tài)完成(Save View Complete):
            ? 頁面調(diào)用OnSaveStateComplete方法以引發(fā)SaveStateComplete事件,這表示所有狀態(tài)保存活動都完成了。

            21:呈現(xiàn):
            ? 在頁面生命周期的這個階段執(zhí)行一下操作。
            ?? a:創(chuàng)建一個HtmlTextWriter類實例,該實例封裝了輸出響應(yīng)流
            ?? b:調(diào)用RenderCOntrol方法,并將HtmlTextWriter實例傳遞給該方法。
            ? RenderControl方法遞歸調(diào)用子控件的RenderControl方法,以便允許每個控件能夠呈現(xiàn)其
            Html標(biāo)記文本。子控件的Html標(biāo)記文本組成了最終發(fā)送給客戶端瀏覽器的Html標(biāo)記文本。



            ?

            新一篇:?單點登錄在ASP.NET上的簡單實現(xiàn)

            ?????在ASP.NET中,有很多種保存信息的對象.例如:APPlication,Session,Cookie,ViewState和Cache等,那么它們有什么區(qū)別呢?每一種對象應(yīng)用的環(huán)境是什么?
            ? ? 為了更清楚的了解,我們總結(jié)出每一種對象應(yīng)用的具體環(huán)境,如下表所示: ?
            方法信息量大小保存時間應(yīng)用范圍保存位置
            Application任意大小整個應(yīng)用程序的生命期服務(wù)器端
            Session小量,簡單的數(shù)據(jù)用戶活動時間+一段延遲時間(一般
            為20分鐘)

            單個用戶服務(wù)器端
            Cookie小量,簡單的數(shù)據(jù)可以根據(jù)需要設(shè)定單個用戶客戶端
            Viewstate小量,簡單的數(shù)據(jù)一個Web頁面的生命期單個用戶客戶端
            Cache任意大小可以根據(jù)需要設(shè)定所有用戶服務(wù)器端
            隱藏域小量,簡單的數(shù)據(jù)一個Web頁面的生命期單個用戶客戶端
            查詢字符串小量,簡單的數(shù)據(jù)直到下次頁面跳轉(zhuǎn)請求單個用戶客戶端
            Web.Config文件不變或極少改變的小量數(shù)據(jù)直到配置文件被更新單個用戶服務(wù)器端

            1.Application對象
            ? ? Application用于保存所有用戶的公共的數(shù)據(jù)信息,如果使用Application對象,一個需要考慮的問題是任何寫操作都要在Application_OnStart事件(global.asax)中完成.盡管使用Application.Lock和Applicaiton.Unlock方法來避免寫操作的同步,但是它串行化了對Application對象的請求,當(dāng)網(wǎng)站訪問量大的時候會產(chǎn)生嚴(yán)重的性能瓶頸.因此最好不要用此對象保存大的數(shù)據(jù)集合. 下面我們做個在線用戶統(tǒng)計的例子來說明這個問題:
            ? ? (以文件的形式存放網(wǎng)站總訪問量)
            ? ? (1)Global.asax類 using System;
            using
            System.Collections;
            using
            System.ComponentModel;
            using
            System.Web;
            using
            System.Web.SessionState;
            using
            System.IO;

            namespace
            WebAppCounter
            {
            ? ?/**/
            /// <summary>
            ? ?/// Global 的摘要說明。
            ? ?/// </summary>

            ? ?public class Global : System.Web.HttpApplication
            ? ?
            {
            ? ?? ? /**/
            /// <summary>
            ? ?? ? /// 必需的設(shè)計器變量。
            ? ?? ? /// </summary>

            ? ?? ? private System.ComponentModel.IContainer components = null;

            ? ?? ? private
            FileStream fileStream;
            ? ?? ? private StreamReader reader;//讀字符流

            ? ?? ? private StreamWriter writer;//寫字符流
            ? ?? ?
            ? ?? ? public
            Global()
            ? ?? ?
            {
            ? ?? ?? ?? InitializeComponent();
            ? ?? ? }
            ? ?

            ? ?? ? protected void
            Application_Start(Object sender, EventArgs e)
            ? ?? ?
            {
            ? ?? ?? ?? Application["CurrentGuests"]=0;//初始花為0;

            ? ?? ?? ?? fileStream = File.Open(Server.MapPath("counts.text"),FileMode.OpenOrCreate);//文件不存在,創(chuàng)建文件
            ? ?? ?? ?? reader = new StreamReader(fileStream);//要讀取的完整路徑
            ? ?? ?? ?? Application["AllGuests"] = Convert.ToInt32(reader.ReadLine()); //從當(dāng)前流中讀取一行字符并將數(shù)據(jù)作為字符串返回
            ? ?? ?? ?? reader.Close();//關(guān)閉流
            ? ?? ? }

            ? ?? ?
            protected void Session_Start(Object sender, EventArgs e)//當(dāng)用戶訪問網(wǎng)站時,在線用戶+1,總訪問數(shù)+1
            ? ?? ? {
            ? ?? ?? ?? Application.Lock();//同步,避免同時寫入

            ? ?? ?? ??
            ? ?? ?? ?? Application["CurrentGuests"] =(int)Application["CurrentGuests"]+ 1;//總在線用戶數(shù)

            ? ?? ?? ?? Application["AllGuests"] =(int)Application["AllGuests"]+ 1;//訪問網(wǎng)站的總用戶數(shù)
            ? ?? ?? ?? fileStream = new FileStream(Server.MapPath("counts.text"),FileMode.OpenOrCreate,FileAccess.ReadWrite);//
            ? ?? ?? ?? writer = new StreamWriter(fileStream);//實現(xiàn)一個寫入流,使其以一種特定的編碼向流中寫入字符

            ? ?? ?? ?? writer.WriteLine(Application["AllGuests"].ToString());//把訪問網(wǎng)站的總用戶數(shù)再次寫入到文件
            ? ?? ?? ?? writer.Close();//關(guān)閉寫入流

            ? ?? ?? ?? Application.UnLock();//同步結(jié)束
            ? ?? ? }

            ? ?? ?
            protected void Application_BeginRequest(Object sender, EventArgs e)
            ? ?? ?
            {
            ? ?? ?? ?? ?
            ? ?? ? }


            ? ?? ?
            protected void Application_EndRequest(Object sender, EventArgs e)
            ? ?? ?
            {
            ? ?? ?? ??
            ? ?? ? }


            ? ?? ?
            protected void Application_AuthenticateRequest(Object sender, EventArgs e)
            ? ?? ?
            {

            ? ?? ? }


            ? ?? ?
            protected void Application_Error(Object sender, EventArgs e)
            ? ?? ?
            {

            ? ?? ? }


            ? ?? ?
            protected void Session_End(Object sender, EventArgs e)//當(dāng)前用戶退出網(wǎng)站時,在線用戶數(shù)量-1,
            ? ?? ? {
            ? ?? ?? ?? Application.Lock();
            ? ?? ?? ?? Application["CurrentGuests"] =(int)Application["CurrentGuests"] - 1;//總在線用戶數(shù)量-1

            ? ?? ?? ?? Application.UnLock(); ? ?

            ? ?? ? }

            ? ?? ?
            protected void Application_End(Object sender, EventArgs e)
            ? ?? ?
            {

            ? ?? ? }

            ? ?? ?? ??
            ? ?? ?
            Web 窗體設(shè)計器生成的代碼#region Web 窗體設(shè)計器生成的代碼
            ? ?? ? /**//// <summary>
            ? ?? ? /// 設(shè)計器支持所需的方法 - 不要使用代碼編輯器修改
            ? ?? ? ///
            此方法的內(nèi)容。
            ? ?? ? /// </summary>

            ? ?? ? private void InitializeComponent()
            ? ?? ?
            { ? ?
            ? ?? ?? ?? this.components = new
            System.ComponentModel.Container();
            ? ?? ? }

            ? ?? ? #endregion
            ? ?}
            }



            bifek,2007-07-19 15:24:23

            ?? ?(2) WebForm1.aspx private void Page_Load(object sender, System.EventArgs e)
            ? ? ?? ?{
            ? ? ?? ?? ? this.Label1.Text = "正在訪問站點的用戶數(shù):" + Application["CurrentGuests"].ToString(); ? ?
            ? ? ?? ?? ? this.Label2.Text ="訪問過站點的總用戶數(shù):" + Application["AllGuests"].ToString();
            ? ? ?? ?}
            2.Session對象
            ? ? Session用于保存每個用戶的專用信息.她的生存期是用戶持續(xù)請求時間再加上一段時間(一般是20分鐘左右).S
            ession中的信息保存在Web服務(wù)器內(nèi)容中,保存的數(shù)據(jù)量可大可小.當(dāng)Session超時或被關(guān)閉時將自動釋放保存的數(shù)據(jù)信息.由于用戶停止使用應(yīng)用程序后它仍然在內(nèi)存中保持一段時間,因此使用Session對象使保存用戶數(shù)據(jù)的方法效率很低.對于小量的數(shù)據(jù),使用Session對象保存還是一個不錯的選擇.使用Session對象保存信息的代碼如下: //存放信息
            Session["username"]="zhouhuan";
            //讀取數(shù)據(jù)
            string UserName=Session["username"].ToString();????

            3.Cookie對象
            ? ? Cookie用于保存客戶瀏覽器請求服務(wù)器頁面的請求信息,程序員也可以用它存放非敏感性的用戶信息,信息保存的時間可以根據(jù)需要設(shè)置.如果沒有設(shè)置Cookie失效日期,它們僅保存到關(guān)閉瀏覽器程序為止.如果將Cookie對象的Expires屬性設(shè)置為Minvalue,則表示Cookie永遠(yuǎn)不會過期.Cookie存儲的數(shù)據(jù)量很受限制,大多數(shù)瀏覽器支持最大容量為4096,因此不要用來保存數(shù)據(jù)集及其他大量數(shù)據(jù).由于并非所有的瀏覽器都支持Cookie,并且數(shù)據(jù)信息是以明文文本的形式保存在客戶端的計算機(jī)中,因此最好不要保存敏感的,未加密的數(shù)據(jù),否則會影響網(wǎng)站的安全性.使用Cookie對象保存的代碼如下: //存放信息
            Response.Cookies["UserID"].Value="0001";
            //讀取信息
            string UserID=Response.Cookies["UserID"].Value;????

            4.ViewState對象
            ? ? ViewState 常用于保存單個用戶的狀態(tài)信息,有效期等于頁面的生存期。ViewState容器可以保持大量的數(shù)據(jù),但是必須謹(jǐn)慎使用,因為過多使用會影響應(yīng)用程序的性能。所有Web服務(wù)器控件都使用ViewState在頁面回發(fā)期音保存自己的狀態(tài)信息。如果某個控件不需要在回發(fā)期間保存狀態(tài)信息,最好關(guān)閉該對象的ViewState,避免不必要的資源浪費。通過給@Page指令添加“EnableViewState=false”屬性可以禁止整個頁面的ViewState。使用ViewState對象保存信息的代碼如下。
            ? //存放信息
            ? ViewState["nameID"]="0001";
            ? //讀取信息
            ? string NameID=ViewState["nameID"].ToString();
            5.Cache對象
            ? ?Cache對象用于在HTTP請求間保存頁面或數(shù)據(jù)。該對象的使用可以極大地提高整個應(yīng)用程序的效率。它允許將頻繁訪問的大量服務(wù)器資源存儲在內(nèi)存中,當(dāng)用戶發(fā)出相同的請求后服務(wù)器不再次處理而是將Cache中保存的信息返回給用戶,節(jié)省了服務(wù)器處理請求的時間。此對象的實例是每個應(yīng)用程序?qū)S玫模渖嫫谝蕾囉谠搼?yīng)用程序的生存期。當(dāng)重新啟動應(yīng)用程序時,將重新創(chuàng)建其Cache對象的實例。使用Cache對象保存信息的代碼如下。 ? //存放信息
            ? Cache["nameID"]="0001";
            ? //存放信息
            ? Cache.Insert("nameID","0001"1);
            ? //讀取信息
            ? string NameID=Cache["nameID"].ToString(); ? 6.隱藏域
            ? Hidden控件是屬于HTML類型的服務(wù)器控件,使用此控件可以實現(xiàn)隱藏域的功能。其實此控件和其它服務(wù)器控件的使用沒有太大區(qū)別,只是它不會在用戶端的瀏覽器中顯示,始終處于隱藏狀態(tài)。但是每次頁面提交的時候,此控件和其它服務(wù)器控件一同提交到服務(wù)器端,因此在服務(wù)器端可以使用Value屬性獲取或保存一些數(shù)據(jù)信息。使用Hidden控件保存信息的代碼如下。
            ? //存放信息
            ? Hidden.Value="0001";
            ? //獲取信息
            ? string NameID=Hidden.Value; ?7.查詢字符串
            ? 查詢字符串的方式是將要傳遞的值連接在URL后面,然后通過Response.Redirect方法實現(xiàn)客戶端的重定向。這種方式可以實現(xiàn)在兩個頁面之間傳遞信息。由于URL的長度有一定的限制,因此不能傳遞太大的信息,加外安全性也不是很好。
            ? 傳遞信息如下。
            ? Response.Redirect("List.aspx?nameID=0001&gradeID=002");
            ? //執(zhí)行上面的語句后在IE地址欄顯示的URL的代碼如下。
            ? http://localhost/List.aspx?nameID=0001&grade=002
            ? //當(dāng)跳轉(zhuǎn)到List.aspx后,可以通過以下代碼獲得所傳遞的信息。
            ? string NameID.GradeID;
            ? NameID=Request.Params["nameID"];
            ? GradeID=Request.Params["gradeID"];???

            6,QueryString多半是地址欄參數(shù),到下一頁使用,明文,無所謂安全;??

            除了以上介紹的幾種對象外,還可以使用Context對象和Web.Config配置文件。它們也都可以實現(xiàn)保存用戶信息的功能,在這里不再詳細(xì)介紹。至于使用何種對象保存信息可以根據(jù)以下的原則進(jìn)行選定:

            viewstate/session/cache/application
            單頁級的自身狀態(tài)的保存建議用viewstate,??
            用戶級的重要的信息的保存建議用session(如每個用戶的狀態(tài))??
            用戶級的非重要性的信息的保存建議用cache??
            整個站級的狀態(tài)的保存建議用application.或用singleton模式的全局對象??
            cache和session的用法相同,不過cache中的信息在內(nèi)存緊張時會消失,所以cache的使用時需如下形式??
            if(cache["key"]==null)??
            {????
            ?? //重新檢索數(shù)據(jù);??
            ?? cache["key"]=檢索到的數(shù)據(jù);??
            }
            所需數(shù)據(jù)=cache["key"]
            全局變量的讀寫建議"加鎖",不然會出現(xiàn)不正常的.


            我對.NET技術(shù)不熟悉,因此對ViewState,Cache這兩個功能不清楚,但可以對其他三個Cookie,Session,Application提供一些實踐方面的建議.??
            ? 1)cookie是大家最喜歡的一種存儲方式,但不是好的存儲方法,我的建議是在商業(yè)性網(wǎng)站開發(fā)中最好不用.典型的使用方法如下:??
            ? <%??
            ? Dim?? strUserID,?? strPassword,?? blnAutoLogin??
            ???
            ? strUserID?? =?? Request.Form("txtUserID")??
            ? strPassword?? =?? Request.Form("txtPassword")??
            ? blnAutoLogin?? =?? Request.Form("chkAutoLogin")??
            ???
            ? if?? blnAutoLogin?? =?? "on"?? then??
            ? 'Save?? Username?? and?? password?? to?? cookie??
            ? Response.Cookies("Credentials")("userid")?? =?? strUserID??
            ? Response.Cookies("Credentials")("password")?? =?? strPassword??
            ? Response.Cookies("Address")?? =?? "123?? Main?? Street"??
            ? End?? If??
            ? %>??
            ???
            ? 2)session是一種比較好的使用方式,但是不要使用過大.比如在一個商城的網(wǎng)站中,只有customerID用session保存,其他都從數(shù)據(jù)庫中獲得數(shù)據(jù).典型代碼如下:??
            ? <%??
            ? Session.Contents("FavColour")?? =?? Request.Form("txtColour")??
            ? Response.write?? "Your?? colour?? has?? been?? saved"??
            ? %>??
            ? 3)Application是一個全程對象,通常的一個應(yīng)用是做counter,看了國內(nèi)的許多代碼,好象大家不喜歡用這個對象.典型代碼如下:??
            ? <%??
            ? Application.Lock??
            ? Application("anotherVariable")=Application("anotherVariable")+1??
            ? Application.Unlock??
            ? Response.write?? Application("anotherVariable")??
            ? %>??
            ???
            ? 我在開發(fā)時只用1個session作為用戶的ID.


            Application

            1.???????? Application用來保存所有用戶共用的信息

            2.???????? 在Asp時代,如果要保存的數(shù)據(jù)在應(yīng)用程序生存期內(nèi)不會或者很少發(fā)生改變,那么使用Application是理想的選擇。但是在Asp.net開發(fā)環(huán)境中我們把類似的配置數(shù)據(jù)放在Web.config中。

            3.???????? 如果要使用Application 要注意的是所有的寫操作都要在Application_OnStart事件中完成(global.Asax),盡管可以使用Application.Lock()避免了沖突,但是它串行化了對Application的請求,會產(chǎn)生嚴(yán)重的性能瓶頸。

            4.???????? 不要使用Application保存大數(shù)據(jù)量信息

            5.???????? 代碼:Application[“UserID”]=”test”;

            ??????? String UserName=Application[“UserID”].ToString();

            Session

            1.???????? Session用來保存每一個用戶的專有信息

            2.???????? Session的生存期是用戶持續(xù)請求時間加上一段時間(一般是20分鐘左右)

            3.???????? Session信息是保存在Web服務(wù)器內(nèi)存中的,保存數(shù)據(jù)量可大可小

            4.???????? Session超時或者被關(guān)閉將自動釋放數(shù)據(jù)信息

            5.???????? 由于用戶停止使用應(yīng)用程序之后它仍在內(nèi)存中存留一段時間,因此這種方法效率較低

            6.???????? 代碼:Session[“UserID”]=”test”;

            ??????? String UserName=Session[“UserID”].ToString();

            Cookie

            1.???????? Cookie用來保存客戶瀏覽器請求服務(wù)器頁面的請求信息

            2.???????? 我們可以存放非敏感的用戶信息,保存時間可以根據(jù)需要設(shè)置

            3.???????? 如果沒有設(shè)置Cookie失效日期,它的生命周期保存到關(guān)閉瀏覽器為止

            4.???????? Cookie對象的Expires屬性設(shè)置為MinValue表示永不過期

            5.???????? Cookie存儲的數(shù)據(jù)量受限制,大多數(shù)的瀏覽器為4K因此不要存放大數(shù)據(jù)

            6.???????? 由于并非所有的瀏覽器都支持Cookie,數(shù)據(jù)將以明文的形式保存在客戶端

            7.???????? 代碼:Resopnse.Cookies[“UserID”]=”test”;

            ??????? String UserName= Resopnse.Cookies [“UserID”].ToString();

            ViewState

            1.???????? ViewState用來保存用戶的狀態(tài)信息,有效期等于頁面的生命周期

            2.???????? 可以保存大量數(shù)據(jù)但是要慎用,因為會影響程序性能

            3.???????? 所有的Web服務(wù)器控件都是用ViewState在頁面PostBack期間保存狀態(tài)

            4.???????? 不需要則關(guān)閉 @page 里面設(shè)置EnableViewState=false

            5.???????? 代碼:ViewState[‘”ID”]=”yiner”;
            ?? String ID =ViewState[“ID”].ToString();

            Cache

            1.???????? Cache用于在Http請求期間保存頁面或者數(shù)據(jù)

            2.???????? Cache的使用可以大大的提高整個應(yīng)用程序的效率

            3.???????? 它允許將頻繁訪問的服務(wù)器資源存儲在內(nèi)存中,當(dāng)用戶發(fā)出相同的請求后
            服務(wù)器不是再次處理而是將Cache中保存的數(shù)據(jù)直接返回給用戶

            4.???????? 可以看出Cache節(jié)省的是時間—服務(wù)器處理時間

            5.???????? Cache實例是每一個應(yīng)用程序?qū)S械模渖芷?=該應(yīng)用程序周期
            應(yīng)用程序重啟將重新創(chuàng)建其實例

            6.???????? 注意:如果要使用緩存的清理、到期管理、依賴項等功能必須使用Insert 或者Add方法方法添加信息

            7.???????? 代碼:Cache[‘”ID”]=”yiner”;或者Cache.Insert(“ID”,”test”);
            ?? String ID =Cache[“ID”].ToString();

            Hidden

            1.???????? Hidden控件屬于Html類型的服務(wù)器控件,始終處于隱藏狀態(tài)

            2.???????? 每一次提交的時候它會和其他服務(wù)器控件一起提交到服務(wù)器端

            3.???????? 代碼如下:Hidden.Value=”king”;
            string id=Hidden.Value; 要使用Runat=server


            1,ViewState多半存儲本頁內(nèi)的信息,適用少量數(shù)據(jù),具有基本安全性; 使用視圖狀態(tài)的優(yōu)點是: a, 不需要任何服務(wù)器資源。視圖狀態(tài)包含在頁代碼內(nèi)的結(jié)構(gòu)中。 b, 簡單的實現(xiàn)。 c, 頁和控件狀態(tài)的自動保持。 d, 增強(qiáng)的安全功能。視圖狀態(tài)中的值是散列的、壓縮的并且是為 Unicode 實現(xiàn)而編碼的,這意味著比隱藏域具有更高的安全性狀態(tài)。 使用視圖狀態(tài)的缺點是: a, 性能。由于視圖狀態(tài)存儲在頁本身,因此如果存儲較大的值,在用戶顯示頁和發(fā)送頁時,頁的速度就可能會減慢。 b, 安全性。視圖狀態(tài)存儲在頁上的隱藏域中。雖然視圖狀態(tài)以哈希格式存儲數(shù)據(jù),但它可以被篡改。如果直接查看頁輸出源,可以看到隱藏域中的信息,這導(dǎo)致潛在的安全性問題。

            2,QueryString多半是地址欄參數(shù),到下一頁使用,明文,無所謂安全;

            3,Cookies需要在客戶端存儲少量信息,不需要較高的安全性; 使用 Cookie 的優(yōu)點是: a, 不需要任何服務(wù)器資源。Cookie 存儲在客戶端并在發(fā)送后由服務(wù)器讀取。 b, 簡單。Cookie 是具有簡單鍵值對的輕量的、基于文本的結(jié)構(gòu)。 c,可配置到期時間。Cookie 可以在瀏覽器會話結(jié)束時到期,或者可以在客戶端計算機(jī)上無限期存在,這取決于客戶端的到期規(guī)則。 使用 Cookie 的缺點是: a, 大小受到限制。 b, 用戶配置為拒絕接受。有些用戶禁用了瀏覽器或客戶端設(shè)備接收 Cookie 的能力,因此限制了這一功能。 c, 安全性。Cookie 可能會受到篡改。用戶可能會操縱其計算機(jī)上的 Cookie,這可能意味著安全性會受到影響或者導(dǎo)致依賴于 Cookie 的應(yīng)用程序失敗。 d, 持久性。客戶端計算機(jī)上 Cookie 的持久性受到客戶端 Cookie 到期進(jìn)程以及用戶干預(yù)的制約。 e, Cookie 通常用于為已知用戶自定義內(nèi)容的個性化情況。在大多數(shù)此類情況中,Cookie 是作為“標(biāo)識”而不是“身份驗證”,所以在 Cookie 中只存儲用戶名、賬戶名或唯一用戶 ID(例如 GUID)并使用它來訪問站點的用戶個性化結(jié)構(gòu)是足夠的了。

            4,Session存儲在會話狀態(tài)變量中的理想數(shù)據(jù)是特定于單獨的、短期的、敏感的數(shù)據(jù),應(yīng)該是安全的,但過多使用會降低服務(wù)其性能; 使用會話狀態(tài)的優(yōu)點是: a, 易于實現(xiàn)。會話狀態(tài)功能易于使用。 b, 會話特定的事件。會話管理事件可以由應(yīng)用程序引發(fā)和使用。 c, 持久性。放置于會話狀態(tài)變量中的數(shù)據(jù)可以經(jīng)受得住 Internet 信息服務(wù) (IIS) 重新啟動和輔助進(jìn)程重新啟動,而不丟失會話數(shù)據(jù),這是因為這些數(shù)據(jù)存儲在另一個進(jìn)程空間中。 d, 平臺可縮放性。會話狀態(tài)對象可在多計算機(jī)和多進(jìn)程配置中使用,因而優(yōu)化了可縮放性方案。 e, 盡管會話狀態(tài)最常見的用途是與 Cookie 一起向 Web 應(yīng)用程序提供用戶標(biāo)識功能,但會話狀態(tài)可用于不支持 HTTP Cookie 的瀏覽器。 使用會話狀態(tài)的缺點是: a, 性能。會話狀態(tài)變量在被移除或替換前保留在內(nèi)存中,因而可能降低服務(wù)器性能。如果會話狀態(tài)變量包含類似大型數(shù)據(jù)集的信息塊,則可能會因服務(wù)器負(fù)荷的增加影響 Web 服務(wù)器的性能。

            5,Application插入到應(yīng)用程序狀態(tài)變量的理想數(shù)據(jù)是那些由多個會話共享并且不經(jīng)常更改的數(shù)據(jù)使用應(yīng)用程序狀態(tài)的優(yōu)點是: a, 易于實現(xiàn)。應(yīng)用程序狀態(tài)易于使用。 b, 全局范圍。由于應(yīng)用程序狀態(tài)可供應(yīng)用程序中的所有頁來訪問,因此在應(yīng)用程序狀態(tài)中存儲信息可能意味著僅保留信息的一個副本(例如,相對于在會話狀態(tài)或在單獨頁中保存信息的多個副本)。 使用應(yīng)用程序狀態(tài)的缺點是: a, 全局范圍。應(yīng)用程序狀態(tài)的全局性可能也是一項缺點。在應(yīng)用程序狀態(tài)中存儲的變量僅對于該應(yīng)用程序正在其中運行的特定進(jìn)程而言是全局的,并且每一應(yīng)用程序進(jìn)程可能具有不同的值。因此,不能依賴應(yīng)用程序狀態(tài)來存儲唯一值或更新網(wǎng)絡(luò)園和網(wǎng)絡(luò)場配置中的全局計數(shù)器。 b, 持久性。因為在應(yīng)用程序狀態(tài)中存儲的全局?jǐn)?shù)據(jù)是易失的,所以如果包含這些數(shù)據(jù)的 Web 服務(wù)器進(jìn)程被損壞(最有可能是因服務(wù)器崩潰、升級或關(guān)閉而損壞),將丟失這些數(shù)據(jù)。 c, 資源要求。應(yīng)用程序狀態(tài)需要服務(wù)器內(nèi)存,這可能會影響服務(wù)器的性能以及應(yīng)用程序的可縮放性。應(yīng)用程序狀態(tài)的精心設(shè)計和實現(xiàn)可以提高 Web 應(yīng)用程序性能。例如,如果將常用的、相關(guān)的靜態(tài)數(shù)據(jù)集放置到應(yīng)用程序狀態(tài)中,則可以通過減少對數(shù)據(jù)庫的數(shù)據(jù)請求總數(shù)來提高站點性能。但是,這里存在一種性能平衡。當(dāng)服務(wù)器負(fù)載增加時,包含大塊信息的應(yīng)用程序狀態(tài)變量就會降低 Web 服務(wù)器的性能。在移除或替換值之前,將不釋放在應(yīng)用程序狀態(tài)中存儲的變量所占用的內(nèi)存。因此,最好只將應(yīng)用程序狀態(tài)變量用于更改不頻繁的小型數(shù)據(jù)集。

            6,Cache,.NET 為您提供了一個強(qiáng)大的、便于使用的緩存機(jī)制,允許您將需要大量的服務(wù)器資源來創(chuàng)建的對象存儲在內(nèi)存中。其生存期依賴于該應(yīng)用程序的生存期。重新啟動應(yīng)用程序后,將重新創(chuàng)建 Cache 對象。他的作用比較豐富,機(jī)制也比較多,請參閱相關(guān)的資料。
            Web Service安全機(jī)制探討
            http://hi.baidu.com/%D3%F1%D3%E3/blog/item/99f3ec4ac8fe832608f7ef57.html
            net面試題大全(有答案) & asp.net面試集合
            C#基礎(chǔ)概念二十五問(轉(zhuǎn))
            http://www.cnblogs.com/jiayong/archive/2008/01/07/1029138.html

            .net工程師必懂題(筆試題目)轉(zhuǎn)
            http://www.cnblogs.com/jiayong/archive/2008/01/08/1030175.html

            ?

            posted on 2008-07-01 16:10 snowball 閱讀(10299) 評論(6)  編輯 收藏 引用 所屬分類: 文章轉(zhuǎn)載學(xué)習(xí)

            評論

            # re: 面向?qū)ο蟮娜齻€基本特征(講解) 2010-07-30 08:52 KERI25BURTON

            When you are in a not good position and have got no cash to go out from that point, you will need to receive the <a href="http://bestfinance-blog.com/topics/personal-loans">personal loans</a>. Just because it will help you emphatically. I get car loan every time I need and feel OK just because of it.   回復(fù)  更多評論   

            # re: 面向?qū)ο蟮娜齻€基本特征(講解) 2010-08-05 19:44 custom papers

            Study process requires good writing skills, nevertheless, students, which are short of time can destroy their academic success. Hence, to buy the academic essay from the narrative essay writing service will be a perfect decision.   回復(fù)  更多評論   

            # re: 面向?qū)ο蟮娜齻€基本特征(講解) 2010-08-12 03:36 custom essay writing

            One, who knows someth. just about essay writing should appreciate your fantastic article. I think that the buy essay paper service could use it for the research paper blogs.   回復(fù)  更多評論   

            # re: 面向?qū)ο蟮娜齻€基本特征(講解) 2010-08-23 12:10 essay papers

            Some students need a long time to realize the key issue of the customized essay. But when students do not have time, that will be the best to buy papers online. Then this will be available to save time.   回復(fù)  更多評論   

            # re: 面向?qū)ο蟮娜齻€基本特征(講解) 2013-12-10 18:55 essays.mightystudents.com

            只記得你想完成的總體目標(biāo)是讓讀者認(rèn)同你,然後,希望把你的建議適當(dāng)?shù)男袆印?nbsp; 回復(fù)  更多評論   

            # re: 面向?qū)ο蟮娜齻€基本特征(講解) 2014-07-29 02:34 Friv 1000

            very nice article and it is quite interesting also for me personally. thank you so much for sharing this very interesting information. I will try to come back here again if in case there have been new and interesting article.  回復(fù)  更多評論   

            導(dǎo)航

            留言簿(1)

            隨筆分類

            友情鏈接

            搜索

            最新隨筆

            最新評論

            閱讀排行榜

            久久水蜜桃亚洲av无码精品麻豆| 亚洲а∨天堂久久精品9966| 国产麻豆精品久久一二三| 99久久综合狠狠综合久久止| 久久99精品国产| 狠狠色丁香婷婷久久综合五月| 麻豆AV一区二区三区久久| 久久综合久久综合久久| 精品久久久久久久久免费影院 | 狠狠精品干练久久久无码中文字幕| 欧美激情精品久久久久久| AV无码久久久久不卡网站下载| 国产视频久久| avtt天堂网久久精品| 久久人妻AV中文字幕| 国产精品一区二区久久精品无码| 久久精品中文騷妇女内射| 欧美亚洲国产精品久久久久| 亚洲乱亚洲乱淫久久| 欧美一区二区三区久久综| 久久亚洲中文字幕精品一区四 | 欧美一级久久久久久久大片| 狠狠色丁香久久婷婷综合五月| 中文成人久久久久影院免费观看| 99久久精品免费| 久久久九九有精品国产| 久久久久久夜精品精品免费啦| 久久夜色精品国产噜噜亚洲a| 精品久久久久久国产三级| 国产一级持黄大片99久久| 久久综合香蕉国产蜜臀AV| 久久久精品人妻一区二区三区蜜桃| 久久精品这里只有精99品| 91精品日韩人妻无码久久不卡 | 99久久人妻无码精品系列| 日本强好片久久久久久AAA| 欧美日韩精品久久久免费观看| 久久伊人中文无码| 中文成人无码精品久久久不卡 | 国产成人精品白浆久久69| 久久99国产乱子伦精品免费|