[續]使用 Scala Macro Annotation 實現配置項綁定

v0.1.2

上篇, 因 @連城404 的轉發原文:git

@連城404:頗有趣,有可能把 @conf val port = 0 中的 "= 0" 去掉換成現實的類型說明嗎?即 @conf port: Int 這個賦值在此處徹底沒有實際意義,更像是實現限制而引入的噪聲。github

class Server {
  @conf val port = 0 
  //  上面的賦值閒得多餘, 能夠寫成:
  // @conf val port: Int
}

確實沒有必要, 但意義仍是有的: IDE 不會報錯. (至少我用的IDEA 14 的 scala plugin 還不支持 Scala Macro)segmentfault

= 0 能夠去掉嗎?post

答案是能夠, 只是在被 @連城404 問到的時候我尚未想到辦法.scala

解決辦法方法其實不難, 前提是要對 Scala AST 很熟悉.設計

經查:code

case class ValDef(mods: Modifiers, name: TermName, tpt: Tree, rhs: Tree)

Modifiers 中有個 flags 屬性, 其中標記了val port 的各類特徵.對象

那麼 val port: Intval port = 0 之間的區別則能夠從 flags 中體現出來.blog

class ModifierFlags {
  final val DEFERRED      = 1 << 4        // was `abstract' for members | trait is virtual
  final val DEFAULTINIT   = 1L << 41      // symbol is initialized to the default value: used by -Xcheckinit

  ...
}

ModifierFlags 裏定義了全部的 flags 值, 這裏摘錄其中兩個枚舉則對應上面兩種不一樣的寫法.字符串

具體實現的代碼的提交, 請見0475d92c.

如何發現這些細節 ?

  • print(x), 探查未知對象的展示信息, 多半可以提供重要線索;
  • print(x.getClass), 上面的辦法無論用, 則須要查明它的類型, 而後去翻源碼了.

v0.2.0

上面 v0.1.x 是一種基於ValDef 的聲明風格, 實際使用的時候會發現:

編寫簡單的同時, 代價是類名定義要與配置路徑要嚴格一致, 這並不是適合全部場景

好比寫個 Kafka 的消費客戶端, 要爲它設計其訪問服務的配置項

kafka_broker {
  host = 10.0.0.1
  port = 12306
}

此時, 對應的類名就要是KafkaBroker, 這顯然違背了設計意圖, 像 KafkaConsumer 纔是最爲天然的選擇.

與其大量時間糾結於命名上, 不如換個更好的聲明方式.

@conf 帶參數能夠更改默認配置項路徑 :

class KafkaConsumer {
  @conf("kafka_broker.host") val host: String
}

嗯, 不錯, 可以解決問題. 只是以爲像繞了一圈又回去了的感受:

class KafkaConsumer {
  val host = conf.getString("kafka_broker.host")
}

對比這兩種寫法, 前者彷佛沒什麼優點了.

回顧用 Macro Annotation 解決配置項綁定的初衷, 是但願 將代碼的元信息與配置項創建映射關係, 減小因中間字符串的 Hard Code 爲重構帶來的額外成本.

爲此, 另外一種寫法誕生了, 它是受這篇 Adding Reflection to Scala Macros 的啓發.

其中的細節請見 v0.2.0README, 就不累述於此了.

這種新寫法還有個好處, 就是可以方便經過從代碼來生成配置文件, 轉義的代價很小.

嗯...再寫個 sbt plugin 吧 :)

相關文章
相關標籤/搜索