首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

ActiveRecord 回調與觀察者模式:參與對象生命週期的 20 個鉤子與 AOP 實踐

     編輯:Java 相關知識

回調:生命週期的切面編程

ActiveRecord 定義了 20 個回調,覆蓋對象從誕生到銷毀的全過程:

  • 18 個成對:before_* / after_* / around_*
  • 2 個特殊:after_find / after_initialize(無 before 對應)

回調生命週期圖

回調執行順序(以創建為例)

# 完整鏈
before_validation → after_validation
before_save → around_save → after_save
before_create → around_create → after_create
after_commit / after_rollback(事務提交/回滾後)

# 更新時替換 create 為 update
# 銷毀時替換為 destroy

兩種定義方式

1. 實例方法(簡單、私有邏輯)

class Order < ActiveRecord::Base
  def after_save
    self.payment_due ||= Time.now + 30.days
  end
end

2. 聲明式處理器(推薦:復用、排序、條件、可測試)

class Order < ActiveRecord::Base
  before_validation :normalize_credit_card_number
  after_create { |order| logger.info "Order #{order.id} created" }
  after_update :send_confirmation_email, if: :shipped?
  
  protected
  def normalize_credit_card_number
    self.cc_number.gsub!(/[-\s]/, '')
  end
  
  def shipped?
    status == 'shipped'
  end
end
  • 多個處理器按聲明順序執行,任一返回 false 中斷鏈(before_* 還會阻止後續動作)。
  • around_* 接收 block,需手動 yield 執行核心動作。

特殊回調:after_find / after_initialize

為性能考慮,這兩個回調只能定義為實例方法,聲明式方式會被忽略。且必須在類中顯式定義(哪怕是空方法)才會觸發:

class Order < ActiveRecord::Base
  after_find :log_access
  
  def after_find      # 必須聲明,哪怕為空
    # 這裡可以不寫邏輯,僅為激活回調機制
  end
  
  private
  def log_access
    logger.debug "Order #{id} loaded"
  end
end

回調分組復用:提取共享邏輯

class CreditCardCallbacks
  def before_validation(model)
    model.cc_number.gsub!(/[-\s]/, '')
  end
end

class Order < ActiveRecord::Base
  before_validation CreditCardCallbacks.new
end

class Subscription < ActiveRecord::Base
  before_validation CreditCardCallbacks.new
end

處理器類只需實現對應回調方法名,即可跨模型復用。要求模型擁有同名屬性(cc_number)。

實戰模式:透明加密/解密

class Encrypter
  def initialize(attrs)
    @attrs = attrs
  end
  
  def before_save(model)
    @attrs.each { |f| model[f].tr!('a-zA-Z', 'b-zaB-ZA') }
  end
  
  def after_save(model)
    @attrs.each { |f| model[f].tr!('b-zaB-ZA', 'a-zA-Z') }
  end
  
  alias_method :after_find, :after_save
end

class Order < ActiveRecord::Base
  encrypter = Encrypter.new([:name, :email])
  before_save encrypter
  after_save encrypter
  after_find encrypter
  def after_find; end  # 激活 after_find
end

擴展 Base 實現聲明式宏

class ActiveRecord::Base
  def self.encrypt(*attrs)
    encrypter = Encrypter.new(attrs)
    before_save encrypter
    after_save encrypter
    after_find encrypter
    define_method(:after_find) { }
  end
end

class Order < ActiveRecord::Base
  encrypt :name, :email
end

Observer:橫切關注點徹底解耦

回調易導致模型承擔過多副作用(發郵件、寫日志、清緩存、觸發 Webhook)。Observer 透明注入行為,零修改模型代碼。

基本用法

class OrderObserver < ActiveRecord::Observer
  def after_save(order)
    order.logger.info "Order #{order.id} created"
  end
end

# 約定:OrderObserver 自動觀察 Order 模型

指定觀察多模型

class AuditObserver < ActiveRecord::Observer
  observe Order, Payment, Refund
  
  def after_save(model)
    model.logger.info "[Audit] #{model.class} #{model.id} created"
  end
end

激活 Observer

# config/application.rb
config.active_record.observers = :order_observer, :audit_observer

# 或單獨應用中手動實例化
OrderObserver.instance
AuditObserver.instance
  • 約定放在 app/models 目錄。
  • 觀察者實例化後才生效,適合插件化架構。

回調 vs Observer 選擇指南

場景推薦理由
數據標準化/格式化/衍生屬性計算回調屬於模型核心不變量,內聚性高
發送通知郵件/推送Observer / Service Object副作用、可失敗、需重試、屬於用例編排
審計日志/安全記錄Observer橫切所有模型,透明無侵入
緩存失效/搜索索引更新Observer / after_commit需在事務提交後執行,避免髒讀
第三方系統同步Service Object + 異步隊列耦合外部依賴,不屬於模型職責

性能與避坑提示

  1. 避免在回調中觸發額外查詢:after_save { UserMailer.welcome(self).deliver_later } 改用異步。
  2. after_commit / after_rollback 替代 after_save 做副作用:確保事務已提交,避免回滾時副作用已執行。
  3. 大量導入數據時跳過回調:User.import(records, validate: false, callbacks: false)(需 activerecord-import gem)。
  4. 測試中驗證回調調用:expect(order).to receive(:normalize_credit_card_number)。

總結

ActiveRecord 回調提供了強大的生命週期介入能力,但「大權在手,責任在肩」:

  1. 掌握 20 個回調的觸發順序與語義。
  2. 優先用聲明式處理器,支持復用、條件、排序。
  3. after_find/after_initialize 必須定義實例方法激活。
  4. 將副作用(IO、外部調用)遷移至 Observer 或 Service Object,保持模型純粹。
  5. 利用 after_commit 確保事務一致性邊界。

「回調是模型的內部事務,Observer 是系統的橫切關注點。」劃清界限,方得始終。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved