Struts+Spring+Hibernate組裝web應用

來源:互聯網
上載者:User
web

  摘要:

  這篇文章將討論怎樣組合幾個著名的架構去做到松耦合的目的,怎樣建立你的構架,怎樣讓你的各個應用程式層保持一致。富於挑戰的是:組合這些架構使得每一層都以一種松耦合的方式彼此溝通,而與底層的技術無關。這篇文章將使用3種流行的開源架構來討論群組合架構的策略

  其實,就算用Java建造一個不是很煩瑣的web應用程式,也不是件輕鬆的事情。當為一個應用程式建造一個構架時有許多事情需要考慮。從高層來說,開發人員需要考慮:怎樣建立使用者介面?在哪裡處理商務邏輯?和怎樣持久化應用資料。這三層每一層都有它們各自的問題需要回答。 各個層次應該使用什麼技術?怎樣才能把應用程式設計得松耦合和能靈活改變?構架允許層的替換不會影響到其它層嗎?應用程式怎樣處理容器級的服務,比如交易處理?

  當為你的web應用程式建立一個構架時,需要涉及到相當多的問題。幸運的是,已經有不少開發人員已經遇到過這類重複發生的問題,並且建立了處理這類問題的架構。一個好架構具備以下幾點: 減輕開發人員處理複雜的問題的負擔(“不重複發明輪子”);內部定義為可擴充的;有一個強大的使用者群支援。架構通常能夠很好的解決一方面的問題。然而,你的應用程式有幾個層可能都需要它們各自的架構。就如解決你的使用者介面(UI)問題時你就不應該把事務邏輯和持久化邏輯摻雜進來。例如,你不應該在控制器裡面寫jdbc代碼,使它包含有商務邏輯,這不是控制器應該提供的功能。它應該是輕量級的,代理來自使用者介面(UI)外的調用請求給其它服務於這些請求的應用程式層。好的架構自然的形成代碼如何分布的指導。更重要的是,架構減輕開發人員從頭開始寫像持久層這樣的代碼的痛苦,使他們專註於對客戶來說很重要的應用邏輯。

  這篇文章將討論怎樣組合幾個著名的架構去做到松耦合的目的,怎樣建立你的構架,怎樣讓你的各個應用程式層保持一致。富於挑戰的是:組合這些架構使得每一層都以一種松耦合的方式彼此溝通,而與底層的技術無關。這篇文章將使用3種流行的開源架構來討論群組合架構的策略。表現層我們將使用Struts;業務層我們將使用Spring;持久層使用Hibrenate.你也可以在你的應用程式中替換這些架構中的任何一種而得到同樣的效果。圖1展示了當這些架構組合在一起時從高層看是什麼樣子。



