個人專題目錄
1.5. AOP
- 核心業(yè)務(wù)功能和切面功能分別獨立進行開發(fā) ,然后把切面功能和核心業(yè)務(wù)功能 "編織" 在一起茄螃,這就叫AOP
- 讓關(guān)注點代碼與業(yè)務(wù)代碼分離
- 面向切面編程就是指: 對很多功能都有的重復(fù)的代碼抽取缝驳,再在運行的時候往業(yè)務(wù)方法上動態(tài)植入“切面類代碼”。
- 應(yīng)用場景:日志归苍,事務(wù)管理用狱,權(quán)限控制AOP注解使用
代理分類
- jdk動態(tài)代理
- 主要通過Proxy.newProxyInstance()和InvocationHandler這兩個類和方法實現(xiàn)
- 創(chuàng)建代理類proxy實現(xiàn)Invocation接口,重寫invoke()方法
- 調(diào)用被代理類方法時默認調(diào)用此方法
- 將被代理類作為構(gòu)造函數(shù)的參數(shù)傳入代理類proxy
- 調(diào)用Proxy.newProxyInsatnce(classloader,interfaces,handler)方法生成代理類
- 生成的代理類
- $Proxy0 extends Proxy implements Person
- 類型為$Proxy0
- 因為已經(jīng)繼承了Proxy,所以java動態(tài)代理只能對接口進行代理
- 代理對象會實現(xiàn)用戶提供的這組接口霜医,因此可以將這個代理對象強制類型轉(zhuǎn)化為這組接口中的任意一個
- 通過反射生成對象
- 生成的代理類
- 總結(jié): 代理類調(diào)用自己方法時齿拂,通過自身持有的中介類對象來調(diào)用中介類對象的invoke方法,從而達到代理執(zhí)行被代理對象的方法肴敛。
- cglib
- 生成對象類型為Enhancer
- 實現(xiàn)原理類似于 jdk 動態(tài)代理署海,只是他在運行期間生成的代理對象是針
對目標(biāo)類擴展的子類
- 靜態(tài)代理
- 缺點
- 如果要代理一個接口的多個實現(xiàn)的話需要定義不同的代理類
- 代理類 和 被代理類 必須實現(xiàn)同樣的接口,萬一接口有變動医男,代理砸狞、被代理類都得修改
- 在編譯的時候就直接生成代理類
- 缺點
- JDK動態(tài)代理和cglib的對比
- CGLib所創(chuàng)建的動態(tài)代理對象在實際運行時候的性能要比JDK動態(tài)代理高
- 1.6和1.7的時候,CGLib更快
- 1.8的時候镀梭,jdk更快
- CGLib在創(chuàng)建對象的時候所花費的時間卻比JDK動態(tài)代理多
- singleton的代理對象或者具有實例池的代理刀森,因為無需頻繁的創(chuàng)建代理對象,所以比較適合采用CGLib動態(tài)代理报账,反之研底,則適合用JDK動態(tài)代理
- JDK動態(tài)代理是面向接口的,CGLib動態(tài)代理是通過字節(jié)碼底層繼承代理類來實現(xiàn)(如果被代理類被final關(guān)鍵字所修飾透罢,那么會失敯窕蕖)
- JDK生成的代理類類型是Proxy(因為繼承的是Proxy),CGLIB生成的代理類類型是Enhancer類型
- 如果要被代理的對象是個實現(xiàn)類羽圃,那么Spring會使用JDK動態(tài)代理來完成操作(Spirng默認采用JDK動態(tài)代理實現(xiàn)機制)乾胶;
如果要被代理的對象不是實現(xiàn)類,那么Spring會強制使用CGLib來實現(xiàn)動態(tài)代理朽寞。
- CGLib所創(chuàng)建的動態(tài)代理對象在實際運行時候的性能要比JDK動態(tài)代理高
AOP注解使用
- AOP:【動態(tài)代理】
- 指在程序運行期間動態(tài)的將某段代碼切入到指定方法指定位置進行運行的編程方式识窿;
- 導(dǎo)入aop模塊;Spring AOP:(spring-aspects)
- 定義一個業(yè)務(wù)邏輯類(MathCalculator)脑融;在業(yè)務(wù)邏輯運行的時候?qū)⑷罩具M行打佑髌怠(方法之前、方法運行結(jié)束吨掌、方法出現(xiàn)異常半抱,xxx)
- 定義一個日志切面類(LogAspects):切面類里面的方法需要動態(tài)感知MathCalculator.div運行到哪里然后執(zhí)行脓恕;
- 通知方法:
- 前置通知(@Before):logStart:在目標(biāo)方法(div)運行之前運行
- 后置通知(@After):logEnd:在目標(biāo)方法(div)運行結(jié)束之后運行(無論方法正常結(jié)束還是異常結(jié)束)
- 返回通知(@AfterReturning):logReturn:在目標(biāo)方法(div)正常返回之后運行
- 異常通知(@AfterThrowing):logException:在目標(biāo)方法(div)出現(xiàn)異常以后運行
- 環(huán)繞通知(@Around):動態(tài)代理,手動推進目標(biāo)方法運行(joinPoint.procced())
- 通知方法:
- 給切面類的目標(biāo)方法標(biāo)注何時何地運行(通知注解)窿侈;
- 將切面類和業(yè)務(wù)邏輯類(目標(biāo)方法所在類)都加入到容器中;
- 必須告訴Spring哪個類是切面類(給切面類上加一個注解:@Aspect)
- 給配置類中加 @EnableAspectJAutoProxy 【開啟基于注解的aop模式】炼幔,在Spring中很多的 @EnableXXX;
public class MathCalculator {
public int div(int i, int j) {
System.out.println("MathCalculator...div...");
return i / j;
}
}
/**
* 切面類
*
* @author xubh
* @Aspect: 告訴Spring當(dāng)前類是一個切面類
*/
@Aspect
public class LogAspects {
//抽取公共的切入點表達式
//1、本類引用
//2史简、其他的切面引用
@Pointcut("execution(public int com.xubh.aop.MathCalculator.*(..))")
public void pointCut() {
}
;
//@Before在目標(biāo)方法之前切入乃秀;切入點表達式(指定在哪個方法切入)
@Before("pointCut()")
public void logStart(JoinPoint joinPoint) {
Object[] args = joinPoint.getArgs();
System.out.println("" + joinPoint.getSignature().getName() + "運行。圆兵。跺讯。@Before:參數(shù)列表是:{" + Arrays.asList(args) + "}");
}
@After("com.xubh.aop.LogAspects.pointCut()")
public void logEnd(JoinPoint joinPoint) {
System.out.println("" + joinPoint.getSignature().getName() + "結(jié)束。殉农。刀脏。@After");
}
//JoinPoint一定要出現(xiàn)在參數(shù)表的第一位
@AfterReturning(value = "pointCut()", returning = "result")
public void logReturn(JoinPoint joinPoint, Object result) {
System.out.println("" + joinPoint.getSignature().getName() + "正常返回。超凳。愈污。@AfterReturning:運行結(jié)果:{" + result + "}");
}
@AfterThrowing(value = "pointCut()", throwing = "exception")
public void logException(JoinPoint joinPoint, Exception exception) {
System.out.println("" + joinPoint.getSignature().getName() + "異常。轮傍。暂雹。異常信息:{" + exception + "}");
}
}
/**
* AOP:【動態(tài)代理】
* 三步:
* 1)、將業(yè)務(wù)邏輯組件和切面類都加入到容器中创夜;告訴Spring哪個是切面類(@Aspect)
* 2)杭跪、在切面類上的每一個通知方法上標(biāo)注通知注解,告訴Spring何時何地運行(切入點表達式)
* 3)驰吓、開啟基于注解的aop模式涧尿;@EnableAspectJAutoProxy
*/
@EnableAspectJAutoProxy
@Configuration
public class MainConfigOfAOP {
//業(yè)務(wù)邏輯類加入容器中
@Bean
public MathCalculator calculator() {
return new MathCalculator();
}
//切面類加入到容器中
@Bean
public LogAspects logAspects() {
return new LogAspects();
}
}
public class IOCTest_AOP {
@Test
public void test01(){
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfigOfAOP.class);
//1、不要自己創(chuàng)建對象
// MathCalculator mathCalculator = new MathCalculator();
// mathCalculator.div(1, 1);
MathCalculator mathCalculator = applicationContext.getBean(MathCalculator.class);
mathCalculator.div(1, 0);
applicationContext.close();
}
}
AOP原理-@EnableAspectJAutoProxy
- AOP原理:【看給容器中注冊了什么組件檬贰,這個組件什么時候工作现斋,這個組件的功能是什么?】@EnableAspectJAutoProxy偎蘸;
-
@EnableAspectJAutoProxy是什么?
@Import(AspectJAutoProxyRegistrar.class):給容器中導(dǎo)入AspectJAutoProxyRegistrar
利用AspectJAutoProxyRegistrar自定義給容器中注冊bean瞬内;BeanDefinetion internalAutoProxyCreator=AnnotationAwareAspectJAutoProxyCreator 給容器中注冊一個AnnotationAwareAspectJAutoProxyCreator迷雪;
-
AnnotationAwareAspectJAutoProxyCreator:
AnnotationAwareAspectJAutoProxyCreator
->AspectJAwareAdvisorAutoProxyCreator ->AbstractAdvisorAutoProxyCreator ->AbstractAutoProxyCreator implements SmartInstantiationAwareBeanPostProcessor, BeanFactoryAware 關(guān)注后置處理器(在bean初始化完成前后做事情)、自動裝配BeanFactory
AOP原理-AnnotationAwareAspectJAutoProxyCreator分析
AbstractAutoProxyCreator.setBeanFactory()
AbstractAutoProxyCreator.有后置處理器的邏輯虫蝶;
AbstractAdvisorAutoProxyCreator.setBeanFactory()->initBeanFactory()
AnnotationAwareAspectJAutoProxyCreator.initBeanFactory()
AOP原理-注冊AnnotationAwareAspectJAutoProxyCreator
流程:
傳入配置類章咧,創(chuàng)建ioc容器
注冊配置類,調(diào)用refresh()刷新容器能真;
-
registerBeanPostProcessors(beanFactory);注冊bean的后置處理器來方便攔截bean的創(chuàng)建赁严;
- 先獲取ioc容器已經(jīng)定義了的需要創(chuàng)建對象的所有BeanPostProcessor
- 給容器中加別的BeanPostProcessor
- 優(yōu)先注冊實現(xiàn)了PriorityOrdered接口的BeanPostProcessor扰柠;
- 再給容器中注冊實現(xiàn)了Ordered接口的BeanPostProcessor;
- 注冊沒實現(xiàn)優(yōu)先級接口的BeanPostProcessor疼约;
- 注冊BeanPostProcessor卤档,實際上就是創(chuàng)建BeanPostProcessor對象,保存在容器中程剥; 創(chuàng)建internalAutoProxyCreator的BeanPostProcessor【AnnotationAwareAspectJAutoProxyCreator】
- 創(chuàng)建Bean的實例
- populateBean劝枣;給bean的各種屬性賦值
- initializeBean:初始化bean;
- invokeAwareMethods():處理Aware接口的方法回調(diào)
- applyBeanPostProcessorsBeforeInitialization():應(yīng)用后置處理器的postProcessBeforeInitialization()
- invokeInitMethods()织鲸;執(zhí)行自定義的初始化方法
- applyBeanPostProcessorsAfterInitialization()舔腾;執(zhí)行后置處理器的postProcessAfterInitialization();
- BeanPostProcessor(AnnotationAwareAspectJAutoProxyCreator)創(chuàng)建成功搂擦; -->aspectJAdvisorsBuilder
- 把BeanPostProcessor注冊到BeanFactory中;beanFactory.addBeanPostProcessor(postProcessor);
=======以上是創(chuàng)建和注冊AnnotationAwareAspectJAutoProxyCreator的過程========
AOP原理-AnnotationAwareAspectJAutoProxyCreator執(zhí)行時機
-
finishBeanFactoryInitialization(beanFactory);完成BeanFactory初始化工作稳诚;創(chuàng)建剩下的單實例bean
-
遍歷獲取容器中所有的Bean,依次創(chuàng)建對象getBean(beanName);
- getBean->doGetBean()->getSingleton()->
-
創(chuàng)建bean瀑踢,【AnnotationAwareAspectJAutoProxyCreator在所有bean創(chuàng)建之前會有一個攔截扳还,InstantiationAwareBeanPostProcessor,會調(diào)用postProcessBeforeInstantiation()】
先從緩存中獲取當(dāng)前bean丘损,如果能獲取到普办,說明bean是之前被創(chuàng)建過的,直接使用徘钥,否則再創(chuàng)建衔蹲;只要創(chuàng)建好的Bean都會被緩存起來
-
createBean();創(chuàng)建bean;AnnotationAwareAspectJAutoProxyCreator 會在任何bean創(chuàng)建之前先嘗試返回bean的實例呈础;【BeanPostProcessor是在Bean對象創(chuàng)建完成初始化前后調(diào)用的】【InstantiationAwareBeanPostProcessor是在創(chuàng)建Bean實例之前先嘗試用后置處理器返回對象的】
-
resolveBeforeInstantiation(beanName, mbdToUse);解析BeforeInstantiation舆驶;希望后置處理器在此能返回一個代理對象;如果能返回代理對象就使用而钞,如果不能就繼續(xù)
- 后置處理器先嘗試返回對象沙廉;
bean = applyBeanPostProcessorsBeforeInstantiation(): 拿到所有后置處理器,如果是InstantiationAwareBeanPostProcessor; 就執(zhí)行postProcessBeforeInstantiation if (bean != null) { bean = applyBeanPostProcessorsAfterInitialization(bean, beanName); }
doCreateBean(beanName, mbdToUse, args);真正的去創(chuàng)建一個bean實例臼节;和3.6流程一樣撬陵;
-
-
AOP原理-AnnotationAwareAspectJAutoProxyCreator執(zhí)行時機
參考上面步驟4
AOP原理-創(chuàng)建AOP代理
- AnnotationAwareAspectJAutoProxyCreator【InstantiationAwareBeanPostProcessor】 的作用:
- 每一個bean創(chuàng)建之前,調(diào)用postProcessBeforeInstantiation(); 關(guān)心MathCalculator和LogAspect的創(chuàng)建
- 判斷當(dāng)前bean是否在advisedBeans中(保存了所有需要增強bean)
- 判斷當(dāng)前bean是否是基礎(chǔ)類型的Advice网缝、Pointcut巨税、Advisor、AopInfrastructureBean粉臊,或者是否是切面(@Aspect)
- 是否需要跳過
- 獲取候選的增強器(切面里面的通知方法)【List<Advisor> candidateAdvisors】每一個封裝的通知方法的增強器是 InstantiationModelAwarePointcutAdvisor草添;判斷每一個增強器是否是 AspectJPointcutAdvisor 類型的;返回true
- 永遠返回false
- 創(chuàng)建對象,postProcessAfterInitialization扼仲;return wrapIfNecessary(bean, beanName, cacheKey);//包裝如果需要的情況下
- 獲取當(dāng)前bean的所有增強器(通知方法) Object[] specificInterceptors
- 找到候選的所有的增強器(找哪些通知方法是需要切入當(dāng)前bean方法的)
- 獲取到能在bean使用的增強器远寸。
- 給增強器排序
- 保存當(dāng)前bean在advisedBeans中抄淑;
- 如果當(dāng)前bean需要增強,創(chuàng)建當(dāng)前bean的代理對象驰后;
- 獲取所有增強器(通知方法)
- 保存到proxyFactory
- 創(chuàng)建代理對象:Spring自動決定肆资;JdkDynamicAopProxy(config);jdk動態(tài)代理;ObjenesisCglibAopProxy(config);cglib的動態(tài)代理倡怎;
- 給容器中返回當(dāng)前組件使用cglib增強了的代理對象迅耘;
- 以后容器中獲取到的就是這個組件的代理對象,執(zhí)行目標(biāo)方法的時候监署,代理對象就會執(zhí)行通知方法的流程颤专;
- 獲取當(dāng)前bean的所有增強器(通知方法) Object[] specificInterceptors
AOP原理-獲取攔截器鏈-MethodInterceptor
- 目標(biāo)方法執(zhí)行;容器中保存了組件的代理對象(cglib增強后的對象)钠乏,這個對象里面保存了詳細信息(比如增強器栖秕,目標(biāo)對象,xxx)晓避;
- CglibAopProxy.intercept();攔截目標(biāo)方法的執(zhí)行
- 根據(jù)ProxyFactory對象獲取將要執(zhí)行的目標(biāo)方法攔截器鏈簇捍;List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
- List<Object> interceptorList保存所有攔截器 5;一個默認的ExposeInvocationInterceptor 和 4個增強器俏拱;
- 遍歷所有的增強器暑塑,將其轉(zhuǎn)為Interceptor;registry.getInterceptors(advisor);
- 將增強器轉(zhuǎn)為List<MethodInterceptor>锅必;如果是MethodInterceptor事格,直接加入到集合中;如果不是搞隐,使用AdvisorAdapter將增強器轉(zhuǎn)為MethodInterceptor驹愚;轉(zhuǎn)換完成返回MethodInterceptor數(shù)組;
- 如果沒有攔截器鏈劣纲,直接執(zhí)行目標(biāo)方法;攔截器鏈(每一個通知方法又被包裝為方法攔截器逢捺,利用MethodInterceptor機制)
- 如果有攔截器鏈,把需要執(zhí)行的目標(biāo)對象癞季,目標(biāo)方法劫瞳,攔截器鏈等信息傳入創(chuàng)建一個 CglibMethodInvocation 對象,并調(diào)用 Object retVal = mi.proceed();
- 攔截器鏈的觸發(fā)過程;
- 如果沒有攔截器執(zhí)行執(zhí)行目標(biāo)方法绷柒,或者攔截器的索引和攔截器數(shù)組-1大小一樣(指定到了最后一個攔截器)執(zhí)行目標(biāo)方法柠新;
- 鏈?zhǔn)将@取每一個攔截器,攔截器執(zhí)行invoke方法辉巡,每一個攔截器等待下一個攔截器執(zhí)行完成返回以后再來執(zhí)行;攔截器鏈的機制蕊退,保證通知方法與目標(biāo)方法的執(zhí)行順序郊楣;
AOP原理-鏈?zhǔn)秸{(diào)用通知方法及總結(jié)
- @EnableAspectJAutoProxy 開啟AOP功能
- @EnableAspectJAutoProxy 會給容器中注冊一個組件 AnnotationAwareAspectJAutoProxyCreator
- AnnotationAwareAspectJAutoProxyCreator是一個后置處理器憔恳;
- 容器的創(chuàng)建流程:
- registerBeanPostProcessors()注冊后置處理器;創(chuàng)建AnnotationAwareAspectJAutoProxyCreator對象
- finishBeanFactoryInitialization()初始化剩下的單實例bean
- 創(chuàng)建業(yè)務(wù)邏輯組件和切面組件
- AnnotationAwareAspectJAutoProxyCreator攔截組件的創(chuàng)建過程
- 組件創(chuàng)建完之后净蚤,判斷組件是否需要增強钥组;是:切面的通知方法,包裝成增強器(Advisor);給業(yè)務(wù)邏輯組件創(chuàng)建一個代理對象(cglib)今瀑;
- 執(zhí)行目標(biāo)方法:
- 代理對象執(zhí)行目標(biāo)方法
- CglibAopProxy.intercept()程梦;
- 得到目標(biāo)方法的攔截器鏈(增強器包裝成攔截器MethodInterceptor)
- 利用攔截器的鏈?zhǔn)綑C制,依次進入每一個攔截器進行執(zhí)行橘荠;
- 效果:
- 正常執(zhí)行:前置通知-》目標(biāo)方法-》后置通知-》返回通知
- 出現(xiàn)異常:前置通知-》目標(biāo)方法-》后置通知-》異常通知