| 術語 | 含義 |
|---|---|
| 切面 | 橫切關注點模塊化(如事務、日志、權限) |
| 連接點 | 可被織入的方法(通常為業務接口方法) |
| 切入點 | 實際被織入的連接點(由切入點表達式匹配) |
| 通知 | 切面在特定時刻執行的增強邏輯 |
| 織入 | 將通知應用到目標對象的過程(編譯期/類加載期/運行期) |
| 目標對象 | 被代理的業務對象 |
Spring AOP 基於動態代理:JDK 接口代理 / CGLIB 繼承代理。final 方法不可代理,故不能作為切入點。
// 目標接口
public interface IUserBiz { void save(User user); }
// 目標實現
public class UserBiz implements IUserBiz {
private IDao dao;
public void save(User user) { dao.save(user); }
// setter 注入 dao
}
// 前置通知:MethodBeforeAdvice
public class LoggerBefore implements MethodBeforeAdvice {
public void before(Method method, Object[] args, Object target) {
System.out.println("前置增強:" + method.getName());
}
}
// 後置通知:AfterReturningAdvice
public class LoggerAfter implements AfterReturningAdvice {
public void afterReturning(Object returnValue, Method method, Object[] args, Object target) {
System.out.println("後置增強,返回值:" + returnValue);
}
}
// 異常拋出通知:ThrowsAdvice
public class ErrorLog implements ThrowsAdvice {
public void afterThrowing(Method method, Object[] args, Object target, RuntimeException ex) {
System.err.println("異常增強:" + method.getName() + " -> " + ex.getMessage());
}
}
// 環繞通知:MethodInterceptor (最強大,可控制目標方法執行)
public class AroundLog implements MethodInterceptor {
public Object invoke(MethodInvocation invocation) throws Throwable {
System.out.println("環繞前:" + invocation.getMethod().getName());
Object result = invocation.proceed(); // 執行目標方法
System.out.println("環繞後,返回:" + result);
return result;
}
}
<aop:config>
<aop:pointcut id="pc" expression="execution(public void save(cn.zhang.entity.User))"/>
<aop:advisor advice-ref="loggerBefore" pointcut-ref="pc"/>
<aop:advisor advice-ref="loggerAfter" pointcut-ref="pc"/>
<aop:advisor advice-ref="errorLog" pointcut-ref="pc"/>
<aop:advisor advice-ref="aroundLog" pointcut-ref="pc"/>
</aop:config>
執行順序:環繞前 → 前置 → 目標方法 → 後置 → 環繞後。異常時:環繞前 → 前置 → 目標方法拋異常 → 異常通知 → 環繞 catch 塊(可選重拋)。
<bean id="serviceProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="targetName" value="biz"/>
<property name="interceptorNames" value="loggerBefore,loggerAfter"/>
</bean>
@Aspect
public class AnnotationAdvice {
@Before("execution(* cn.service.UserService.*(..))")
public void before(JoinPoint jp) {
System.out.println("前置:" + jp.getSignature().getName() + " args=" + Arrays.toString(jp.getArgs()));
}
@AfterReturning(pointcut = "execution(* cn.service.UserService.*(..))", returning = "ret")
public void after(JoinPoint jp, Object ret) {
System.out.println("後置:" + jp.getSignature().getName() + " return=" + ret);
}
// 環繞通知
@Around("execution(* cn.service.UserService.*(..))")
public Object around(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("環繞前");
Object ret = pjp.proceed();
System.out.println("環繞後");
return ret;
}
// 異常通知
@AfterThrowing(pointcut = "execution(* cn.service.UserService.*(..))", throwing = "ex")
public void afterThrow(JoinPoint jp, Exception ex) {
System.err.println("異常:" + ex.getMessage());
}
}
<aop:aspectj-autoproxy />
無需顧問配置,掃描 @Aspect Bean 自動代理匹配的 Bean。
getArgs():方法參數數組getSignature():方法簽名(名稱、參數類型、返回類型)getTarget():目標對象getThis():代理對象| 表達式 | 含義 |
|---|---|
| execution(public * *(..)) | 所有 public 方法 |
| execution(* cn.service.*.*(..)) | service 包下所有方法 |
| execution(* save(User)) | 參數為 User 類型的 save 方法 |
| @annotation(Transactional) | 標註 @Transactional 的方法 |
| within(cn.service.*) | service 包下所有類的方法 |
| this(cn.service.UserService) | 實現 UserService 接口的代理對象方法 |
| target(cn.service.UserServiceImpl) | 目標對象為 UserServiceImpl 的方法 |
Spring AOP 提供了聲明式橫切關注點分離能力。掌握五大通知類型、切入點表達式、XML 與註解兩種配置方式、JoinPoint API,並理解代理機制與執行順序,是構建整潔、可維護企業級應用的關鍵技能。