圖1 用Struts, Spring, 和 Hibernate架構構建的概覽

  應用程式的分層

  大多數不複雜的web應用都能被分成至少4個各負其責的層次。這些層次是:表現層、持久層、業務層、領域模型層。每層在應用程式中都有明確的責任,不應該和其它層混淆功能。每一應用程式層應該彼此獨立但要給他們之間放一個通訊介面。讓我們從審視各個層開始,討論這些層應該提供什麼和不應該提供什麼。

  表現層

  在一個典型的web應用的一端是表現層。很多Java開發人員也理解Struts所提供的。然而,太常見的是,他們把像商務邏輯之類的耦合的代碼放進了一個org.apache.struts.Action。所以,讓我們在像Struts這樣一個架構應該提供什麼上取得一致意見。這兒是Struts負責的:

  ·為使用者管理請求和響應;
  ·提供一個控制器代理調用商務邏輯和其它上層處理;
  ·處理從其它層擲出給一個Struts Action的異常;
  ·為顯示提供一個模型;
  ·執行使用者介面驗證。

  這兒是一些經常用Struts編寫的但是卻不應該和Struts表現層相伴的項目:

  ·直接和資料庫通訊,比如JDBC調用;
  ·商務邏輯和與你的應用程式相關的驗證;
  ·交易管理;
  ·在表現層中引入這種代碼將導致典型耦合和討厭的維護。

  持久層

  在典型web應用的另一端是持久層。這通常是使事情迅速失控的地方。開發人員低估了構建他們自己的持久層架構的挑戰性。一般來說,機構內部自己寫的持久層不僅需要大量的開發時間,而且還經常缺少功能和變得難以控制。有幾個開源的“對象-關係映射”架構非常解決問題。尤其是,Hibernate架構為java提供了"對象-關係持久化"機制和查詢服務。Hibernate對那些已經熟悉了SQL和JDBC API的Java開發人員有一個適中的學習曲線。Hibernate持久對象是基於簡單舊式Java對象和Java集合。此外,使用Hibernate並不妨礙你正在使用的IDE。下面的列表包含了你該寫在一個持久層架構裡的代碼類型:

  查詢相關的資訊成為對象。Hibernate通過一種叫作HQL的物件導向的查詢語言或者使用條件運算式API來做這個事情。 HQL非常類似於SQL-- 只是把SQL裡的table和columns用Object和它的fields代替。有一些新的專用的HQL語言成分要學;不過,它們容易理解而且文檔做得好。HQL是一種使用來查詢對象的自然語言,花很小的代價就能學習它。

  儲存、更新、刪除儲存在資料庫中的資訊。

  像Hibernate這樣的進階“對象-關係”映射架構提供對大多數主流SQL資料庫的支援,它們支援“父/子”關係、交易處理、繼承和多態。

  這兒是一些應該在持久層裡被避免的項目:

  商務邏輯應該在你的應用的一個高一些的層次裡。持久層裡僅僅允許資料存取操作。

  你不應該把持久層邏輯和你的表現層邏輯攪在一起。避免像JSPs或基於servlet的類這些表現層組件裡的邏輯和資料存取直接通訊。通過把持久層邏輯隔離進它自己的層,應用程式變得易於修改而不會影響在其它層的代碼。例如:Hebernate能夠被其它持久層架構或者API代替而不會修改在其它任何層的代碼。

  業務層

  在一個典型的web應用程式的中間的組件是業務層或服務層。從編碼的視角來看,這個服務層是最容易被忽視的一層。不難在使用者介面層或者持久層裡找到散布在其中的這種類型的代碼。這不是正確的地方,因為這導致了應用程式的緊耦合,這樣一來,隨著時間推移代碼將很難維護。幸好,針對這一問題有好幾種Frameworks存在。在這個領域兩個最流行的架構是Spring和PicoContainer,它們叫作微容器,你可以不費力不費神的把你的對象連在一起。所有這些架構都工作在一個簡單的叫作“依賴注入”(也通稱“控制反轉”)的概念上。這篇文章將著眼於Spring的為指定的配置參數通過bean屬性的setter注入的使用。Spring也提供了一個構建器注入的複雜形式作為setter注入的一個替代。對象們被一個簡單的XML檔案連在一起,這個XML檔案含有到像交易管理員、對象工廠、包含商務邏輯的服務物件、和資料存取對象這些對象的引用。

  這篇文章的後面將用例子來把Spring使用這些概念的方法說得更清楚一些。業務層應該負責下面這些事情:

  ·處理應用程式的商務邏輯和業務驗證;
  ·管理事務;
  ·預留和其它層互動的介面;
  ·管理業務層對象之間的依賴;
  ·增加在表現層和持久層之間的靈活性,使它們互不直接通訊;
  ·從表現層中提供一個上下文給業務層獲得商務服務;
  ·管理從商務邏輯到持久層的實現。

  領域模型層

  最後,因為我們討論的是一個不是很複雜的、基於web的應用程式,我們需要一組能在不同的層之間移動的對象。領域對象層由那些代表現實世界中的業務對象的對象們組成,比如:一份訂單、訂單項、產品等等。這個層讓開發人員停止建立和維護不必要的資料轉送對象(或者叫作DTOs),來匹配他們的領域對象。例如,Hibernate允許你把資料庫資訊讀進領域對象的一個對象圖,這樣你可以在串連斷開的情況下把這些資料顯示到UI層。那些對象也能被更新和送回到持久層並在資料庫裡更新。而且,你不必把對象轉化成DTOs,因為DTOs在不同的應用程式層間移動,可能在轉換中丟失。這個模型使得Java開發人員自然地以一種物件導向的風格和對象打交道,沒有附加的編碼。

  結合一個簡單的例子

  既然我們已經從一個高的層次上理解了這些組件, 現在就讓我們開始實踐吧。在這個例子中,我們還是將合并Struts、Spring、Hibernate架構。每一個這些架構在一篇文章中都有太多的細節覆蓋到。這篇文章將用一個簡單的例子代碼展示怎樣把它們結合在一起,而不是進入每個架構的許多細節。應用程式範例將示範一個請求怎樣跨越每一層被服務的。這個應用程式範例的一個使用者能儲存一個訂單到資料庫中和查看一個在資料庫中存在的訂單。進一步的增強可以使使用者更新或刪除一個存在的訂單。

  因為領域對象將和每一層互動,我們將首先建立它們。這些對象將使我們定義什麼應該被持久化,什麼商務邏輯應該被提供,和哪種表現介面應該被設計。然後,我們將配置持久層和用Hibernate為我們的領域對象定義“對象-關係”映射。然後,我們將定義和配置我們的業務對象。在有了這些組件後,我們就能討論用Spring把這些層連在一起。最後,我們將提供一個表現層,它知道怎樣和商務服務層交流和知道怎樣處理從其它層產生的異常。

  領域對象層

  因為這些對象將和所有層互動,這也許是一個開始編碼的好地方。這個簡單的領域模型將包括一個代表一份訂單的對象和一個代表一個訂單項的對象。訂單對象將和一組訂單項對象有一對多的關係。例子代碼在領域層有兩個簡單的對象:

  ·com.meagle.bo.Order.java: 包括一份訂單的概要資訊;
  ·com.meagle.bo.OrderLineItem.java: 包括一份訂單的詳細資料;

  考慮一下為你的對象選擇包名,它將反映你的應用程式是怎樣分層的。例如:簡單應用的領域對象可以放進com.meagle.bo包。更多專門的領域對象將放入在com.meagle.bo下面的子包裡。商務邏輯在com.meagle.service包裡開始打包,DAO對象放進com.meagle.service.dao.hibernate包。對於forms和actions的表現類分別放入com.meagle.action 和 com.meagle.forms包。準確的包命名為你的類提供的功能提供一個清楚的區分,使當故障維護時更易於維護,和當給應用程式增加新的類或包時提供一致性。

  持久層配置

  用Hibernate設定持久層涉及到幾個步驟。第一步是進行配置持久化我們的領域業務對象。因為我們用於領域對象持久化的Hibernate和POJOs一起工作,因此,訂單和訂單項對象包括的所有的欄位的都需要提供getter和setter方法。訂單對象將包括像ID、使用者名稱、合計、和訂單項這樣一些欄位的標準的JavaBean格式的setter和getter方法。訂單項對象將同樣的用JavaBean的格式為它的欄位設定setter和getter方法。

  Hibernate在XML檔案裡映射領域對象到關聯式資料庫。訂單和訂單項對象將有兩個對應檔來表達這種映射。有像XDoclet這樣的工具來協助這種映射。Hibernate將映射領域對象到這些檔案:

  Order.hbm.xml
  OrderLineItem.hbm.xml

  你可以在WebContent/WEB-INF/classes/com/meagle/bo目錄裡找到這些產生的檔案。配置Hibernate SessionFactory使它知道是在和哪個資料庫通訊,使用哪個資料來源或串連池,載入哪些持久對象。SessionFactory提供的Session對象是Java對象和像選取、儲存、更新、刪除對象這樣一些持久化功能間的翻譯介面。我們將在後面的部分討論Hibernate操作Session對象需要的SessionFactory配置。

  業務層配置

  既然我們已經有了領域對象,我們需要有商務服務對象來執行應用邏輯、執行向持久層的調用、獲得從使用者介面層的請求、處理事務、處理異常。為了將所有這些串連起來並且易於管理,我們將使用Spring架構的bean管理方面。Spring使用“控制反轉”,或者“setter依賴注入”來把這些對象連好,這些對象在一個外部的XML檔案中被引用。“控制反轉”是一個簡單的概念,它允許對象接受其它的在一個高一些的層次被建立的對象。使用這種方法,你的對象從必須建立其它對象中解放出來並降低對象耦合。

  這兒是個不使用IoC的對象建立它的從屬對象的例子,這導致緊的對象耦合:



  圖2:沒有使用IoC的對象組織。對象A建立對象B和C。

  這兒是一個使用IoC的例子,它允許對象在一個高一些層次被建立和傳進另外的對象,所以另外的對象能直接使用現成的對象·[譯者註:另外的對象不必再親自建立這些要使用的對象]:


圖3:對象使用IoC組織。對象A包含setter方法,它們接受到對象B和C的介面。這也可以用對象A裡的接受對象B和C的構建器完成。

  建立我們的商務服務對象

  我們將在我們的業務對象中使用的setter方法接受的是介面,這些介面允許對象的鬆散定義的實現,這些對象將被設定或者注入。在我們這個例子裡我們將使我們的商務服務對象接受一個DAO去控制我們的領域對象的持久化。當我們在這篇文章的例子中使用Hibernate,我們可以容易的轉換到一個不同的持久架構的實現,通知Spring使用新的實現的DAO對象。你能明白編程到介面和使用“依賴注入”模式是怎樣寬鬆耦合你的商務邏輯和你的持久化機制的。

  這兒是商務服務對象的介面,它是一個DAO對象依賴的樁。

public interface IOrderService {
 public abstract Order saveNewOrder(Order order)
  throws OrderException,
      OrderMinimumAmountException;
 public abstract List findOrderByUser(String user)
              throws OrderException;
 public abstract Order findOrderById(int id)
              throws OrderException;
 public abstract void setOrderDAO(IOrderDAO orderDAO);
}

  注意上面的代碼有一個為DAO對象準備的setter方法。這兒沒有一個getOrderDAO方法因為它不是必要的,因為不太有從外面訪問連著的OrderDAO對象的需要。DAO對象將被用來和我們的持久層溝通。我們將用Spring把商務服務對象和DAO對象連在一起。因為我們編碼到介面,我們不會緊耦合實現。

  下一步是寫我們的DAO實現對象。因為Spring有內建的對Hibernate的支援,這個例子DAO將繼承HibernateDaoSupport類,這使得我們容易取得一個到HibernateTemplate類的引用,HibernateTemplate是一個協助類,它能簡化Hibernate Session的編碼和處理HibernateExceptions。這兒是DAO的介面:

public interface IOrderDAO {
 public abstract Order findOrderById(final int id);
 public abstract List findOrdersPlaceByUser(final String placedBy);
 public abstract Order saveOrder(final Order order);
}

  我們還有兩個對象要和我們的業務層連在一起。這包括HibernateSessionFactory和一個TransactionManager對象。這在Spring設定檔裡直接完成。Spring提供一個HibernateTransactionManager,它將從工廠綁定一個Hibernate Session到一個線程來支援事務。這兒是HibernateSessionFactory和HibernateTransactionManager的Spring配置。

<bean id="mySessionFactory"
    class="org.springframework.orm.hibernate.
       LocalSessionFactoryBean">
 <property name="mappingResources">
  <list>
   <value>
    com/meagle/bo/Order.hbm.xml
   </value>
   <value>
    com/meagle/bo/OrderLineItem.hbm.xml
   </value>
  </list>
 </property>
 <property name="hibernateProperties">
  <props>
   <prop key="hibernate.dialect">
    net.sf.hibernate.dialect.MySQLDialect
   </prop>
   <prop key="hibernate.show_sql">
    false
   </prop>
   <prop key="hibernate.proxool.xml">
    C:/MyWebApps/.../WEB-INF/proxool.xml
   </prop>
   <prop key="hibernate.proxool.pool_alias">
     spring
   </prop>
  </props>
 </property>
</bean>
<!-- Transaction manager for a single Hibernate
SessionFactory (alternative to JTA) -->
<bean id="myTransactionManager"
     class="org.
         springframework.
        orm.
        hibernate.
        HibernateTransactionManager">
 <property name="sessionFactory">
  <ref local="mySessionFactory"/>
 </property>
 </bean>

  每一個對象能被Spring配置裡的一個<bean>標記引用。在這個例子裡,bean “mySessionFactory”代表一個HibernateSessionFactory,bean “myTransactionManager”代表一個Hibernate transaction manager。注意transactionManger bean有一個叫作sessionFactory的屬性元素。HibernateTransactionManager有一個為sessionFactory準備的setter和getter方法,它們是用來當Spring容器啟動時的依賴注入。sessionFactory屬性引用mySessionFactory bean。這兩個對象現在當Spring容器初始化時將被連在一起。這種串連把你從為引用和建立這些對象而建立singleton對象和工廠中解放出來,這減少了你應用程式中的代碼維護。mySessionFactory bean有兩個屬性元素,它們翻譯成為mappingResources 和 hibernatePropertes準備的setter方法。通常,如果你在Spring之外使用Hibernate,這個配置將被儲存在hibernate.cfg.xml檔案中。不管怎樣,Spring提供了一個便捷的方式--在Spring設定檔中合并Hibernate的配置。

  既然我們已經配置了我們的Container Servicebeans和把它們連在了一起,我們需要把我們的商務服務對象和我們的DAO對象連在一起。然後,我們需要把這些對象串連到交易管理員。

  這是在Spring設定檔裡的樣子:

<!-- ORDER SERVICE -->
<bean id="orderService"
 class="org.
     springframework.
     transaction.
     interceptor.
     TransactionProxyFactoryBean">
 <property name="transactionManager">
  <ref local="myTransactionManager"/>
 </property>
 <property name="target">
  <ref local="orderTarget"/>
 </property>
 <property name="transactionAttributes">
  <props>
   <prop key="find*">
  PROPAGATION_REQUIRED,readOnly,-OrderException
   </prop>
   <prop key="save*">
  PROPAGATION_REQUIRED,-OrderException
   </prop>
  </props>
 </property>
</bean>
<!-- ORDER TARGET PRIMARY BUSINESS OBJECT:
Hibernate implementation -->
<bean id="orderTarget"
     class="com.
        meagle.
        service.
        spring.
        OrderServiceSpringImpl">
 <property name="orderDAO">
  <ref local="orderDAO"/>
 </property>
</bean>
<!-- ORDER DAO OBJECT -->
<bean id="orderDAO"
     class="com.
        meagle.
        service.
        dao.
        hibernate.
        OrderHibernateDAO">
 <property name="sessionFactory">
  <ref local="mySessionFactory"/>
 </property>
</bean>

  圖4是我們已經連在一起的東西的一個概覽。它展示了每個對象是怎樣相關聯的和怎樣被Spring設定進其它對象中。把這幅圖和樣本應用中的Spring設定檔對比查看它們之間的關係。


圖4:這是Spring怎樣將在這個配置的基礎上裝配beans。

  這個例子使用一個TransactionProxyFactoryBean,它有一個為我們已經定義了的交易管理者準備的setter方法。這是一個有用的對象,它知道怎樣處理聲明的事務操作和你的服務物件。你可以通過transactionAttributes屬性定義事務怎樣被處理,transactionAttributes屬性為方法名定義模式和它們怎樣參與進一個事務。

  TransactionProxyFactoryBean類也有一個為一個target準備的setter,target將是一個到我們的叫作orderTarget的商務服務對象的引用。 orderTarget bean定義使用哪個商務服務對象並有一個指向setOrderDAO()的屬性。orderDAO bean將居於這個屬性中,orderDAO bean是我們的和持久層交流的DAO對象。

  還有一個關於Spring和bean要注意的是bean能以兩種模式工作。這兩種模式被定義為singleton和prototype。一個bean預設的模式是singleton,意味著一個共用的bean的執行個體將被管理。這是用於無狀態操作--像一個無狀態會話bean將提供的那樣。當bean由Spring提供時,prototype模式允許建立bean的新執行個體。你應當只有在每一個使用者都需要他們自己的bean的拷貝時才使用prototype模式。

  供一個服務定位器

  既然我們已經把我們的服務和我們的DAO連起來了,我們需要把我們的服務暴露給其它層。通常是一個像使用Struts或Swing這樣的使用者介面層裡的代碼來使用這個服務。一個簡單的處理方法是使用一個服務定位器模式的類從一個Spring上下文中返回資源。這也可以靠引用bean ID通過Spring來直接完成。

  這兒是一個在Struts Action中怎樣配置一個服務定位器的例子:

public abstract class BaseAction extends Action {
 private IOrderService orderService;
 public void setServlet(ActionServlet
                 actionServlet) {
  super.setServlet(actionServlet);
  ServletContext servletContext =
        actionServlet.getServletContext();
  WebApplicationContext wac =
   WebApplicationContextUtils.
     getRequiredWebApplicationContext(
                 servletContext);
   this.orderService = (IOrderService)
           wac.getBean("orderService");
 }
 protected IOrderService getOrderService() {
  return orderService;
 }
}

   使用者介面層配置

  樣本應用的使用者介面層使用Struts架構。這兒我們將討論當為一個應用分層時和Struts相關的部分。讓我們從在struts-config.xml檔案裡檢查一個Action配置開始。

