摘要:是什么是面向切面編程的簡稱。負責實施切面,它將切面所定義的橫切邏輯織入到切面所指定的連接點鐘。靜態正則表達式匹配切面是正則表達式方法匹配的切面實現類。流程切面的流程切面由和實現。
aop是什么
aop是面向切面編程(aspect oriented programing)的簡稱。aop的出現并不是要完全替代oop,僅是作為oop的有益補充。
aop的應用場合是有限的,一般只適合于那些具有橫切邏輯的應用場合。
性能監測
訪問控制
事務管理
日志記錄
...
一個類或一段程序代碼擁有一些具有邊界性質的特定點,這些代碼中的特定點就稱為連接點。比如,
類開始初始化前,后
類中某個方法調用前,后
方法拋出異常后
...
連接點由兩個信息確定:
用方法表示的程序執行點
用相對點表示的方位
如在Test.foo()方法執行前的連接點,執行點為Test.foo(),方位為該方法執行前的位置。
spring使用切點對執行點定位,而方位則在增強類型中定義.
每個程序類都可能有多個連接點,aop通過切點定位特定點。類比于數據庫查詢:連接點相當于數據庫中的記錄,切點相當于查詢條件。
切點和連接點不是一對一關系,一個切點可以匹配多個連接點。
切點只定位到某個方法上,如果希望定位到具體的連接點上,還需要提供方位信息。
增強是織入到目標類連接點上的一段代碼.它除用于描述一段代碼外,還擁有另一個和連接點相關的信息,這便是執行點的方位。結合執行點方位信息和切點信息,就可以找到特定的連接點了。
spring提供的增強接口都是帶方位名的:BeforeAdvice,AfterReturningAdvice,ThrowsAdvice等。
增強邏輯的織入目標類。
引介(introduction)引介是一種特殊的增強,它為類添加一些屬性和方法。這樣,即使一個業務類原來沒有實現某個接口,通過引介,也可以動態的為業務類添加接口的實現邏輯,讓業務類成為這個接口的實現類。
織入(weaving)織入是將增強添加對目標類具體連接點的過程。aop有三種織入方式:
編譯期織入,這要求使用特殊的java編譯器
類裝載期織入,這要求使用特殊的類裝載器
動態代理織入,在運行期為目標類添加增強,生成子類
spring使用第3種方式織入,aspectj使用第1,2種方式。
代理(proxy)一個類被aop織入增強后,就產生一個結果類,它融合了原來類和增強邏輯的代理類。我們可以采用調用原來類相同的方式調用代理類。
切面(aspect)切面由切點和增強(引介)組成,它既包括了橫切邏輯的定義,也包括了連接點的定義。spring aop負責實施切面,它將切面所定義的橫切邏輯織入到切面所指定的連接點鐘。
創建增強類 前置增強場景:高級餐廳的服務員在回答顧客之前都會說"你好!...".
public class Waiter { public void check(String name){ System.out.println("結賬?"+name); } public void serve(String name){ System.out.println("要點什么?"+name); } }
前置增強
import java.lang.reflect.Method; import org.springframework.aop.MethodBeforeAdvice; public class GreetAdvice implements MethodBeforeAdvice{ @Override public void before(Method method, Object[] args, Object obj)throws Throwable { String clientName=args[0].toString(); System.out.println("你好!"+clientName); } }
測試
import org.springframework.aop.BeforeAdvice; import org.springframework.aop.framework.ProxyFactory; public class TestBeforeAdvice { public static void main(String[] args){ Waiter target=new Waiter(); BeforeAdvice advice=new GreetAdvice(); ProxyFactory pf=new ProxyFactory();//spring提供的代理工廠 pf.setTarget(target);//設置代理目標 pf.addAdvice(advice);//添加增強 Waiter proxy=(Waiter)pf.getProxy();//代理實例 proxy.serve("TheViper"); proxy.check("TheViper"); } }
結果
你好!TheViper 來點什么?TheViper 你好!TheViper 結賬?TheViper
ProxyFactory內部使用JDK代理或CGLib代理,將增強應用到目標類。
還可以將接口設置為代理目標。
... ProxyFactory pf=new ProxyFactory(); pf.setInterfaces(target,getClass().getInterfaces); pf.setTarget(target); ...在spring中配置
application-context.xml
測試
import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class TestBeforeAdvice { public static void main(String[] args){ ApplicationContext ctx=new ClassPathXmlApplicationContext("application-context.xml"); Waiter waiter=(Waiter)ctx.getBean("waiter"); waiter.serve("TheViper"); waiter.check("TheViper"); } }
ProxyFactoryBean常用配置:
target:代理的目標對象
proxyInterfaces:代理所要實現的接口,可以是多個接口。該屬性還有一個別名屬性interfaces
interceptorNames:需要植入目標對象的Bean列表。這些Bean必須是實現了org.aopalliance.intercept.MethodInterceptor或org.springframework.aop.Advisor的Bean,配置中的順序對應調用的順序。
singleton:返回的代理是否為單例,默認為單例
optimize:設置為true時,強制使用CGLib代理。對于singleton代理,推薦使用CGLib,對于其他作用域類型的代理,最好使用JDK代理。因為CGLib創建代理速度慢,而創建出的代理對象運行效率較高。JDK代理的表現與之相反
proxyTargetClass:是否對類進行代理(不是對接口進行代理),設置為true時,使用CGLib
從上面spring配置可以看到,ProxyFactoryBean用的是JDK代理,如果將proxyTargetClass設置為true后,無需再設置proxyInterfaces屬性,即使設置了也會被忽略。
后置增強場景:服務員和顧客交流后,禮貌的說"please enjoy yourself".
后置增強
import java.lang.reflect.Method; import org.springframework.aop.AfterReturningAdvice; public class GreetAfterAdvice implements AfterReturningAdvice{ @Override public void afterReturning(Object returnObj,Method method,Object[] args,Object obj)throws Throwable { //returnObj:目標實例方法返回的結果 method:目標類的方法 args:目標實例的方法參數 obj:目標類實例 System.out.println("please enjoy yourself!"); } }
spring配置
......
結果
你好!TheViper 要點什么?TheViper please enjoy yourself! 你好!TheViper 結賬?TheViper please enjoy yourself!環繞增強
環繞增強允許在目標類方法調用前后織入橫切邏輯,它綜合實現了前置,后置增強兩種的功能。
import org.aopalliance.intercept.MethodInterceptor; import org.aopalliance.intercept.MethodInvocation; public class GreetingInterceptor implements MethodInterceptor{ @Override public Object invoke(MethodInvocation invocation) throws Throwable { Object[] args=invocation.getArguments(); String clientName=args[0].toString(); System.out.println("你好!"+clientName); Object obj=invocation.proceed();//通過反射調用目標方法 System.out.println("please enjoy yourself!"); return obj; } }
...異常拋出增強...
異常拋出增強最適合的場景是事務管理。
import java.lang.reflect.Method; import org.springframework.aop.ThrowsAdvice; public class TransactionManager implements ThrowsAdvice{ public void afterThrowing(Method method,Object[] args,Object target,Exception ex)throws Throwable{ System.out.println("method:"+method.getName()); System.out.println("拋出異常"+ex.getMessage()); System.out.println("回滾"); } }引介增強
引介增強不是在目標方法周圍織入增強,而是為目標類創建新的方法和屬性。所以引介增強的連接點是類級別的,非方法級別.
創建切面前面織入增強時,都是織入到目標類的所有方法中。這節將會介紹如何讓增強提供連接點方位的信息,如織入到方法前面,后面等,而切點進一步描述具體織入哪些類的哪些方法上。
spring通過org.springframework.aop.Pointcut接口描述切點,Pointcut由ClassFilter和MethodMatcher構成。
通過ClassFilter定位到某些特定類上,通過MethodMatcher定位到某些特定方法上。
此外,spring還提供注解切點和表達式切點,兩者都使用AspectJ的切點表達式語言。
靜態方法切點
動態方法切點
注解切點
表達式切點
流程切點
復合切點
靜態普通方法名匹配切面StaticMethodMatcherPointcutAdvisor代表一個靜態方法匹配切面,它通過StaticMethodMatcherPointcut定義切點,通過類過濾和方法名匹配定義切點。
例子,Seller類也有serve方法
public class Seller { public void serve(String name){ System.out.println("seller說:要點什么?"+name); } }
前置增強(advice)
public class GreetingBeforeAdvice implements MethodBeforeAdvice{ @Override public void before(Method method, Object[] args, Object obj)throws Throwable { String clientName=args[0].toString(); System.out.println("你好!"+clientName); } }
切面(advisor)
import java.lang.reflect.Method; import org.springframework.aop.ClassFilter; import org.springframework.aop.support.StaticMethodMatcherPointcutAdvisor; public class GreetAdvisor extends StaticMethodMatcherPointcutAdvisor{ @Override public boolean matches(Method method, Class> cls) {//切點方法匹配 return "serve".equals(method.getName()); } //切點類匹配規則:Waiter的類或子類 public ClassFilter getClassFilter(){ return new ClassFilter(){ public boolean matches(Class cls){ return Waiter.class.isAssignableFrom(cls); } }; } }
spring配置
......
測試
ApplicationContext ctx=new ClassPathXmlApplicationContext("application-context.xml"); Waiter waiter=(Waiter)ctx.getBean("waiter"); Seller seller=(Seller)ctx.getBean("seller"); waiter.serve("TheViper"); waiter.check("TheViper"); seller.serve("TheViper");
結果
你好!TheViper waiter說:要點什么?TheViper waiter說:結賬?TheViper seller說:要點什么?TheViper
可以看到切面只是織入到Waiter.serve()方法調用前的連接點上,而Waiter.check()和Seller.serve()都沒有織入切面。
靜態正則表達式匹配切面RegexpMethodPointcutAdvisor是正則表達式方法匹配的切面實現類。該類已經是功能齊備的的實現類了,一般情況下,無需擴展該類。
...
.*che.* ...
這里定義了一個匹配模式.*che.*,它會匹配check()方法。
... waiter.serve("TheViper"); waiter.check("TheViper"); ...
waiter說:要點什么?TheViper 你好!TheViper waiter說:結賬?TheViper動態切面
import org.springframework.aop.ClassFilter; import org.springframework.aop.support.DynamicMethodMatcherPointcut; public class GreetingDynamicPointcut extends DynamicMethodMatcherPointcut{ private static ListspecialCients=new ArrayList (); static{ specialCients.add("Tom");//添加白名單 specialCients.add("TheViper"); } //切點類匹配規則:Waiter的類或子類 public ClassFilter getClassFilter(){//靜態匹配 return new ClassFilter(){ public boolean matches(Class cls){ System.out.println("對"+cls.getName()+"類做靜態檢查"); return Waiter.class.isAssignableFrom(cls); } }; } public boolean matches(Method method, Class> cls) {//切點方法靜態匹配 System.out.println("對"+cls.getName()+"類的"+method.getName()+"方法做靜態檢查"); return "serve".equals(method.getName()); } @Override public boolean matches(Method method, Class> cls, Object[] args) {//動態匹配 System.out.println("對"+cls.getName()+"類的"+method.getName()+"方法做動態檢查"); String clientName=args[0].toString(); return specialCients.contains(clientName); } }
匹配規則:目標類為Waiter或其子類,方法名為serve,動態傳入的參數name必須在白名單中存在。
Waiter waiter=(Waiter)ctx.getBean("waiter1"); waiter.serve("Peter"); waiter.check("Peter"); waiter.serve("TheViper"); waiter.check("TheViper");
對com.Waiter類做靜態檢查 對com.Waiter類的serve方法做靜態檢查 對com.Waiter類做靜態檢查 對com.Waiter類的check方法做靜態檢查 對com.Waiter類做靜態檢查 對com.Waiter類的clone方法做靜態檢查 對com.Waiter類做靜態檢查 對com.Waiter類的toString方法做靜態檢查 //上面是織入前spring對目標類中的所有方法進行的靜態切點檢查 對com.Waiter類做靜態檢查 對com.Waiter類的serve方法做靜態檢查 對com.Waiter類的serve方法做動態檢查 waiter說:要點什么?Peter 對com.Waiter類做靜態檢查 對com.Waiter類的check方法做靜態檢查 //靜態方法檢查沒通過,不用動態檢查了 waiter說:結賬?Peter 對com.Waiter類的serve方法做動態檢查 //第二次調用不用執行靜態檢查 你好!TheViper //動態檢查,滿足白名單,執行前置增強 waiter說:要點什么?TheViper waiter說:結賬?TheViper
定義動態切點時,切勿忘記同時覆蓋getClassFilter()和matches(Method method,Class cls)方法,通過靜態切點檢查可以排除掉大部分不符合匹配規則的方法。
流程切面spring的流程切面由DefaultPointcutAdvisor和ControlFlowPointcut實現。流程切點代表由某個方法直接或間接發起調用的其他方法。
定義Waiter的代理
public class WaiterDelegate { private Waiter waiter; public void setWaiter(Waiter waiter) { this.waiter = waiter; } public void service(String name){ waiter.serve(name); waiter.check(name); } }
Waiter waiter=(Waiter)ctx.getBean("waiter2"); WaiterDelegate wd=new WaiterDelegate(); wd.setWaiter(waiter); waiter.serve("TheViper"); waiter.check("TheViper"); wd.service("TheViper");
waiter說:要點什么?TheViper waiter說:結賬?TheViper //直接調用,增強不起作用 你好!TheViper waiter說:要點什么?TheViper 你好!TheViper waiter說:結賬?TheViper復合切面
spring提供ComposablePointcut把兩個切點組合起來,通過切點的復合運算表示。
ComposablePointcut本身也是一個切點,它實現了Pointcut接口。
交集運算的方法
并集運算
import java.lang.reflect.Method; import org.springframework.aop.MethodMatcher; import org.springframework.aop.Pointcut; import org.springframework.aop.support.ComposablePointcut; import org.springframework.aop.support.ControlFlowPointcut; import org.springframework.aop.support.NameMatchMethodPointcut; public class GreetingComposablePointcut { public Pointcut getIntersectionPointcut(){ ComposablePointcut cp=new ComposablePointcut(); Pointcut pt1=new ControlFlowPointcut(WaiterDelegate.class,"service"); MethodMatcher pt2=new NameMatchMethodPointcut(){ public boolean matches(Method method, Class> cls) {// 切點方法靜態匹配 return "check".equals(method.getName()); } }; return cp.intersection(pt1).intersection(pt2); } }
#{gcp.intersectionPointcut}表示引用gcp.getIntersectionPointcut()方法返回的復合切點
Waiter waiter=(Waiter)ctx.getBean("waiter3"); WaiterDelegate wd=new WaiterDelegate(); wd.setWaiter(waiter); waiter.serve("TheViper"); waiter.check("TheViper"); wd.service("TheViper");
waiter說:要點什么?TheViper waiter說:結賬?TheViper //直接調用,增強不起作用 waiter說:要點什么?TheViper 你好!TheViper//匹配check方法 waiter說:結賬?TheViper引介切面
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/64690.html
摘要:配置切面編程方式實現說結賬說要點什么注解表示前置增強后面的切點表達式表示在目標類的方法織入增強,方法可以帶任意的傳入參數和任意的返回值。類相當于上一篇中的增強切點,切面三者聯合表達的信息。 @AspectJ配置切面 編程方式實現 public class Waiter { public void check(String name){ System.out.pr...
摘要:如何降低開發的復雜性最小侵入編程通過面向接口和依賴注入實現松耦合基于編程慣例和切面進行聲明式編程通過模板減少樣板式代碼容器在應用中,不再由對象自行創建或管理它們之間的依賴關系容器負責創建對象裝配對象配置它們并管理它們的整個生命周期。 歡迎大家關注我的微信公眾號,一起探討Java相關技術 showImg(https://segmentfault.com/img/bVboaBO?w=129...
摘要:開頭正式開啟我入職的里程,現在已是工作了一個星期了,這個星期算是我入職的過渡期,算是知道了學校生活和工作的差距了,總之,盡快習慣這種生活吧。當時是看的廖雪峰的博客自己也用做爬蟲寫過幾篇博客,不過有些是在前人的基礎上寫的。 showImg(https://segmentfault.com/img/remote/1460000010867984); 開頭 2017.08.21 正式開啟我...
摘要:是一種特殊的增強切面切面由切點和增強通知組成,它既包括了橫切邏輯的定義也包括了連接點的定義。實際上,一個的實現被拆分到多個類中在中聲明切面我們知道注解很方便,但是,要想使用注解的方式使用就必須要有源碼因為我們要 前言 只有光頭才能變強 上一篇已經講解了Spring IOC知識點一網打盡!,這篇主要是講解Spring的AOP模塊~ 之前我已經寫過一篇關于AOP的文章了,那篇把比較重要的知...
摘要:一讓廣播明星黯然失色要建立頁面,需要創建用超文本標記語言,編寫的文件,把它們放在一個服務器上二服務器能做什么服務器在互聯網上有一份全天候的工作。一、Web讓廣播明星黯然失色 要建立Web頁面,需要創建用超文本標記語言(HyperText Markup Language,HTML)編寫的文件,把它們放在一個Web服務器上二、Web服務器能做什么? Web服務器在互聯網上有一份全天候的工...
閱讀 1166·2021-11-22 15:22
閱讀 3837·2021-10-19 13:13
閱讀 3570·2021-10-08 10:05
閱讀 3292·2021-09-26 10:20
閱讀 2984·2019-08-29 14:21
閱讀 2192·2019-08-27 10:55
閱讀 1871·2019-08-26 10:31
閱讀 2578·2019-08-23 16:47