WTO檢討 促台推動農產品貿易自由化

摘錄自2018年9月11日中央社布魯塞爾報導

經濟部次長王美花率團出席世界貿易組織(WTO)對台灣第4次貿易政策檢討會議,歐盟代表在會中讚台灣經濟基本面良好,但呼籲台灣加速制定殘留許可限制,推動農產品貿易自由化。

台灣第4次貿易政策檢討會議12日至14日在WTO瑞士日內瓦總部召開,由經濟部次長王美花率團與會,與會代表包括行政院經貿談判辦公室、外交部、農委會、財政部、金管會、經濟部工業局、智慧財產局及國際貿易局等單位。

根據經濟部發布新聞稿,王美花在會中說明台灣過去4年經貿政策,重申支持自由多邊貿易體系及WTO,表示將與會員合作改善WTO;也說明台灣正實行推動加入跨太平洋夥伴全面進步協定(CPTPP)、推動數位經濟及前瞻計畫等經貿政策,並以新南向政策強化與該地區連結。

此外,台灣正大力推動永續能源政策,目標在2025年達到再生能源占比20%目標,藉此鼓勵投資與創造就業,並承諾協助其他WTO開發中會員共同達到聯合國永續發展目標。

擔任與談人的歐盟駐WTO大使范亥克倫(Marc Vanheukelen)彙整22個會員國提出的400多項書面問題,主要關注議題包括簡化外人投資審查程序、食品安全應採用國際標準,並基於科學證據及簡化農藥殘留容許量的程序、農業進口稅率遠高於工業產品、履行農業境內支持通知義務、漁業補貼、擴大政府採購開放範圍、擴大對低度開發國家產品進口優惠。

范亥克倫強調,台灣應加速制訂殘留許可限制(MRL),推動農產品貿易自由化,以及推動人才培養與經濟永續發展。

本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※別再煩惱如何寫文案,掌握八大原則!

※教你寫出一流的銷售文案?

※超省錢租車方案

FB行銷專家,教你從零開始的技巧

聚甘新

創新減廢 啤酒公司淘汰塑膠套環 改用可回收膠

環境資訊中心綜合外電;姜唯 編譯;林大利 審校

本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※超省錢租車方案

※別再煩惱如何寫文案,掌握八大原則!

※回頭車貨運收費標準

※教你寫出一流的銷售文案?

FB行銷專家,教你從零開始的技巧

聚甘新

中國第四季電動車市場需求回溫,全年挑戰55萬輛

2016年中國電動車市場雖有取締騙補政策影響原成長力道,但在預期2017年補貼將降低而刺激消費者提前購車的情況下,今年第四季仍有很大的銷售成長空間。TrendForce旗下拓墣產業研究所最新研究顯示,今年1~9月的中國電動車市場銷售已近35萬輛,超越去年總量,預估2016年將挑戰55萬輛,引領全球電動車邁向近百萬輛市場規模。

拓墣車輛電子分析師張仙平表示,中國2014及2015年已連續兩年以超過300%的銷售成長率,超越日、美,成為全球最大電動車區域市場,2015年更占據了全球逾半的銷售量,因此中國電動車市場已成為全球電動車銷售引擎。

張仙平指出,外界持續看好中國電動車市場,然而,在2016年初中國政府積極取締騙補,以及從2016年起逐年遞減的購車補貼政策下,使得中國雖然在乘用車市場仍能維持快速成長力道,但在商用車市場上,包含電動巴士與電動專車的銷售狀況均受到衝擊,銷量呈現衰退。而中國政府也在騙補事件後,提高廠商申請生產許可證的資格,因此在補助減少而門檻提高的影響下,2017年中國電動商用車銷量仍可能衰退。

在預期2017年中國電動車購車補貼將降低的情況下,拓墣預估市場需求將集中在2016第四季,拉升中國市場電動車全年銷售達55萬輛,其中主要受惠於乘用車市場的高速成長動能。除中國市場外,美國與日本市場也一掃去年銷售頹勢,在2016年維持成長。美國市場在特斯拉Model S與Model X的熱銷下,有望締造近15萬輛的銷售佳績。拓墣預估2016年全球電動車市場將在中國市場領頭的情形下,突破95萬輛的銷售量。

全球市場研究機構TrendForce將在2016年11月10日,於臺大醫院國際會議中心舉辦「集邦拓墣2017年科技產業大預測」研討會。活動網址:

本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※別再煩惱如何寫文案,掌握八大原則!

※教你寫出一流的銷售文案?

※超省錢租車方案

FB行銷專家,教你從零開始的技巧

聚甘新

電動車有害道路安全?美國規定汽車不能太安靜!

根據外電報導,美國政府正式通過一則汽車行駛新規定,也就是未來一些「安靜的汽車」,如電動汽車或混合動力汽車等,在行駛速度超過時速18.6 英里 (約30 公里)的情況下,必須增加噪音警報裝置,以避免傷及行人、盲人和騎車的路人。

根據美國國家公路交通安全管理局(NHTSA)發布的這則新規定表示,該規定將在2019 年9 月1 日起生效。屆時,包括特斯拉 (TESLA)、日產汽車 (Nisson) 和豐田汽車 (TOYOTA) 等廠商都需要為所有汽車增加噪音裝置。美國政府希望通過此規定,能夠防止每年多達2,400 件的行人受傷事故,並為53 萬輛汽車增設噪音專制。

NHTSA 指出,這項新規每年約會給汽車廠商增加3,900 萬美元的花費。因為,他們要為汽車增設一個外設防水喇叭。但是,能夠透過降低交通事故而每年節省約2.5 億到3.2 億美元的醫療費用。

該規定未來將適用於全美各地區,並且涵蓋路上行駛的電動汽車和混合動力汽車等。另外,包括帶有4 個輪子、總重量低於10,000 磅的車輛,也都必須在向後或向前行駛時,在速度達到每小時30 公里之際發出聲響,以警告周遭的路人與騎車的民眾。不過,NHTSA 也認為,規定汽車喇叭可有可無,因為輪胎和風也可以製造出足夠的噪音聲響,只要該聲響能達到警告的標準即可。

該項新的規定雖然受到全美盲人主組織的歡迎,但是卻讓汽車廠商表示擔憂。因為噪音也許會太大,並且實施起來將可能相當複雜。因為,新規只規定了最低的聲音要求,卻沒有制定必須發出什麼聲音。

(本文由《》授權提供。照片來源:特斯拉)

本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※超省錢租車方案

※別再煩惱如何寫文案,掌握八大原則!

※回頭車貨運收費標準

※教你寫出一流的銷售文案?

FB行銷專家,教你從零開始的技巧

聚甘新

重學 Java 設計模式:實戰責任鏈模式「模擬618電商大促期間,項目上線流程多級負責人審批場景」

作者:小傅哥
博客:https://bugstack.cn – 原創系列專題文章

沉澱、分享、成長,讓自己和他人都能有所收穫!

一、前言

場地和場景的重要性

射擊需要去靶場學習、滑雪需要去雪場體驗、開車需要能上路實踐,而編程開發除了能完成產品的功能流程,還需要保證系統的可靠性能。就像你能聽到的一些系統監控指標;QPSTPSTP99TP999可用率響應時長等等,而這些指標的總和評估就是一個系統的健康度。但如果你幾乎沒有聽到這樣的技術術語,也沒接觸過類似高併發場景,那麼就很像駕駛證的科目1考了100分,但不能上路。沒有這樣的技術場景給你訓練,讓你不斷的體會系統的脾氣秉性,即便你有再多的想法都沒法實現。所以,如果真的想學習一定要去一個有實操的場景,下水試試才能學會狗刨。

你的視覺盲區有多大

同樣一本書、同樣一條路、同樣一座城,你真的以為生活有選擇嗎?有時候很多選項都是擺設,給你多少次機會你都選的一模一樣。這不是你選不選而是你的認知範圍決定了你下一秒做的事情,另外的一個下一秒又決定了再下一個下一秒。就像管中窺豹一樣,20%的面積在你視覺里都是黑色的,甚至就總是忽略看不到,而這看不到的20%就是生命中的時運!但,人可以學習,可以成長,可以脫胎換骨,可以努力付出,通過一次次的蛻變而看到剩下的20%!

沒有設計圖紙你敢蓋樓嗎