<action path="/SaveNewOrder"
  type="com.meagle.action.SaveOrderAction"
  name="OrderForm"
  scope="request"
  validate="true"
  input="/NewOrder.jsp">
 <display-name>Save New Order</display-name>
 <exception key="error.order.save"
  path="/NewOrder.jsp"
  scope="request"
  type="com.meagle.exception.OrderException"/>
 <exception key="error.order.not.enough.money"
  path="/NewOrder.jsp"
  scope="request"
  type="com.
  meagle.
  exception.
  OrderMinimumAmountException"/>
 <forward name="success" path="/ViewOrder.jsp"/>
 <forward name="failure" path="/NewOrder.jsp"/>
</action>

  SaveNewOrder Action被用來持久化一個使用者從使用者介面層提交的訂單。這是一個典型的Struts Action;然而,注意這個action的異常配置。這些Exceptions為我們的商務服務對象也在Spring 設定檔中配置了。當這些異常被從業務層擲出我們能在我們的使用者介面裡恰當的處理它們。第一個異常,OrderException,當在持久層裡儲存訂單對象失敗時將被這個action使用。這將引起交易回復和通過業務對象傳遞把異常傳回給Struts層。OrderMinimumAmountException,在業務對象邏輯裡的一個事務因為提交的訂單達不到最小訂單數量而失敗也將被處理。然後,事務將復原和這個異常能被使用者介面層恰當的處理。

  最後一個串連步驟是使我們的表現層和我們的業務層互動。這已經通過使用前面討論的服務定位器來完成了。服務層充當一個到我們的商務邏輯和持久層的介面。這兒是 Struts中的SaveNewOrder Action可能怎樣使用一個服務定位器調用一個業務方法:

public ActionForward execute(
 ActionMapping mapping,
 ActionForm form,
 javax.servlet.http.HttpServletRequest request,
 javax.servlet.http.HttpServletResponse response)
 throws java.lang.Exception {
 OrderForm oForm = (OrderForm)form;
 // Use the form to build an Order object that
 // can be saved in the persistence layer.
 // See the full source code in the sample app.
 // Obtain the wired business service object
 // from the service locator configuration
 // in BaseAction.
 // Delegate the save to the service layer and
 // further upstream to save the Order object.
 getOrderService().saveNewOrder(order);
 oForm.setOrder(order);
 ActionMessages messages = new ActionMessages();
 messages.add(
   ActionMessages.GLOBAL_MESSAGE,
new ActionMessage(
   "message.order.saved.successfully"));
 saveMessages(request, messages);
 return mapping.findForward("success");
}

   結論

  這篇文章按照技術和架構覆蓋了許多話題。從中而取出的主要思想是怎樣更好的給你的應用程式分層:使用者介面層、持久邏輯層、和其它任何你需要的應用程式層。這樣可以解耦你的代碼,允許添加新的程式碼群組件,使你的應用在將來更易維護。這裡覆蓋的技術能很好的解決這類的問題。不管怎樣,使用這樣的構架可以讓你用其他技術代替現在的層。例如,你也許不想使用Hibernate持久化。因為你在你的DAO對象中編碼到介面,你能怎樣使用其它的技術或架構,比如 iBATIS,作為一個替代是顯而易見的。或者你可能用不同於Struts的架構替代你的UI層。改變UI層的實現不會直接影響你的商務邏輯層或者你的持久層。替換你的持久層不會影響你的UI邏輯或商務服務層。整合一個web應用其實也不是一件煩瑣的工作,靠解耦你的各應用程式層和用適當的架構組成它,它能變得更容易處理。



相關文章

Beyond APAC's No.1 Cloud

19.6% IaaS Market Share in Asia Pacific - Gartner IT Service report, 2018

Learn more >

Apsara Conference 2019

The Rise of Data Intelligence, September 25th - 27th, Hangzhou, China

Learn more >

Alibaba Cloud Free Trial

Learn and experience the power of Alibaba Cloud with a free trial worth $300-1200 USD

Learn more >

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。