1.Aop: 面向切面編程
具體的視圖可以這樣理解:
即通過切面來使代碼不冗餘,同樣也便於修改。
2. Aop術語
- 切面(Aspect): 橫切關注點(跨越應用程序多個模塊的功能)被模塊化的特殊對象
- 通知(Advice): 切面必須要完成的工作 目標(Target): 被通知的對象
- 代理(Proxy): 向目標對象應用通知之後創建的對象
- 連接點(Joinpoint):程序執行的某個特定位置:如類某個方法調用前、調用後、方法拋出異常後等。連接點由兩個信息確定:方法表示的程序執行點;相對點表示的方位。例如 ArithmethicCalculator#add() 方法執行前的連接點,執行點爲 ArithmethicCalculator#add(); 方位爲該方法執行前的位置
- 切點(pointcut):每個類都擁有多個連接點:例如 ArithmethicCalculator 的所有方法實際上都是連接點,即連接點是程序類中客觀存在的事務。AOP 通過切點定位到特定的連接點。類比:連接點相當於數據庫中的記錄,切點相當於查詢條件。切點和連接點不是一對一的關係,一個切點匹配多個連接點,切點通過 org.springframework.aop.Pointcut 接口進行描述,它使用類和方法作爲連接點的查詢條件。
3. 用 AspectJ 註解聲明切面
具體步驟:
- 要在 Spring 中聲明 AspectJ 切面, 只需要在 IOC 容器中將切面聲明爲 Bean 實例.
- 在 AspectJ 註解中, 切面只是一個帶有 @Aspect 註解的 Java 類.
- 通知是標註有某種註解的簡單的 Java 方法.
AspectJ 支持 5 種類型的通知註解:
@Before: 前置通知, 在方法執行之前執行
@After: 後置通知, 在方法執行之後執行
@AfterRunning: 返回通知, 在方法返回結果之後執行
@AfterThrowing: 異常通知, 在方法拋出異常之後
@Around: 環繞通知, 圍繞着方法執行
首先寫一個接口類 一個實現類,用於實現代碼
接口類:
package SpringAop;
public interface Userformula {
public int add(int i,int j);
public int sub(int i,int j);
public int mul(int i,int j);
public int div(int i,int j);
}
實現類 (注意添加註解 @Component(value="userformula") )
package SpringAop;
import org.springframework.stereotype.Component;
@Component(value="userformula")
public class formulaimpl implements Userformula {
@Override
public int add(int i, int j) {
// TODO 自動生成的方法存根
int result=i+j;
return result;
}
@Override
public int sub(int i, int j) {
// TODO 自動生成的方法存根
int result=i-j;
return result;
}
@Override
public int mul(int i, int j) {
// TODO 自動生成的方法存根
int result=i*j;
return result;
}
@Override
public int div(int i, int j) {
// TODO 自動生成的方法存根
int result=i/j;
return result;
}
}
創建切面類:
需要添加 @Component @Aspect 兩個註解
① : 前置通知
創建方法, 添加前置通知
如果要獲取切入點的數據或者值,可以通過 JoinPoint 來獲取
@Before("execution(public int SpringAop.*.*(int, int))")
public void beformethod(JoinPoint joinPoint) {
//切入點方法名
String methodName = joinPoint.getSignature().getName();
//將切入點獲取的值傳入集合
Object [] args = joinPoint.getArgs();
System.out.println("The method " + methodName + " begins with " + Arrays.asList(args));
}
②:後置通知 (同理與前置)
@After("execution(public int SpringAop.*.*(int, int))")
public void afterMethod(JoinPoint joinPoint){
String methodName = joinPoint.getSignature().getName();
System.out.println("The method " + methodName + " ends");
}
③:返回通知
//返回通知 可以通過returning="result" 以及方法名中加Object result 來獲取返回值
@AfterReturning(value="execution(public int SpringAop.*.*(int, int))",
returning="result")
public void afterreturnning(JoinPoint joinPoint,Object result) {
String methodName=joinPoint.getSignature().getName();
System.out.println("The method"+methodName+"result:"+result);
}
④:異常通知
//異常通知 同返回通知一樣
@AfterThrowing(value="execution(public int SpringAop.*.*(int, int))",throwing="ex")
public void afterthrowig(JoinPoint joinPoint,Exception ex) {
String methodname=joinPoint.getSignature().getName();
System.out.println("The method"+methodname+"exception:"+ex);
}
⑤:環繞通知(不常用)
將通知全部圍繞在一個裏面
@Around(value="execution(public int SpringAop.*.*(int, int))")
public Object around(ProceedingJoinPoint pjd) {
Object result = null;
String methodName = pjd.getSignature().getName();
try {
//前置通知
System.out.println("The method " + methodName + " begins with " + Arrays.asList(pjd.getArgs()));
//執行目標方法
result = pjd.proceed();
//返回通知
System.out.println("The method " + methodName + " ends with " + result);
} catch (Throwable e) {
//異常通知
System.out.println("The method " + methodName + " occurs exception:" + e);
throw new RuntimeException(e);
}
//後置通知
System.out.println("The method " + methodName + " ends");
return result;
}
從上面可以看到每一個註解後面都需要添加exetution() 如果需要修改則需要修改多處,不符合現代代碼風格,違背設計模式。
我們需要通過一種方式來封裝起來。
利用 @Pointcut("execution(public int SpringAop.*.*(int, int))") 然後後面添加一個空方法,下面則只需要調用空方法就可以實現。
@Pointcut("execution(public int SpringAop.*.*(int, int))")
public void point() {
}
調用切入點方法實現前置通知。
@Before("point()")
public void beformethod(JoinPoint joinPoint) {
//切入點方法名
String methodName = joinPoint.getSignature().getName();
//將切入點獲取的值傳入集合
Object [] args = joinPoint.getArgs();
System.out.println("The method " + methodName + " begins with " + Arrays.asList(args));
}
配置文件中添加Context 和 Aop
<context:component-scan base-package="SpringAop"></context:component-scan>
<!-- 起到切面作用 -->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
寫一個主函數:
package SpringAop;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class main {
public static void main(String[] args) {
// TODO 自動生成的方法存根
ApplicationContext ctx=new ClassPathXmlApplicationContext("Applicationcontext.xml");
Userformula user=(Userformula) ctx.getBean("userformula");
int r=user.add(3, 6);
System.out.println("r的值爲: "+r);
int s=user.div(100, 10);
System.out.println("s的值爲"+s);
}
}
運行即可完成。
運行結果:
分別爲 前置通知 後置通知 返回通知 因爲無錯所以沒有報異常通知