編程開發中最好的什麼,是設計。運用架構思維、經驗心得、才華靈感,構建出最佳的系統。真正的研發會把自己寫的代碼當做作品來欣賞,你說這是一份工作,但在這樣的人眼裡這可不是一份工作,而是一份工匠精神。就像可能時而你也會為自己因為一個niubility的設計而豪邁萬丈,為能上線一個扛得住每秒200萬訪問量的系統會精神煥發。這樣的自豪感就是一次次壘磚一樣墊高腳底,不斷的把你的視野提高,讓你能看到上層設計也能知曉根基建設。可以把控全局,也可以治理細節。這一份份知識的沉澱,來幫助你繪製出一張系統架構藍圖。

二、開發環境

  1. JDK 1.8
  2. Idea + Maven
  3. 涉及工程三個,可以通過關注公眾號bugstack蟲洞棧,回復源碼下載獲取(打開獲取的鏈接,找到序號18)
工程 描述
itstack-demo-design-13-00 場景模擬工程;模擬一個上線流程審批的接口。
itstack-demo-design-13-01 使用一坨代碼實現業務需求
itstack-demo-design-13-02 通過設計模式優化改造代碼,產生對比性從而學習

三、責任鏈模式介紹

擊鼓傳雷,看上圖你是否想起周星馳有一個電影,大家坐在海邊圍成一個圈,拿着一個點燃的炸彈,互相傳遞。

責任鏈模式的核心是解決一組服務中的先後執行處理關係,就有點像你沒錢花了,需要家庭財務支出審批,10塊錢以下找閨女審批,100塊錢先閨女審批在媳婦審批。你可以理解想象成當你要跳槽的時候被安排的明明白白的被各個領導簽字放行。

四、案例場景模擬

在本案例中我們模擬在618大促期間的業務系統上線審批流程場景

像是這些一線電商類的互聯網公司,阿里、京東、拼多多等,在618期間都會做一些運營活動場景以及提供的擴容備戰,就像過年期間百度的紅包一樣。但是所有開發的這些系統都需要陸續的上線,因為臨近618有時候也有一些緊急的調整的需要上線,但為了保障線上系統的穩定性是盡可能的減少上線的,也會相應的增強審批力度。就像一級響應、二級響應一樣。

而這審批的過程在隨着特定時間點會增加不同級別的負責人加入,每個人就像責任鏈模式中的每一個核心點。對於研發小夥伴並不需要關心具體的審批流程處理細節,只需要知道這個上線更嚴格,級別也更高,但對於研發人員來說同樣是點擊相同的提審按鈕,等待審核。

接下來我們就模擬這樣一個業務訴求場景,使用責任鏈的設計模式來實現此功能。

1. 場景模擬工程

itstack-demo-design-13-00
└── src
    └── main
        └── java
            └── org.itstack.demo.design
                └── AuthService.java
  • 這裏的代碼結構比較簡單,只有一個模擬審核和查詢審核結果的服務類。相當於你可以調用這個類去審核工程和獲取審核結構,這部分結果信息是模擬的寫到緩存實現。

2. 場景簡述

2.1 模擬審核服務

public class AuthService {

    private static Map<String, Date> authMap = new ConcurrentHashMap<String, Date>();

    public static Date queryAuthInfo(String uId, String orderId) {
        return authMap.get(uId.concat(orderId));
    }

    public static void auth(String uId, String orderId) {
        authMap.put(uId.concat(orderId), new Date());
    }

}
  • 這裏面提供了兩個接口一個是查詢審核結果(queryAuthInfo)、另外一個是處理審核(auth)。
  • 這部分是把由誰審核的和審核的單子ID作為唯一key值記錄到內存Map結構中。

五、用一坨坨代碼實現

這裏我們先使用最直接的方式來實現功能

按照我們的需求審批流程,平常系統上線只需要三級負責人審批就可以,但是到了618大促時間點,就需要由二級負責以及一級負責人一起加入審批系統上線流程。在這裏我們使用非常直接的if判斷方式來實現這樣的需求。

1. 工程結構

itstack-demo-design-13-01
└── src
    └── main
        └── java
            └── org.itstack.demo.design
                └── AuthController.java
  • 這部分非常簡單的只包含了一個審核的控制類,就像有些夥伴開始寫代碼一樣,一個類寫所有需求。

2. 代碼實現

public class AuthController {

    private SimpleDateFormat f = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");// 時間格式化

    public AuthInfo doAuth(String uId, String orderId, Date authDate) throws ParseException {

        // 三級審批
        Date date = AuthService.queryAuthInfo("1000013", orderId);
        if (null == date) return new AuthInfo("0001", "單號:", orderId, " 狀態:待三級審批負責人 ", "王工");

        // 二級審批
        if (authDate.after(f.parse("2020-06-01 00:00:00")) && authDate.before(f.parse("2020-06-25 23:59:59"))) {
            date = AuthService.queryAuthInfo("1000012", orderId);
            if (null == date) return new AuthInfo("0001", "單號:", orderId, " 狀態:待二級審批負責人 ", "張經理");
        }

        // 一級審批
        if (authDate.after(f.parse("2020-06-11 00:00:00")) && authDate.before(f.parse("2020-06-20 23:59:59"))) {
            date = AuthService.queryAuthInfo("1000011", orderId);
            if (null == date) return new AuthInfo("0001", "單號:", orderId, " 狀態:待一級審批負責人 ", "段總");
        }

        return new AuthInfo("0001", "單號:", orderId, " 狀態:審批完成");
    }

}
  • 這裏從上到下分別判斷了在指定時間範圍內由不同的人員進行審批,就像618上線的時候需要三個負責人都審批才能讓系統進行上線。
  • 像是這樣的功能看起來很簡單的,但是實際的業務中會有很多部門,但如果這樣實現就很難進行擴展,並且在改動擴展調整也非常麻煩。

3. 測試驗證

3.1 編寫測試類

@Test
public void test_AuthController() throws ParseException {
    AuthController authController = new AuthController();  

    // 模擬三級負責人審批
    logger.info("測試結果:{}", JSON.toJSONString(authController.doAuth("小傅哥", "1000998004813441", new Date())));
    logger.info("測試結果:{}", "模擬三級負責人審批,王工");
    AuthService.auth("1000013", "1000998004813441");  

    // 模擬二級負責人審批                                 
    logger.info("測試結果:{}", JSON.toJSONString(authController.doAuth("小傅哥", "1000998004813441", new Date())));
    logger.info("測試結果:{}", "模擬二級負責人審批,張經理");
    AuthService.auth("1000012", "1000998004813441");    

    // 模擬一級負責人審批
    logger.info("測試結果:{}", JSON.toJSONString(authController.doAuth("小傅哥", "1000998004813441", new Date())));
    logger.info("測試結果:{}", "模擬一級負責人審批,段總");
    AuthService.auth("1000011", "1000998004813441");            

    logger.info("測試結果:{}", "審批完成");
}
  • 這裏模擬每次查詢是否審批完成,隨着審批的不同節點,之後繼續由不同的負責人進行審批操作。
  • authController.doAuth,是查看審批的流程節點、AuthService.auth,是審批方法用於操作節點流程狀態。

3.2 測試結果

23:25:00.363 [main] INFO  org.itstack.demo.design.test.ApiTest - 測試結果:{"code":"0001","info":"單號:1000998004813441 狀態:待三級審批負責人 王工"}
23:25:00.366 [main] INFO  org.itstack.demo.design.test.ApiTest - 測試結果:模擬三級負責人審批,王工
23:25:00.367 [main] INFO  org.itstack.demo.design.test.ApiTest - 測試結果:{"code":"0001","info":"單號:1000998004813441 狀態:待二級審批負責人 張經理"}
23:25:00.367 [main] INFO  org.itstack.demo.design.test.ApiTest - 測試結果:模擬二級負責人審批,張經理
23:25:00.368 [main] INFO  org.itstack.demo.design.test.ApiTest - 測試結果:{"code":"0001","info":"單號:1000998004813441 狀態:待一級審批負責人 段總"}
23:25:00.368 [main] INFO  org.itstack.demo.design.test.ApiTest - 測試結果:模擬一級負責人審批,段總
23:25:00.368 [main] INFO  org.itstack.demo.design.test.ApiTest - 測試結果:審批完成

