Spring in Anction:Spring AOP 小記
阿新 • • 發佈:2019-02-05
1.什麼是AOP(面向切面程式設計)
不扯那些概念的東西,簡單說來AOP是OOP的一個補充,AOP可以在程式執行期追加一些公用的功能,比如許可權判斷,日誌記錄,這些功能都是專案需要的,但是又不能每個地方都呼叫,這樣無疑增加了程式碼的複雜度和工作量,我們可以將這些分散在系統中的公用程式碼集中於一個地方並通過aop技術應用於系統各個地方。
2. AOP術語
- 切點:我們需要插入這些公用功能的點?比如哪些類?哪些方法?等等(何處呼叫)
- 切面:這些公用功能程式碼(呼叫什麼)
- 通知:在什麼時候呼叫?
3.通知分類
- 前置通知(Before):在目標方法呼叫前呼叫
- 後置通知(After):在目標方法呼叫後呼叫
- 返回通知(AfterReturning):在目標方法成功執行後呼叫
- 異常通知(AfterThrowing):在目標方法執行失敗拋異常後呼叫
- 環繞通知(Around):將目標方法包裹,由你決定什麼時候呼叫(最強大,最常用)
3.Spring AOP
Spring 通過生成代理類將目標物件包裹從而實現AOP,呼叫者呼叫目標方法時,其實是在呼叫代理類,再由代理類
呼叫目標方法,從而控制對目標方法的呼叫,Spring只支援最細方法級別的連線點,如果需要實現更細粒度的控制
則需要藉助專業的AOP框架AspectJ,可以控制欄位,構造方法等更細的東西,不過需要學習新的語法。
4.Spring 切點表示式
可以通過這些表示式配置切點
上面是Spring支援的AspectJ 切點表示式。除execution是增加切點範圍的外,其他都是用於縮小範圍
5.編寫切點
6.編寫切面
用註解@Aspect表示一個切面,註解用於類上
@Aspect //表示是切面
@Component //Bean注入Spring
@Order(1)//優先執行該切面
public class TicketLoginAspect {
@Autowired
private HttpServletRequest request;
/**
* 切點
*/
//表示作用於TicketLogin註解
@Pointcut("@annotation(com.cnct.webchat.common.annotation.TicketLogin)")
public void loginPointCut() {
//擴充套件作用範圍,不用在每個@Around後寫很長的表示式了,只需寫方法名即可,像下面
}
@Around("loginPointCut()")
public Object around(ProceedingJoinPoint point) throws Throwable {
}
}
寫完切面後要開啟Spring aop自動代理才能生效,如果使用JavaConfig的話,在配置類上使用
@EnableAspectJ-AutoProxy即可,如果用XML配置則加入
7.通過註解為目標類增加新功能(方法)
既然AOP通過代理實現,那麼就可以為目標物件增加新的代理方法。
@Aspect //定義切面
@Component
public class TestAop{
@DeclareParents(value = "com.lyq3.NewFunctionInterface+",
defaultImpl = NewFunctionImpl.class
)
public static Abc abc;
}
- value 指定那種型別的Bean要引入,(後面的“+”號表示所有的子型別而不是本身)
- defaultImpl 指定引入功能的實現類
- @DeclareParents 所標註的靜態屬性指明要引入的介面,也就是要為Abc介面增加新功能
8.注意:踩坑記
@Aspect
@Component
@Order(2)//登入註解執行後執行該註解
public class HandleRecordAspect {
@Autowired
private HandleRecordMapper handleRecordMapper;
@Autowired
private HttpSession session;
/**
* 切點
*/
@Pointcut("@annotation(com.cnct.webchat.common.annotation.HandleRecord)")
public void handlePointCut() {
}
@Around("handlePointCut()")
public Object around(ProceedingJoinPoint point) throws Throwable {
//先執行方法
Object result = point.proceed();
//訪問目標方法的引數:
Object[] args = point.getArgs();
Signature sig = point.getSignature();
MethodSignature msig = null;
msig = (MethodSignature) sig;
Method method = msig.getMethod();
// String methodName = msig.getName();//方法名
HandleRecord handleRecord = method.getAnnotation(HandleRecord.class);//獲取註解
//操作標識(增?刪?改?)
int handle_status = handleRecord.handle_status();
String remark = handleRecord.value();
}
}
上面的程式碼是我在專案中實現一個存操作紀錄的部分程式碼,咋一看沒毛病。@HandleRecord這個自定義註解在Controller層能用,我放到Service層的時候(service有介面和介面實現,註解放實現類方法上)
HandleRecord handleRecord = method.getAnnotation(HandleRecord.class);
這句程式碼是獲取不到註解的,也就是說 handleRecord = null;
原因是AOP採用的jdk代理,MethodSignature 轉型之後,會丟失子類的方法註解,所以得先獲取實現類的方法
修改下程式碼,就能用了:
//先執行方法
Object result = point.proceed();
//訪問目標方法的引數:
Object[] args = point.getArgs();
Signature sig = point.getSignature();
MethodSignature msig = null;
msig = (MethodSignature) sig;
Method method = msig.getMethod();
//MethodSignature 轉型之後,會丟失子類的方法註解,所以得先獲取實現類的方法
//============================================
Method soruceMethod = point.getTarget().getClass().getMethod(method.getName(), method.getParameterTypes());
//=============================================
HandleRecord handleRecord = soruceMethod.getAnnotation(HandleRecord.class);//獲取註解
//操作標識(增?刪?改?)
int handle_status = handleRecord.handle_status();
String remark = handleRecord.value();