與基于代理類的AOP實作相比,基于XML的聲明式AspectJ要便捷多,但是它也存在着一些缺點,那就是要在Spring檔案中配置大量的代碼資訊。為了解決這個問題,AspectJ架構為AOP的實作提供了一套注解,用以取代Spring配置檔案中為實作AOP功能所配置的臃腫代碼。
@Aspect | 定義類為切入類 |
---|---|
@Pointcut | 聲明一個切入政策供 |
@Before | 被切入方法執行前執行 |
@After | 被切入方法執行後執行 |
@Around | 被切入方法前後都可以加入一些邏輯 |
@AfterReturning | 被切入方法傳回時執行 |
JoinPoint | 加入這個參數可以擷取被切入方法的名稱和參數 |
步驟
- 建立Maven工程,配置pom.xml,導入對應的jar包
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>Sakura</groupId> <artifactId>Spring_annotationAop</artifactId> <version>1.0-SNAPSHOT</version> <packaging>jar</packaging> <dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.0.2.RELEASE</version> </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.8.7</version> </dependency> </dependencies> </project>
- 在SpringAop.Service下建立接口IAccountService類,以及在SpringAop.Service.impl下建立接口的實作類
/** * 賬戶的業務層接口 */ public interface IAccountService { /** * 模拟儲存賬戶 */ void saveAccount(); /** * 模拟更新賬戶 * @param i */ void updateAccount(int i); /** * 删除賬戶 * @return */ int deleteAccount(); }
=========================================
@Service("accountService")
public class AccountServiceImpl implements IAccountService {
public void saveAccount() {
System.out.println("執行了儲存");
}
public void updateAccount(int i) {
System.out.println("執行了更新"+i);
}
public int deleteAccount() {
System.out.println("執行了删除");
return 0;
}
}
- 在SpringAop.Service.Util下建立切面類Logger.java,并在類中定義環繞通知
@Component("logger") @Aspect public class Logger { @Pointcut("execution(* SpringAop.Service.impl.*.*(..))") private void pt1(){} /** * 環繞通知 * 問題: * 當我們配置了環繞通知之後,切入點方法沒有執行,而通知方法執行了。 * 分析: * 通過對比動态代理中的環繞通知代碼,發現動态代理的環繞通知有明确的切入點方法調用,而我們的代碼中沒有。 * 解決: * Spring架構為我們提供了一個接口:ProceedingJoinPoint。該接口有一個方法proceed(),此方法就相當于明确調用切入點方法。 * 該接口可以作為環繞通知的方法參數,在程式執行時,spring架構會為我們提供該接口的實作類供我們使用。 * * spring中的環繞通知: * 它是spring架構為我們提供的一種可以在代碼中手動控制增強方法何時執行的方式。 */ @Around("pt1()") public Object aroundPringLog(ProceedingJoinPoint pjp){ Object rtValue = null; try{ Object[] args = pjp.getArgs();//擷取方法執行所需的參數 System.out.println("Logger類中的aroundPringLog方法開始記錄日志了。。。前置"); rtValue = pjp.proceed(args);//明确調用業務層方法(切入點方法) System.out.println("Logger類中的aroundPringLog方法開始記錄日志了。。。後置"); return rtValue; }catch (Throwable t){ System.out.println("Logger類中的aroundPringLog方法開始記錄日志了。。。異常"); throw new RuntimeException(t); }finally { System.out.println("Logger類中的aroundPringLog方法開始記錄日志了。。。最終"); } } }
- 在resources下定義bean.xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <!--配置Spring建立容器時要掃描的包--> <context:component-scan base-package="SpringAop"></context:component-scan> <!--配置Spring開啟注解Aop的支援--> <aop:aspectj-autoproxy></aop:aspectj-autoproxy> </beans>
- 在SpringAop.Test建立測試類AopTest
public class AopTest { public static void main(String[] args) { //擷取容器 ApplicationContext applicationContext = new ClassPathXmlApplicationContext("bean.xml"); //擷取對象 IAccountService iAccountService = (IAccountService) applicationContext.getBean("accountService"); //執行方法 iAccountService.updateAccount(8000); } }
控制台輸出結果如下
總結:雖說是要使用注解的形式來進行配置,但其中也用到了xml,我認為将注解和xml結合在一起使用是比較簡便的,若是要用純注解的方式來寫的話,執行再建立一個SpringConfiguration.java進行配置即可,代碼如下
@Configuration
@ComponentScan(basePackages = "SpringAop")
@EnableAspectJAutoProxy
public class SpringConfiguration {
}