Process finished with exit code 0
  • 從測試結果上可以看到一層層的由不同的人員進行審批,審批完成後到下一個人進行處理。單看結果是滿足我們的訴求,只不過很難擴展和調整流程,相當於代碼寫的死死的。

六、責任鏈模式重構代碼

接下來使用裝飾器模式來進行代碼優化,也算是一次很小的重構。

責任鏈模式可以讓各個服務模塊更加清晰,而每一個模塊間可以通過next的方式進行獲取。而每一個next是由繼承的統一抽象類實現的。最終所有類的職責可以動態的進行編排使用,編排的過程可以做成可配置化。

1. 工程結構

itstack-demo-design-13-02
└── src
    └── main
        └── java
            └── org.itstack.demo.design
                ├── impl
                │    ├── Level1AuthLink.java
                │    ├── Level2AuthLink.java
                │    └── Level3AuthLink.java
                ├── AuthInfo.java
                └── AuthLink.java

責任鏈模式模型結構

  • 上圖是這個業務模型中責任鏈結構的核心部分,通過三個實現了統一抽象類AuthLink的不同規則,再進行責任編排模擬出一條鏈路。這個鏈路就是業務中的責任鏈。
  • 一般在使用責任鏈時候如果是場景比較固定,可以通過寫死到代碼中進行初始化。但如果業務場景經常變化可以做成xml配置的方式進行處理,也可以落到庫里進行初始化操作。

2. 代碼實現

2.1 責任鏈中返回對象定義

public class AuthInfo {

    private String code;
    private String info = "";

    public AuthInfo(String code, String ...infos) {
        this.code = code;
        for (String str:infos){
            this.info = this.info.concat(str);
        }
    }
    
    // ...get/set
}
  • 這個類的是包裝了責任鏈處理過程中返回結果的類,方面處理每個責任鏈的返回信息。

2.2 鏈路抽象類定義

public abstract class AuthLink {

    protected Logger logger = LoggerFactory.getLogger(AuthLink.class);

    protected SimpleDateFormat f = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");// 時間格式化
    protected String levelUserId;                           // 級別人員ID
    protected String levelUserName;                         // 級別人員姓名
    private AuthLink next;                                  // 責任鏈

    public AuthLink(String levelUserId, String levelUserName) {
        this.levelUserId = levelUserId;
        this.levelUserName = levelUserName;
    }

    public AuthLink next() {
        return next;
    }

    public AuthLink appendNext(AuthLink next) {
        this.next = next;
        return this;
    }

    public abstract AuthInfo doAuth(String uId, String orderId, Date authDate);

}
  • 這部分是責任鏈,鏈接起來的核心部分。AuthLink next,重點在於可以通過next方式獲取下一個鏈路需要處理的節點。
  • levelUserIdlevelUserName,是責任鏈中的公用信息,標記每一個審核節點的人員信息。
  • 抽象類中定義了一個抽象方法,abstract AuthInfo doAuth,這是每一個實現者必須實現的類,不同的審核級別處理不同的業務。

2.3 三個審核實現類

Level1AuthLink

public class Level1AuthLink extends AuthLink {

    public Level1AuthLink(String levelUserId, String levelUserName) {
        super(levelUserId, levelUserName);
    }

    public AuthInfo doAuth(String uId, String orderId, Date authDate) {
        Date date = AuthService.queryAuthInfo(levelUserId, orderId);
        if (null == date) {
            return new AuthInfo("0001", "單號:", orderId, " 狀態:待一級審批負責人 ", levelUserName);
        }
        AuthLink next = super.next();
        if (null == next) {
            return new AuthInfo("0000", "單號:", orderId, " 狀態:一級審批完成負責人", " 時間:", f.format(date), " 審批人:", levelUserName);
        }

        return next.doAuth(uId, orderId, authDate);
    }

}

Level2AuthLink

public class Level2AuthLink extends AuthLink {

    private Date beginDate = f.parse("2020-06-11 00:00:00");
    private Date endDate = f.parse("2020-06-20 23:59:59");

    public Level2AuthLink(String levelUserId, String levelUserName) throws ParseException {
        super(levelUserId, levelUserName);
    }

    public AuthInfo doAuth(String uId, String orderId, Date authDate) {
        Date date = AuthService.queryAuthInfo(levelUserId, orderId);
        if (null == date) {
            return new AuthInfo("0001", "單號:", orderId, " 狀態:待二級審批負責人 ", levelUserName);
        }
        AuthLink next = super.next();
        if (null == next) {
            return new AuthInfo("0000", "單號:", orderId, " 狀態:二級審批完成負責人", " 時間:", f.format(date), " 審批人:", levelUserName);
        }

        if (authDate.before(beginDate) || authDate.after(endDate)) {
            return new AuthInfo("0000", "單號:", orderId, " 狀態:二級審批完成負責人", " 時間:", f.format(date), " 審批人:", levelUserName);
        }

        return next.doAuth(uId, orderId, authDate);
    }

}

Level3AuthLink

public class Level3AuthLink extends AuthLink {

    private Date beginDate = f.parse("2020-06-01 00:00:00");
    private Date endDate = f.parse("2020-06-25 23:59:59");

    public Level3AuthLink(String levelUserId, String levelUserName) throws ParseException {
        super(levelUserId, levelUserName);
    }

    public AuthInfo doAuth(String uId, String orderId, Date authDate) {
        Date date = AuthService.queryAuthInfo(levelUserId, orderId);
        if (null == date) {
            return new AuthInfo("0001", "單號:", orderId, " 狀態:待三級審批負責人 ", levelUserName);
        }
        AuthLink next = super.next();
        if (null == next) {
            return new AuthInfo("0000", "單號:", orderId, " 狀態:三級審批負責人完成", " 時間:", f.format(date), " 審批人:", levelUserName);
        }

        if (authDate.before(beginDate) || authDate.after(endDate)) {
            return new AuthInfo("0000", "單號:", orderId, " 狀態:三級審批負責人完成", " 時間:", f.format(date), " 審批人:", levelUserName);
        }

        return next.doAuth(uId, orderId, authDate);
    }

}
  • 如上三個類;Level1AuthLinkLevel2AuthLinkLevel3AuthLink,實現了不同的審核級別處理的簡單邏輯。
  • 例如第一個審核類中會先判斷是否審核通過,如果沒有審核通過則返回結果給調用方,引導去審核。(這裏簡單模擬審核後有時間信息不為空,作為判斷條件)
  • 判斷完成后獲取下一個審核節點;super.next();,如果不存在下一個節點,則直接返回結果。
  • 之後是根據不同的業務時間段進行判斷是否需要,二級和一級的審核。
  • 最後返回下一個審核結果;next.doAuth(uId, orderId, authDate);,有點像遞歸調用。

3. 測試驗證

3.1 編寫測試類

@Test
public void test_AuthLink() throws ParseException {
    AuthLink authLink = new Level3AuthLink("1000013", "王工")
            .appendNext(new Level2AuthLink("1000012", "張經理")
                    .appendNext(new Level1AuthLink("1000011", "段總")));

    logger.info("測試結果:{}", JSON.toJSONString(authLink.doAuth("小傅哥", "1000998004813441", new Date())));

    // 模擬三級負責人審批
    AuthService.auth("1000013", "1000998004813441");
    logger.info("測試結果:{}", "模擬三級負責人審批,王工");
    logger.info("測試結果:{}", JSON.toJSONString(authLink.doAuth("小傅哥", "1000998004813441", new Date())));

    // 模擬二級負責人審批
    AuthService.auth("1000012", "1000998004813441");
    logger.info("測試結果:{}", "模擬二級負責人審批,張經理");
    logger.info("測試結果:{}", JSON.toJSONString(authLink.doAuth("小傅哥", "1000998004813441", new Date())));

    // 模擬一級負責人審批
    AuthService.auth("1000011", "1000998004813441");
    logger.info("測試結果:{}", "模擬一級負責人審批,段總");
    logger.info("測試結果:{}", JSON.toJSONString(authLink.doAuth("小傅哥", "1000998004813441", new Date())));
}
  • 這裏包括最核心的責任鏈創建,實際的業務中會包裝到控制層; AuthLink authLink = new Level3AuthLink("1000013", "王工") .appendNext(new Level2AuthLink("1000012", "張經理") .appendNext(new Level1AuthLink("1000011", "段總"))); 通過把不同的責任節點進行組裝,構成一條完整業務的責任鏈。
  • 接下里不斷的執行查看審核鏈路authLink.doAuth(...),通過返回結果對數據進行3、2、1級負責人審核,直至最後審核全部完成。

