#1 2015-10-14 AOP討論java
強烈推薦文章正則表達式
本文則是根據上述文章進行的羣內討論的提綱編程
#2 從AOP攔截的時機來看待AOPjvm
如圖:ide
1 aspectj : 在編譯期靜態植入工具
2 JDK Proxy: 目標類加載後,爲其接口動態生成代理類this
3 cglib : 目標類加載後,爲其動態生成子類,將切面邏輯加入子類中.net
4 自定義類加載器:在目標類加載前,將切面邏輯加入目標類字節碼中代理
5 字節碼轉換器:在目標類加載前,將切面邏輯加入目標類字節碼中(jdk1.5的Instrumentation);在目標類加載後,將切面邏輯加入目標類字節碼中(jdk1.6的Instrumentation觸發jvm從新類加載)日誌
在類的字節碼載入jvm前會調用ClassFileTransformer的transform方法,從而實現修改原類方法的功能,實現aop
如日誌記錄工具anylog,就是使用自定義的ClassFileTransformer來動態修改字節碼(內部藉助於javassist來修改字節碼)
#3 角色梳理:AOP聯盟、Aspectj、SpringAOP
##3.1 AOP的概念
Pointcut:切入點,表示在哪裏進行攔截,簡單理解如正則表達式
Joinpoint:鏈接點,符合上述正則表達式的一個具體對象
Advice:加強,即攔截邏輯
Advice的類型:
Aspect:切面,包含切入點和加強
Target Object:要代理的目標對象
AOP Proxy:代理對象
Weaving:織入,能夠是編譯期、字節碼加載前、字節碼加載後
##3.2 Aspectj
以下簡單使用案例:
這裏有個系列文章跟我學aspectj
#4 SpringAOP的發展歷程
##4.1 JDK和CGLib動態代理的原始方式
JDK動態代理:
CGLib動態代理:
##4.2 ProxyFactory對jdk和cglib的封裝
ProxyFactory簡單的對jdk和cglib的封裝,根據配置(強制採用cglib)或者自行決定(根據是否實現接口)採用jdk動態代理仍是cglib動態代理。
使用案例以下:
對jdk動態代理的封裝(cglib也相似再也不說明):
InvocationHandler h:固定是JdkDynamicAopProxy,其中對
InvocationHandler的接口方法Object invoke(Object proxy, Method method, Object[] args)實現以下:
第一步:根據調用的類和方法獲取攔截器MethodInterceptor集合,內部會使用pointcut來進行過濾。後面詳細說說MethodInterceptor和Advice的區別。
ProxyFactory有以下兩種方法:
addAdvice(Advice advice) addAdvisor(Advisor advisor)
Advisor:實際上是Advice和Pointcut的集合,因此在添加Advice是會給出一個默認的Pointcut
Pointcut能夠是:
正則表達式類型 JdkRegexpMethodPointcut
aspectj的pointcut表達式類型,如
execution(* com.aspectj.demo.test.HelloWorld.main(..)) @annotation(aop.demo.Tag)
第二步:若是攔截器集合爲空(即沒有代理該方法),則經過反射直接執行目標對象target的方法
第三步:若是攔截器集合不爲空,依次執行攔截器MethodInterceptor中的invoke方法。
MethodInterceptor和Advice的區別:
Advice:定義了攔截邏輯,如MethodBeforeAdvice,接口定義以下:
public interface MethodBeforeAdvice extends BeforeAdvice { void before(Method method, Object[] args, Object target) throws Throwable; }
而MethodInterceptor則是決定Advice的調用時機,同時對外提供統一的調用方法invoke,如MethodBeforeAdviceInterceptor
public class MethodBeforeAdviceInterceptor implements MethodInterceptor, Serializable { private MethodBeforeAdvice advice; public MethodBeforeAdviceInterceptor(MethodBeforeAdvice advice) { Assert.notNull(advice, "Advice must not be null"); this.advice = advice; } @Override public Object invoke(MethodInvocation mi) throws Throwable { this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis() ); return mi.proceed(); } }
上述就是ProxyFactory對jdk和cglib的封裝,簡化了咱們的操做。可是還有以下問題:
##4.3 批量代理
上述ProxyFactory還只能針對某一個target進行代理
咱們如今的需求是:對一批對象進行批量代理。這時須要兩大部分:
一批Advice和Pointcut組合,即會有多個Advisor,符合不一樣Pointcut的進行不一樣的Advice加強。這裏即切面,切面並無具體的類進行描述,能夠理解成一個Advisor的集合(一個Advisor包含了一個Advice和一個Pointcut),以下所示
如xml配置形式配置切面:
註解形式配置切面,同時要開啓aop:aspectj-autoproxy配置:
切面僅僅是描述哪些類的哪些方法被哪些Advice加強,僅僅是描述而已,須要有一個類來承擔這樣的批量代理的工做,這就是基類AbstractAutoProxyCreator來完成這一項工做,對於每個符合條件的bean都仍然採用ProxyFactory來進行代理的建立:
因此Spring中的AOP所有是基於ProxyFactory這套建立流程的。而AbstractAutoProxyCreator子類則是分別取解析不一樣的切面配置
如上述的aop:config配置是啓用了AspectJAwareAdvisorAutoProxyCreator來進行批量代理的
如上述註解形式開啓的aop:aspectj-autoproxy是啓用了AnnotationAwareAspectJAutoProxyCreator來進行批量代理的
##4.4 Spring對aspectj的引入的體現
咱們知道aspectj是靜態織入,Spring是怎麼來引入aspectj呢?
aspectj是靜態織入,須要單獨的編譯器來進行編譯,可是功能強大。如使用案例:
強大的pointcut表達式功能(具體的自行去搜),同時還具備了註解功能,以下:
等這些都是aspectj的實現
而什麼才叫Spring最原始的AOP呢?就是ProxyFactory這一層,僅僅對jdk和cglib進行了簡單的封裝。
Spring原始的ProxyFactory代理,手動編程或配置進行代理都比較麻煩,引入了aspectj的某些功能
可是引入以後呢,仍是所有轉化成Spring本來的Advisor,最終仍然使用本來的ProxyFactory來建立代理,而不是像aspectj那樣須要單獨的編譯器。