詳解服務器端的項目框架

導讀

我一直相信這句話,他山之石能夠攻玉。在本身能力不夠時,多學習別人的東西。這樣,對本身只有好處,沒有壞處。於是,通過將近一年的工做,研讀了公司所使用的框架。我本想往架構師的方向靠近,但,本身的能力可能還不夠,於是,不斷地給本身充電。css

公司的項目是先後端分離的,前端使用HTML5,css三、jquery、vue.js、bootstrap等,以SVN作代碼託管。後端採用maven構建項目,以git lab作代碼託管。確定有人會問,這兩個都是版本庫,但它們有什麼區別?若是想要了解的話,能夠參考該文檔:Svn與Git的區別html

如今幾乎全部的公司都採用maven構建項目,不多會採用導入jar包的方式依賴第三方框架。前端


maven介紹

maven構建的項目有不少好處,首先其能夠統一管理jar包,也就是說,咱們在項目中不用手動導入jar包,咱們只要添加依賴便可,如代碼所示:vue

<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>${jdbc.version}</version>
</dependency>

添加依賴以後,maven就會導入該jar包,導入jar包的順序爲:java

  • 首先查找本地倉庫,若是本地倉庫沒有,進入下面步驟。
  • maven settings profile中的repository;
  • pom.xml中profile中定義的repository。profile激活配置文件,好比正式環境的配置文件prd.properties和開發環境的Dev.properties文件。這也是打包的依據,是打開發環境的包,仍是打正式環境的包,如圖所示:

正式環境和配置環境

  • pom.xml中的repositorys(定義多個repository,按定義順序找);
  • 若是通過上面的步驟,沒有找到相應的jar包,最後到咱們的鏡像(mirror)中查找。若是mirror中存在該jar包,從mirror中拷貝下來,存儲到本地倉庫中,進入到最初一步。若是mirror中也沒有,maven就會報相應的錯誤。

maven報出相應的錯誤時,也許,是咱們本地沒有該jar包,遠程倉庫也沒有該jar包,咱們能夠參考這篇博客:在maven的pom.xml中添加本地jar包。它會教你如何建立本地倉庫,並導入建立好的本地倉庫。mysql

【備註】這篇博客以架構師的角度來說解maven,因此,不具體講解maven各個標籤的含義。若是你想了解pom的各個標籤的含義,能夠參考這篇文檔:pom.xml詳解,或者,參考這篇教程:maven教程|菜鳥教程jquery

上面解說只是配置jar文件,可是,maven的功能遠不止這些,從咱們建立maven項目時,就已經進入到maven的開發環境中。css3


maven項目

有人和我說過,學一項知識,什麼方式最快?那就是經過作項目。你在作項目的過程當中,確定會遇到不少問題,而你不得不去查找資料,從根源上認識到這個問題。於是,我以公司所作的某個項目爲例,來說解maven的依賴、繼承、聚合等關係。c++

咱們所說的maven中的關係,其實就是pom的關係,即項目對象模型(Project Object Model)的簡稱。git


maven聚合

首先,咱們在建立cloudCodeSale項目時,就已經建立了父pom文件,如圖所示:

父pom文件

上圖就是咱們的父pom文件,你很清楚的看到gav座標。同時,你從這張圖上,也能獲得其餘信息,其打包方式是 pom,其還關聯其餘module,module名稱和左邊的列表名稱同樣。這就是咱們所說的maven的聚合。父類同時聚合其子類。

聚合的條件有兩個:

  1. 修改被聚合項目的pom.xml中的packaging元素的值爲pom
  2. 在被聚合項目的pom.xml中的modules元素下指定它的子模塊項目

既然全部的子模塊的pom都繼承父pom,爲何父pom要聚合子模塊的pom文件?這個問題很好。

由於對於聚合而言,當咱們在被聚合的項目上使用Maven命令時,實際上這些命令都會在它的子模塊項目上使用。這就是Maven中聚合的一個很是重要的做用。在實際開發的過程當中,咱們只須要打包(mvn install)父pom文件。

咱們在父pom上使用mvn celan、mvn compile和mvn package,其會自動對子模塊:platform-core、platform-core-controller、portal/member-portal、portal/platform-portal、platform-cms、platform-cms-controller、platform-custom執行mvn celan、mvn compile和mvn package。不必一個一個地打包,這樣極容易出現錯誤,如圖所示:

mvn install


maven繼承機制

若是不少模塊都須要相同的jar包,咱們能夠單獨寫在一個pom中,其餘模塊使用該模塊的公共部分,這就是咱們常說的父類。不管是java語言,仍是c++語言,或者如今的pom,其都體現這個思想。

咱們在上文也提到了子模塊,如今模塊platform-core講解。繼承父類的結構通常是這樣的:

<parent>  
    <groupId>parent.groupId</groupId>  
    <artifactId>parent.artifactId</artifactId>  
    <version>parent.version</version>  
       <relativePath>../pom.xml</relativePath>  
  </parent>

relativePath是父pom.xml文件相對於子pom.xml文件的位置,針對被繼承的父pom與繼承pom的目錄結構是否是父子關係。若是是父子關係,則不用該標籤;若是不是,那麼就用該標籤。由於在當前項目中,platform-core模塊的目錄的pom在父目錄的pom中,其和父pom的目錄結構是父子關係,於是能夠省略relativePath該標籤,如圖所示:

clipboard.png

parent標籤中的groupId、artifactId、version要和父pom中的標籤中一致。


maven的依賴關係

正如咱們所知道的,maven構建項目,不只是由於其可以管理jar包,其還使模塊化開發更簡單了。於是,咱們在開發的過程當中,通常都分模塊化開發。模塊與模塊之間的信息是不通的,可是,咱們想要模塊間的之間可以通訊,這時,咱們就想到了java中的依賴關係。

好比,咱們有一個模塊,這個模塊封裝好了微信支付、支付寶支付、處理json格式、操做文件、ResultUtil、lambdaUtil、commonUtil等工具類,還有附件、頭像、用戶等實體類。這些工具類在任何項目中不會輕易改變,若是爲了知足某些需求而不得不得修改,須要獲得架構師的贊成。