3.2 測試結果

23:49:46.585 [main] INFO  org.itstack.demo.design.test.ApiTest - 測試結果:{"code":"0001","info":"單號:1000998004813441 狀態:待三級審批負責人 王工"}
23:49:46.590 [main] INFO  org.itstack.demo.design.test.ApiTest - 測試結果:模擬三級負責人審批,王工
23:49:46.590 [main] INFO  org.itstack.demo.design.test.ApiTest - 測試結果:{"code":"0001","info":"單號:1000998004813441 狀態:待二級審批負責人 張經理"}
23:49:46.590 [main] INFO  org.itstack.demo.design.test.ApiTest - 測試結果:模擬二級負責人審批,張經理
23:49:46.590 [main] INFO  org.itstack.demo.design.test.ApiTest - 測試結果:{"code":"0001","info":"單號:1000998004813441 狀態:待一級審批負責人 段總"}
23:49:46.590 [main] INFO  org.itstack.demo.design.test.ApiTest - 測試結果:模擬一級負責人審批,段總
23:49:46.590 [main] INFO  org.itstack.demo.design.test.ApiTest - 測試結果:{"code":"0000","info":"單號:1000998004813441 狀態:一級審批完成負責人 時間:2020-06-18 23:49:46 審批人:段總"}

Process finished with exit code 0
  • 從上述的結果可以看到我們的責任鏈已經生效,按照責任鏈的結構一層層審批,直至最後輸出審批結束到一級完成的結果。
  • 這樣責任鏈的設計方式可以方便的進行擴展和維護,也把if語句幹掉了。

七、總結

  • 從上面代碼從if語句重構到使用責任鏈模式開發可以看到,我們的代碼結構變得清晰乾淨了,也解決了大量if語句的使用。並不是if語句不好,只不過if語句並不適合做系統流程設計,但是在做判斷和行為邏輯處理中還是非常可以使用的。
  • 在我們前面學習結構性模式中講到過組合模式,它像是一顆組合樹一樣,我們搭建出一個流程決策樹。其實這樣的模式也是可以和責任鏈模型進行組合擴展使用,而這部分的重點在於如何關聯鏈路的關聯,最終的執行都是在執行在中間的關係鏈。
  • 責任鏈模式很好的處理單一職責和開閉原則,簡單了耦合也使對象關係更加清晰,而且外部的調用方並不需要關心責任鏈是如何進行處理的(以上程序中可以把責任鏈的組合進行包裝,在提供給外部使用)。但除了這些優點外也需要是適當的場景才進行使用,避免造成性能以及編排混亂調試測試疏漏問題。

八、推薦閱讀

  • 1. 重學 Java 設計模式:實戰工廠方法模式「多種類型商品不同接口,統一發獎服務搭建場景」
  • 2. 重學 Java 設計模式:實戰原型模式「上機考試多套試,每人題目和答案亂序排列場景」
  • 3. 重學 Java 設計模式:實戰橋接模式「多支付渠道(微信、支付寶)與多支付模式(刷臉、指紋)場景」
  • 4. 重學 Java 設計模式:實戰組合模式「營銷差異化人群發券,決策樹引擎搭建場景」
  • 5. 重學 Java 設計模式:實戰外觀模式「基於SpringBoot開發門面模式中間件,統一控制接口白名單場景」

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※別再煩惱如何寫文案,掌握八大原則!

※教你寫出一流的銷售文案?

※超省錢租車方案

FB行銷專家,教你從零開始的技巧

聚甘新

Mybatis Generator逆向工程的使用

一、MyBatis Generator簡介

    MyBatis Generator(MBG)是MyBatis和iBATIS的代碼生成器。它將為所有版本的MyBatis以及版本2.2.0之後的iBATIS版本生成代碼。它將審查數據庫表(或許多表),並將生成可用於訪問表的構件。這減少了設置對象和配置文件以與數據庫表交互的初始麻煩。MBG尋求對簡單CRUD(創建,檢索,更新,刪除)的大部分數據庫操作產生重大影響。您仍然需要為連接查詢或存儲過程手動編寫SQL和對象代碼。MyBatis Generator將生成:

  • 與表結構匹配的Java POJO。這可能包括:

    • 一個匹配表的主鍵的類(如果有主鍵)

    • 一個匹配表的非主鍵字段的類(BLOB字段除外)

    • 包含表的BLOB字段的類(如果表具有BLOB字段)

    • 用於啟用動態選擇,更新和刪除的類

    這些類之間存在適當的繼承關係。請注意,生成器可以配置為生成不同類型的POJO層次結構 – 例如,如果您願意,可以選擇為每個表生成單個域對象。

  • MyBatis/iBATIS兼容的SQL Map XML文件。MBG為配置中的每個表上的簡單CRUD函數生成SQL。生成的SQL語句包括:

    • insert 插入

    • update by primary key  按主鍵更新

    • update by example (using a dynamic where clause)  通過條件更新(使用動態where子句)

    • delete by primary key  按主鍵刪除

    • delete by example (using a dynamic where clause)  按條件刪除(使用動態where子句)

    • select by primary key  按主鍵查詢

    • select by example (using a dynamic where clause)  按條件查詢(使用動態where子句)

    • count by example  按條件查詢記錄總數

    根據表結構的不同,這些語句有不同的變體(例如,如果表沒有主鍵,則MBG不會通過主鍵功能生成更新)。

    • 適當使用上述對象的Java客戶端類。Java客戶端類的生成是可選的。MBG將為MyBatis 3.x生成以下類型的Java客戶端:

      • 適用於MyBatis 3.x映射器基礎結構的映射器接口

MBG將為iBATIS 2.x生成以下類型的Java客戶端:

    • 符合Spring框架的DAO

    • 僅使用iBATIS SQL映射API的DAO。這些DAO可以生成兩種:通過構造函數或setter注入提供SqlMapClient。

    • 符合iBATIS DAO框架的DAO(iBATIS的可選部分,現在不推薦使用此框架,我們建議您使用Spring框架)

    MyBatis生成器設計為在迭代開發環境中運行良好,並且可以作為Ant任務或Maven插件包含在連續構建環境中。迭代運行MBG時需要注意的重要事項包括:

  1. 如果存在與新生成的XML文件同名的現有文件,MBG將自動合併XML文件。MBG不會覆蓋您對其生成的XML文件所做的任何自定義更改。您可以反覆運行它,而不必擔心會丟失對XML的自定義更改。MBG將替換先前運行中生成的任何XML元素。

  2. MBG不會合併Java文件,它可以覆蓋現有文件或使用不同的唯一名稱保存新生成的文件。如果對生成的Java文件進行更改並以迭代方式運行MBG,則必須手動合併更改。當作為Eclipse插件運行時 ,MBG可以自動合併Java文件。

二、MyBatis Generator使用

1、新建MBG的配置文件generatorConfig.xml文件

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
        PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
        "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
