AOP之前學習過但是一直沒有記下了,今天趁機會趕緊寫一篇:
AOP:
AOP利用”橫切”技術,剖解開封裝的對象内部,将那些影響了多個類的公共行為封裝到一個可重用子產品,并将其命名為”Aspect”,即切面。所謂”切面”,簡單說就是那些與業務無關,卻為業務子產品所共同調用的邏輯或責任封裝起來,便于減少系統的重複代碼,降低子產品之間的耦合度,并有利于未來的可操作性和可維護性。
———————————————————————————————————————
使用”橫切”技術,AOP把軟體系統分為兩個部分:核心關注點和橫切關注點。業務處理的主要流程是核心關注點,與之關系不大的部分是橫切關注點。橫切關注點的一個特點是,他們經常發生在核心關注點的多處,而各處基本相似,比如權限認證、日志、事物。AOP的作用在于分離系統中的各種關注點,将核心關注點和橫切關注點分離開來。
接下來寫一個切面類:
/**
* 定義名稱為addMethod的pointcut,隻是一個辨別,不進行調用
* (* add*(..)) 比對沒有傳回值,以add開頭的方法 、且參數任意
*/
@Pointcut("execution(* add*(..))")
private void addMethod(){};
/**
* 利用上面的辨別:addMethod
* 表示在調用(* add*(..))方法Before要執行這個方法
* 在advice中添加JoinPoint參數可以擷取用戶端的方法名及參數
*/
@Before("addMethod()")
private void checkSecurity(JoinPoint joinPoint){
for (int i = ; i < joinPoint.getArgs().length; i++) {
System.out.println(joinPoint.getArgs()[i]);//可以擷取參數
}
joinPoint.getSignature().getName();//方法名
System.out.println("你好—————————————");
}
切面類中的切入點可以靈活配置:
execution(modifiers-pattern? ret-type-pattern declaring-type-pattern? name-pattern(param-pattern)throws-pattern?)
execution(<修飾符模式>?<傳回類型模式><方法名模式>(<參數模式>)<異常模式>?)
除了傳回類型模式、方法名模式和參數模式外,其它項都是可選的;
·傳回類型模式決定了方法的傳回類型,必須一次比對一個連接配接點
.名字模式比對的是方法名,可用通配符*
.參數模式()不接受參數、(..)接受任意數量的參數方法、(*)接受一個任何類型的參 數的方法
//execution 傳回值類型 包名/方法名
@Pointcut("execution(* add*(..)) || execution(* com.mjx.net.*.del*(..))")
private void showPointcutTags(){}
白話Spring(基礎篇)—AOP(execution表達式)
Spring AOP中pointcut expression表達式解析 及比對多個條件
通過applicationContext.xml檔案将類加載到spring容器中
<!--啟用aspect對annotation的支援-->
<aop:aspectj-authoproxy/>
<!--業務處理-->
<bean id="userManagerImpl" class="com.mjx.study.UserManagerImpl"/>
<!--切面,含切點和方法-->
<bean id="annotationAOP" class="com.mjx.study.AnnotationAOP"/>
業務處理類:add*
public class UserManagerImpl implements UserManager {
public void addUser(String usrname, String password) {
System.out.println("add--------add------");
}
}
測試類:
@Test
public void test() {
ClassPathXmlApplicationContext factory = new ClassPathXmlApplicationContext("applicationContext.xml");//讀取配置檔案
UserManager userManager = (UserManager) factory.getBean("userManager");//從容器中擷取userManager對應的類
userManager.addUser("張三","124");//執行方法,切面切入
}
補充:
<!--方式二,将類中的切面、方法寫到配置檔案中-->
<aop:config>
<aop:aspect id="securityAspect" ref="annotationAOP">
<aop:pointcut id = "addMethod" expression="execution(* add*(..))"/>
<aop:before method="checkSecutiry" pointbut-ref="addMethod"/>
</aop:aspect>
</aop:config>
AOP和動态代理的結合:
JDK動态代理:
在運作時使用位元組碼動态生成的代理類(代理類的位元組碼在運作時生成并載入目前代理的ClassLoader)
通過反射類Proxy以及InvocationHandler回調接口實作的 == 隻能對該類所實作接口中定義的方法進行代理
1、如果目标對象實作了接口,預設情況下才有JDK的動态代理AOP,也可以強制使用CGLIB生成的代理實作APP
- 強制使用CGLIB
引入jar包、配置檔案中:
<aop:aspectj-autoproxy proxy-target-class="true"/>
示例:
public class TestCglib {
public static void main(String[] args) {
//位元組碼增強器,http://blog.sina.com.cn/s/blog_790c59140102w7y3.html
// 在Java位元組碼生成之後,對其進行修改,增強其功能,這種方式相當于對應用程式的二進制檔案進行修改
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(TargetObject.class);//被代理類TargetObject設定成父類
//方法攔截器
CallbackFilter callbackFilter = new TargetMethodCallbackFilter();
Callback noopCb = NoOp.INSTANCE;//即什麼操作也不做,代理類直接調用被代理的方法不進行攔截。
Callback callback = (Callback) new TargetInterceptor();//調用目标攔截器
Callback fixedValue = (Callback) new TargetResultFixed ();//鎖定方法傳回值:無論被代理類的方法傳回什麼值,回調方法都傳回固定值
Callback[] callbacks = new Callback[]{callback,noopCb,fixedValue};
enhancer.setCallback(new TargetInterceptor());//設定回調
enhancer.setCallbacks((net.sf.cglib.proxy.Callback[]) callbacks);
enhancer.setCallbackFilter(callbackFilter);
TargetObject targetObject2 = (TargetObject) enhancer.create();//生成代理對象
enhancer.setCallback(new TargetInterceptor());//設定攔截器TargetInterceptor
TargetObject targetObject = (TargetObject) enhancer.create();//動态生成一個代理類,轉型成父類型TargetObject
System.out.println(targetObject);//在代理類上調用方法時會被攔截器攔截
System.out.println(targetObject.method1("mmm"));
System.out.println(targetObject.method2());
System.out.println(targetObject.method2());
}
}
目标攔截器:
public class TargetInterceptor implements MethodInterceptor {
/**
* 重寫方法攔截
*
* @param o 目标對象,動态生成的代理類執行個體
* @param method 目标方法,上文中實體類所調用的被代理的方法引用
* @param objects 參數
* @param methodProxy 代理對象,生成的代理類對方法的代理引用
* @return
* @throws Throwable
*/
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("調用前");
Object result = methodProxy.invokeSuper(o, objects);//目标對象和參數
System.out.println("調用後" + result);
return result;
}
}
鎖定方法傳回值:
/**
* 表示鎖定方法傳回值,無論被代理類的方法傳回什麼值,回調方法都傳回固定值
* Created by phoebeM on 2018/04/15.
*/
public class TargetResultFixed implements FixedValue {
public Object loadObject() throws Exception {
System.out.println("鎖定結果");
Object object = ;
return object;
}
}
申請的callback長什麼樣呐?
一個接口?一個接口!
關鍵都在綠色箭頭上,很多實作這個接口的方法
2、如果目标對象沒有實作接口,必須引入CGLIB,spring會在JDK的動态代理和CGLIB代理間切換
JDK和CGLIB的差別:
1、JDK動态代理 處理 對實作了接口的類
2、CGLIB代理可以對類代理,為指定的類生成子類(繼承,目标最好不要用final聲明)
說了這麼些,什麼是CGLIB呐?
一個代碼生成包,如上所說其會動态生成一個要代理類的子類,子類重寫要代理的類的所有不是final的方法,然後在子類中采用方法攔截技術攔截所有父類方法的調用,順勢織入橫切邏輯。比使用java反射的JDK動态代理要快(底層采用ASM位元組碼生成架構)。
ASM是java位元組碼操縱架構,從類檔案中讀入資訊後,能夠改變類行為,分析類資訊,甚至能夠根據使用者要求生成新類(動态生成類或者增強既有類的功能)。
後語:
很簡單的一個例子,出去前置還有後置與環繞,想起了裝飾者模式,spring真是很強大,查資料看到一句話、分享給大家[一知半解,就是給自己挖坑]
感謝分享:
獨具匠心:Spring AOP詳解