Spring AOP的一些總結
前言
其實從開始使用spring開始,對spring中的aop一直就有了解,但是每次使用過後都遺忘了。導致再次使用的時候,又得重新翻找資料,重新開始寫demo,今天來個總結,搞定aop的基本例項。真正從原始碼基本進行學習還得等一段時間。
一些概念
網上已經有很多部落格針對aop的一些概念有介紹,自己也能理解一二。從實用的角度來說,概念的介紹並不是這篇部落格的重點,針對這些概念的介紹可以參考以下部落格,個人覺得講解的比較通俗的是這一篇:aop概念通俗版,講解的稍微全面一點的是這一篇:aop相關概念
簡單例項
這裡還是弄一個簡單例項吧。針對一些細節後續會進行總結
先建立一個maven專案,pom檔案如下:
<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>com.learn</groupId> <artifactId>spring_aop</artifactId> <version>0.0.1-SNAPSHOT</version> <url>http://maven.apache.org</url> <properties> <spring.version>3.1.1.RELEASE</spring.version> </properties> <dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aop</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-beans</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjrt</artifactId> <version>1.6.12</version> </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.6.12</version> </dependency> <dependency> <groupId>cglib</groupId> <artifactId>cglib</artifactId> <version>2.2</version> </dependency> </dependencies> </project>
在spring的配置檔案中加入如下配置:
<aop:aspectj-autoproxy />
<context:component-scan base-package="com.learn" />
編寫一個簡單的業務類
package com.learn.service; import org.springframework.stereotype.Service; @Service public class PersonService { public void addPerson(String personName) { System.out.println("add person"); } public boolean deletePerson(String personName) { System.out.println("delete person"); return true; } public void editPerson(String personName) { System.out.println("edit person "+personName); throw new RuntimeException("edit person infomation error!"); } }
編寫切面類
package com.learn.aspect; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.AfterReturning; import org.aspectj.lang.annotation.AfterThrowing; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Pointcut; import org.springframework.stereotype.Component; @Component @Aspect public class SimpleAspect { @Pointcut("execution(* com.learn.service.*Service*.*(..))") public void pointCut() { } @Pointcut("execution(* com.learn.service.*Service*.edit*(..))") public void pointCutException() { } @After("pointCut()") public void after(JoinPoint joinPoint) { System.out.println("after aspect executed"); } @Before("pointCut()") public void before(JoinPoint joinPoint) { System.out.println("before aspect executing"); } @AfterReturning(pointcut="pointCut()",returning = "returnVal") public void afterReturning(JoinPoint joinPoint ,Object returnVal) { System.out.println("afterReturning executed,return result is "+returnVal); } @Around("pointCut()") public void around(ProceedingJoinPoint pjp) throws Throwable{ System.out.println("around start.."); try { pjp.proceed(); } catch (Throwable ex) { System.out.println("error in around"); throw ex; } System.out.println("around end"); } @SuppressWarnings("rawtypes") @AfterThrowing(pointcut="pointCutException()",throwing="error") public void afterThrowing(JoinPoint jp,Throwable error) { Object[] args = jp.getArgs(); System.out.println("目標函式中的引數資訊:"); for(Object o:args) { System.out.println(o.toString()); } Object target = jp.getTarget(); System.out.println("目標函式中的target資訊:"+target.toString()); Class class1 = jp.getClass(); String kind = jp.getKind(); System.out.println("切面的型別:"+kind); System.out.println("目標函式的類資訊:"+class1.getName()); System.out.println("切面捕獲異常資訊"); System.out.println("error:"+error); } }
切面類中的註解需要注意一下,@Component是將其交給spring管理,@Aspect是將其標記為切面,其實還有xml配置的方式實現,這裡就不做詳細探討了,XML配置更加簡單。
測試方法
package com.learn.func;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.learn.service.PersonService;
public class App {
public static void main(String[] args) {
try {
ApplicationContext appContext = new ClassPathXmlApplicationContext("classpath:appContext.xml");
PersonService personService = appContext.getBean(PersonService.class);
String personName = "liman";
personService.addPerson(personName);
personService.deletePerson(personName);
personService.editPerson(personName);
} catch (Exception e) {
System.out.println("異常資訊:"+e.getMessage());
}
}
}
程式執行結果
補充
spring aop中重要的就是切入點表示式,切入點表示式是聯絡目標類方法和切面增強方法的橋樑。因此需要針對切入點表示式進行一些補充
execution(方法修飾符 方法返回值 方法所屬類 匹配方法名 (方法的引數列表) 方法丟擲的異常)
其中紅色字型的不能省略,每個部分都支援萬用字元('*')來匹配
方法修飾符:public private protected。
引數列表中:'*'——表示一個任意型別的引數,".."——表示零個或多個任意型別的引數
(*,Integer)匹配一個接受兩個引數的方法,第一個引數可以為任意型別,第二個必須為Integer型別。
execution只是切入點表示式的一種,還有within,this,target,args。
針對within,this,target和args的介紹,我建議還是參考官網文件吧,國內有些文件將這幾種切入點表示式翻譯的很怪。
如果出現一個增強類需要切入到多個目標類,可以進行切入點表示式的組合,||,&&等邏輯語法在切入點表示式中依舊適用
針對切面類中需要獲取目標方法的相關資訊,一般是通過JoinPoint這個引數來獲取,同時在切面類中,為了呼叫目標類中的相關方法,可能需要用到反射。
最後說一句:這次的使用是基於在新網實習中,涉及到異常日誌資訊處理的問題來實現的,當時腦子裡首先反應的就是利用AOP,經歷過各種苛刻碰碰,勉強算是完成了功能,在後面迭代中進一步完善