<generatorConfiguration>
 
 
    <!--導入屬性配置-->
    <properties resource="generator.properties"></properties>
    <!--指定特定數據庫的jdbc驅動jar包的位置-->
    <!--<classPathEntry location="${jdbc.driverLocation}"/>-->
 
    <context id="default" targetRuntime="MyBatis3">
 
        <!-- optional,旨在創建class時,對註釋進行控制,false生成註釋,true無註釋 -->
        <commentGenerator>
            <property name="suppressDate" value="false"/>
            <property name="suppressAllComments" value="false"/>
        </commentGenerator>
 
        <!--jdbc的數據庫連接 -->
        <jdbcConnection
                driverClass="${jdbc.driverClass}"
                connectionURL="${jdbc.connectionURL}"
                userId="${jdbc.userId}"
                password="${jdbc.password}">
        </jdbcConnection>
 
 
        <!--
         默認false,把JDBC DECIMAL 和 NUMERIC 類型解析為 Integer,
         為 true時把JDBC DECIMAL 和 NUMERIC 類型解析為java.math.BigDecimal
        -->
        <!-- 非必需,類型處理器,在數據庫類型和java類型之間的轉換控制-->
        <javaTypeResolver>
            <property name="forceBigDecimals" value="false"/>
        </javaTypeResolver>
 
 
        <!-- Model模型生成器,用來生成含有主鍵key的類,記錄類 以及查詢Example類
            targetPackage     指定生成的model生成所在的包名
            targetProject     指定在該項目下所在的路徑|指定生成到的工程名稱
        -->
        <javaModelGenerator targetPackage="com.test.model"
                            targetProject=".\src\main\java">
            <!-- 是否允許子包,即targetPackage.schemaName.tableName -->
            <property name="enableSubPackages" value="false"/>
            <!-- 是否對model添加 構造函數 true添加,false不添加-->
            <property name="constructorBased" value="false"/>
            <!-- 是否對類CHAR類型的列的數據進行trim操作 -->
            <property name="trimStrings" value="true"/>
            <!-- 建立的Model對象是否 不可改變  即生成的Model對象不會有 setter方法,只有構造方法 -->
            <property name="immutable" value="false"/>
        </javaModelGenerator>
 
        <!--Mapper映射文件生成所在的目錄 為每一個數據庫的表生成對應的SqlMapper文件 -->
        <sqlMapGenerator targetPackage="com.test.mapper"
                         targetProject=".\src\main\java">
            <property name="enableSubPackages" value="false"/>
        </sqlMapGenerator>
 
        <!-- 客戶端代碼,生成易於使用的針對Model對象和XML配置文件 的代碼
                type="ANNOTATEDMAPPER",生成Java Model 和基於註解的Mapper對象
                type="MIXEDMAPPER",生成基於註解的Java Model 和相應的Mapper對象
                type="XMLMAPPER",生成SQLMapper XML文件和獨立的Mapper接口
        -->
        <javaClientGenerator targetPackage="com.test.mapper"
                             targetProject=".\src\main\java" type="XMLMAPPER">
            <property name="enableSubPackages" value="true"/>
        </javaClientGenerator>
 
        <!--需要映射的數據庫的表名-->
        <table tableName="t_userinfo" domainObjectName="UserInfo"
               enableCountByExample="false" enableUpdateByExample="false"
               enableDeleteByExample="false" enableSelectByExample="false"
               selectByExampleQueryId="false">
        </table>
    </context>
</generatorConfiguration>

2、新建generator.properties文件

jdbc.driverLocation=C:\\mysql-connector-java-5.1.43.jar
jdbc.driverClass=com.mysql.jdbc.Driver
jdbc.connectionURL=jdbc:mysql://localhost:3306/mybatis
jdbc.userId=root
jdbc.password=tiger

3、配置執行mybatis generator操作,這裡有兩種方式

第1種方式:如果使用maven項目就可以省去編寫Java啟動類,使用maven插件和配置文件pom.xml即可,插件啟動maven-generator,在pom.xml中添加maven-generator插件

<plugins>
    <!--myBatis逆向工程插件-->
    <plugin>
        <groupId>org.mybatis.generator</groupId>
        <artifactId>mybatis-generator-maven-plugin</artifactId>
        <version>1.3.2</version>
        <configuration>
            <verbose>true</verbose>
            <overwrite>true</overwrite>
            <configurationFile>${project.basedir}/src/main/resources/generatorConfig.xml</configurationFile>
        </configuration>
        <dependencies>
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <version>5.1.35</version>
            </dependency>
        </dependencies>
    </plugin>
</plugins>

點擊mybatis-generator:generate就能執行mybatis generator了

第2種方式:

1、如果不是maven項目添加該mybatis-generator-core-1.3.2.jar,編寫main方法指向mybatis逆向工程,我給依賴粘貼到下面了

<!-- https://mvnrepository.com/artifact/org.mybatis.generator/mybatis-generator-core -->
<dependency>
    <groupId>org.mybatis.generator</groupId>
    <artifactId>mybatis-generator-core</artifactId>
    <version>1.3.2</version>
</dependency>

2、修改generatorConfig.xml文件,放開註釋的該配置

<classPathEntry location="${jdbc.driverLocation}"/>

3、然後編寫測試類執行

/**
 * 如果不是maven項目可以這樣生成
 */
public class MybatisGeneratorTest {
    public static void main(String[] args) throws InterruptedException, SQLException, IOException, InvalidConfigurationException, XMLParserException {
        List<String> warnings = new ArrayList<String>();
        //生成的java文件是否覆蓋
        boolean overwrite = true;
        //指定逆向工程配置文件
        //File configFile = new File("E:\\project\\mybatis-generator\\src\\main\\resources\\generatorConfig.xml");
        InputStream resourceAsStream = MybatisGeneratorTest.class.getClassLoader().getResourceAsStream("generatorConfig.xml");
        ConfigurationParser cp = new ConfigurationParser(warnings);
        Configuration config = cp.parseConfiguration(resourceAsStream);
        DefaultShellCallback callback = new DefaultShellCallback(overwrite);
        MyBatisGenerator myBatisGenerator = new MyBatisGenerator(config,callback, warnings);
        myBatisGenerator.generate(null);
    }
}

介紹完這兩種方式,查看數據表:

查看生成的實體類:


TIP:可以看出如果實體類想要遵循駝峰命名規範,數據庫表字段名設計需要用”_”來劃分

查看生成的文件信息:


TIP1:必須在<plugin></plugin>標籤里添加數據庫驅動,在其他地方添加無效,如果不添加會報找不到驅動錯誤,如過不在該插件添加數據庫依賴的話可以使用 <classPathEntry location=”${jdbc.driverLocation}”/> 來指定數據庫驅動位置。

TIP2:如果你在使用mybatis generator插件執行的時候報[ERROR] Failed to execute goal org.mybatis.generator:mybatis-generator-maven-plugin:1.3.2:generate (default-cli) on project mybatis-generator: <properties> resource generator.properties does not exist -> [Help 1]


儘管你的 <properties resource=”generator.properties”></properties>配置的沒有問題,但是還是找不到generator.properties,查看該配置,註釋掉


該配置會改變generatorConfig.xml中讀取generator.properties文件的默認路徑

TIP3:Mybatis Generator反向工程默認不會覆蓋生成的*.java文件。也可以設置覆蓋生成的*.java文件,在反向工程插件mybatis-generator-maven-plugin添加該配置<overwrite>true</overwrite>則會覆蓋生成的*.java文件,如圖


    Mybatis Generator不會覆蓋你的mapper.xml文件,MBG會合併追加到mapper.xml和你自定義的存在一起,但是如果你修改MBG第一次默認生成的SQL(MBG生成的CRUD),MBG會重新把自己生成的CRUD恢復默認,說白了,MBG只會覆蓋他自己生成的SQL,不會覆蓋你自定義的,你自定義的不變。。。如圖,他不會動你的自定義SQL,只會覆蓋Mybatis反向工程自己生成的SQL,前提MBG自動生成SQL語句的註釋要存在。


在最常見的用例中,MyBatis Generator(MBG)由XML配置文件驅動。配置文件告訴MBG

  • 如何連接到數據庫

  • 生成什麼對象,以及如何生成它們

  • 應使用哪些表生成對象

官方MBG配置文件詳解地址:http://mybatis.org/generator/configreference/xmlconfig.html

附帶一個MBG的中文配置文件詳解:https://www.jianshu.com/p/e09d2370b796

 

更多Mybatis逆向工程的使用參考:http://www.mybatis.org/generator/index.html


● XStream學習手冊

● 別在 Java 代碼里亂打日誌了,這才是正確的打日誌姿勢!

● 高可用Redis服務架構分析與搭建

● 8 種方案,幫你解決重複提交問題!請拿走

● IDEA 解決 Maven 依賴衝突的高能神器,這一篇夠不夠?

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※超省錢租車方案

※別再煩惱如何寫文案,掌握八大原則!

※回頭車貨運收費標準

※教你寫出一流的銷售文案?

FB行銷專家,教你從零開始的技巧

聚甘新

Java 從入門到進階之路(二十四)

在之前的文章我們介紹了一下 Java 中的  集合框架中的Collection 的泛型,本章我們來看一下 Java 集合框架中的Collection 的子接口 List。

Collection 接口有 3 種子類型,List、Set 和 Queue,其中 List 和 Set 的區別是 Set 中不能存放相同的元素,而 List 中可以,本章我們就來介紹一下 List。

 

 

 從上圖我們可以知道 List 有兩個實例類,ArrayList 和 LinkedList,