於是,咱們能夠把它拿出來,單獨定義爲一個模塊,也就是platform-core模塊。可是,咱們還有一個模塊,在這個模塊中,根據不一樣的項目,其定義不一樣的實體類、dao層類、事務層類、枚舉類、接收前端傳來參數封裝成的query類、從數據庫中取出的數據封裝成的data類,到事務層可能會調用模塊plateform-core中的方法,好比調用第三方系統接口的HTTPClientUtil.doPost(String url, Map<String, String> param),判斷處理lambda表達式的LambdaUtil.ifNotBlankThen(String value, Consumer<String> function) ,等等。

這個自定義類的模塊,咱們可定義爲plateform-custom。plateform-custom須要用到platform-core中的方法,於是,這時,咱們就須要考慮依賴關係,怎麼添加對platform-core的依賴呢?如代碼所示:

<dependency>
    <groupId>com.zfounder.platform</groupId>
    <artifactId>platform-core</artifactId>
</dependency>

咱們這邊是先後臺分離的,後臺用來錄入數據,前臺用來展現數據,於是,咱們有一個portal目錄,該目錄下有兩個子模塊。一個是member-portal模塊,一個是platform-portal模塊,前者接收前臺的接口參數,後者接收後臺的接口參數。但不論哪一個模塊,都須要依賴plateform-custom中的事務層方法,同時,咱們傳的參數,可能信息分發的platform-cms-controller中的接口,也多是核心接口platform-core-controller中的接口。於是,咱們這裏以member-portal模塊來舉例,依賴其餘模塊的代碼以下:

<dependencies>
    <dependency>
        <groupId>com.zfounder.platform</groupId>
        <artifactId>platform-core-controller</artifactId>
    </dependency>
    <dependency>
        <groupId>com.zfounder.platform</groupId>
        <artifactId>platform-cms-controller</artifactId>
    </dependency>
    <dependency>
        <groupId>com.zfounder.platform</groupId>
        <artifactId>platform-custom</artifactId>
        <version>1.0-SNAPSHOT</version>
    </dependency>
</dependencies>

這些模塊你會在上面的圖片找獲得。同時,咱們來看member-portal的pom文件繼承的父pom是怎麼寫的:

member-portal的文件繼承的父pom

補充上面的繼承關係。這裏面用到了<relativePath>../../pom.xml</relativePath>你會奇怪的是,爲何這裏面用到了呢?其和父pom不是父子關係,而是孫子關係。這裏使用到了兩次點點,這是什麼意思呢? ..表示上級目錄。舉個例子說明:

好比,在個人服務器上的www目錄中,有三個文件,分別是rsa_private_key.pem, rsa_private_key_pkcs8.pem, rsa_public_key.pem,還有一個testDir目錄,testDir目錄中還有目錄testDir,如今咱們經過 cd ../testDir/testDir進入到子目錄中,如今,咱們想返回到www的根目錄中,並查看rsa_public_key.pem文件的內容,於是,咱們能夠用 cat ../../rsa_public_key.pem命令,其首先返回兩級目錄,而後找到rsa_public_key.pem文件並打開該文件。

返回兩級目錄

「被繼承的父pom與繼承pom的目錄結構是否是父子關係」也不是絕對的,主要是選擇繼承者的pom中的子目錄和父目錄之間的關係,其中間隔了幾層目錄。


maven激活文件

激活文件在上文也提到了,咱們爲何須要激活文件?以下面的兩個配置文件,一個是測試環境的配置文件,名爲platform-dev.properties,一個是正式環境的配置文件,名爲platform-prd.properties。兩個配置文件中都存在與數據庫的鏈接,可是呢,數據庫的ip地址是不同的。如一下的代碼所示:

  • 正式服的platform-prd.properties配置文件
jdbc.url=jdbc:mysql://localhost/prd_database
jdbc.username=prd_username
jdbc.password=prd_password

jdbc.validationQuery=select 1 from dual
jdbc.removeAbandonedTimeout=180
jdbc.initialSize=10
jdbc.minIdle=30
jdbc.maxActive=100
jdbc.maxWait=30000

。。。
  • 測試服的platform-dev.properties配置文件
jdbc.url=jdbc:mysql://intranet_ip/dev_database
jdbc.username=dev_username
jdbc.password=dev_password

jdbc.validationQuery=select 1 from dual
jdbc.removeAbandonedTimeout=180
jdbc.initialSize=10
jdbc.minIdle=30
jdbc.maxActive=100
jdbc.maxWait=30000

。。。

咱們的在配置文件中配置好了數據項,可是呢,咱們怎麼切換不一樣的配置文件呢?換句話說,咱們怎麼想要打正式服的包放到正式服上,怎麼選擇platform-prd.properties的配置文件呢?反之,怎麼選擇platform-dev.properties配置文件?

這時,咱們就用到了maven當中的profile標籤,以下代碼所示:

<profiles>
    <profile>
        <id>dev</id>
        <activation>
            <activeByDefault>true</activeByDefault>
        </activation>
        <build>
            <filters>
                <filter>../../platform-dev.properties</filter>
            </filters>
        </build>
    </profile>
    <profile>
        <id>prd</id>
        <build>
            <filters>
                <filter>../../platform-prd.properties</filter>
            </filters>
        </build>
    </profile>
</profiles>

這些配置文件時寫在member-portal、platform-portal、plateform-core和plateform-cms、plateform-customer模塊的pom中的。可是,plateform-core和plateform-cms的配置中的filter和上面連個略有差別,其filter是這樣的 <filter>../platform-dev.properties</filter> <filter>../platform-prd.properties</filter>,這就涉及到目錄點的問題。


maven依賴第三方包

maven項目除了依賴本項目的,其還會依賴第三方包,好比自動生成set和get方法的lombok包,處理json格式的阿里巴巴下的fastjson包等等,咱們也可使用這種格式的依賴:

<!--mysql jdbc驅動包 開始-->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>${jdbc.version}</version>
</dependency>
<!--mysql jdbc驅動包 結束-->

開發經常使用的jar包

lombok

<!-- lombok驅動包 開始-->
<dependency>
  <groupId>org.projectlombok</groupId>
  <artifactId>lombok</artifactId>
  <version>1.16.10</version>
</dependency>
<!-- lombok驅動包 開始-->

咱們在沒有使用lombok以前,常常手動建立javabean的set個get方法,使用這個框架以後,其以註解的方式,能夠自動生成set和get方法。同時,其強大的功能遠不止這些,也能夠生成無參構造器、全參構造器,指定參數構造器、重寫toString方法、重寫Hashcode、equals方法等等。

如代碼所示:

/**
 * Created By zby on 17:37 2019/1/30
 */
@AllArgsConstructor
@NoArgsConstructor
@Data
@ToString
@EqualsAndHashCode
public class Address {

    /**
     * 收貨人
     */
    private String consignee;

    /**
     * 手機號碼
     */
    private String phone;

    /**
     * 所在地區
     */
    private String area;

    /**
     * 詳細地址
     */
    private String detail;

    /**
     * 標籤
     */
    private AddressTagEnum addressTag;
}

想要更深層次的瞭解這個框架,能夠參考這個博客:Lombok使用詳解


fastjson

<!-- fastjson驅動包 開始-->
<dependency>
  <groupId>com.alibaba</groupId>
  <artifactId>fastjson</artifactId>
  <version>1.2.28</version>
</dependency>
<!-- fastjson驅動包 結束-->

fastjson是阿里巴巴開源的框架,其用來處理服務器端的json格式的數據。好比,咱們須要將服務端的對象以json(JavaScript object Notation,js對象標記)格式傳輸到前端,可是,若是本身手動建立的話,勢必會很是的麻煩,因而,咱們藉助這個框架,幫助咱們生成json格式的對象。

同時,若是咱們要調用第三方接口,好比調用連連綁定銀行卡的接口,其返回給咱們的也是json對象的數據。可是,咱們須要將其轉化爲咱們定義的對象,調用其save方法,保存到數據庫中,對帳所用。

對於,將對象轉化爲json格式的對象,如代碼所示:

@Test
public void test() {
//        地址
    Address address = new Address();
    address.setAddressTag(AddressTagEnum.ADDRESS_TAG_COMPANY);
    address.setArea("杭州市....");
    address.setConsignee("zby");

//        用戶
    User user = new User();
    user.setHobby(HobbyEnum.HOBBY_DANCING);
    user.setGender("男");
    user.setUserName("蔣三");

//        訂單
    OrderSnapshot orderSnapshot = new OrderSnapshot();
    orderSnapshot.setAddress(address);
    orderSnapshot.setId(1L);
    orderSnapshot.setName("復讀機");
    orderSnapshot.setOrderNo(Long.valueOf(System.currentTimeMillis()).toString() + "1L");
    orderSnapshot.setUser(user);

    System.out.println(JSON.toJSON(orderSnapshot));
}

其輸出結果如圖所示:

輸出結果

可是,相似於解析json格式的數據,不僅有fastjson框,還有org.json框架、Jackson框架。但通過有人驗證呢,仍是fastjson的效率更高一些。能夠參考這篇博客:Gson、FastJson、org.JSON到底哪個效率更高,速度更快

org.json也是經過maven配置的,如代碼所示:

<!-- json驅動包 開始-->
<dependency>
    <groupId>org.json</groupId>
    <artifactId>json</artifactId>
    <version>20140107</version>
</dependency>
<!-- json驅動包 開始-->

若是想要深層次瞭解org.json,能夠參考這篇博客:Java使用org.json.jar構造和解析Json數據

想要更深層次的瞭解fastjson,能夠參考這篇博客:Fastjson 簡明教程


spring相關配置

若是從事java-web開發,通常會用到spring框架,這方面的教程太多了,筆者就不在這介紹,但咱們會用到spring的這些框架:

<!--spring 相關配置開始-->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context-support</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-jdbc</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-orm</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aspects</artifactId>
</dependency>

spring會集合不少框架,好比具備攔截效果的shiro框架,持久層的hibernate和mybatis框架等等。spring經過配置文件經過註解或者配置文件的方式,實現依賴注入(dependency injection)和控制反轉(inversion of control)。經過@controller遍歷相應的接口,實現先後端的接口對接。spring能夠實現面向切面編程,實現某個業務點的單一執行。好比,專門處理事務的業務點。

spring並不難,很快就能掌握到其精髓。

若是想深刻了解,能夠參考這篇教程:Spring教程


hibernate框架

hibernate框架就相似於mybatis框架,其專門處理持久層的技術。咱們將瞬時狀態的對象存儲到數據庫中,變成持久狀態的對象。咱們也能夠從數據庫中取數據,以瞬時態的對象返回到前端。這就是一存一取的框架。

其可使用註解的方式建立數據表,也能夠經過配置文件建立瞬時態的對象。但就目前爲止,在不少狀況下,咱們都是經過註解的方式,實現數據表的建立。導入hibernate相關的框架,以下所示:

<!--hibernate相關配置 開始-->
<!--hibernateh核心框架-->
<dependency>
    <groupId>org.hibernate</groupId>
    <artifactId>hibernate-core</artifactId>
</dependency>
<!--hibernateh驗證器-->
<dependency>
    <groupId>org.hibernate</groupId>
    <artifactId>hibernate-validator</artifactId>
</dependency>
<!--hibernateh緩存技術-->
<dependency>
    <groupId>org.hibernate</groupId>
    <artifactId>hibernate-ehcache</artifactId>
</dependency>
 <!--Java Persistence API ORM映射元數據 查詢語言-->
<dependency>
    <groupId>org.hibernate.java-persistence</groupId>
    <artifactId>jpa-api</artifactId>
</dependency>
<!--hibernate相關配置 結束-->

hibernate和mybatis具備一樣的功能,若是想要了解mybatis,能夠參考這篇教程:mybatis教程

想要深刻理解hibernate,可參考這篇教程:hibernate教程_w3cschool


jbdc驅動包

