1. 程式人生 > >漫談WebSphere應用伺服器之事務

漫談WebSphere應用伺服器之事務

http://www.cnblogs.com/sunwei2012/archive/2010/01/08/1642295.html

1.事務及事務管理器的基本概念和歷史

   說起事務,大部分人都馬上能說出事務的四個屬性:ACID(原子性、一致性、隔離性和永續性)。
   事務這個概念最先來自資料庫領域。我們可以看一下wikipedia上如何定義資料庫事務的:

A database transaction comprises a unit of work performed within a database management system (or similar system) against a database, and treated in a coherent and reliable way independent of other transactions.


從該定義其實已經可以體現出事務的四個特性了。而提供事務能力的事務處理系統早在60年代就出現了,典型代表有到現在還在企業中穩定執行的IBM的CICS、BEA的Texedo等。而後事務處理最為一個新興的學科開始發展起來,並在偉大的圖靈獎得主、一個傳奇式的人物 - Jim Gray下得到發揚光大。他的事務處理-概念和技術這本著作也成為這個領域的不朽經典。

除此之外,影響深遠的當屬X/Open提出的DTP模型了。
在90年代,隨著分散式計算的興起,企業的計算模式也發生了質的變化。一個業務的生命週期往往需要跨越多個系統和多種資源(如資料庫,訊息系統等)。因此如何保證這種分散式計算模式下面的事務性成了當時研究的熱點。其中X/Open的DTP模型規範了事務處理系統中各元件的職能及互動的契約,它也成為目前這個領域的事實標準。



如上圖所示,該模型提供了一個構建分散式事務處理系統的方法。其核心包括三個元件和兩組介面。
三個元件:
   1. 應用程式(AP):應用程式是事務的發起者;它決定了一個事務的生命週期(比如什麼時候開始一個新的事務、什麼時候該提交或者回滾事務)。
   2. 事務管理器(TM):如其名字所表述的,它提供了對事務的管理和恢復。
   3. 資源管理器(RM)(Resource Manager),負責對資源的訪問。

兩組介面:
   1. TX介面。該介面定義了應用程式和事務管理器之間互動的方法。比如應用程式可以呼叫事務管理器的tx_open/tx_close方法用來建立/斷開事務管理器。然後使用tx_begin/tx_rollback/tx_commit來通知事務管理器開始/ 回滾/提交事務。

   當事務管理器接收到開始/回滾/提交事務的請求後,事務管理器必須告知加入到當前事務的資源管理器來執行對應的操作,這是通過另一組XA介面來實現的。
  2.  XA介面定義了事務管理器和資源管理器之間互動的方法。重要的的方法有:
       xa_open/xa_close。事務管理器通過呼叫這兩個方法來通知資源管理器進行初始化和結束。
      xa_start/xa_end。事務管理器通過這兩個方法來通知資源管理器開始和結束事務。這兩個方法也就確定了一個事務的邊界。
      xa_prepare/xa_commit/xa_rollback。事務管理器通過兩階段提交協議來通知資源管理器進行事務的提交或者回滾。

   在這個模型下,一個典型的事務處理可以表示如下:
   -------------------------------------------------------------------------------
  //1. 應用程式開始事務。
    tx_begin.
    //應用程式進行業務操作,在需要保證事務的部分開始事務。
    tx_begin
    //應用程式呼叫資源管理器進行業務操作
         //事務管理器通知資源管理器進行初始化 
             xa_open
               //事務管理器通知資源管理器事務開始
                 xa_start

     //資源管理器記錄事務性操作。。。

     //最後應用提交事務。
     tx_commit
        //事務管理器通知資源管理器開始兩階段提交,最後結束事務
              xa_prepare
             xa_commit
            xa_close
    tx_close
--------------------------------------------------------------------------------------------------------

   CORBA的興起也給X/Open的DTP模型找到了一個絕好的實踐場地。
   在OMG的OTS規範中給出瞭如下的事務架構:



可以看到該模型其實和DTP模型是完全一致的,只不過是用CORBA來重新描述了而已。而且在OTS中也明確說明了是可以和X/Open的DTP互操作的。