ArrayList 是數組實現,查找快,增上慢,由於是數組實現,在增和刪的時候牽扯到數組的增容,以及靠背元素,所以慢,數組是可以直接按索引查找,所以查找時較快。

LinkedList 是鏈表實現,增刪快,查找慢,由於鏈表實現,增加時只要讓前一個元素記住自己就可以了,刪除時讓前一個元素記住后一個元素,后一個元素記住前一個元素,這樣的增刪效率高但查詢時需要一個一個遍歷,所以效率低。

LinkedList 我們可以形象的比作老式手錶的鏈子,一節扣一節,增刪時只需要打開兩個之間的節扣即可,不需要牽扯到其他節扣。

ArrayList 和 LinkedList 都有各自的優缺點,在用的時候可以根據需求自行選擇,避免性能消耗。在現在計算機計算能力越來越強,做的也不是大型項目的時候,這兩個之間的性能差異我們其實是可以忽略的。

接下來我們就來看一下 List 接口的一些基礎用法,如下:

 1 import java.util.ArrayList;
 2 import java.util.List;
 3 
 4 /**
 5  * java.util.List
 6  * 可重複集合,並且有序
 7  * 特點是可以根據下錶操作元素
 8  * ArrayList:使用數組實現,查詢更快
 9  * LinkedList:使用鏈表實現,增刪更快(收尾增刪效果更明顯)
10  */
11 
12 public class Main {
13     public static void main(String[] args) {
14         List<String> list = new ArrayList<String>();
15         list.add("one");
16         list.add("two");
17         list.add("three");
18         list.add("four");
19         /**
20          * E set(int index, E e)
21          * 將給定元素設置到制定位置上,返回原位置的元素
22          * 所以是替換元素操作
23          * 如果超出了元素的長度,則使用 add 添加,否則編譯錯誤
24          * */
25         String old = list.set(1, "2"); // 將下標為 1 的元素改為 2,返回值則是被替換的元素
26         System.out.println(old); // two
27         System.out.println(list); // [one, 2, three, four]
28 
29         /**
30          * E get(int index)
31          * 獲取給定下標對應的元素
32          * */
33         String two = list.get(1); // 獲取第二個元素
34         System.out.println(two); // 2
35 
36         /**
37          * 可以通過傳統的循環遍歷 List 集合
38          * */
39         for (int i = 0; i < list.size(); i++) {
40             System.out.println(list.get(i)); // one 2 three four
41         }
42     }
43 }

在上面的代碼中,我們通過 set 和 get 方法來設置和獲取我們想要的下標的元素,當然還有其他方法,如下:

 1 /**
 2  * List 集合提供了一對重載的 add,remove 方法
 3  * void add(int index, E e)
 4  * 將給定元素插入到指定位置,
 5  * 如果不指定下標,則插入到末尾
 6  * <p>
 7  * E remove(int index)
 8  * 從集合中刪除指定位置的元素,並將其返回
 9  */
10 
11 public class Main {
12     public static void main(String[] args) {
13         List<String> list = new ArrayList<String>();
14         list.add("one");
15         list.add("two");
16         list.add("three");
17         list.add("four");
18 
19         list.add(1, "2"); // 將下標為 1 的元素插入 2
20         System.out.println(list); // [one, 2, two, three, four]
21 
22         String string = list.remove(1); // 將下標為 1 的元素刪除,返回值為該元素
23         System.out.println(string); // 2
24         System.out.println(list); // [one, two, three, four]
25     }
26 }

我們在將 Collection 的時候講過 add 和 remove 方法,在 List 中這兩個方法被重載了,可以根據需求插入和刪除想要刪除的下標的元素,那如果我們想要獲取兩個下標之間的元素和刪除兩個下標之間的元素該怎麼辦呢,如下:

 1 import java.util.ArrayList;
 2 import java.util.List;
 3 
 4 public class Main {
 5     public static void main(String[] args) {
 6         List<Integer> list = new ArrayList<Integer>();
 7         for (int i = 0; i < 10; i++) {
 8             list.add(i);
 9         }
10         System.out.println(list); // [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
11         List<Integer> subList = list.subList(2, 5); // 獲取下標從 2 到 5 的元素,含 2 不含 5
12         System.out.println(subList); // [2, 3, 4]
13         // 將 subList 中每個元素擴大 10 倍
14         for (int i = 0; i < subList.size(); i++) {
15             subList.set(i, subList.get(i) * 10);
16         }
17         System.out.println(subList); // [20, 30, 40]
18         /**
19          * 對子集的修改,就是修改原集合相應內容
20          * */
21         System.out.println(list); // [0, 1, 20, 30, 40, 5, 6, 7, 8, 9]
22         /**
23          * 刪除集合中 2-5 的元素
24          * */
25         list.subList(2, 5).clear();
26         System.out.println(list); // [0, 1, 5, 6, 7, 8, 9]
27     }
28 }

我們說集合和數組有很多相似的地方,那課可以進行相互轉換呢,當然是可以的,如下:

 1 import java.util.ArrayList;
 2 import java.util.Collection;
 3 
 4 public class Main {
 5     public static void main(String[] args) {
 6         Collection<String> collection = new ArrayList<String>();
 7         collection.add("one");
 8         collection.add("two");
 9         collection.add("three");
10         collection.add("four");
11         System.out.println(collection); // [one, two, three, four]
12         /**
13          * 集合提供了一個 toArray,可以將當前集合轉換為數組
14          * */
15         // Object[] array = collection.toArray(); // 不常用
16         /**
17          * collection.size() 表示要轉換的數組的 length
18          * 如果大於給定的 collection 的 size,則自動填充完整 array
19          * 如果小於給定的 collection 的 size,則自動創建給你一樣長度的 size
20          * */
21         String[] array = collection.toArray(new String[collection.size()]);
22         System.out.println(array.length); // 4
23         for (String string : array) {
24             System.out.println(string); // one two three four
25         }
26 
27         String[] array1 = collection.toArray(new String[6]);
28         System.out.println(array.length); // 4
29         for (String string : array1) {
30             System.out.println(string); // one two three four null null
31         }
32 
33         String[] array2 = collection.toArray(new String[1]);
34         System.out.println(array.length); // 4
35         for (String string : array2) {
36             System.out.println(string); // one two three four
37         }
38     }
39 }

在上面的代碼中我們實現了集合轉換為數組的方法,接下來我們再看一下數組轉換為集合的方法:

 1 import java.util.ArrayList;
 2 import java.util.Arrays;
 3 import java.util.List;
 4 
 5 /**
 6  * 數組轉換為集合
 7  * 需要注意,轉換隻能轉換為 List 集合
 8  * 使用的是數組的工具類 Arrays 的靜態方法 asList
 9  * 只能轉換為 List 集合的主要原因是:Set 不能存放重複元素
10  * 所以若轉換為 Set 集合可能會出現丟失元素的情況
11  */
12 public class Main {
13     public static void main(String[] args) {
14         String[] array = {"one", "two", "three", "four"};
15         List<String> list = Arrays.asList(array);
16         System.out.println(list); // [one, two, three, four]
17 
18         /**
19          * 向集合中添加元素,會出現編譯錯誤
20          * 相當於在原數組添加元素
21          * 該集合時由數組轉換過來的,那麼該集合就表示原來的數組
22          * 所以對集合的操作就是對數組的操作
23          * 那麼添加元素會導致原數組擴容
24          * 那麼久不能表示原來的數組了
25          * 所以不允許向該集合添加元素
26          */
27         // list.add("five"); // 編譯錯誤 Exception in thread "main" java.lang.UnsupportedOperationException
28 
29         /**
30          * 若希望增刪元素,需要另外創建一個集合
31          * */
32         /**
33          * 所有的集合都提供了一個帶有 Collection 類型參數的構造方法
34          * 該構造方法稱為:複製構造器
35          * 作用是在創建當前集合的同時,
36          * 集合中包含給定集合中的所有元素
37          * */
38         // List<String> list1 = new ArrayList<String>(list); // 複製構造器,一步到位
39         List<String> list1 = new ArrayList<String>();
40         list1.addAll(list);
41         list1.add("five");
42         System.out.println(list1); // [one, 2, three, four, five]
43 
44         /**
45          * 修改集合元素,數組元素也會改變
46          * */
47         list.set(1, "2");
48         System.out.println(list); // [one, 2, three, four]
49         for (String string : array) {
50             System.out.println(string); // one 2 three four
51         }
52     }
53 }

    

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※別再煩惱如何寫文案,掌握八大原則!

