Spring AOP 支援兩種主流配置風格:基於 @AspectJ 註解(推薦,類型安全、IDE 友好)與傳統 XML 命名空間(舊項目常見、集中式管理)。本文以統一業務場景(用戶註冊服務加日誌切面)為例,完整演示兩種方式的配置細節、依賴引入、切點表達式寫法及驗證測試。
// 領域模型
public class User { private String username, password; /* getter/setter */ }
// DAO 接口與實現
public interface UserDAO { void save(User user); }
@Component("u")
public class UserDAOImpl implements UserDAO {
public void save(User user) { System.out.println("user saved"); }
}
// Service 層
@Component("userService")
public class UserService {
@Resource(name="u") private UserDAO userDAO;
public void add(User user) { userDAO.save(user); }
}
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.x</version>
</dependency>
@Aspect
@Component
public class LogInterceptor {
@Pointcut("execution(public * com.bjsxt.service..*.add(..))")
public void serviceAdd() {}
@Before("serviceAdd()")
public void before() { System.out.println("method start"); }
@After("serviceAdd()")
public void after() { System.out.println("method after"); }
@AfterReturning("execution(public * com.bjsxt.dao..*.*(..))")
public void afterReturn() { System.out.println("method afterReturning"); }
@AfterThrowing("execution(public * com.bjsxt.dao..*.*(..))")
public void afterThrow() { System.out.println("method afterThrowing"); }
}
<beans xmlns="https://www.springframework.org/schema/beans"
xmlns:context="https://www.springframework.org/schema/context"
xmlns:aop="https://www.springframework.org/schema/aop"
xsi:schemaLocation="...
https://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">
<context:component-scan base-package="com.bjsxt"/>
<aop:aspectj-autoproxy/> <!-- 關鍵:啟用 @AspectJ 自動代理 -->
</beans>
切面類改為普通 POJO,移除 @Aspect、@Pointcut 等註解,僅保留業務方法(before()、after() 等)。
<beans ... xmlns:aop="https://www.springframework.org/schema/aop">
<context:component-scan base-package="com.bjsxt"/>
<bean id="logInterceptor" class="com.bjsxt.aop.LogInterceptor"/>
<aop:config>
<aop:pointcut id="serviceAddPointcut"
expression="execution(public * com.bjsxt.service..*.add(..))"/>
<aop:aspect id="logAspect" ref="logInterceptor">
<aop:before method="before" pointcut-ref="serviceAddPointcut"/>
<aop:after method="after" pointcut-ref="serviceAddPointcut"/>
<aop:after-returning method="afterReturn" pointcut-ref="serviceAddPointcut"/>
<aop:after-throwing method="afterThrow" pointcut-ref="serviceAddPointcut"/>
</aop:aspect>
</aop:config>
</beans>
| 維度 | @AspectJ 註解 | XML 配置 |
|---|---|---|
| 類型安全 | 編譯期檢查切點表達式拼寫 | 運行期解析,易拼寫錯誤 |
| IDE 支援 | 跳轉、重構、提示完善 | 僅 XML 編輯器輔助 |
| 集中管理 | 切面邏輯分散在類上 | 所有切點、增強集中在 XML |
| 適用場景 | 新項目、團隊熟悉註解 | 舊項目、運維需動態調整切點 |
execution(modifiers? ret-type declaring-type?name(param) throws?)*(任意返回類型/方法名/包)、..(任意參數/子包)。execution(* com..service.*.*(..)) 匹配 service 包下所有公共方法。@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class AopTest {
@Autowired UserService userService;
@Test public void test() { userService.add(new User()); }
}
// 輸出:method start → user saved → method afterReturning → method after
新項目首選 @AspectJ 註解,代碼即配置,重構安全;老項目維護或需運維動態調整切點時,XML 仍有優勢。兩者可混用(XML 中 <aop:aspectj-autoproxy/> 會同時掃描註解切面)。