在不改變類的代碼的前提下,對類方法進行功能加強。
複製代碼
向用戶提供咱們的AOP功能讓他們來進行對類方法的加強。
複製代碼
1.進行功能加強:功能---Advice 通知
2.對類方法加強:可選的加強方法---PointCut 切入點
3.不改原類代碼:與原類解耦---Weaving 織入
複製代碼
Advice:通知,加強的功能
Join Point:鏈接點,可選的方法點
Pointcut:切入點,選擇切入的方法點
Aspect:切面,選擇的(多個)方法點+加強的功能
Introduction:引入,添加新的方法或屬性到已存在的類中
Weaving:織入,不改變原類的代碼進行加強
複製代碼
其中 Advice, Join Point,Pointcut,Aspect 都是用戶提供咱們使用
Weaving 須要咱們本身實現
Introduction 在spring中存在可是不多涉及
ps:提供者和使用者的概念在設計模式中常常涉及
複製代碼
Advice:
1.由用戶提供加強功能邏輯代碼
2.不一樣的加強需求會存在不一樣的邏輯
3.可選時機,在方法先後或異常時進行加強
4.同一個切入點,可能存在多個加強
Pointcut
1.用戶指定切入點
2.用戶可在多點進行加強
Weaving
1.不改變原類代碼
2.在框架中已經實現
複製代碼
Aspect 分析:html
(1)Advice是由用戶提供咱們使用,且是多變的,那咱們如何能認識用戶提供的東西, (2)如何讓咱們的代碼隔絕用戶提供的多變?java
可否由咱們提供一套標準接口,用戶經過實現接口來提供他們不一樣的邏輯
應對變化---面向接口編程(好比JDBC,日誌等)
複製代碼
此時咱們做爲空殼接口來編寫便可,做爲加強功能的總標識spring
public interface Advice {
}
複製代碼
首先圍繞Advice的特色3,可選時機這塊,它能夠進行前置加強,後置加強,環繞加強,異常處理加強,這時咱們須要定義一個標準接口方法,讓用戶作到實現它就能夠進行加強。此時咱們須要考慮的因素有:express
四種加強所需的參數是否同樣?編程
目的是對方法進行加強,應該須要提供的是方法相關的信息,咱們也僅能提供有關方法的信息
其中方法包含的信息有:
1.方法自己:Method
2.方法所屬對象:Object
3.方法的參數:Object[]
複製代碼
在方法執行前進行加強,不須要任何的返回值
複製代碼
1.方法自己:Method
2.方法所屬對象:Object
3.方法的參數:Object[]
4.方法的返回值:Object
複製代碼
方法執行後進行加強也不須要返回值
複製代碼
1.方法自己:Method
2.方法所屬對象:Object
3.方法的參數:Object[]
複製代碼
方法被它包裹,也就是自己類方法的執行須要這個方法的執行邏輯來帶動執行,
因此它須要返回方法的返回值Object
複製代碼
異常信息
複製代碼
已經異常了···
複製代碼
須要的,正常來講是使用try-catch來根據不一樣的異常來進行不一樣的處理,
就是在不一樣的catch中進行不一樣的加強處理,那其實就是能夠藉助環繞加強的效果來實現
複製代碼
通過前面的分析,咱們已經能夠總結出咱們所須要的三個方法設計模式
分三個接口,此時還能夠經過類型來區分不一樣的Advice
複製代碼
public interface MethodBeforeAdvice extends Advice{
/**
* 實現方法的前置加強
*
* @param method 被加強的方法
* @param args 方法的參數
* @param target 被加強的目標對象
* @throws Throwable
*/
void before(Method method,Object[] args,Object target) throws Throwable;
}
複製代碼
public interface AfterReturnAdvice extends Advice {
/**
* 實現方法的後置加強
*
* @param returnValue 返回值
* @param method 被加強的方法
* @param args 方法的參數
* @param target 方法的目標對象
* @throws Throwable
*/
void afterReturn(Object returnValue, Method method,Object[] args,Object target) throws Throwable;
}
複製代碼
public interface MethodSurroudAdvice extends Advice {
/**
* 對方法進行環繞加強還有異常處理的加強
*
* @param method
* @param args
* @param target
* @return
*/
Object invoke(Method method,Object[] args,Object target);
}
複製代碼
Pointcut的特色?---用戶指定並多點指定api
咱們須要爲用戶提供一個東西讓他們來靈活指定多個方法點,切入點就是這些點。那問題來了框架
1.指定方法,是否以方法做爲描述信息
2.如何指定方法?---XX類的XX方法
3.方法重載如何處理?---加上參數類型
複製代碼
此時是否有感受,這些東西恰好就組成了一個完整的方法簽名呢!ide
ps:一類類的某些方法,好比說,某個包或者某個類的全部方法,全部類中do開頭的方法,全部類中帶有service的方法等等學習
咱們須要一個表達式來描述這些信息
包名:有父子特色,要能模糊匹配
類名:模糊匹配
方法名與參數類型:模糊匹配,參數能夠多個
咱們常見的表達式,好比正則(其實也是可行),Ant Path,
AspectJ裏面的pointcut(首選,由於AspectJ自己就是面向切面編程的組件)
複製代碼
execution(modifiers-pattern? ret-type-pattern declaring-type-pattern?name-pattern(param-pattern)
throws-pattern?)
複製代碼
經過spring的官網能夠進行學習:
docs.spring.io/spring/docs…
切點定義表達式
複製代碼
對類,方法進行匹配
切點應提供匹配類,匹配方法的行爲
複製代碼
支持可變性問題須要由咱們來定義一個標準接口,定義好基本行爲,面向接口編程
屏蔽掉具體的實現.(像實現Advice同樣)
複製代碼
因此咱們設計一個Pointcut的標準接口
public interface Pointcut {
//提供兩個方法,匹配類和匹配方法
boolean matchClass(Class<?> targetClass);
boolean matchMethod(Method method,Class<?> targetClass);
}
複製代碼
public class AspectJExpressionPointcut implements Pointcut{
private String expression;
public AspectJExpressionPointcut(String expression){
this.expression = expression;
}
public String getExpression(){
return this.expression;
}
@Override
public boolean matchClass(Class<?> targetClass) {
return false;
}
@Override
public boolean matchMethod(Method method, Class<?> targetClass) {
return false;
}
}
複製代碼
此時咱們完成了一個空殼實現,還需引入AspectJ的jar包來完成切點表達式的實現,Spring AOP也僅僅使用了AspectJ的表達式api
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.10</version>
</dependency>
複製代碼
大體理解下AspectJ的簡單應用,咱們應該執行的步驟是:
PointcutParser pp = PointcutParser
.getPointcutParserSupportingAllPrimitivesAndUsingContextClassloaderForResolution();
複製代碼
PointcutExpression pe =
pp.parsePointcutExpression(expression)
複製代碼
pe.couldMatchJoinPointsInType(targetClass)爲匹配類的方法,但有匹配不許確的問題
因此咱們須要匹配方法的api
pe.matchesMethodExecution(method)
而後使用ShadowMatch類中的alwaysMatches()方法便可
複製代碼
public class AspectJExpressionPointcut implements Pointcut{
//獲得了一個全局的切點解析器
private static PointcutParser pp = PointcutParser
.getPointcutParserSupportingAllPrimitivesAndUsingContextClassloaderForResolution();
private String expression;
private PointcutExpression pe;
public AspectJExpressionPointcut(String expression) {
super();
this.expression = expression;
//解析成對應的表達式對象
pe = pp.parsePointcutExpression(expression);
}
@Override
public boolean matchClass(Class<?> targetClass) {
return pe.couldMatchJoinPointsInType(targetClass);
}
@Override
public boolean matchMethod(Method method, Class<?> targetClass) {
ShadowMatch sm = pe.matchesMethodExecution(method);
return sm.alwaysMatches();
}
public String getExpression() {
return expression;
}
}
複製代碼
爲了給用戶提供操做優化,咱們設計一個Advisor把Advice和Pointcut組合起來,當用戶使用aspectJ來指定他的切入點時,就只需指定adviceBeanName,expression便可
public interface Advisor {
String getAdviceBeanName();
String getExpression();
}
複製代碼
public class AspectJPointcutAdvisor implements Advisor{
private String adviceBeanName;
private String expression;
private AspectJExpressionPointcut pointcut;
public AspectJPointcutAdvisor(String adviceBeanName, String expression) {
super();
this.adviceBeanName = adviceBeanName;
this.expression = expression;
this.pointcut = new AspectJExpressionPointcut(this.expression);
}
public Pointcut getPointcut() {
return this.pointcut;
}
@Override
public String getAdviceBeanName() {
return this.adviceBeanName;
}
@Override
public String getExpression() {
return this.expression;
}
}
複製代碼
比較純理論,代碼很少且簡單,更多地仍是要理清楚一些概念性的問題.不足之處請在評論處留言...望共同進步,望能在點滴積累中慢慢收穫成長