※教你寫出一流的銷售文案?

※超省錢租車方案

FB行銷專家,教你從零開始的技巧

聚甘新

Flutter學習筆記(35)–通知Notification,Flutter學習筆記(35)–通知Notification

如需轉載,請註明出處:Flutter學習筆記(35)–通知Notification

通知的NotificationListener和我們之前寫的事件的Listener一樣,都是功能性的組件,而且也都是從子節點順着widget樹向上冒泡,不同的是,事件的Listener不可以被終止,但是通知的NotificationListener是可以被終止的。

是否終止根據NotificationListener的返回值來決定。

說一下我個人的理解:

通知Notification的發送是通過disPatch進行分發的,就好像Android裏面的事件分發,當NotificationListener監聽到了通知事件,這時候會走到其onNotification回調中,根據回調中的返回值類型(true還是false)來決定是否還繼續向父親節點發送通知。

返回true就是繼續分發,返回false就是終止分發,返回false就意味着上層節點的NotificationListener就不會接收到通知事件了。

舉個例子就是:

兩層NotificationListener嵌套,子節點的NotificationListener返回true,那麼父親節點的NotificationListener可以接收到通知事件,反之如果返回false,那麼父親節點的NotificationListener就不會接收到通知事件了。

下面看一下demo示例:

demo就是簡單的發送通知,監聽到通知事件后改變text的內容。

1.創建一個事件通知類,要繼承Notification,它其實就是一個數據載體,在裏面定義通知數據的類型和內容。

import 'package:flutter/material.dart';

class MyNotification extends Notification{
  String notificationStr;

  MyNotification(this.notificationStr);
}

2.NotificationListener的使用和通知事件的分發

import 'package:flutter/material.dart';
import 'package:study_app/util/MyNotification.dart';

class NotificationDemo extends StatefulWidget {
  @override
  State<StatefulWidget> createState() {
    return _NotificationDemoState();
  }
}

class _NotificationDemoState extends State {
  String _notificationData = 'default_data';

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'NotificationDemo',
      home: new Scaffold(
          appBar: AppBar(
            title: Text('NotificationDemo'),
          ),
          body: NotificationListener<MyNotification>(
            onNotification: (notification) {
              setState(() {
                _notificationData = notification.notificationStr;
              });
              return true;
            },
            child: Column(
              children: <Widget>[
                Text(_notificationData),
                Builder(
                  builder: (context) {
                    return Container(
                      width: double.infinity,
                      child: RaisedButton(
                          child: Text('發送通知'),
                          onPressed: () {
                            MyNotification('notification_data')
                                .dispatch(context);
                          }),
                    );
                  },
                )
              ],
            ),
          )),
    );
  }
}

在看書的時候,作者強調了一種錯誤的寫法,如下圖註釋的部分:

原因是通知在分發的時候,需要一個context參數,這個參數指的是Notification監聽的子widget的context,如果按照註釋部分的寫法的話,context是根widget的,這樣會導致監聽不到子widget了。

所以需要我們通過Builder構建出我們子widget的context,這裏需要特別注意一下。

最後看一下效果截圖:

   

以上!有任何疑問歡迎留言!

 

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※超省錢租車方案

※別再煩惱如何寫文案,掌握八大原則!

※回頭車貨運收費標準

※教你寫出一流的銷售文案?

FB行銷專家,教你從零開始的技巧

聚甘新

Oracle SQL調優系列之SQL Monitor Report

@

目錄

  • 1、SQL Monitor簡介
  • 2、捕捉sql的前提
  • 3、SQL Monitor 參數設置
  • 4、SQL Monitor Report
    • 4.1、SQL_ID獲取
    • 4.2、Text文本格式
    • 4.3、Html格式
    • 4.4、ACTIVE格式
    • 4.5 SQL Monitoring list
  • 5、SQL Monitor Report查詢
    • 5.1、查看所有的sql monitor report
    • 5.2、查看某個sql的sql monitor report
    • 5.3、查看某個sql的整體性能
    • 5.4、查看整個系統的性能

1、SQL Monitor簡介

sql monitor是oracle官方提供的自動監控符合特定條件的SQL,用於收集執行時的細節信息的監控工具,常用於sql調優和系統性能監控

2、捕捉sql的前提

sql monitor 捕捉sql的前提:

  • 并行執行的sql語句
  • 單次執行消耗的CPU或IO超過5秒
  • statistics_level級別必須是TYPICAL 或者ALL
  • 使用/* +MONITOR*/ HINT的SQL語句

3、SQL Monitor 參數設置

  • STATISTICS_LEVEL必須設置為:’TYPICAL’(缺省)或者 ‘ALL’
  • CONTROL_MANAGEMENT_PACK_ACCESS設置為:’DIAGNOSTIC+TUNING’

查看statistics_level參數

show parameter statistics_level;

建議還是改變Session就可以

alter session set statistics_level=ALL;

查看參數CONTROL_MANAGEMENT_PACK_ACCESS

show parameter CONTROL_MANAGEMENT_PACK_ACCESS;

4、SQL Monitor Report

本博客採用DBMS_SQLTUNE包DBMS_SQLTUNE.report_sql_monitor的方式獲取,報告格式有:’TEXT’,’HTML’,’XML’ ,’ACTIVE’,其中’ACTIVE’只在11g R2以後才支持

4.1、SQL_ID獲取

sql monitor使用,必須在sql中使用/* +MONITOR*/ Hint,然後數據會存在v$sql_monitor表裡

隨意找條sql,注意要加/*+ moniotr*/


select /*+ moniotr*/ a.user_code, a.full_name, a.user_pwd, c.unit_code, c.unit_name
  from base_user a
  left join (select ur.user_code, ur.unit_code
               from t_user_role ur
              where ur.user_role < 10) b
    on a.user_code = b.user_code
  left join t_unit_info c
    on b.unit_code = c.unit_code
 where c.unit_code in
       (select uinfo.unit_code
          from t_unit_info uinfo
         start with uinfo.unit_code = '15803'
        connect by prior uinfo.unit_code = uinfo.para_unit_code);
     

提供sql查詢,獲取sql_id

select sql_id,sql_text from v$sql_monitor where sql_text like '%t_unit_info%

4.2、Text文本格式

將上面查詢到的sql_id改下,然後執行如下SQL:

SET LONG 1000000
SET LONGCHUNKSIZE 1000000
SET LINESIZE 1000
SET PAGESIZE 0
SET TRIM ON
SET TRIMSPOOL ON
SET ECHO OFF
SET FEEDBACK OFF
spool report_sql_monitor_text.txt
SELECT DBMS_SQLTUNE.REPORT_SQL_MONITOR(
  SQL_ID => 'g9rtj389t0g66',
  TYPE => 'TEXT',
  REPORT_LEVEL => 'ALL') AS REPORT
FROM dual;
spool off

獲取到text格式的sql monitor

4.3、Html格式

SET LONG 1000000
SET LONGCHUNKSIZE 1000000
SET LINESIZE 1000
SET PAGESIZE 0
SET TRIM ON
SET TRIMSPOOL ON
SET ECHO OFF
SET FEEDBACK OFF
spool report_sql_monitor_html.html
SELECT DBMS_SQLTUNE.REPORT_SQL_MONITOR(
  SQL_ID => 'g9rtj389t0g66',
  TYPE => 'HTML',
  REPORT_LEVEL => 'ALL') AS REPORT
FROM dual;
spool off

獲取到對應報告,可以看到執行計劃、Buffer Gets 等等信息

4.4、ACTIVE格式

ACTIVE格式需要下載相應的flash組件、腳本,詳細見SQL Monitor Report 使用詳解

SET LONG 1000000
SET LONGCHUNKSIZE 1000000
SET LINESIZE 1000
SET PAGESIZE 0
SET TRIM ON
SET TRIMSPOOL ON
SET ECHO OFF
SET FEEDBACK OFF
spool report_sql_monitor_active.html
SELECT DBMS_SQLTUNE.REPORT_SQL_MONITOR(
  SQL_ID => '2rjh5d5k2yujz',
  TYPE => 'ACTIVE',
  REPORT_LEVEL => 'ALL',
  BASE_PATH => 'http://ip/script') AS REPORT
