【Flutter實戰】圖片組件及四大案例

老孟導讀:大家好,這是【Flutter實戰】系列文章的第三篇,這一篇講解圖片組件,Image有很多高級用法,希望對您有所幫助。

圖片組件是Flutter基礎組件之一,和文本組件一樣必不可少。圖片組件包含Image和Icon兩個組件,本質上Icon不屬於圖片組件,但其外形效果上類似於圖片。

在項目中建議優先使用Icon組件,Icon本質上是一種字體,只不過显示的不是文字,而是圖標,而Image組件先通過圖片解碼器將圖片解碼,所以Icon有如下優點:

  • 通常情況下,圖標比圖片體積更小,顯著的減少App包體積。
  • 圖標不會出現失真或者模糊的現象,例如將20×20的圖片,渲染在200×200的屏幕上,圖片會失真或模糊,而圖標是矢量圖,不會失真,就像字體一樣。
  • 多個圖標可以存放在一個文件中,方便管理。
  • 全平台通用。

Image

Image組件用於显示圖片,圖片的來源可以是網絡、項目中圖片或者設備上的圖片。

加載網絡圖片:

Image.network(
  'http://pic1.win4000.com/pic/c/cf/cdc983699c.jpg',
)

加載項目中圖片:

首先將圖片拷貝到項目中,通常情況下,拷貝到assets/images/目錄下,assets/images/目錄為手動創建,新建的項目默認是沒有此目錄的。

設置pubspec.yaml配置文件:

assets:
  - assets/images/

或者指定具體圖片的名稱:

assets:
  - assets/images/aa.jpg

通常情況下,使用第一種方式,因為圖片會有很多張,增加一張就這裏配置一個太麻煩。

注意:assets前面的空格問題,極容易引發編譯異常,正確格式如下:

加載圖片:

Image.asset('assets/images/aa.jpg')

加載設備上的圖片:

要加載設備(手機)上的圖片首先需要獲取設備圖片的路徑,由於不同平台的路徑不同,因此路徑的獲取必須依靠原生支持,如果了解原生(Android和iOS)開發,可以直接使用MethodChannel獲取路徑,如果不懂原生(Android和iOS)開發,可以使用第三方插件獲取路徑,這裏推薦官方的path_provider

加載設備上的圖片:

Image.file(File('path'))

設置圖片的大小:

Image.asset('assets/images/aa.jpg',width: 100,height: 200,),

當Image的大小和圖片大小不匹配時,需要設置填充模式fit,設置組件大小為150×150,

Container(
  color: Colors.red.withOpacity(.3),
  child: Image.asset('assets/images/aa.jpg',width: 150,height: 150),
)

看到,圖片左右兩邊有空白區域(淺紅色填充的區域),如果想要圖片充滿整個區域,設置如下:

Container(
  color: Colors.red.withOpacity(.3),
  child: Image.asset('assets/images/aa.jpg',width: 150,height: 150,fit: BoxFit.fill,),
)

雖然圖片充滿整個區域,但圖片變形了,使圖片等比拉伸,直到兩邊都充滿區域:

Container(
  color: Colors.red.withOpacity(.3),
  child: Image.asset('assets/images/aa.jpg',width: 150,height: 150,fit: BoxFit.cover,),
)

此時,圖片未變形且兩邊都充滿區域,不過圖片被裁減了一部分。

fit參數就是設置填充方式,其值介紹如下:

  • fill:完全填充,寬高比可能會變。
  • contain:等比拉伸,直到一邊填充滿。
  • cover:等比拉伸,直到2邊都填充滿,此時一邊可能超出範圍。
  • fitWidth:等比拉伸,寬填充滿。
  • fitHeight:等比拉伸,高填充滿。
  • none:當組件比圖片小時,不拉伸,超出範圍截取。
  • scaleDown:當組件比圖片小時,圖片等比縮小,效果和contain一樣。

BoxFit.none的裁減和alignment相關,默認居中,

Image.asset(
  'assets/images/aa.jpg',
  width: 150,
  height: 150,
  fit: BoxFit.none,
  alignment: Alignment.centerRight,
),

左邊為原圖。

設置對齊方式:

Container(
  color: Colors.red.withOpacity(.3),
  child: Image.asset(
    'assets/images/aa.jpg',
    width: 150,
    height: 150,
    alignment: Alignment.centerLeft,
  ),
),

colorcolorBlendMode用於將顏色和圖片進行顏色混合,colorBlendMode表示混合模式,下面介紹的混合模式比較多,瀏覽一遍即可,此屬性可以用於簡單的濾鏡效果。

  • clear:清楚源圖像和目標圖像。
  • color:獲取源圖像的色相和飽和度以及目標圖像的光度。
  • colorBurn:將目標的倒數除以源,然後將結果倒數。
  • colorDodge:將目標除以源的倒數。
  • darken:通過從每個顏色通道中選擇最小值來合成源圖像和目標圖像。
  • difference:從每個通道的較大值中減去較小的值。合成黑色沒有效果。合成白色會使另一張圖像的顏色反轉。
  • dst:僅繪製目標圖像。
  • dstATop:將目標圖像合成到源圖像上,但僅在與源圖像重疊的位置合成。
  • dstIn:显示目標圖像,但僅显示兩個圖像重疊的位置。不渲染源圖像,僅將其視為蒙版。源的顏色通道將被忽略,只有不透明度才起作用。
  • dstOut:显示目標圖像,但僅显示兩個圖像不重疊的位置。不渲染源圖像,僅將其視為蒙版。源的顏色通道將被忽略,只有不透明度才起作用。
  • dstOver:將源圖像合成到目標圖像下。
  • exclusion:從兩個圖像的總和中減去兩個圖像的乘積的兩倍。
  • hardLight:調整源圖像和目標圖像的成分以使其適合源圖像之後,將它們相乘。
  • hue:獲取源圖像的色相,以及目標圖像的飽和度和光度。
  • lighten:通過從每個顏色通道中選擇最大值來合成源圖像和目標圖像。
  • luminosity:獲取源圖像的亮度,以及目標圖像的色相和飽和度。
  • modulate:將源圖像和目標圖像的顏色分量相乘。
  • multiply:將源圖像和目標圖像的分量相乘,包括alpha通道。
  • overlay:調整源圖像和目標圖像的分量以使其適合目標后,將它們相乘。
  • plus:對源圖像和目標圖像的組成部分求和。
  • saturation:獲取源圖像的飽和度以及目標圖像的色相和亮度。
  • screen:將源圖像和目標圖像的分量的逆值相乘,然後對結果求逆。
  • softLight:對於低於0.5的源值使用colorDodge,對於高於0.5的源值使用colorBurn。
  • src:放置目標圖像,僅繪製源圖像。
  • srcATop:將源圖像合成到目標圖像上,但僅在與目標圖像重疊的位置合成。
  • srcIn:显示源圖像,但僅显示兩個圖像重疊的位置。目標圖像未渲染,僅被視為蒙版。目標的顏色通道將被忽略,只有不透明度才起作用。
  • srcOut:显示源圖像,但僅显示兩個圖像不重疊的位置。
  • srcOver:將源圖像合成到目標圖像上。
  • xor:將按位異或運算符應用於源圖像和目標圖像。