2.  J2EE事務

      網路就是計算機這個口號雖然沒有造就一個網際網路軟體帝國,卻讓Java成為了企業級應用的首選。隨著EJB程式設計模型在J2EE中的引入,JTA和JTS也隨之釋出。
      如下圖所示,J2EE世界中的事務模型完全是延續DTP的。它也定義了應用、資源管理器和事務管理器。



     JTA協議簡單來說就是用Java來定義了X/Open的DTP模型中的兩組介面(TX和XA)。具體而言,JTA把應用程式分為了兩類,一類是真正的客戶應用程式,另一類是應用伺服器。針對這兩類事務客戶端它分別定義了javax.transaction.UserTransaction和javax.transaction.TransactionManager介面,這兩組介面直接對應了TX介面。通過UserTransaction介面,客戶應用程式就可以顯示的控制事務的開始和結束。但在J2EE的世界中更推崇讓容器來管理事務,這樣應用無需硬編碼,只要通過宣告式的方式就可以讓自己的EJB執行在特定的事務上下文中。在J2EE規範中定義了好多種型別的事務屬性,根據這些屬性容器在呼叫EJB之前和之後就會執行相應的事務控制操作。比如如果事務屬性設定為RequireNew:
-----------------------------------------------------------------------------------------------------------
          //在開始呼叫Bean方法之前,容器程式碼先拿到一個事務管理器物件。如何拿到
        //這個物件在規範中沒有說明,因此不同的容器提供商有不同的方法。
        TransactionManager txManager = TransactionManagerFactory.getTransactionManager();

        //由於是需要新的事務,因此需要把當前已有的事務掛起(如果有事務的話)。
        Transaction current = txManager.suspend();

        //接下來就可以發起一個新的事務了。
        txManager.begin();

        //然後容器把請求發給EJB例項,業務邏輯開始執行。在執行過程中任何
       //事務相關的操作都會被自動的加入到當前事務中。最後執行完成推出。

        //容器重新接管控制權,如果沒有異常,則提交當前事務。
        txManager.commit();

        //最後恢復被掛起的事務。
        txManager.resume(current);
----------------------------------------------------------------------------------------------------------
而XA介面則是由javax.transaction.xa.XAResource來提供的。該介面定義了資源管理器和事務管理器之間的契約。在上一章節中我們已經 簡要的介紹過XA介面的方法。這兒需要注意的是在J2EE的環境下,open和close並不會被呼叫到,因此資源的初始化和關閉時由資源管理器自己完成的。

而JTS相對而言就比較低層得多,它規範了一個J2EE下的事務管理器在上層需要提供對JTA的支援,而下層則需要實現OTS的Java對映。

說到這兒,J2EE事務基本上可以告一段落。但其實還有一個問題是在上述的程式碼中,業務邏輯進行事務性操作的時候,比如往資料庫裡面加入一條記錄,或者往JMS Queue中寫入一條訊息。這些資源是如何自動的加入到當前的事務中呢?這部分內容是不可能在J2EE的規範中找到的。目前絕大部分容器的實現都是在XA資料來源上做的文章。比如我們在WebSphere應用伺服器上定義了一個jdbc/Order的資料來源,然後在一個Session Bean中用下面的示例程式碼來新增一個訂單:

---------------------------------------------------------------------------------------------
InitialContext ic = new InitialContext();
DataSource ds = (DataSource) ic.lookup("jdbc/Order");
Connection con = ds.getConnection();
Statement st  = con.createStatement();
String sql ="insert into order .....";
st.execute(sql);
//最後cleanup
st.close();
con.close();

---------------------------------------------------------------------------------------------
在上述例子中,如果這個Session Bean的事務標記為Required、RequiredNew、Support,則這兒的資料庫操作會自動加入到當前的事務中。如何自動加入的祕密就在於這兒返回的資料來源。一個簡單的實現是當ds.getConnection方法被呼叫時,資料來源的實現裡面首先呼叫底層的資源管理器拿到一個Connection,然後呼叫Connection的getXAResource拿到XAResource,接著拿到當前的transactionManager應用,然後通過transactionManager.getTranaction().enlistResource把該資源加到當前的事務中。最後返回一個Connection的封裝。通過這種方式資源就被自動的加入到當前事務中,然後在業務邏輯執行完成後,當容器重新拿到控制權後,容器就會根據配置來呼叫事務管理器來完成事務的提交或者回滾。

3. WebSphere事務


  WAS作為一個J2EE伺服器,自然也就實現了JTS跟JTA。這兒再溫習一下整個事務的一個完整的運作過程.。這兒我們使用如下的一個場景:應用在完成一個業務時需要同時訪問資料庫和訊息系統,而且需要保證對這兩種資源的更新的事務性,這兒應用使用了userTransaction來自己控制事務。示例程式碼如下:

-----------------------------------------------------------------------------------------   
        InitialContext ic = new InitialContext();
        DataSource ds = (DataSource) ic.lookup("jdbc/order");
        ConnectionFactory connectionFactory =
            (ConnectionFactory)ic.lookup("jms/orderCF");

        UserTransaction ut = (UserTransaction) ic.lookup("java:comp/UserTransaction");

        try{

            ut.begin();

            javax.jms.Connection jmsConnection = connectionFactory.createConnection();
            Session session = jmsConnection.createSession(true, Session.AUTO_ACKNOWLEDGE);
            Queue sendQueue = (Queue) ic.lookup("jms/orderQueue");
            MessageProducer producer = session.createProducer(sendQueue);
            jmsConnection.start();
            TextMessage requestMessage = session.createTextMessage("data");
            producer.send(requestMessage);

            session.close();
            jmsConnection.close();


            java.sql.Connection jdbcConnection = ds.getConnection();
            java.sql.Statement st = jdbcConnection.createStatement();
            String sql = "insert into ...";
            st.execute(sql);

            st.close();
            jdbcConnection.close();

            ut.commit();
        }catch(Exception e) {
            try {
                ut.rollback();
            } catch (IllegalStateException e1) {
                // TODO Auto-generated catch block
                e1.printStackTrace();
            } catch (SecurityException e1) {
                // TODO Auto-generated catch block
                e1.printStackTrace();
            } catch (SystemException e1) {
                // TODO Auto-generated catch block
                e1.printStackTrace();
            }
        }

-----------------------------------------------------------------------------------------

我們可以用下面的兩幅圖來分析整個事務的處理過程。
首先看一下這兩個資源是如何註冊到事務管理器中的。



1. 應用通過userTransaction.begin()開始事務。JTS把建立事務並把它關聯到當前的執行緒上。
2. 使用者執行JDBC操作:拿到資料來源、連線、建立Statement,最後呼叫statement.execute來執行sql語句來在資料庫中建立一條新條目。
3. 因為這兒使用的statement是經過容器包裝的一個Wrapper,在其實現中首先向真正的XAConnection拿到XAResource。
4 然後呼叫transactionManager.getTransaction().enlistResource()方法來把該XAResource加入到當前事務中。
5  事務管理器呼叫該XAResource的start方法來通知資源管理器開始記錄事務性操作。
6 最後呼叫真正的Statement的execute()方法來執行資料庫操作。

7到11步和上述類似。通過類似的操作把JMS資源加到當前的事務中。

當業務步驟完成後,呼叫close來關閉連線,然後呼叫commit來提交事務。這些操作都會觸發對應的XA的動作,如下所示:


12. 當connection的close方法被呼叫到後
13. 容器提供的Connection wrapper會先呼叫transactionManager.getTransaction().delistResource()方法來停止記錄。
14. JTS呼叫XAResouce的end方法來結束事務。
15. 真正的connection的close方法被呼叫,資源管理器釋放資源。
16 到19步完成JMS的close操作。
20. 然後userTransaction的commit被呼叫,此時開始兩階段提交。
21 首先JTS向JDBC的XAResource發起prepare請求,如果返回Prepared,則
22 JTS向JMS的XAResource發起prepare請求,如果還是返回Prepared,則
23. 呼叫JDBC的XAResource的commit方法落實所有修改
24. 呼叫JMS的XAResource的commit方法落實所有修改

除了經典的XA的支援,還值得提及的是WAS的一些特別的擴充套件。

  首先要介紹的是本地事務
  這兒先回顧一下為什麼在EJB這種分散式計算模型下面為什麼還有本地事務的需求。讓我們先看一下在J2EE規範中明確提到了未指定的事務上下文這個概念。

--------------------------------------------------------------------------------------------------------------
  The term “an unspecified transaction context” is used in the EJB specification to refer to the cases in which the EJB architecture does not fully define the transaction semantics of an enterprise bean method execution:

The execution of a method of an enterprise bean with container-managed transaction demarcation for which the value of the transaction attribute is NotSupported, Never, or Supports (onyl applies to the case when the client calls without a transaction context)

The execution of the ejbCreate<METHOD>, ejbRemove, ejbPassivate, and ejbActivate methods of a session bean with container-managed transaction demarcation.

The execution of the ejbCreate<METHOD> and ejbRemove methods of a message-driven
bean with container-managed transaction demarcation.
-----------------------------------------------------------------------------------------------------------

在上述提到的場景中,由於缺乏事務的語義,因此沒有辦法規定在這些場景下的事務屬性。因此規範索性把這部分定義留給了具體的容器提供商。
在這些情況下,WAS總是會啟動一個本地事務(LTC)來對事務進行控制。LTC針對資源管理器的本地事務提供了三個層次的支援。首先是事務的邊界,預設是Bean方法;然後是對於沒有完成的本地事務的處理策略,預設是回滾。最後是對資源的回收,保證在本地事務推出時候裡面所使用到得資源得以釋放。
LTC本身沒有程式設計介面,只能通過WAS對EJB部署描述符的擴充套件來配置。

分散式事務的一些擴充套件
      分散式事務上WAS也提供了兩種擴充套件。
      第一種是Last participant。這種擴充套件用在一個事務中如果有多個資源管理器支援XA(2PC,兩階段),但恰好有一個只能支援RMLT (resource manager local transactions)(1PC,一階段,沒有Prepare,只有commit)的場景下。在這種情況下, 可以通過配置last participant來支援這種場景。具體實現上是在開始兩階段的prepare時,事務管理器首先詢問其他的XA資源是否準備好,如果回答都是OK,接下來就呼叫1PC資源的提交,如果提交成功,則呼叫其他的2PC的commit,否則rollback。

      上面的場景要求只能有一個1PC的資源,如果把這個限制放鬆,則沒有辦法用這個方式來實現對不同型別的資源的統一協調。ActivitySession就是為了滿足這種涉及了多個1PC的資源的應用場景。可以說它擴充套件了傳統的XA的概念,把它延伸了道了1PC資源上。和分散式事務類似,它的事務邊界的劃分也可以通過宣告式或者是程式設計來實現。但不同點在於它沒有Prepare階段,也沒有Recovery。

其他程式設計介面
      在J2EE規範中,UserTransaction只能在Bean或者Servlet裡面使用。那麼在普通的POJO裡面,如果訪問事務,如果發起新的事務呢?WebSphere提供了下面的一些辦法:

ExtendedJTATransaction.
通過該介面可以知道當前有沒有全域性事務,如果有則可以註冊一個SynchronizationCallback物件到當前的事務或者容器中所有的事務中。
可以在EJB、Servlet、JSP或者普通的POJO中通過下面的程式碼訪問到該物件:
ExtendedJTATransactionexJTA = (ExtendedJTATransaction)ctx.lookup("
java:comp/websphere/ExtendedJTATransaction");
//判斷是否有活動的事務
if (exJTA.getGlobalId() != null) {
//給當前的事務註冊一個callback
SynchronizationCallback sync = new SynchronizationCallback();
exJTA.
(sync);}

ExtensionHelper.
如果需要在POJO中發起新的事務,則可以用ExtensionHelper。示例程式碼如下:

InitialContext ctx = new InitialContext();
ExtensionHelper eh = (ExtensionHelper) ctx.lookcup(ExtensionHelper.JNDI_NAME);
TransactionControl tc = eh.getTransactionControl();
TxHandle txh = tc.preinvoke();
try {
// Do some work ...

// Commit the transaction
tc.postinvoke(txh);
} catch (Throwable fatalException) {
// Rollback the transaction
tc.handleException(txh);
}


UOWManager
在WAS6.1以後提供了一個更簡潔的介面來實現對UOW的管理。上面在POJO中啟動一個事務的程式碼可以重寫如下:
try
{
UOWManager uowManager = UOWManagerFactory.getUOWManager();
uowManager.runUnderUOW(UOWSynchronizationRegistry.UOW_TYPE_GLOBAL_TRANSACTION, false, new UOWAction()
{
public void run() throws Exception
{
// Perform. transactional work here.
}
});
}
catch (UOWActionException uowae)
{
// Transactional work resulted in a checked exception being thrown.
// The UOW was not affected.
}
catch (RuntimeException re)
{
// Transactional work resulted in an unchecked exception being thrown.
// The UOW was rolled back
}
catch (UOWException uowe)
{
// The completion of the UOW failed unexpectedly.
}

4. Web服務事務

現在SOA火的一塌糊塗。而作為SOA基石之一的Web服務業早就深入人心。那麼在處處都是服務的時候,如何保證服務之間互動的事務性也是必須要解決的問題。
和CORBA的OTS、J2EEE的JTA/JTS一樣,Web服務也搞了一套自己的事務解決方案。目前,Web服務的事務是由WS-Coordination和WS-Transaction兩個規範構成。WS-Coordination中提出了一套WS-Coordination Framework來定義了在Web服務的場景下如何確定事務邊界、如何開始和提交事務(這其實也就是定義了JTA,但JTA中的XA部分則是放在WS-Trsansaction中定義的)。同時考慮到Web服務的特性,在很多場景下傳統的分散式事務(XA)是不能滿足長時間執行的事務的需求的,因此Web服務事務在WS-Transaction規範中提供了兩類提交協議:WS-AT和WS-BA。前者是經典的分散式事務,後者則是為了長事務而提出的,主要是利用了補償的概念。

這兩個協議具體是如何運作的細節可以參考Web Service Transaction連結。該文章通過一個例子非常詳細的描述了一個Web服務事務的運作過程,詳細列出了其中互動的Web服務事務的SOAP訊息,非常值得學習

相關推薦

漫談WebSphere應用伺服器事務

http://www.cnblogs.com/sunwei2012/archive/2010/01/08/1642295.html 1.事務及事務管理器的基本概念和歷史    說起事務,大部分人都馬上能說出事務的四個屬性:ACID(原子性、一致性、隔離性和永續性)。 

JAVA企業級應用伺服器TOMCAT實戰

一、Tomcat簡介 Tomcat是Apache軟體基金會(Apache Software Foundation)的Jakarta專案中的一個核心專案,由Apache,Sun和其他一些公司及個人共同開發而成。 Tomcat伺服器是一個免費的開放原始碼的Web應用伺服器,屬於輕量級應

WebSphere應用伺服器記憶體洩漏

引言 記憶體洩漏是比較常見的一種應用程式效能問題,一旦發生,則系統的可用記憶體和效能持續下降;最終將導致記憶體不足(OutOfMemory),系統徹底 宕掉,不能響應任何請求,其危害相當嚴重。同時,Java堆(Heap)中大量的物件以及物件間之複雜關係,導致記憶體洩漏問

WebSphere 應用伺服器 V6.1 中的 LDAP 身份認證

企業資訊化建設多年,建立了包括人事、財務、市場、銷售、客戶關係管理等一系列的業務和應用資訊系統。這些系統種類繁多並且由不同的應用開發商提供,比如 SAP、金碟的 ERP 軟體,微軟、ORACLE、用友的財務軟體等等。不同的系統和開發商有自己專用的一套使用者儲存和管理系統

Java應用伺服器tomcat基礎配置(一)

  前文我們聊到了java相關重要元件和它們之間的關係以及jdk、tomcat部署回顧請參考https://www.cnblogs.com/qiuhom-1874/p/13302938.html;今天我們來聊一聊tomcat的配置使用相關話題;   一、tomcat配置相關檔案簡介   1、server.xm

Java應用伺服器tomcat基礎配置(二)

  前文我們聊了下tomcat的配置檔案相關格式和元件簡介以及webapp目錄結構,manger部署和host managera部署,回顧請參考https://www.cnblogs.com/qiuhom-1874/p/13307892.html;今天我們來詳細聊一聊server.xml中的各元件配置和屬性;

Java應用伺服器tomcat session server msm搭建配置

  在上一篇部落格中,我們介紹了tomcat自帶的cluster元件配置session replication cluster,回顧請參考https://www.cnblogs.com/qiuhom-1874/p/13363590.html;session複製叢集的原理就是通過多播通訊的方式,把節點的sess

數據庫高級應用事務

不可 mil body 情況 blog comm rollback 安排 覆蓋 事務的基本構成: begin transation update 支付表 set 賬戶總額 = 賬戶總額 - n where 賬戶名 = ‘A‘ update 支付表

程式架構探討—005 應用伺服器叢集的伸縮性反向代理負載均衡

利用反向代理也可以做負載均衡。如下圖所示, 反向代理伺服器處於WEB伺服器的前面,既可以提供反向代理,也可以管理一組web伺服器,將請求根據負載均衡演算法轉發到不同的web伺服器上。web伺服器處理完

構建負載均衡伺服器二 LVS詳解及應用

在上一篇中介紹了負載均衡及叢集的原理,本篇主要介紹下下實現負載均衡的軟體之LVS的原理及應用。 一、LVS的介紹 1、  LVS的定義 一般來說,LVS採用三層結構:負載排程器、伺服器池、共享儲存。工作在TCP/IP協議的四層,其轉發是依賴於四層協議的特徵進行轉發的,由於其轉發要依賴於協議的特徵進行轉

JNDI旅--各應用伺服器JNDI初始配置

  使用Java EE應用伺服器開發應用程式時,不管是在Web應用還是簡單的Java Main中,當需要使用JDBC DataSource、JMS連線工廠、EJB的Home介面時,均需要通過應用伺服器提供的JNDI服務,從JNDI Context中獲取相應的物件。而且在獲取初

redis學習(五) redis進階事務和過期時間的應用

事務redis中的事務是一組命令的集合。使得一個事務中的redis命令要麼全執行,要麼全不執行使用方式: multi 和exec完成multi: 告訴redis將同一個事務的命令儲存起來。  之後傳送兩個SADD, redis返回queued表示命令進入了等待執行的事務佇列中

如何搭建個人網站(二)應用伺服器搭建

        在上一篇文章中,博主為大家介紹了有關租賃伺服器和連線伺服器的步驟。接下來就給大家介紹一下如何在我們租用的伺服器上面搭建我們需要的應用伺服器和釋出產品。本篇文章以nginx、tomcat搭建wordpress論壇以及釋出個人網站為例為大家提供一個可實施的方案

數據庫知識事務

事務日誌 相關 數據庫 class 得到 現象 現實 操作系統 數據 事務(Transaction),一般是指要做的或所做的事情。在計算機術語中是指訪問並可能更新數據庫中各種數據項的一個程序執行單元(unit)。在計算機術語中,事務通常就是指數據庫事務。 概念 一個數據庫事

第一篇:linux系統應用管理用戶的切換

修改用戶、用戶切換、添加普通用戶、系統應用管理管理Linux系統運維之前,先來查看一下當前Linux系統的版本、內核等信息。命令如下:[[email protected]/* */ ~]# cat /etc/redhat-release CentOS release 6.8 (Final)

Android應用開發所有動畫使用詳解

factory 技術分享 resource bsp phi 顯示 程序 恢復 分享 題外話:有段時間沒有更新博客了,這篇文章也是之前寫了一半一直放在草稿箱,今天抽空把剩余的補上的。消失的這段時間真的好忙,節奏一下子有些適應不過來,早晨七點四十就得醒來,晚上九點四十才準備下班

OneNET麒麟座應用開發五:獲取加速度傳感器ADXL345數據

命令 多個 data lag 基本 采集 .cn 端口 成了 由於數據采集站基本都安裝在野外或者樓頂,安裝位置以及震動對檢測數據的準確性有一定影響。所以想要有一個位置狀態數據,正好發現麒麟作上有ADXL345,這樣一個數字輸出的加速度傳感器。如圖中紅框所示: 1、ADXL

Java進擊C#——應用開發Linq和EF

了吧 -1 擴展 有一點 增刪改 adk 對象 structure mis 本章簡言 上一章筆者對於WinForm開發過程用到的幾個知識點做了講解。筆者們可以以此為開端進行學習。而本章我們來講一個跟ORM思想有關的知識點。在講之前讓我們想一下關於JAVA的hib

Java進擊C#——應用開發WinForm開發

logs i++ mex 有時 數據 rst 流動 應用程序 dir 本章簡言 上一章筆者介紹了關於WinForm環境。這一章筆者將繼續講WinForm。只不過更加的面向開發了。事實就是在學習工具箱裏面的控件。對於WinForm開發來講,企業對他的要求並沒有那麽

Java進擊C#——應用開發WinForm環境

png 程序員 esp renderer 復制 表示 com ont mode 本章簡言 上一章筆者講到關於IO文件操作類,了解如何處理文件流。從這一章開始筆者將講解相對比較高級的知識點。而本章筆者就對WinForm開發的知識點進行講解和引導。現在很多業務都是面