FROM dual;
spool off

4.5 SQL Monitoring list

如果要獲取所有sql monitor,就可以使用如下SQL:

SET LONG 1000000
SET LONGCHUNKSIZE 1000000
SET LINESIZE 1000
SET PAGESIZE 0
SET TRIM ON
SET TRIMSPOOL ON
SET ECHO OFF
SET FEEDBACK OFF
SPOOL report_sql_monitor_list.html
SELECT dbms_sqltune.report_sql_monitor_list(
  type         => 'HTML',
  report_level => 'ALL') AS report
FROM dual;
SPOOL OFF

5、SQL Monitor Report查詢

提供sql monitor常用的查詢腳本

5.1、查看所有的sql monitor report

  • 查看所有的sql monitor report
   select dbms_sqltune.report_sql_monitor from dual;

5.2、查看某個sql的sql monitor report

  • 查看某個sql的sql monitor report
  SELECT DBMS_SQLTUNE.report_sql_monitor(sql_id => '2rjh5d5k2yujz', type => 'TEXT') from dual;

5.3、查看某個sql的整體性能

  • 查看某個sql的整體性能
   SELECT DBMS_SQLTUNE.report_sql_monitor_list(sql_id=>'2rjh5d5k2yujz',type =>'TEXT',report_level => 'ALL') AS report FROM dual;

5.4、查看整個系統的性能

  • 查看整個系統的性能
   SELECT DBMS_SQLTUNE.report_sql_monitor_list(type =>'TEXT',report_level => 'ALL') AS report FROM dual;

相關SQL腳本下載:sql download

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※廣告預算用在刀口上,台北網頁設計公司幫您達到更多曝光效益

※別再煩惱如何寫文案,掌握八大原則!

※教你寫出一流的銷售文案?

※超省錢租車方案

FB行銷專家,教你從零開始的技巧

聚甘新

JVM源碼分析之Object.wait/notify(All)完全解讀

概述

本文其實一直都想寫,因為各種原因一直拖着沒寫,直到開公眾號的第一天,有朋友再次問到這個問題,這次讓我靜心下來準備寫下這篇文章,本文有些東西是我自己的理解,比如為什麼JDK一開始要這麼設計,初衷是什麼,沒怎麼去找相關資料,所以只能談談自己的理解,所以大家看到文章之後可以談談自己的看法,對於實現部分我倒覺得說清楚問題不大,code is here,看明白了就知道怎麼回事了。

Object.wait/notify(All)大家都知道主要是協同線程處理的,大家用得也很多,大概邏輯和下面的用法差不多

看到上面代碼,你會有什麼疑惑嗎?至少我會有幾個問題會問自己: * 為什麼進入wait和notify的時候要加synchronized鎖 * 既然加了synchronized鎖,那當某個線程調用了wait的時候明明還在synchronized塊里,其他線程怎麼進入到鎖里去執行notify的 * 為什麼wait方法可能會拋出InterruptedException異常 * 如果有多個線程都進入wait狀態,那某個線程調用notify喚醒線程時是否按照順序喚起那些wait線程 * wait的線程是在某個線程執行完notify之後立馬就被喚起嗎 * notifyAll又是怎麼實現全喚起的 * wait的線程是否會影響load

如果上面這些問題也都是你想了解的,那這篇文章或許能給你一個答案。

為何要加synchronized鎖

從實現上來說,這個鎖至關重要,正因為這把鎖,才能讓整個wait/notify玩轉起來,當然我覺得其實通過其他的方式也可以實現類似的機制,不過hotspot至少是完全依賴這把鎖來實現wait/notify的。

如果要我們來實現這種機制我們會怎麼去做,我們知道wait/notify是為了線程間協作而設計的,當我們執行wait的時候讓線程掛起,當執行notify的時候喚醒其中一個掛起的線程,那需要有個地方來保存對象和線程之間的映射關係(可以想象一個map,key是對象,value是一個線程列表),當調用這個對象的wait方法時,將當前線程放到這個線程列表裡,當調用這個對象的notify方法時從這個線程列表裡取出一個來讓其繼續執行,這樣看來是可行的,也比較簡單,那現在的問題這種映射關係放到哪裡。而synchronized正好也是為線程間協作而設計的,上面碰到的問題它也要解決,或許正因為這樣wait和notify的實現就直接依賴synchronzied(monitorenter/monitorexit是jvm規範里要求要去實現的)來實現了,這隻是我的理解,可能初衷不是這個原因,這其實也是這篇文章遲遲未寫的一個原因吧,因為我無法取證自己的理解是對的,歡迎各位在這塊談談自己的見解。

wait方法執行后未退出同步塊,其他線程如何進入同步塊

這個問題其實要回答很簡單,因為在wait處理過程中會臨時釋放同步鎖,不過需要注意的是當某個線程調用notify喚起了這個線程的時候,在wait方法退出之前會重新獲取這把鎖,只有獲取了這把鎖才會繼續執行,想象一下,我們知道wait的方法是被monitorenter和monitorexit包圍起來,當我們在執行wait方法過程中如果釋放了鎖,出來的時候又不拿鎖,那在執行到monitorexit指令的時候會發生什麼?當然這可以做兼容,不過這實現起來還是很奇怪的。

為什麼wait方法可能拋出InterruptedException異常

這個異常大家應該都知道,當我們調用了某個線程的interrupt方法時,對應的線程會拋出這個異常,wait方法也不希望破壞這種規則,因此就算當前線程因為wait一直在阻塞,當某個線程希望它起來繼續執行的時候,它還是得從阻塞態恢復過來,因此wait方法被喚醒起來的時候會去檢測這個狀態,當有線程interrupt了它的時候,它就會拋出這個異常從阻塞狀態恢復過來。

這裡有兩點要注意: * 如果被interrupt的線程只是創建了,並沒有start,那等他start之後進入wait態之後也是不能會恢復的 * 如果被interrupt的線程已經start了,在進入wait之前,如果有線程調用了其interrupt方法,那這個wait等於什麼都沒做,會直接跳出來,不會阻塞

被notify(All)的線程有規律嗎

這裏要分情況: * 如果是通過notify來喚起的線程,那先進入wait的線程會先被喚起來 * 如果是通過nootifyAll喚起的線程,默認情況是最後進入的會先被喚起來,即LIFO的策略

notify執行之後立馬喚醒線程嗎

其實這個大家可以驗證一下,在notify之後寫一些邏輯,看這些邏輯是在其他線程被喚起之前還是之後執行,這個是個細節問題,可能大家並沒有關注到這個,其實hotspot里真正的實現是退出同步塊的時候才會去真正喚醒對應的線程,不過這個也是個默認策略,也可以改的,在notify之後立馬喚醒相關線程。

notifyAll是怎麼實現全喚起的

或許大家立馬想到這個簡單,一個for循環就搞定了,不過在jvm里沒實現這麼簡單,而是藉助了monitorexit,上面我提到了當某個線程從wait狀態恢復出來的時候,要先獲取鎖,然後再退出同步塊,所以notifyAll的實現是調用notify的線程在退出其同步塊的時候喚醒起最後一個進入wait狀態的線程,然後這個線程退出同步塊的時候繼續喚醒其倒數第二個進入wait狀態的線程,依次類推,同樣這這是一個策略的問題,jvm里提供了挨個直接喚醒線程的參數,不過都很罕見就不提了。

wait的線程是否會影響load

這個或許是大家比較關心的話題,因為關乎系統性能問題,wait/nofity是通過jvm里的park/unpark機制來實現的,在linux下這種機制又是通過pthread_cond_wait/pthread_cond_signal來玩的,因此當線程進入到wait狀態的時候其實是會放棄cpu的,也就是說這類線程是不會佔用cpu資源。

 

一起來學習吧:

PerfMa KO 系列課之 JVM 參數【Memory篇】

Hotspot GC研發工程師也許漏掉了一塊邏輯

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

※超省錢租車方案

※別再煩惱如何寫文案,掌握八大原則!

※回頭車貨運收費標準

※教你寫出一流的銷售文案?

FB行銷專家,教你從零開始的技巧

聚甘新