咱們上面說了hibernate框架,但前提是,咱們須要導入jdbc的框架包,如代碼所示:

<!-- 數據庫驅動包相關 開始-->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
</dependency>
<!-- 數據庫驅動包相關 結束-->

這個驅動包主要處理java與數據庫的鏈接,實現數據的增、刪、改、查。咱們沒有用到這個包,可是hibernate用到了這個包,於是,咱們須要導入這個包,以避免數據庫報錯。


alibaba的Druid包

這個包有什麼用嗎?咱們既然經過hibernate實現與數據庫的交互,那麼就須要在初始化時建立鏈接池。如今鏈接池有不少種,咱們爲何選擇了它Druid。

<!-- Druid驅動包相關 開始-->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid</artifactId>
</dependency>
<!-- Druid驅動包相關 結束-->

它是目前最好的數據庫鏈接池,在功能、性能、擴展性方面,都超過其餘數據庫鏈接池,包括DBCP、C3P0、BoneCP、Proxool、JBoss DataSource。Druid已經在阿里巴巴部署了超過600個應用,通過一年多生產環境大規模部署的嚴苛考驗。Druid是阿里巴巴開發的號稱爲監控而生的數據庫鏈接池!

時代在變化,咱們也應該應世而生,與時俱進,纔不會和時代脫軌。

咱們使用Druid來實現數據的配置,如代碼所示:

<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"
      init-method="init" destroy-method="close">
    <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
    <property name="url" value="${jdbc.url}"/>
    <property name="username" value="${jdbc.username}"/>
    <property name="password" value="${jdbc.password}"/>
    <property name="maxActive" value="${jdbc.maxActive}"/>
    <property name="initialSize" value="${jdbc.initialSize}"/>
    <property name="removeAbandoned" value="true"/>
    <property name="removeAbandonedTimeout" value="${jdbc.removeAbandonedTimeout}"/>
    <property name="testOnBorrow" value="true"/>
    <property name="minIdle" value="${jdbc.minIdle}"/>
    <property name="maxWait" value="${jdbc.maxWait}"/>
    <property name="validationQuery" value="${jdbc.validationQuery}"/>
    <property name="connectionProperties" value="clientEncoding=UTF-8"/>
</bean>

大家能夠看到,value值是形參,而不是具體的值。由於咱們根據不一樣的打包方式,其傳入形參對應的實參不一樣。這也就是咱們上文提到的,platform-dev.properties和platform-prd.properties配置文件,以及maven配置的激活文件。

若是想要深刻了解阿里巴巴的Druid框架,能夠參考這篇博客:DRUID鏈接池的實用 配置詳解


阿里雲短信

短信業務通常固定不變,由架構師封裝好,其餘人直接調用便可,於是,該框架能夠寫進plateform-core模塊中,其配置的代碼以下所示:

<!-- 阿里短息驅動包配置 開始 -->
<dependency>
    <groupId>com.aliyun</groupId>
    <artifactId>aliyun-java-sdk-dysmsapi</artifactId>
</dependency>
<dependency>
    <groupId>com.aliyun</groupId>
    <artifactId>aliyun-java-sdk-core</artifactId>
</dependency>
<!-- 阿里短息驅動包配置 結束 -->

日誌相關配置

咱們在開發的過程當中,常常會使用到日誌,來記錄相應的錯誤、警告、信息。好比,我在使用連連支付作提現業務時,提現成功後其會回調咱們的接口,從而顯示在服務端的Tomcat頁面中。再好比,咱們在登陸時,其會在Tomcat中顯示相關信息,如圖所示:

日誌信息

咱們都知道日誌分爲幾種級別。這裏就再也不贅述了。日誌分爲好多種,咱們推薦使用slf4j+logback模式。由於logback自身實現了slf4j的接口,無須額外引入適配器,另外logback是log4j的升級版,具有比log4j更多的優勢,咱們能夠經過以下配置進行集成:

<!-- 日誌驅動包配置 開始 -->
<dependency>
    <groupId>org.slf4j</groupId>
    <artifactId>slf4j-api</artifactId>
    <version>1.7.21</version>
</dependency>
<dependency>
    <groupId>ch.qos.logback</groupId>
    <artifactId>logback-classic</artifactId>
    <version>1.1.7</version>
</dependency>
<dependency>
    <groupId>ch.qos.logback</groupId>
    <artifactId>logback-core</artifactId>
    <version>1.1.7</version>
</dependency>
<!-- 日誌驅動包配置 結束 -->

咱們這時就用到了plateform-prd.properties文件和plateform-dev.properties文件,由於,咱們須要在這裏面配置日誌的輸出位置。而後,在logback.xml中以參數的形式,調用文件中的輸出位置,如圖所示:

日誌配置信息

若是想要了解更多的配置文件信息,請參考這篇博客:使用 logback + slf4j 進行日誌記錄


commons家族

咱們在開發的過程當中,常常用到Commons家族的驅動包,好比文件操做的io包,MD5加密和解密用的codec包。固然,咱們也會用到java自帶的local_policy驅動包,但有時須要替換替換該驅動包,不然,就會報出Illegal Key Size的錯誤。文件上傳下載的fileupload驅動包,操做字符串類型的lang3包,配置的驅動包以下所示:

<!--comon包相關配置-->
<commons-io.version>2.4</commons-io.version>
<commons-lang3.version>3.4</commons-lang3.version>
<commons-codec.version>1.10</commons-codec.version>
<commons-fileupload.version>1.3.1</commons-fileupload.version>

<!-- apache common 開始 -->
<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-lang3</artifactId>
    <version>${commons-lang3.version}</version>
</dependency>
<dependency>
    <groupId>commons-io</groupId>
    <artifactId>commons-io</artifactId>
    <version>${commons-io.version}</version>
</dependency>
<dependency>
    <groupId>commons-codec</groupId>
    <artifactId>commons-codec</artifactId>
    <version>${commons-codec.version}</version>
</dependency>
<dependency>
    <groupId>commons-fileupload</groupId>
    <artifactId>commons-fileupload</artifactId>
    <version>${commons-fileupload.version}</version>
