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

Spring AOP 實戰入門:從 ProxyFactory 到聲明式事務的完整演進

     編輯:Java 相關知識

AOP 核心概念速覽

術語定義本文對應
Join Point程式執行點 (方法呼叫、異常拋出...)方法執行
Pointcut切點運算式,選擇 Join Point 集合.*print.*
Advice切面邏輯 (Before/After/Around/Throws)PrintBeforeAdvice
AspectPointcut + Advice 模組化Advisor 封裝
Target被代理對象FooImpl
Proxy代理對象,攔截請求並織入 AdviceProxyFactory.getProxy()
Weaving將 Advice 植入 Target 的過程運行期動態代理

步驟 1:編程式 ProxyFactory (理解原理)

// 目標介面與實現
public interface FooInterface { void printFoo(); void dummyFoo(); }
public class FooImpl implements FooInterface {
    public void printFoo() { System.out.println("In FooImpl.printFoo"); }
    public void dummyFoo() { System.out.println("In FooImpl.dummyFoo"); }
}

// Before Advice
public class PrintBeforeAdvice implements MethodBeforeAdvice {
    public void before(Method m, Object[] args, Object target) {
        System.out.println("In PrintBeforeAdvice");
    }
}

// 組裝代理
public class AopTestMain {
    public static void main(String[] args) {
        FooImpl target = new FooImpl();
        PrintBeforeAdvice advice = new PrintBeforeAdvice();
        ProxyFactory factory = new ProxyFactory(target);
        factory.addAdvice(advice); // 預設為 Before
        FooInterface proxy = (FooInterface) factory.getProxy();
        proxy.printFoo();
        proxy.dummyFoo();
    }
}
// 輸出:
// In PrintBeforeAdvice
// In FooImpl.printFoo
// In PrintBeforeAdvice
// In FooImpl.dummyFoo

缺點:代碼耦合、難維護、非聲明式。

步驟 2:Spring XML 聲明式配置 (最佳實踐)

<beans xmlns="https://www.springframework.org/schema/beans"
       xmlns:aop="https://www.springframework.org/schema/aop">

  <!-- 目標 Bean -->
  <bean id="fooTarget" class="com.example.FooImpl"/>

  <!-- Advice Bean -->
  <bean id="printBeforeAdvice" class="com.example.PrintBeforeAdvice"/>

  <!-- ProxyFactoryBean (Spring 1.x/2.x 風格) -->
  <bean id="foo" class="org.springframework.aop.framework.ProxyFactoryBean">
    <property name="target" ref="fooTarget"/>
    <property name="interceptorNames">
      <list><value>printBeforeAdvice</value></list>
    </property>
  </bean>

  <!-- 或 Schema-based (Spring 2.0+ 推薦) -->
  <aop:config>
    <aop:advisor advice-ref="printBeforeAdvice" pointcut="execution(* printFoo())"/>
  </aop:config>
</beans>

步驟 3:引入 Advisor 實現精準切點

<aop:config>
  <aop:pointcut id="printMethods" expression="execution(* *print*())"/>
  <aop:advisor advice-ref="printBeforeAdvice" pointcut-ref="printMethods"/>
</aop:config>

結果:僅 printFoo() 觸發 Advice,dummyFoo() 不觸發。

步驟 4:@AspectJ 註解風格 (現代標準)

@Aspect
@Component
public class LoggingAspect {
    @Before("execution(* com.example..*print*())")
    public void logBefore(JoinPoint jp) {
        System.out.println("In Aspect: " + jp.getSignature());
    }
}
// 配置啟用:@EnableAspectJAutoProxy

核心應用:聲明式事務管理

<!-- DataSource + Hibernate SessionFactory -->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
  <property name="driverClassName" value="${jdbc.driverClassName}"/>
  <property name="url" value="${jdbc.url}"/>
  <property name="username" value="${jdbc.username}"/>
  <property name="password" value="${jdbc.password}"/>
</bean>

<bean id="sessionFactory" class="org.springframework.orm.hibernate5.LocalSessionFactoryBean">
  <property name="dataSource" ref="dataSource"/>
  <property name="hibernateProperties">
    <props>
      <prop key="hibernate.dialect">${hibernate.dialect}</prop>
    </props>
  </property>
</bean>

<!-- 事務管理器 -->
<bean id="transactionManager" class="org.springframework.orm.hibernate5.HibernateTransactionManager">
  <property name="sessionFactory" ref="sessionFactory"/>
</bean>

<!-- TX Advice 定義 -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
  <tx:attributes>
    <tx:method name="get*" read-only="true"/>
    <tx:method name="find*" read-only="true"/>
    <tx:method name="load*" read-only="true"/>
    <tx:method name="*"/> <!-- 默認 REQUIRED -->
  </tx:attributes>
</tx:advice>

<!-- AOP 織入事務 -->
<aop:config>
  <aop:pointcut id="serviceMethods" expression="execution(* com.example.service.*.*(..))"/>
  <aop:advisor advice-ref="txAdvice" pointcut-ref="serviceMethods"/>
</aop:config>

代理機制選擇:JDK vs CGLIB

特性JDK Dynamic ProxyCGLIB
前提目標實現介面無需介面 (繼承類)
原理實現介面、委派調用繼承目標類、覆寫方法
final 方法不可代理不可代理
啟用預設 (有介面)proxy-target-class="true" 或 @EnableAspectJAutoProxy(proxyTargetClass=true)

總結

Spring AOP 以 運行期動態代理 實現橫切關注點分離。演進路徑:ProxyFactory (教學) → ProxyFactoryBean (XML) → aop:config (Schema) → @AspectJ (註解)。核心價值在於 聲明式事務、安全、日誌、快取、監控等基礎設施邏輯與業務代碼零耦合。現代 Spring Boot 專案:@EnableTransactionManagement + @Transactional 即可獲得完整事務 AOP,底層仍是此機制。

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