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

Spring 進階:減少不必要的介面編程 —— 使用 CGLIB 代理實現無介面 Service 事務管理

     編輯:Java 相關知識

痛點:介面泛濫的 Service 層

Spring 提倡「面向介面編程」,但實際專案中許多 Service 類(如 SaleOrderManager / SaleOrderManagerImpl)極少有第二種實現。強行抽取介面反而增加維護成本,類似 C++ 的 .h/.cpp 分離卻無多態需求。

核心原理:Spring AOP 的代理機制

  • JDK 動態代理:需實現介面,生成實現同介面的代理類。
  • CGLIB:針對類生成子類,攔截非 final 方法,無需介面。

Spring 宣告式事務(@Transactional 或 XML ``)底層使用 Spring AOP。預設對介面用 JDK 代理,對類用 CGLIB。關鍵在於 強制啟用 CGLIB。

解決方案:配置 proxy-target-class="true"

XML 配置範例(TransactionProxyFactoryBean 父 Bean)

<bean id="baseService" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean" abstract="true">
    <property name="transactionManager">
        <ref bean="transactionManager"/>
    </property>
    <property name="proxyTargetClass">
        <value>true</value> <!-- 關鍵:強制使用 CGLIB -->
    </property>
    <property name="transactionAttributes">
        <props>
            <prop key="save*">PROPAGATION_REQUIRED</prop>
            <prop key="update*">PROPAGATION_REQUIRED</prop>
            <prop key="delete*">PROPAGATION_REQUIRED</prop>
            <prop key="*">PROPAGATION_REQUIRED,readOnly</prop>
        </props>
    </property>
</bean>

<!-- 具體 Service 繼承父 Bean,無需實現介面 -->
<bean id="saleOrderManager" parent="baseService">
    <property name="target">
        <bean class="com.example.service.SaleOrderManager">
            <property name="dao" ref="saleOrderDao"/>
        </bean>
    </property>
</bean>

現代 Spring(3.0+)@EnableTransactionManagement + @Transactional 方式

@Configuration
@EnableTransactionManagement(proxyTargetClass = true) // Java Config 強制 CGLIB
public class TxConfig { ... }

@Service // 直接標註在具體類上,無介面
@Transactional
public class SaleOrderManager {
    @Autowired private SaleOrderDao dao;
    public void saveOrder(Order o) { dao.save(o); }
}

效能考量

Service Bean 皆為單例,CGLIB 生成子類僅在容器啟動時一次,運行期方法攔截開銷極低(FastClass 機制),不構成效能瓶頸。反而減少介面檔案、簡化導航,提升開發體驗。

注意事項與最佳實踐

  • final 方法無法攔截:CGLIB 通過繼承覆寫方法實現 AOP,final、private、static 方法不受事務管理。
  • 構造器注入:配合 CGLIB 代理時,目標類需有無參構造器或使用 @Autowired 構造器(Spring 4.3+ 自動識別)。
  • 序列化:CGLIB 代理實現 Serializable 需目標類也實現,且注意 serialVersionUID。
  • 保留介面的場景:多實現策略、遠程服務(RMI/Hessian)、SPI 擴展點、跨模組契約,仍建議定義介面。

總結

合理使用 proxyTargetClass=true,可在不犧牲 Spring AOP 功能(事務、安全、快取)前提下,大幅減少樣板式介面,讓 Service 層回歸「簡單類」本質。這是務實 Spring 開發的重要技巧之一。

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