</dependency>
<!-- apache common 結束 -->

  • lang3包

咱們能夠用其分割字符串,判斷字符串是否爲空格,判斷字符串是否爲空等等。如如下代碼所示:

public static void main(String[] args) {
    String keyword = "1-1-2";
    if (StringUtils.isNotBlank(keyword)) {
        System.out.println("keyword = " + keyword);
    }
    String[] keys = StringUtils.split(keyword, "-");
    for (String key : keys) {
        System.out.println("key=" + key);
    }
}

咱們有時還會用其操做時間類,好比格式化時間等等,入一下代碼:

@Test
public void testDate(){
   String date1= FastDateFormat.getInstance("yyyy-MM-dd").format(System.currentTimeMillis());
    System.out.println("System.currentTimeMillis:"+date1);
    String date2= FastDateFormat.getInstance("yyyy-MM-dd").format(new Date());
    System.out.println("new Date:"+date2);
}

其功能遠不止這些,具體能夠參考這篇博客:commons-lang3工具包


  • io包

見名知意,IO即input和output的簡寫,即輸入流和輸出流。於是,咱們常用到java自帶的InputStream或FileInputStream的字節輸入流,以及OutputStream或FileOutputStream的輸出流。若是更高級的話,那麼,就使用到了帶有緩存效果的bufferReader輸入流和bufferWrite輸出流。這裏面用到了裝飾設計模式。什麼是裝修設計模式,能夠自行學習。

上面的操做比較複雜,咱們就用到了apache下的io驅動包。這裏就當作拋磚引玉了,想要有更深的瞭解,能夠參考這篇博客:io包工具類


  • codec包

codec包是Commons家族中的加密和解密用的包,這裏不作任何解釋,具體能夠參考這篇博客:Commons Codec基本使用


  • fileupload包

咱們若是作java-web開發,常常會有文件上傳和文件下載的功能。這時,咱們就考慮到了Apache下面的 fileupload包,這能夠完成文件的上傳和下載。這裏的文件不僅僅是指doc文件,也會指圖片和視頻文件。

具體想要有更多的理解,能夠參考這篇文檔:commons-fileupload上傳下載


shiro包

咱們在web開發時,常常會涉及到權限問題,好比哪些頁面不須要登陸就能看,而哪些頁面只能登陸才能看。當用戶在打開該頁面以前,就進入到相應的過濾器中,來作相關業務的判斷。若是經過,就進入到controller層;不經過,則拋出相應的異常給前端。這裏就須要相應的權限控制。

說到權限控制,咱們不得不提到shiro框架。其有三大核心組件Subject, SecurityManager 和 Realms。這個百度百科上也說了,能夠查看其解說內容:java安全框架

<!-- shiro驅動包  開始 -->
<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-core</artifactId>
</dependency>
<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-spring</artifactId>
</dependency>
<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-web</artifactId>
</dependency>
<dependency>
    <groupId>org.apache.shiro</groupId>
    <artifactId>shiro-ehcache</artifactId>
</dependency>
<!--shiro驅動包 結束 -->