是不是感覺看了和沒看差不多,看了也看不懂。正常,估計只有學過視覺算法的才能看懂吧,直接看下各個屬性的效果吧:

repeat表示當組件有空餘位置時,將會重複显示圖片

Image.asset(
  'assets/images/aa.jpg',
  width: double.infinity,
  height: 150,
  repeat: ImageRepeat.repeatX,
)

重複的模式有:

  • repeat:x,y方向都充滿。
  • repeatX:x方向充滿。
  • repeatY:y方向充滿。
  • noRepeat:不重複。

matchTextDirection設置為true時,圖片的繪製方向為TextDirection設置的方向,其父組件必須為Directionality

Directionality(
    textDirection: TextDirection.rtl,
    child: Image.asset(
      'assets/images/logo.png',
      height: 150,
      matchTextDirection: true,
    )),

左邊為原圖,效果是左右鏡像。

filterQuality表示繪製圖像的質量,從高到低為:high->medium->low->none。越高效果越好,越平滑,當然性能損耗越大,默認是low,如果發現圖片有鋸齒,可以設置此參數。

當加載圖片的時候回調frameBuilder,當此參數為null時,此控件將會在圖片加載完成后显示,未加載完成時显示空白,尤其在加載網絡圖片時會更明顯。因此此參數可以用於處理圖片加載時显示佔位圖片和加載圖片的過渡效果,比如淡入淡出效果。

下面的案例是淡入淡出效果:

Image.network(
  'https://flutter.github.io/assets-for-api-docs/assets/widgets/puffin.jpg',
  frameBuilder: (BuildContext context, Widget child, int frame,
      bool wasSynchronouslyLoaded) {
    if (wasSynchronouslyLoaded) {
      return child;
    }
    return AnimatedOpacity(
      child: child,
      opacity: frame == null ? 0 : 1,
      duration: const Duration(seconds: 2),
      curve: Curves.easeOut,
    );
  },
)

loadingBuilder參數比frameBuilder控制的力度更細,可以獲取圖片加載的進度,下面的案例显示了加載進度條:

Image.network(
    'https://flutter.github.io/assets-for-api-docs/assets/widgets/puffin.jpg',
    loadingBuilder: (BuildContext context, Widget child,
        ImageChunkEvent loadingProgress) {
  if (loadingProgress == null) {
    return child;
  }
  return Center(
    child: CircularProgressIndicator(
      value: loadingProgress.expectedTotalBytes != null
          ? loadingProgress.cumulativeBytesLoaded /
              loadingProgress.expectedTotalBytes
          : null,
    ),
  );
})

centerSlice用於.9圖,.9圖用於拉伸圖片的特定區域,centerSlice設置的區域(Rect)就是拉伸的區域。.9圖通常用於控件大小、寬高比不固定的場景,比如聊天背景圖片等。

Container(
    width: 250,
    height: 300,
    decoration: BoxDecoration(
        image: DecorationImage(
            centerSlice: Rect.fromLTWH(20, 20, 10, 10),
            image: AssetImage(
              'assets/images/abc.jpg',
            ),
            fit: BoxFit.fill))),

上面為原圖,下面為拉伸的圖片。

在使用時大概率會出現如下異常:

這是由於圖片比組件的尺寸大,如果使用centerSlice屬性,圖片必須比組件的尺寸小,一般情況下,.9圖的尺寸都非常小。

Icon

Icon是圖標組件,Icon不具有交互屬性,如果想要交互,可以使用IconButton。

Icon(Icons.add),

設置圖標的大小和顏色:

Icon(
  Icons.add,
  size: 40,
  color: Colors.red,
)

上面的黑色為默認大小和顏色。

Icons.add是系統提供的圖標,創建Flutter項目的時候,pubspec.yaml中默認有如下配置:

所有的圖標在Icons中已經定義,可以直接在源代碼中查看,也可以到官網查看所有圖標。

所有圖標效果如下:

案例

聊天背景(.9圖實現)

Container(
  width: 200,
  padding: EdgeInsets.only(left: 8,top: 8,right: 20,bottom: 8),
  decoration: BoxDecoration(
      image: DecorationImage(
          centerSlice: Rect.fromLTWH(20, 20, 1, 1),
          image: AssetImage(
            'assets/images/chat.png',
          ),
          fit: BoxFit.fill)),
  child: Text('老孟,專註分享Flutter技術和應用實戰。'
      '老孟,專註分享Flutter技術和應用實戰。'
      '老孟,專註分享Flutter技術和應用實戰。',),
)

背景圖片大小是57×80:

右側三角已經不在中間了,如果想讓其一直保持居中,修改拉伸區域:

centerSlice: Rect.fromLTWH(20, 10, 1, 60),

圓形帶邊框的頭像

Container(
  width: 100,
  height: 100,
  padding: EdgeInsets.all(3),
  decoration: BoxDecoration(shape: BoxShape.circle, color: Colors.blue),
  child: Container(
    decoration: BoxDecoration(
        shape: BoxShape.circle,
        image: DecorationImage(
            image: AssetImage('assets/images/aa.jpg'), fit: BoxFit.cover)),
  ),
)

圖片佔位符:

Image.network(
  'https://flutter.github.io/assets-for-api-docs/assets/widgets/puffin.jpg',
  height: 150,
  width: 150,
  fit: BoxFit.cover,
  frameBuilder: (
    BuildContext context,
    Widget child,
    int frame,
    bool wasSynchronouslyLoaded,
  ) {
    if (frame == null) {
      return Image.asset(
        'assets/images/place.png',
        height: 150,
        width: 150,
        fit: BoxFit.cover,
      );
    }
    return child;
  },
)

添加自己的圖標庫

如果系統提供的圖標沒有我們想要的圖標,這時需要引入第三方庫的圖標,下面以阿里巴巴的圖標庫為例。

打開阿里巴巴的圖標官網,找到自己想要的圖標后,將鼠標放置到圖標上,加入購物車:

點擊右上角的購物車,然後點擊添加至項目:

如果沒有添加過項目,需要創建一個新項目:

創建好后加入此項目,跳轉到我的項目頁面,點擊下載:

解壓下載的文件,解壓出來的文件有好幾個,如下圖:

選擇iconfont.ttf文件拷貝到 Flutter 項目的assets/fonts目錄下,assets/fonts目錄默認是沒有的,需要手動創建,在pubspec.yaml設置如下:

千萬注意紅框內開頭的空格問題,否則編譯不通過,family後面跟的字符串最好有意義,後面用圖標的時候需要用到。

用法如下:

Icon(IconData(0xe613,fontFamily: 'appIconFonts')

0xe613在下載圖標時已經標註,將&#替換為0,如下圖:

fontFamily是在pubspec.yaml中設置的family屬性,第三方的圖標和系統圖標一樣,可以設置其顏色和大小。

交流

老孟Flutter博客地址(330個控件用法):http://laomengit.com

歡迎加入Flutter交流群(微信:laomengit)、關注公眾號【老孟Flutter】:

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

【其他文章推薦】

※為什麼 USB CONNECTOR 是電子產業重要的元件?

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

※台北網頁設計公司全省服務真心推薦

※想知道最厲害的網頁設計公司"嚨底家"!

※推薦評價好的iphone維修中心

EnergyTrend微信新服務“太陽能價格評析” 立即關注!

EnergyTrend微信(簡中版)
只要在微信關注我們

EnergyTrend專業的分析師將直接告訴您
最即時的太陽能市場價格微評論

讓您一機在手隨時隨地輕鬆掌握太陽能市場最新動態

[加入關注]
1.微信→朋友→新增朋友→掃描QR Code→將右側QR Code置於方框內→關注
2.微信→朋友→新增朋友→查找官方帳戶→輸入”集邦新能源網”→關注

 

 

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

【其他文章推薦】

※為什麼 USB CONNECTOR 是電子產業重要的元件?

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

※台北網頁設計公司全省服務真心推薦

※想知道最厲害的網頁設計公司"嚨底家"!

※推薦評價好的iphone維修中心

空拍機驚嚇小熊可避免 論文提出技術與倫理建議:預警為要

環境資訊中心記者 鄭雅云報導

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

【其他文章推薦】

※為什麼 USB CONNECTOR 是電子產業重要的元件?

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

※台北網頁設計公司全省服務真心推薦

※想知道最厲害的網頁設計公司"嚨底家"!

※推薦評價好的iphone維修中心

Gogoro 宣布與新北市政府合作擴充電池交換站,擴充大台北充電網路

Gogoro 致力於電池交換型式的供電系統,提供比傳統機車更為綠能的選擇。13 日 Gogoro 宣布與新北市政府合作建置電池交換站,加上上個月台北市年底前建置的千座電池交換站,大台北地區將逐漸追上加油站數量。

Gogoro 在新北市於明年第一季前預計完成建置 20 座電池交換站、兩座太陽能電池交換站,建置範圍涵蓋三重區、三峽區、中和區、汐止區、板橋區、林口區、新店區、新莊區、樹林區、蘆洲區等新北都會區域。

Gogoro 營運副總潘璟倫表示:「目前雙北市平均每月電池交換次數高達近 28 萬次,為全台灣使用率最高的地區。為了加速提供車主的能源需求,Gogoro 不斷積極的佈建電池交換站以及尋找適合的地點,這次非常感謝新北市政府給予機會讓我們建置足夠的電池換電站提供消費者使用,希望透過這樣的合作持續帶動電動機車的發展。」

Gogoro 在台北市電池交換站已經超越傳統加油站的數量,而與台北市政府合作的 1,000 座電池交換站將拉大差距。本月在大台北地區新開台北天母忠誠、新北新店民權兩家門市。大台北地區將共有 35 家銷售服務據點同時提供完整的服務,完善台北地區服務網路。

配合開學季,Gogoro 也於 9/15 – 10/14 推試騎送限量「All Pass 文具組」,只要消費者到門市體驗試乘,即可獲得此試騎好禮。

(合作媒體:。首圖來源:)

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

【其他文章推薦】

※為什麼 USB CONNECTOR 是電子產業重要的元件?

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

※台北網頁設計公司全省服務真心推薦

※想知道最厲害的網頁設計公司"嚨底家"!

※推薦評價好的iphone維修中心

一時技癢,擼了個動態線程池,源碼放Github了

闡述背景

線程池在日常工作中用的還挺多,當需要異步,批量處理一些任務的時候我們會定義一個線程池來處理。

在使用線程池的過程中有一些問題,下面簡單介紹下之前遇到的一些問題。

場景一:實現一些批量處理數據的功能,剛開始線程池的核心線程數設的比較小,然後想調整下,只能改完后重啟應用。

場景二:有一個任務處理的應用,會接收 MQ 的消息進行任務的處理,線程池的隊列也允許緩存一定數量的任務。當任務處理的很慢的時候,想看看到底有多少沒有處理完不是很方便。當時為了快速方便,就直接啟動了一個線程去循環打印線程池隊列的大小。

正好之前在我公眾號有轉發過美團的一篇線程池應用的文章(https://mp.weixin.qq.com/s/tIWAocevZThfbrfWoJGa9w),覺得他們的思路非常好,就是沒有開放源碼,所以自己就抽時間在我的開源項目 Kitty 中增加了一個動態線程池的組件,支持了 Cat 監控,動態變更核心參數,任務堆積告警等。今天就給大家分享一下實現的方式。

項目源代碼地址:https://github.com/yinjihuan/kitty

使用方式

添加依賴

依賴線程池的組件,目前 Kitty 未發布,需要自己下載源碼 install 本地或者私有倉庫。

<dependency>
    <groupId>com.cxytiandi</groupId>
    <artifactId>kitty-spring-cloud-starter-dynamic-thread-pool</artifactId>
</dependency>

添加配置

然後在 Nacos 配置線程池的信息,我的這個整合了 Nacos。推薦一個應用創建一個單獨的線程池配置文件,比如我們這個叫 dataId 為 kitty-cloud-thread-pool.properties,group 為 BIZ_GROUP。

內容如下:

kitty.threadpools.nacosDataId=kitty-cloud-thread-pool.properties
kitty.threadpools.nacosGroup=BIZ_GROUP
kitty.threadpools.accessToken=ae6eb1e9e6964d686d2f2e8127d0ce5b31097ba23deee6e4f833bc0a77d5b71d
kitty.threadpools.secret=SEC6ec6e31d1aa1bdb2f7fd5eb5934504ce09b65f6bdc398d00ba73a9857372de00
kitty.threadpools.owner=尹吉歡
kitty.threadpools.executors[0].threadPoolName=TestThreadPoolExecutor
kitty.threadpools.executors[0].corePoolSize=4
kitty.threadpools.executors[0].maximumPoolSize=4
kitty.threadpools.executors[0].queueCapacity=5
kitty.threadpools.executors[0].queueCapacityThreshold=5
kitty.threadpools.executors[1].threadPoolName=TestThreadPoolExecutor2
kitty.threadpools.executors[1].corePoolSize=2
kitty.threadpools.executors[1].maximumPoolSize=4

nacosDataId,nacosGroup

監聽配置修改的時候需要知道監聽哪個 DataId,值就是當前配置的 DataId。

accessToken,secret

釘釘機器人的驗證信息,用於告警。

owner

這個應用的負責人,告警的消息中會显示。

threadPoolName

線程池的名稱,使用的時候需要關注。

剩下的配置就不一一介紹了,跟線程池內部的參數一致,還有一些可以查看源碼得知。

注入使用

@Autowired
private DynamicThreadPoolManager dynamicThreadPoolManager;
dynamicThreadPoolManager.getThreadPoolExecutor("TestThreadPoolExecutor").execute(() -> {
    log.info("線程池的使用");
    try {
        Thread.sleep(30000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}, "getArticle");

通過 DynamicThreadPoolManager 的 getThreadPoolExecutor 方法獲取線程池對象,然後傳入 Runnable,Callable 等。第二個參數是這個任務的名稱,之所以要擴展一個參數是因為如果任務沒有標識,那麼無法區分任務。

這個線程池組件默認集成了 Cat 打點,設置了名稱可以在 Cat 上查看這個任務相關的監控數據。

擴展功能

任務執行情況監控

在 Cat 的 Transaction 報表中會以線程池的名稱為類型显示。

詳情中會以任務的名稱显示。

核心參數動態修改

核心參數目前只支持 corePoolSize,maximumPoolSize,queueCapacity(隊列類型為 LinkedBlockingDeque 才可以修改),rejectedExecutionType,keepAliveTime,unit 這些參數的修改。

一般 corePoolSize,maximumPoolSize,queueCapacity 是最常要動態改變的。

需要改動的話直接在 Nacos 中將對應的配置值修改即可,客戶端會監聽配置的修改,然後同步修改先線程池的參數。

隊列容量告警

queueCapacityThreshold 是隊列容量告警的閥值,如果隊列中的任務數量超過了 queueCapacityThreshold 就會告警。

拒絕次數告警

當隊列容量滿了后,新進來的任務會根據用戶設置的拒絕策略去選擇對應的處理方式。如果是採用 AbortPolicy 策略,也會進行告警。相當於消費者已經超負荷了。

線程池運行情況

底層對接了 Cat,所以將線程的運行數據上報給了 Cat。我們可以在 Cat 中查看這些信息。

如果你想在自己的平台去展示,我這邊暴露了/actuator/thread-pool 端點,你可以自行拉取數據。

{
	threadPools: [{
		threadPoolName: "TestThreadPoolExecutor",
		activeCount: 0,
		keepAliveTime: 0,
		largestPoolSize: 4,
		fair: false,
		queueCapacity: 5,
		queueCapacityThreshold: 2,
		rejectCount: 0,
		waitTaskCount: 0,
		taskCount: 5,
		unit: "MILLISECONDS",
		rejectedExecutionType: "AbortPolicy",
		corePoolSize: 4,
		queueType: "LinkedBlockingQueue",
		completedTaskCount: 5,
		maximumPoolSize: 4
	}, {
		threadPoolName: "TestThreadPoolExecutor2",
		activeCount: 0,
		keepAliveTime: 0,
		largestPoolSize: 0,
		fair: false,
		queueCapacity: 2147483647,
		queueCapacityThreshold: 2147483647,
		rejectCount: 0,
		waitTaskCount: 0,
		taskCount: 0,
		unit: "MILLISECONDS",
		rejectedExecutionType: "AbortPolicy",
		corePoolSize: 2,
		queueType: "LinkedBlockingQueue",
		completedTaskCount: 0,
		maximumPoolSize: 4
	}]
}

自定義拒絕策略

平時我們使用代碼創建線程池可以自定義拒絕策略,在構造線程池對象的時候傳入即可。這裏由於創建線程池都被封裝好了,我們只能在 Nacos 配置拒絕策略的名稱來使用對應的策略。默認是可以配置 JDK 自帶的 CallerRunsPolicy,AbortPolicy,DiscardPolicy,DiscardOldestPolicy 這四種。

如果你想自定義的話也是支持的,定義方式跟以前一樣,如下:

@Slf4j
public class MyRejectedExecutionHandler implements RejectedExecutionHandler {
    @Override
    public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
        log.info("進來了。。。。。。。。。");
    }
}

要讓這個策略生效的話使用的是 SPI 的方式,需要在 resources 下面創建一個 META-INF 的文件夾,然後創建一個 services 的文件夾,再創建一個 java.util.concurrent.RejectedExecutionHandler 的文件,內容為你定義的類全路徑。

自定義告警方式

默認是內部集成了釘釘機器人的告警方式,如果你不想用也可以將其關閉。或者將告警信息對接到你的監控平台去。

如果沒有告警平台也可以在項目中實現新的告警方式,比如短信等。

只需要實現 ThreadPoolAlarmNotify 這個類即可。

/**
 * 自定義短信告警通知
 *
 * @作者 尹吉歡
 * @個人微信 jihuan900
 * @微信公眾號 猿天地
 * @GitHub https://github.com/yinjihuan
 * @作者介紹 http://cxytiandi.com/about
 * @時間 2020-05-27 22:26
 */
@Slf4j
@Component
public class ThreadPoolSmsAlarmNotify implements ThreadPoolAlarmNotify {
    @Override
    public void alarmNotify(AlarmMessage alarmMessage) {
        log.info(alarmMessage.toString());
    }
}

代碼實現

具體的就不講的很細了,源碼在https://github.com/yinjihuan/kitty/tree/master/kitty-dynamic-thread-pool,大家自己去看,並不複雜。

創建線程池

根據配置創建線程池,ThreadPoolExecutor 是自定義的,因為需要做 Cat 埋點。

/**
 * 創建線程池
 * @param threadPoolProperties
 */
private void createThreadPoolExecutor(DynamicThreadPoolProperties threadPoolProperties) {
    threadPoolProperties.getExecutors().forEach(executor -> {
        KittyThreadPoolExecutor threadPoolExecutor = new KittyThreadPoolExecutor(
                executor.getCorePoolSize(),
                executor.getMaximumPoolSize(),
                executor.getKeepAliveTime(),
                executor.getUnit(),
                getBlockingQueue(executor.getQueueType(), executor.getQueueCapacity(), executor.isFair()),
                new KittyThreadFactory(executor.getThreadPoolName()),
                getRejectedExecutionHandler(executor.getRejectedExecutionType(), executor.getThreadPoolName()), executor.getThreadPoolName());
        threadPoolExecutorMap.put(executor.getThreadPoolName(), threadPoolExecutor);
    });
}

刷新線程池

首先需要監聽 Nacos 的修改。

/**
 * 監聽配置修改,spring-cloud-alibaba 2.1.0版本不支持@NacosConfigListener的監聽
 */
public void initConfigUpdateListener(DynamicThreadPoolProperties dynamicThreadPoolProperties) {
    ConfigService configService = nacosConfigProperties.configServiceInstance();
    try {
        configService.addListener(dynamicThreadPoolProperties.getNacosDataId(), dynamicThreadPoolProperties.getNacosGroup(), new AbstractListener() {
            @Override
            public void receiveConfigInfo(String configInfo) {
                new Thread(() -> refreshThreadPoolExecutor()).start();
                log.info("線程池配置有變化,刷新完成");
            }
        });
    } catch (NacosException e) {
        log.error("Nacos配置監聽異常", e);
    }
}

然後再刷新線程池的參數信息,由於監聽事件觸發的時候,這個時候配置其實還沒刷新,所以我就等待了 1 秒鐘,讓配置完成刷新然後直接從配置類取值。

雖然有點挫還是可以用,其實更好的方式是解析 receiveConfigInfo 那個 configInfo,configInfo 就是改變之後的整個配置內容。因為不太好解析成屬性文件,就沒做,後面再改吧。

/**
 * 刷新線程池
 */
private void refreshThreadPoolExecutor() {
    try {
        // 等待配置刷新完成
        Thread.sleep(1000);
    } catch (InterruptedException e) {
    }
    dynamicThreadPoolProperties.getExecutors().forEach(executor -> {
        ThreadPoolExecutor threadPoolExecutor = threadPoolExecutorMap.get(executor.getThreadPoolName());
        threadPoolExecutor.setCorePoolSize(executor.getCorePoolSize());
        threadPoolExecutor.setMaximumPoolSize(executor.getMaximumPoolSize());
        threadPoolExecutor.setKeepAliveTime(executor.getKeepAliveTime(), executor.getUnit());
        threadPoolExecutor.setRejectedExecutionHandler(getRejectedExecutionHandler(executor.getRejectedExecutionType(), executor.getThreadPoolName()));
        BlockingQueue<Runnable> queue = threadPoolExecutor.getQueue();
        if (queue instanceof ResizableCapacityLinkedBlockIngQueue) {
            ((ResizableCapacityLinkedBlockIngQueue<Runnable>) queue).setCapacity(executor.getQueueCapacity());
        }
    });
}

其他的刷新都是線程池自帶的,需要注意的是線程池隊列大小的刷新,目前只支持 LinkedBlockingQueue 隊列,由於 LinkedBlockingQueue 的大小是不允許修改的,所以按照美團那篇文章提供的思路,自定義了一個可以修改的隊列,其實就是把 LinkedBlockingQueue 的代碼複製了一份,改一下就可以。

往 Cat 上報運行信息

往 Cat 的 Heartbeat 報表上傳數據的代碼如下,主要還是 Cat 本身提供了擴展的能力。只需要定時去調用下面的方式上報數據即可。

public void registerStatusExtension(ThreadPoolProperties prop, KittyThreadPoolExecutor executor) {
    StatusExtensionRegister.getInstance().register(new StatusExtension() {
        @Override
        public String getId() {
            return "thread.pool.info." + prop.getThreadPoolName();
        }
        @Override
        public String getDescription() {
            return "線程池監控";
        }
        @Override
        public Map<String, String> getProperties() {
            AtomicLong rejectCount = getRejectCount(prop.getThreadPoolName());
            Map<String, String> pool = new HashMap<>();
            pool.put("activeCount", String.valueOf(executor.getActiveCount()));
            pool.put("completedTaskCount", String.valueOf(executor.getCompletedTaskCount()));
            pool.put("largestPoolSize", String.valueOf(executor.getLargestPoolSize()));
            pool.put("taskCount", String.valueOf(executor.getTaskCount()));
            pool.put("rejectCount", String.valueOf(rejectCount == null ? 0 : rejectCount.get()));
            pool.put("waitTaskCount", String.valueOf(executor.getQueue().size()));
            return pool;
        }
    });
}

定義線程池端點

通過自定義端點來暴露線程池的配置和運行的情況,可以讓外部的監控系統拉取數據做對應的處理。

@Endpoint(id = "thread-pool")
public class ThreadPoolEndpoint {
    @Autowired
    private DynamicThreadPoolManager dynamicThreadPoolManager;
    @Autowired
    private DynamicThreadPoolProperties dynamicThreadPoolProperties;
    @ReadOperation
    public Map<String, Object> threadPools() {
        Map<String, Object> data = new HashMap<>();
        List<Map> threadPools = new ArrayList<>();
        dynamicThreadPoolProperties.getExecutors().forEach(prop -> {
            KittyThreadPoolExecutor executor = dynamicThreadPoolManager.getThreadPoolExecutor(prop.getThreadPoolName());
            AtomicLong rejectCount = dynamicThreadPoolManager.getRejectCount(prop.getThreadPoolName());
            Map<String, Object> pool = new HashMap<>();
            Map config = JSONObject.parseObject(JSONObject.toJSONString(prop), Map.class);
            pool.putAll(config);
            pool.put("activeCount", executor.getActiveCount());
            pool.put("completedTaskCount", executor.getCompletedTaskCount());
            pool.put("largestPoolSize", executor.getLargestPoolSize());
            pool.put("taskCount", executor.getTaskCount());
            pool.put("rejectCount", rejectCount == null ? 0 : rejectCount.get());
            pool.put("waitTaskCount", executor.getQueue().size());
            threadPools.add(pool);
        });
        data.put("threadPools", threadPools);
        return data;
    }
}

Cat 監控線程池中線程的執行時間

本來是將監控放在 KittyThreadPoolExecutor 的 execute,submit 方法里的。後面測試下來發現有問題,數據在 Cat 上確實有了,但是執行時間都是 1 毫秒,也就是沒生效。

不說想必大家也知道,因為線程是後面單獨去執行的,所以再添加任務的地方埋點沒任務意義。

後面還是想到了一個辦法來實現埋點的功能,就是利用線程池提供的 beforeExecute 和 afterExecute 兩個方法,在線程執行之前和執行之後都會觸發這兩個方法。

@Override
protected void beforeExecute(Thread t, Runnable r) {
    String threadName = Thread.currentThread().getName();
    Transaction transaction = Cat.newTransaction(threadPoolName, runnableNameMap.get(r.getClass().getSimpleName()));
    transactionMap.put(threadName, transaction);
    super.beforeExecute(t, r);
}
@Override
protected void afterExecute(Runnable r, Throwable t) {
    super.afterExecute(r, t);
    String threadName = Thread.currentThread().getName();
    Transaction transaction = transactionMap.get(threadName);
    transaction.setStatus(Message.SUCCESS);
    if (t != null) {
        Cat.logError(t);
        transaction.setStatus(t);
    }
    transaction.complete();
    transactionMap.remove(threadName);
}

後面的代碼大家自己去看就行了,本文到這裏就結束了。如果感覺本文還不錯的記得轉發下哦!

多謝多謝。

最後感謝美團技術團隊的那篇文章,雖然沒有分享源碼,但是思路什麼的和應用場景都講的很明白。

感興趣的 Star 下唄:https://github.com/yinjihuan/kitty

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

【其他文章推薦】

※為什麼 USB CONNECTOR 是電子產業重要的元件?

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

※台北網頁設計公司全省服務真心推薦

※想知道最厲害的網頁設計公司"嚨底家"!

※推薦評價好的iphone維修中心

載不動聖誕老人 瑞典夏季乾旱 部分馴鹿餓死

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

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

【其他文章推薦】

※為什麼 USB CONNECTOR 是電子產業重要的元件?

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

※台北網頁設計公司全省服務真心推薦

※想知道最厲害的網頁設計公司"嚨底家"!

※推薦評價好的iphone維修中心

Python 為什麼推薦蛇形命名法?

關於變量的命名,這又是一個容易引發程序員論戰的話題。如何命名才能更具有可讀性、易寫性與明義性呢?眾說紛紜。

本期“Python為什麼”欄目,我們將聚焦於變量命名中的連接方式,來切入這塊是非之地,想要回答的問題是——Python 為什麼要推薦蛇形命名法?

首先一點,對於單個字符或者單詞 (例如:a、A、PYTHON、Cat),當它們被用作變量名時,大致有全小寫、全大寫和首字母大寫這幾種情況。編程語言中出現這些情況時,它們基本上跟英語的表達習慣是相同的。

但是,編程語言為了令變量名表達出更豐富的含義,通常需要使用多個單詞或符號。 英語習慣使用空格來間隔開單詞,然而這種用法在編程語言中會帶來一些麻煩,所以程序員們就創造出了另外的方法:

  • 蛇形命名法(snake case)
  • 駝峰命名法(camel case)
  • 匈牙利命名法(HN case)
  • 帕斯卡命名法(Pascal case)
  • 脊柱命名法(spinal case)
  • 自由命名法(studly caps)
  • 駝峰蛇形命名法

總體而言,這些命名法都是要克服單詞間的空格,從而把不同單詞串連起來, 最終達到創造出一種新的“單詞”的效果。

我畫了一張思維導圖,大略區分了這幾種命名法:

如果按照受眾量與知名程度排名,毫無疑問排前兩位的是駝峰命名法和蛇形命名法。

我們可以簡單比較一下它們的優缺點:

  • 可讀性:蛇形命名法用下劃線拉大詞距,更清楚易讀;駝峰命名法的變量名緊湊,節省行寬
  • 易寫性:駝峰命名法以大小寫為區分,不引入額外的標識符;蛇形命名法統一小寫,輸入相對方便
  • 明義性:對於某些縮寫成的專有名詞,例如 HTTP、RGB、DNS等等,一般習慣全用大寫表示,但是如果嚴格遵循這兩種命名法的話,須得只留首字母大寫或者全小寫,這樣對原意都會造成一些“破壞”,有時候甚至讓人感覺到彆扭。如果保留全大寫,IDE 可能識別不準,反而會出現波浪提示

由此可見,它們各有優缺點,但哪一方都不具有壓倒性。我個人稍微偏好於蛇形命名法,但是在需要用駝峰命名的時候(比如寫 Java 時),也能無障礙切換。

需要指出的是,Python 也推薦使用駝峰式命名,那是在類名、Type 變量、異常 exception 名這些情況。而在包名、模塊名、方法名和普通變量名 等情況,則是推薦用蛇形命名(lower_case_with_underscores)。

那麼,為什麼 Python 會推薦用蛇形命名法呢?

最大的原因是歷史原因。 蛇形命名方式起源於 1960 年代,那時它甚至還沒有特定的名稱。Python 從 C 語言中借鑒過來后,給它起名為“lower_case_with_underscores”,即帶下劃線的小寫命名。

直到 21 世紀初的幾年,在 Intel 和 Ruby 社區中,才有人開始以“snake_case”即蛇形命名來稱呼它。

現今有不少編程語言在某些場景下會推薦使用蛇形命名法,而 Python 則是其中最早這麼做的之一,並且是使用場景最多的語言之一。

維基百科上統計了一份清單,可以看出 Python 對它的偏好:

其次,還有一個比較重要的原因,那就是 Python 對下劃線“_”的獨特偏愛。

比如類似於 _xx、__xx、xx_、__xx__ 等等的寫法就隨處可見,甚至還有孤零零一個下劃線 _ 作為變量的特殊情況。這樣看來,下劃線作為單詞間的連接,恰恰是這種傳統習慣的一部分。

最後,我還看到過一種解釋:因為 Python 是蟒蛇啊,理所當然是用蛇形命名……

對於這三個解釋,你們是如何感想的呢?對於蛇形命名法,大家是喜歡還是不喜歡呢?歡迎留言交流。

寫在最後:本文屬於“Python為什麼”系列(Python貓出品),該系列主要關注 Python 的語法、設計和發展等話題,以一個個“為什麼”式的問題為切入點,試着展現 Python 的迷人魅力。部分話題會推出視頻版,請在 B 站收看,觀看地址:視頻地址

公眾號【Python貓】, 本號連載優質的系列文章,有Python為什麼系列、喵星哲學貓系列、Python進階系列、好書推薦系列、技術寫作、優質英文推薦與翻譯等等,歡迎關注哦。

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

【其他文章推薦】

※為什麼 USB CONNECTOR 是電子產業重要的元件?

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

※台北網頁設計公司全省服務真心推薦

※想知道最厲害的網頁設計公司“嚨底家”!

※推薦評價好的iphone維修中心

聚甘新

福斯拼2025年躍居電動車龍頭、砸500億歐元採購電池

  福斯集團(Volkswagen Group)11月18日宣布,2018~2022年期間旗下核心品牌將在全球投資228億歐元、當中有140億歐元將會投入德國地區工廠。位於德國茲威考(Zwickau)的工廠將獲得10億歐元的挹注、藉此轉型為純電動車生產線。   福斯預估2025年旗下電動車產量至少可達100萬輛。採用MEB(Modular Electric Drive Kit)模組化電動車平台技術的電動車續航力將介於400~600公里之間。首款搭載MEB的Volkswagen I.D.預計在2020年發表、當年產量預估將達10萬輛。   福斯集團11月17日宣布,2018~2022年期間集團將投入340億歐元在電動移動、自主駕駛、新型移動服務以及數位化等領域。福斯9月宣布,2030年底以前旗下所有車款都將具備電動驅動選項。   福斯執行長Matthias Muller表示,上述投資將為集團在2025年躍居全球最大電動車廠商奠定基礎。Muller提到,未來數年整個汽車業將面臨根本性的變化。 為了滿足對電池產能的巨大需求,福斯集團已啟動了旨在建立長期戰略合作夥伴關係的全球(包括中國在內)招標程序、金額預估將超過500億歐元。   福斯集團預期巴西、中國、俄羅斯以及北美將會是未來數年的主要成長來源。   福斯汽車公司(中國)11月16日宣布,從現在到2025年這段期間公司與合資企業夥伴將投資逾100億歐元在電動移動工業化領域。   Thomson Reuters報導,英國財政部19日表示,財長Philip Hammond將在11月22日公布的政府預算書當中宣布提供總額達1.0億英鎊的電動車購買補助金。   (本文內容由授權使用。首圖來源:pixabay)  

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

【其他文章推薦】

※為什麼 USB CONNECTOR 是電子產業重要的元件?

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

※台北網頁設計公司全省服務真心推薦

※想知道最厲害的網頁設計公司“嚨底家”!

※推薦評價好的iphone維修中心

聚甘新

電動巴士開進太魯閣,花縣繼續推動綠能公車政策

花蓮縣太魯閣客運公司今年繼續辦理增資,以採購新的電動巴士。自9月起,往來於花蓮市區、太魯閣、東華大學等地的公車路線,將有電動巴士陸續開始運行。

花蓮縣政府的「綠能公車」政策於2014年核定太魯閣客運公司的301線、302線兩條公車路線上路,且須採用電動巴士運行。一開始因電動巴士品質不夠穩定,一度改租用柴油巴士,但受到反彈而停駛;之後在地方民代的協調下,301、302線規劃復駛,並透過增資採購7輛低底盤K9電動巴士車輛,重新展開電動巴士服務。

太魯閣客運公司經理表示,本次採購的7輛K9電動巴士為台灣凱勝綠能科技所生產,車體採用全鋁合金打造,並配有緊急自動滅火系統、ABS防鎖剎車等。車輛搭載的電池為台灣長利科技的磷酸鋰鐵電池,每次充飽電的續航力可達250公里以上。

K9巴士的性能佳,目前在苗栗客運、捷順交通、雲林客運、南台灣客運等公司都有同款車輛正在提供服務。太魯閣客運採購K9巴士時,為挑戰巴士的續航力,還安排一輛K9巴士直接從苗栗車體廠繞過北台灣、蘇花公路一路行駛到花蓮東華大學,總里程320.7公里,全程開冷氣;駛達目的地後,電池的電量還有43.9%之多。

目前,301線已配合東華大學開學而正式復駛。太魯閣客運希望新城到天祥的302線也能在年底前復駛,預計將有6輛電動巴士投入服役。

(照片來源:東華大學/太魯閣客運)

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

【其他文章推薦】

※為什麼 USB CONNECTOR 是電子產業重要的元件?

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

※台北網頁設計公司全省服務真心推薦

※想知道最厲害的網頁設計公司“嚨底家”!

※推薦評價好的iphone維修中心

聚甘新

spring源碼分析——BeanPostProcessor接口

 

  BeanPostProcessor是處理bean的後置接口,beanDefinitionMaps中的BeanDefinition實例化完成后,完成populateBean,屬性設置,完成

初始化后,這個接口支持對bean做自定義的操作。

一:BeanPostProcessor的使用

定義一個測試用的model對象,name屬性默認為hello

public class BeanDemo {

	private String name = "hello";

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	@Override
	public String toString() {
		final StringBuffer sb = new StringBuffer("BeanDemo{");
		sb.append("name='").append(name).append('\'');
		sb.append('}');
		return sb.toString();
	}
}

  

自定義一個MyBeanPostProcessor類,實現BeanPostProcessor接口

@Service
public class MyBeanPostProcessor implements BeanPostProcessor {

	public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
		return null;
	}

	public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
		if(beanName.equals("beanDemo")){
			BeanDemo beanDemo = (BeanDemo)bean;
			beanDemo.setName("kitty");
			return beanDemo;
		}
		return bean;
	}
}

  

 

 

從運行結果看,spring中維護的beanName為beanDemo的對象,name屬性為ketty

 

 

二:看看源碼怎麼實現的

1:實例化並且註冊到beanPostProcessors集合中

 

 

 

 

主要的實例化邏輯在這個接口,這個接口的作用就是把所有實現BeanPostProcessor接口的類實例化,然後註冊到 beanPostProcessors這個緩存中

 

 

	public static void registerBeanPostProcessors(
			ConfigurableListableBeanFactory beanFactory, AbstractApplicationContext applicationContext) {

		// 獲取所有實現接口BeanPostProcessor的beanName
		String[] postProcessorNames = beanFactory.getBeanNamesForType(BeanPostProcessor.class, true, false);

		// Register BeanPostProcessorChecker that logs an info message when
		// a bean is created during BeanPostProcessor instantiation, i.e. when
		// a bean is not eligible for getting processed by all BeanPostProcessors.
		int beanProcessorTargetCount = beanFactory.getBeanPostProcessorCount() + 1 + postProcessorNames.length;
		beanFactory.addBeanPostProcessor(new BeanPostProcessorChecker(beanFactory, beanProcessorTargetCount));

		// Separate between BeanPostProcessors that implement PriorityOrdered,
		// Ordered, and the rest.
		/**
		 * 把實現PriorityOrdered 和 Ordered 和 其他的處理器分開
		 */
		List<BeanPostProcessor> priorityOrderedPostProcessors = new ArrayList<>();
		List<BeanPostProcessor> internalPostProcessors = new ArrayList<>();
		List<String> orderedPostProcessorNames = new ArrayList<>();
		List<String> nonOrderedPostProcessorNames = new ArrayList<>();
		/**
		 * 1:遍歷集合postProcessorNames
		 * 2:判斷類型,如果是PriorityOrdered,則實例化對象放入priorityOrderedPostProcessors集合,
		 * Ordered 則放入orderedPostProcessorNames集合,其他的放入nonOrderedPostProcessorNames集合
 		 */
		for (String ppName : postProcessorNames) {
			if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
				BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);
				priorityOrderedPostProcessors.add(pp);
				if (pp instanceof MergedBeanDefinitionPostProcessor) {
					internalPostProcessors.add(pp);
				}
			}
			else if (beanFactory.isTypeMatch(ppName, Ordered.class)) {
				orderedPostProcessorNames.add(ppName);
			}
			else {
				nonOrderedPostProcessorNames.add(ppName);
			}
		}

		// First, register the BeanPostProcessors that implement PriorityOrdered.
		// 首先對priorityOrderedPostProcessors集合中實例對象排序,然後註冊,放入beanFactory中緩存下來
		sortPostProcessors(priorityOrderedPostProcessors, beanFactory);
		registerBeanPostProcessors(beanFactory, priorityOrderedPostProcessors);

		// Next, register the BeanPostProcessors that implement Ordered.
		// 然後再實例化實現Ordered接口的對象,完成註冊
		List<BeanPostProcessor> orderedPostProcessors = new ArrayList<>();
		for (String ppName : orderedPostProcessorNames) {
			BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);
			orderedPostProcessors.add(pp);
			if (pp instanceof MergedBeanDefinitionPostProcessor) {
				internalPostProcessors.add(pp);
			}
		}
		sortPostProcessors(orderedPostProcessors, beanFactory);
		registerBeanPostProcessors(beanFactory, orderedPostProcessors);

		// Now, register all regular BeanPostProcessors.
		// 最後實例化什麼都沒有實現的,完成實例化並註冊
		List<BeanPostProcessor> nonOrderedPostProcessors = new ArrayList<>();
		for (String ppName : nonOrderedPostProcessorNames) {
			BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);
			nonOrderedPostProcessors.add(pp);
			if (pp instanceof MergedBeanDefinitionPostProcessor) {
				internalPostProcessors.add(pp);
			}
		}
		registerBeanPostProcessors(beanFactory, nonOrderedPostProcessors);

		// Finally, re-register all internal BeanPostProcessors.
		// 最後再次註冊內部postProcessor
		sortPostProcessors(internalPostProcessors, beanFactory);
		registerBeanPostProcessors(beanFactory, internalPostProcessors);

		// Re-register post-processor for detecting inner beans as ApplicationListeners,
		// moving it to the end of the processor chain (for picking up proxies etc).
		beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(applicationContext));
	}

  

 

 

定義四類容器,高優先級有序、有序、無序、內部

 

 分類放入四種容器:

 

 

註冊BeanPostProcessor,將實現BeanPostProcessor接口的對象放入beanPostProcessors緩存中

 

 

 

 

 

 

註冊完PriorityOrdered的實現類后,再處理Ordered的實現類

 

 

註冊什麼都沒有實現的BeanPostProcessor接口實現類,

 

 

最後註冊內部的BeanPostProcessor對象

 

 到這裏BeanPostProcessor的實例化以及註冊工作完成,在beanFactory的beanPostProcessors集合中已經緩存了所有的beanPostProcessor的對象

 

2:BeanPostProcessor的使用

因為這個接口是bean的後置接口,所以需要bean創建並初始化完成,才可以發揮作用,上一步的緩存只是埋好點,以備使用,因為bean的實例化流程我們

還沒有分析,這裏直接看一下怎麼使用的

 

 

我們看一下init方法后的攔截,因為這個時候已經init完成,可以在後置接口中對bean做一下修改的操作

 

 

調用到我們自定義的MyBeanPostProcessor實現類:

 

 

把這個beanDemo對象屬性修改一下,修改完,再返回,將這個對象緩存到spring的一級緩存中。

 

 

總結:

  BeanPostProcessor接口主要是對bean對象做一些自定義的操作,修改bean對象的信息,aop代理也是通過這種方式實現的,

在refresh的registryBeanPostProcessor方法中實例化BeanPostProcessor對象,並且註冊到beanFactory容器的beanPostProcessors的緩存中,

然後在後續的操作中攔截使用。

 

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

【其他文章推薦】

※為什麼 USB CONNECTOR 是電子產業重要的元件?

網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

※台北網頁設計公司全省服務真心推薦

※想知道最厲害的網頁設計公司“嚨底家”!

※推薦評價好的iphone維修中心

聚甘新