公司也會作相應的配置,配置以下:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:util="http://www.springframework.org/schema/util"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns="http://www.springframework.org/schema/beans"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">

    <!-- 緩存管理器 -->
    <bean id="cacheManager" class="com.*.shared.framework.SpringCacheManagerWrapper">
        <property name="cacheManager" ref="springCacheManager"/>
    </bean>
    <bean id="springCacheManager" class="org.springframework.cache.ehcache.EhCacheCacheManager">
        <property name="cacheManager" ref="ehcacheManager"/>
    </bean>
    <bean id="ehcacheManager" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean">
        <property name="configLocation" value="classpath:ehcache.xml"/>
    </bean>

    <!-- 憑證匹配器 -->
    <bean id="credentialsMatcher"
          class="com.*.RetryLimitHashedCredentialsMatcher">
        <constructor-arg ref="cacheManager"/>
        <property name="hashAlgorithmName" value="md5"/>
        <property name="hashIterations" value="2"/>
        <property name="storedCredentialsHexEncoded" value="true"/>
    </bean>

    <!-- Realm實現 -->
    <bean id="userRealm" class="com.**.shared.web.listener.MemberSecurityRealm">
        <!--<property name="credentialsMatcher" ref="credentialsMatcher"/>-->
        <property name="cachingEnabled" value="false"/>
        <!--<property name="authenticationCachingEnabled" value="true"/>-->
        <!--<property name="authenticationCacheName" value="authenticationCache"/>-->
        <!--<property name="authorizationCachingEnabled" value="true"/>-->
        <!--<property name="authorizationCacheName" value="authorizationCache"/>-->
    </bean>

    <!-- 會話ID生成器 -->
    <!--<bean id="sessionIdGenerator" class="org.apache.shiro.session.mgt.eis.JavaUuidSessionIdGenerator"/>-->

    <!-- 會話Cookie模板 -->
    <bean id="sessionIdCookie" class="org.apache.shiro.web.servlet.SimpleCookie">
        <constructor-arg value="platform-portal-sid"/>
        <property name="httpOnly" value="true"/>
        <property name="maxAge" value="7200"/>
    </bean>

    <!-- 會話管理器 -->
    <bean id="sessionManager" class="org.apache.shiro.web.session.mgt.DefaultWebSessionManager">
        <property name="globalSessionTimeout" value="43200000"/>
        <property name="deleteInvalidSessions" value="true"/>     
        <property name="sessionIdCookieEnabled" value="true"/>
        <property name="sessionIdCookie" ref="sessionIdCookie"/>
    </bean>

    <!-- 安全管理器 -->
    <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager">
        <property name="realm" ref="userRealm"/>
        <property name="sessionManager" ref="sessionManager"/>
        <property name="cacheManager" ref="cacheManager"/>
    </bean>

    <!-- 至關於調用SecurityUtils.setSecurityManager(securityManager) -->
    <bean class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
        <property name="staticMethod" value="org.apache.shiro.SecurityUtils.setSecurityManager"/>
        <property name="arguments" ref="securityManager"/>
    </bean>

    <!-- Shiro的Web過濾器 -->
    <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean"
          depends-on="securityManager,memberShiroFilerChainManager">
        <property name="securityManager" ref="securityManager"/>
    </bean>

    <!-- 基於url+角色的身份驗證過濾器 -->
    <bean id="urlAuthFilter" class="com.zfounder.platform.core.shared.web.filter.UrlAuthFilter">
        <property name="ignoreCheckUriList">
            <list>
                <value>/**/common/enums/**</value>
                <value>/**/security/**</value>
                <value>/**/common/dd/**</value>
                <value>/**/pictures/**</value>
                <value>/**/common/sms/**</value>
                <value>/**/wx/**</value>
            </list>
        </property>
    </bean>

    <bean id="memberFilterChainManager"
          class="com.zfounder.platform.core.shared.web.listener.CustomDefaultFilterChainManager">
        <property name="customFilters">
            <util:map>
                <entry key="roles" value-ref="urlAuthFilter"/>
            </util:map>
        </property>
    </bean>

    <bean id="memberFilterChainResolver"
          class="com.**.shared.web.listener.CustomPathMatchingFilterChainResolver">
        <property name="customDefaultFilterChainManager" ref="memberFilterChainManager"/>
    </bean>

    <bean class="org.springframework.beans.factory.config.MethodInvokingFactoryBean" depends-on="shiroFilter">
        <property name="targetObject" ref="shiroFilter"/>
        <property name="targetMethod" value="setFilterChainResolver"/>
        <property name="arguments" ref="memberFilterChainResolver"/>
    </bean>

    <!-- Shiro生命週期處理器-->
    <bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/>

</beans>

想要對其有更深的理解,請參考這篇博客:Shiro講解


工具類

<!--漢字轉拼音開源工具包-->
 <dependency>
    <groupId>com.github.stuxuhai</groupId>
    <artifactId>jpinyin</artifactId>
</dependency>

<!--網絡爬蟲的驅動包-->
<dependency>
    <groupId>org.jsoup</groupId>
    <artifactId>jsoup</artifactId>
</dependency>

<!--驗證碼生成工具包-->
<dependency>
    <groupId>com.github.penggle</groupId>
    <artifactId>kaptcha</artifactId>
</dependency>

<!--發送郵件-->
<dependency>
    <groupId>javax.mail</groupId>
    <artifactId>mail</artifactId>
</dependency>

由於篇幅的限制,這裏就再也不細說了,若是想要更深層次的瞭解,能夠參考如下博客:

  1. 漢字轉拼音開源工具包Jpinyin介紹
  2. 爬蟲+jsoup輕鬆爬知乎
  3. 使用kaptcha生成驗證碼
  4. 使用javax.mail發送郵件

圖片驗證碼的配置文件以下:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns="http://www.springframework.org/schema/beans"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"
       default-lazy-init="true">

    <bean id="captchaProducer" class="com.google.code.kaptcha.impl.DefaultKaptcha">
        <property name="config">
            <bean class="com.google.code.kaptcha.util.Config">
                <constructor-arg>
                    <props>
                        <prop key="kaptcha.border">${kaptcha.border}</prop>
                        <prop key="kaptcha.border.color">${kaptcha.border.color}</prop>
                        <prop key="kaptcha.textproducer.font.color">${kaptcha.textproducer.font.color}</prop>
                        <prop key="kaptcha.textproducer.char.space">${kaptcha.textproducer.char.space}</prop>
                        <prop key="kaptcha.textproducer.font.size">${kaptcha.textproducer.font.size}</prop>
                        <prop key="kaptcha.image.width">${kaptcha.image.width}</prop>
                        <prop key="kaptcha.image.height">${kaptcha.image.height}</prop>
                        <prop key="kaptcha.textproducer.char.length">${kaptcha.textproducer.char.length}</prop>
                        <prop key="kaptcha.textproducer.char.string">1234567890</prop>
                        <prop key="kaptcha.textproducer.font.names">宋體,楷體,微軟雅黑</prop>
                        <prop key="kaptcha.noise.color">${kaptcha.noise.color}</prop>
                        <prop key="kaptcha.noise.impl">com.google.code.kaptcha.impl.NoNoise</prop>
                        <prop key="kaptcha.background.clear.from">${kaptcha.background.clear.from}</prop>
                        <prop key="kaptcha.background.clear.to">${kaptcha.background.clear.to}</prop>
                        <prop key="kaptcha.word.impl">com.google.code.kaptcha.text.impl.DefaultWordRenderer</prop>
                        <prop key="kaptcha.obscurificator.impl">com.google.code.kaptcha.impl.ShadowGimpy</prop>
                    </props>
                </constructor-arg>
            </bean>
        </property>
    </bean>
</beans>

裏面的佔位符來源於plateform-dev.properties或者plateform-prd.properties,這就是咱們maven激活的配置文件的做用。


測試依賴包

咱們在開發完一個功能後,首先會想到測試它走不走得通。咱們可能會在main方法中測試,一個項目類中能夠寫多個main方法。若是每一個功能類中都寫一個main方法,未免會形成代碼的混亂,一點都不美觀和儒雅。

java爲何一直推崇面向對象,任何在現實中真實的、虛擬的事物,均可以將其封裝爲爲java中的對象類。對象與對象之間以方法做爲消息傳遞機制,以屬性做爲數據庫存儲的機制。

若是咱們在每一個功能中都寫一個main方法,勢必會破壞這種對象的美觀性。於是,咱們把測試的數據以對象的方式操做,這樣,將其封裝爲一個測試包,好比,在我寫的spring框架中,就把測試類單獨拿出來,如圖所示:

自定義框架的spring測試包

<!-- 測試依賴包 開始-->

<!-- spring test -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-test</artifactId>
    <version>${spring.version}</version>
</dependency>

<!-- 路徑檢索json或設置Json -->
<dependency>
    <groupId>com.jayway.jsonpath</groupId>
    <artifactId>json-path</artifactId>
    <version>${jsonpath.version}</version>
    <scope>test</scope>
</dependency>

<!-- testng -->
<dependency>
    <groupId>org.testng</groupId>
    <artifactId>testng</artifactId>
    <version>${testng.version}</version>
</dependency>

<!-- 單元測試的powermock -->
<dependency>
    <groupId>org.powermock</groupId>
    <artifactId>powermock-module-testng</artifactId>
    <version>${powermock.version}</version>
</dependency>
<dependency>
    <groupId>org.powermock</groupId>
    <artifactId>powermock-api-mockito</artifactId>
    <version>${powermock.version}</version>
</dependency>
<!--測試相關 結束-->

以上是幾種測試包的依賴,一個是spring的測試包,這裏因爲篇幅的限制,就不作詳細的介紹了,網上有不少這方面的教程,想要深刻的瞭解,可參考這篇博客:Spring-Test(單元測試)

咱們有時也會用到TestNG框架,它是Java中的一個測試框架,相似於JUnit 和NUnit,功能都差很少,只是功能更增強大,使用也更方便。測試人員通常用TestNG來寫自動化測試,開發人員通常用JUnit寫單元測試。若是你是測試人員,想對其有更全面的瞭解,能夠參考這篇教程:TestNG教程,或者這篇博客::testNG經常使用用法總結

若是想要更深層次的瞭解powermock,能夠參考這篇博客:PowerMock從入門到放棄再到使用

若是想要更深層次的瞭解JsonPath,能夠參考這篇博客:JsonPath教程


圖片處理

咱們在開發的過程當中,會把圖片存放到服務器的某個文件夾下,即某個磁盤上。若是圖片過大,會佔用服務器的磁盤,於是,咱們須要將圖片縮略,來減小對內存的佔用。這時,咱們若是使用java原生的圖片縮略圖,是很是複雜的,於是,咱們可使用如下框架對圖片進行操做。

<!--圖片處理驅動包 開始-->
<dependency>
    <groupId>net.coobird</groupId>
    <artifactId>thumbnailator</artifactId>
</dependency>
<!--圖片處理驅動包 結束-->

這裏再也不細說,想要有更多的瞭解,能夠參考這篇博客:Thumbnailator框架的使用


Excel操做

咱們在工做的過程當中,常常會將數據導出到Excel表,或將Excel表的數據導入數據庫。咱們之前使用poi框架,可是,超過必定量的時候,會佔用大量的內存,從而下降導入的效率。阿里巴巴如今開放出操做Excel表的easyexcel框架,對百萬級的導入影響不是很大。

如下是maven配置兩個驅動依賴:

<!--阿里巴巴的easyexcel驅動包 -->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>easyexcel</artifactId>
    <version>{latestVersion}</version>
</dependency>

<!--poi驅動包 -->
<dependency>
    <groupId>org.apache.poi</groupId>
    <artifactId>poi</artifactId>
    <version>${poi.version}</version>
</dependency>
<dependency>
    <groupId>org.apache.poi</groupId>
    <artifactId>poi-ooxml</artifactId>
    <version>${poi-ooxml.version}</version>
</dependency>

這兩個就再也不細說,若是想要對easyexcel更深的瞭解,能夠參考這篇博客:alibaba/easyexcel 框架使用。若是想要對poi有更深的瞭解,能夠參考這篇博客:Apache POI使用詳解


guava包

咱們在開發的過程當中,有時會用到guava驅動包。它是爲了方便編碼,並減小編碼錯誤,用於提供集合,緩存,支持原語句,併發性,常見註解,字符串處理,I/O和驗證的實用方法。

使用它有如下好處:

  • 標準化 - Guava庫是由谷歌託管。
  • 高效 - 可靠,快速和有效的擴展JAVA標準庫
  • 優化 -Guava庫通過高度的優化。

同時,又有增長Java功能和處理能力的函數式編程,提供了須要在應用程序中開發的許多實用程序類的,提供了標準的故障安全驗證機制,強調了最佳的作法等等。它的宗旨就是:提升代碼質量、簡化工做,促使代碼更有彈性、更加簡潔的工具。

咱們在項目中的配置包爲:

<!--guava驅動包 開始-->
  <dependency>
    <groupId>com.google.guava</groupId>
    <artifactId>guava</artifactId>
</dependency>
<!--guava驅動包 結束-->

若是想要對其有更深的瞭解,能夠參考這篇教程:guava入門教程


freemarker包

咱們在開發的過程當中,也許會用到這個框架。爲何要用到這個框架呢?咱們有時須要動態地將xml文件轉爲doc文件,這個時候,就用到了freemarker包,如圖所示:

freemarker包

截圖不是很全面,你會看到畫紅框的部分,這是一種佔位符的標記,就至關於java中的形參同樣。 當用戶點擊前端的下載按鈕時,有些數據是沒法直接轉換成doc的,由於咱們先把數據寫進xml中,再將xml轉化爲doc。具體如何轉換的能夠參考該博客:Java將xml模板動態填充數據轉換爲word文檔

咱們能夠引用這個包:

<!--freemarker驅動包 開始-->
<dependency>
    <groupId>org.freemarker</groupId>
    <artifactId>freemarker</artifactId>
    <version>${freemarker.version}</version>
</dependency>
<!--freemarker驅動包 結束-->

因爲篇幅限制,想要詳細瞭解,能夠參考這篇手冊: freemarker在線手冊


servlet驅動包

我記得當時在學java-web開發時,最開始用的就是servlet。接收客戶端的輸入,並通過一系列DB操做,將數據返回給客戶端。但使用純servlet不利於可視化界面。後來,使用了JSP開發,其是可視化界面。可是,當咱們啓動Tomcat後,JSP經過JSP引擎仍是會轉爲servlet。從本質上來講,JSP和servlet是服務端語言。

我最初用servlet和JSP開發的源碼地址:圖書館項目

後來,工做了之後。後端就用了springMVC,hibernate框架等,前端使用的是HTML、jQuery等。慢慢地脫離了JSP和servlet。可是,並沒與徹底與servlet分隔開,咱們還時不時會用到servlet的一些類,好比HttpServletRequest,HttpServletResponse等類。

既然使用了spring MVC框架,爲何還要用servlet的東西,好比,咱們在導入和導出時,一個是接收前端導入的請求,一個是響應前端導出的請求。

  • 響應前端導出的代碼,這裏就用到了響應
private static void downloadExcel(HttpServletResponse response, File newFile, String fileName) throws IOException {
    InputStream fis = new BufferedInputStream(new FileInputStream(
            newFile));
    String substring = fileName.substring(fileName.indexOf("/") + 1);
    byte[] buffer = new byte[fis.available()];
    fis.read(buffer);
    fis.close();
    response.reset();
    response.setContentType("text/html;charset=UTF-8");
    OutputStream toClient = new BufferedOutputStream(
            response.getOutputStream());
    response.setContentType("application/x-msdownload");
    String newName = URLEncoder.encode(
            substring + System.currentTimeMillis() + ".xlsx",
            "UTF-8");
    response.addHeader("Content-Disposition",
            "attachment;filename=\"" + newName + "\"");
    response.addHeader("Content-Length", "" + newFile.length());
    toClient.write(buffer);
    toClient.flush();
}
  • 接收前端導入的請求
public static LinkedHashMap<String, List<JSONObject>> importMultiSheetExcel(HttpServletRequest request, LinkedHashMap<Integer, Integer> sheetDataStartRowMap, LinkedHashMap<Integer, String> sheetDataEndColMap) {
    LinkedHashMap<String, List<JSONObject>> resMap = new LinkedHashMap<>();
    try {
        MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) request;
        ifNullThrow(multipartRequest, ResultCodeEnum.ILLEGAL_PARAM);
        MultipartFile file = multipartRequest.getFile("file");
        Workbook work = getWorkbook(file.getInputStream(), file.getOriginalFilename());
        ifNullThrow(work, ResultCodeEnum.ILLEGAL_PARAM);
        。。。
}

雖然咱們如今使用了spring MVC,仍是用到了servlet,並且shiro裏面要使用到,如下是代碼的配置:

<!--servlet 開始-->
<!--shiro裏面要使用到-->
<dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>javax.servlet-api</artifactId>
    <version>${servlet.version}</version>
</dependency>
<!--servlet 結束-->
<dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>jstl</artifactId>
    <version>${jstl.version}</version>
</dependency>
<!--servlet 結束-->

若是想要了解servlet的話,能夠參考該文檔:Java Servlet API中文說明文檔


Lucene全文檢索

有時,咱們在開發的過程當中,須要作全文檢索數據,就好比,我在Word文檔中,全文檢索某個詞、某句話等,如圖所示:

全文檢索

這就是web端的全文檢索。可是我作Java,固然,也須要全文檢索。於是,咱們就想到了Lucene。

它是一套用於全文檢索和搜尋的開源程式庫,由Apache軟件基金會支持和提供。提供了一個簡單卻強大的應用程式接口,可以作全文索引和搜尋。在Java開發環境裏,它是一個成熟的免費開源工具。就其自己而言,它是當前以及最近幾年最受歡迎的免費Java信息檢索程序庫。

咱們在java的maven庫中的配置爲:

<!-- lucene 開始 -->
<dependency>
    <groupId>org.apache.lucene</groupId>
    <artifactId>lucene-core</artifactId>
    <version>${lucene.version}</version>
</dependency>
<dependency>
    <groupId>org.apache.lucene</groupId>
    <artifactId>lucene-highlighter</artifactId>
    <version>${lucene.version}</version>
</dependency>
<dependency>
    <groupId>org.apache.lucene</groupId>
    <artifactId>lucene-analyzers-common</artifactId>
    <version>${lucene.version}</version>
</dependency>
<!-- lucene 結束 -->

想要對其有更深的瞭解,能夠參考這篇筆記:Lucene學習筆記


Quartz任務調度

咱們在開發的過程當中,總想着要在某個時間,執行什麼樣的事情,因而呢,咱們就至關了任務調度,好比:

  1. 天天八點按時起牀
  2. 每一年農曆什麼的生日
  3. 每一個星期都要爬一次山

咱們就能夠用到Quartz這個框架,咱們須要作一些配置,如圖所示:

Quartz的配置

咱們能夠在maven中的配置爲:

<!-- quartz驅動包 開始-->
<dependency>
    <groupId>org.quartz-scheduler</groupId>
    <artifactId>quartz</artifactId>
    <version>${quartz.version}</version>
</dependency>
<!-- quartz驅動包 結束-->

想要對其有根深多的瞭解,可參考這篇博客:Quartz使用總結


zxing二維碼

咱們常用到二維碼,好比,添加微信好友的二維碼,支付二維碼、掃一掃二維碼等等,那麼,這是怎麼實現的呢,其實,這有一個工具包,就是zxing工具包。它是谷歌旗下的工具類,咱們能夠用它來生成咱們想要的二維碼,可是,咱們先要在maven項目中配置它。如代碼所示:

<!-- 二維碼驅動包 開始-->
<dependency>
    <groupId>com.google.zxing</groupId>
    <artifactId>core</artifactId>
    <version>${zxing.version}</version>
</dependency>
<dependency>
    <groupId>com.google.zxing</groupId>
    <artifactId>javase</artifactId>
    <version>${zxing.se.version}</version>
</dependency>
<!-- 二維碼驅動包 開始-->

想要對其有根深的瞭解,能夠參考這篇博客:zxing實現二維碼生成和解析

WSDL包

這個我也不大懂,也沒有操做過,若是想要了解的話,能夠參考這篇文檔:WebService中的WSDL詳細解析

咱們在maven中的怕配置爲:

<!-- WSDL驅動包 開始-->
 <dependency>
    <groupId>wsdl4j</groupId>
    <artifactId>wsdl4j</artifactId>
    <version>${wsdl4j.version}</version>
</dependency>
<!-- WSDL驅動包 結束-->

配置文件

配置文件來源於框架中的如下文件,如圖所示:

clipboard.png

全部的配置文件都來源於資源包。這裏就再也不細說。


總結

要想成爲架構師,首先學習別人的東西,他山之石,能夠攻玉。

相關文章
相關標籤/搜索