SpringAOP+原始碼解析,切就完事了
阿新 • • 發佈:2020-09-11
本文是對近期學習知識的一個總結,附帶原始碼註釋及流程圖,如有不足之處,還望評論區批評指正。
[toc]
此處感謝javadoop的原始碼解析,收益匪淺:[https://javadoop.com/post/spring-aop-intro](https://javadoop.com/post/spring-aop-intro)
# 一、AOP、SpringAOP、AspectJ的區別
AOP為`Aspect Oriented Programming`的縮寫,意為:**面向切面程式設計**,通過**預編譯方式和執行期間動態代理**實現程式功能的統一維護的一種技術。利用AOP可以**對業務邏輯的各個部分進行隔離**,從而使得業務邏輯各部分之間的耦合度降低,提高程式的可重用性,同時提高了開發的效率。
文縐縐的,沒用過確實很懵,但是用過之後,不說清晰,起碼有內意思了。
關於SpringAOP和AspectJ,參考Javadoop老師的解釋:[https://javadoop.com/post/spring-aop-intro](https://javadoop.com/post/spring-aop-intro)
> **Spring AOP:**
>
> - 它基於動態代理來實現。預設地,如果使用介面的,用 JDK 提供的動態代理實現,如果沒有介面,使用 CGLIB 實現。大家一定要明白背後的意思,包括什麼時候會不用 JDK 提供的動態代理,而用 CGLIB 實現。
> - Spring 3.2 以後,spring-core 直接就把 CGLIB 和 ASM 的原始碼包括進來了,這也是為什麼我們不需要顯式引入這兩個依賴
> - Spring 的 IOC 容器和 AOP 都很重要,Spring AOP 需要依賴於 IOC 容器來管理。
> - 如果你是 web 開發者,有些時候,你可能需要的是一個 Filter 或一個 Interceptor,而不一定是 AOP。
> - Spring AOP 只能作用於 Spring 容器中的 Bean,它是使用純粹的 Java 程式碼實現的,只能作用於 bean 的方法。
> - Spring 提供了 AspectJ 的支援,後面我們會單獨介紹怎麼使用,一般來說我們用**純的** Spring AOP 就夠了。
> - 很多人會對比 Spring AOP 和 AspectJ 的效能,Spring AOP 是基於代理實現的,在容器啟動的時候需要生成代理例項,在方法呼叫上也會增加棧的深度,使得 Spring AOP 的效能不如 AspectJ 那麼好。
>
> **AspectJ:**
>
> - AspectJ 出身也是名門,來自於 Eclipse 基金會,link:https://www.eclipse.org/aspectj
>
> - 屬於靜態織入,它是通過修改程式碼來實現的,它的織入時機可以是:
> - Compile-time weaving:編譯期織入,如類 A 使用 AspectJ 添加了一個屬性,類 B 引用了它,這個場景就需要編譯期的時候就進行織入,否則沒法編譯類 B。
> - Post-compile weaving:也就是已經生成了 .class 檔案,或已經打成 jar 包了,這種情況我們需要增強處理的話,就要用到編譯後織入。
> - **Load-time weaving**:指的是在載入類的時候進行織入,要實現這個時期的織入,有幾種常見的方法。1、自定義類載入器來幹這個,這個應該是最容易想到的辦法,在被織入類載入到 JVM 前去對它進行載入,這樣就可以在載入的時候定義行為了。2、在 JVM 啟動的時候指定 AspectJ 提供的 agent:`-javaagent:xxx/xxx/aspectjweaver.jar`。
>
> - AspectJ 能幹很多 Spring AOP 幹不了的事情,它是 **AOP 程式設計的完全解決方案**。Spring AOP 致力於解決的是企業級開發中最普遍的 AOP 需求(方法織入),而不是力求成為一個像 AspectJ 一樣的 AOP 程式設計完全解決方案。
> - 因為 AspectJ 在實際程式碼執行前完成了織入,所以大家會說它生成的類是沒有額外執行時開銷的。
# 二、AOP關鍵術語
![](https://img-blog.csdnimg.cn/2020050115000823.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L1NreV9RaWFvQmFfU3Vt,size_16,color_FFFFFF,t_70)
- 切面(Aspect):也就是我們定義的專注於提供輔助功能的模組,比如安全管理,日誌資訊等。
- 連線點(JoinPoint):切面程式碼可以通過連線點切入到正常業務之中,圖中每個方法的每個點都是連線點。
- 切入點(PointCut):一個切面不需要通知所有的連線點,而**在連線點的基礎之上增加切入的規則**,選擇需要增強的點,最終真正通知的點就是切入點。
- 通知方法(Advice):就是切面需要執行的工作,主要有五種通知:before,after,afterReturning,afterThrowing,around。
- 織入(Weaving):將切面應用到目標物件並建立代理物件的過程,SpringAOP選擇再目標物件的執行期動態建立代理對
- 引入(introduction):在不修改程式碼的前提下,引入可以在執行期為類動態地新增方法或欄位。
# 三、通知的五種型別
- 前置通知Before:目標方法呼叫之前執行的通知。
- 後置通知After:目標方法完成之後,無論如何都會執行的通知。
- 返回通知AfterReturning:目標方法成功之後呼叫的通知。
- 異常通知AfterThrowing:目標方法丟擲異常之後呼叫的通知。
- 環繞通知Around:可以看作前面四種通知的綜合。
# 四、切入點表示式
上面提到:連線點增加切入規則就相當於定義了切入點,當然切入點表示式分為兩種:within和execution,這裡主要學習execution表示式。
- 寫法:execution(訪問修飾符 返回值 包名.包名……類名.方法名(引數列表))
- 例:`execution(public void com.smday.service.impl.AccountServiceImpl.saveAccount())`
- 訪問修飾符可以省略,返回值可以使用萬用字元*匹配。
- 包名也可以使用`*`匹配,數量代表包的層級,當前包可以使用`..`標識,例如`* *..AccountServiceImpl.saveAccount()`
- 類名和方法名也都可以使用`*`匹配:`* *..*.*()`
- 引數列表使用`..`可以標識有無引數均可,且引數可為任意型別。
> 全通配寫法:`* *…*.*(…)`
通常情況下,切入點應當設定再業務層實現類下的所有方法:`* com.smday.service.impl.*.*(..)`。
# 五、AOP應用場景
- 記錄日誌
- 監控效能
- 許可權控制
- 事務管理
# 六、AOP原始碼分析
## SpringBean的生命週期
寫了好多篇文章,每次都要來回顧一下SpringBean的生命週期,可見它真的十分重要。
- [Spring的完整生命週期。](https://www.cnblogs.com/summerday152/p/13639896.html#bean-%E7%9A%84%E7%94%9F%E5%91%BD%E5%91%A8%E6%9C%9F)
- [Spring解決迴圈依賴,對例項化之後,初始化之前動了手腳。](https://www.cnblogs.com/summerday152/p/13648377.html#springbean%E7%9A%84%E5%88%9B%E5%BB%BA%E6%B5%81%E7%A8%8B)
Spring的Aop又是在哪完成的對目標物件的代理呢?我們大概也能夠想到,其實就是在執行回撥的時候。按照慣例,先複習一下,從getBean開始到返回Bean經歷了什麼:
![](https://img2020.cnblogs.com/blog/1771072/202009/1771072-20200911172406955-617808414.png)
回顧完SpringBean的建立流程之後,我們以註解方式`@EnableAspectJAutoProxy`配置Aop開啟@Aspectj為例,進行一波AOP的流程總結:
## AOP的流程總結
通過原始碼可以發現,其實是通過`@EnableAspectJAutoProxy`註解注入了一個`AnnotationAwareAspectJAutoProxyCreator`,但這個類中其實並沒有重寫`postProcessAfterInitialization()`,最終實現其實是在`AbstractAutoProxyCreator`中。
具體乾的事情,我已經通過一張圖總結出來了,如果想要了解更加具體的資訊,不妨開啟原始碼,可以看的更加清晰一些。
![](https://img2020.cnblogs.com/blog/1771072/202009/1771072-20200911172534811-1175507146.png)
### AnnotationAwareAspectJAutoProxyCreator的註冊
首先是對`AnnotationAwareAspectJAutoProxyCreator`的註冊環節:【在此不作贅述】
```java
class AspectJAutoProxyBeanDefinitionParser implements BeanDefinitionParser {
@Override
@Nullable
// 1. 註冊proxy creator
public BeanDefinition parse(Element element, ParserContext parserContext) {
AopNamespaceUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(parserContext, element);
extendBeanDefinition(element, parserContext);
return null;
}
}
```
### applyBeanPostProcessorsAfterInitialization入口
AbstractAutowireCapableBeanFactory.java
```java
protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) {
//如果bean實現了BeanNameAware、BeanClassLoaderAware、BeanFactoryAware介面, 回撥
invokeAwareMethods(beanName, bean);
Object wrappedBean = bean;
//aop在init-method之前並沒有進行操作, 目前還是原來那個物件
if (mbd == null || !mbd.isSynthetic()) {
//BeanPostProcessor 的 postProcessBeforeInitialization 回撥
wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
}
//處理bean中定義的init-method或 bean實現了InitializingBean ,呼叫afterPropertiesSet() 方法
invokeInitMethods(beanName, wrappedBean, mbd);
if (mbd == null || !mbd.isSynthetic()) {
//BeanPostProcessor 的 postProcessAfterInitialization 回撥 注意這裡!
wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
}
return wrappedBean;
}
```
```java
@Override
public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)
throws BeansException {
Object result = existingBean;
for (BeanPostProcessor processor : getBeanPostProcessors()) {
//AnnotationAwareAspectJAutoProxyCreator
Object current = processor.postProcessAfterInitialization(result, beanName);
if (current == null) {
return result;
}
result = current;
}
return result;//返回[可能代理後的]結果
}
```
### AbstractAutoProxyCreator的主要方法
```java
//SpringAop在IOC容器建立bean例項的最後對bean進行處理,進行代理增強, AbstractAutoProxyCreato
@Override
public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
if (bean != null) {
Object cacheKey = getCacheKey(bean.getClass(), beanName);
if (this.earlyProxyReferences.remove(cacheKey) != bean) {
return wrapIfNecessary(bean, beanName, cacheKey);//這個方法將返回代理類
}
}
return bean;
}
```
```java
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
//返回匹配當前bean 的所有的advisor, advice, interceptor
Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
if (specificInterceptors != DO_NOT_PROXY) {
this.advisedBeans.put(cacheKey, Boolean.TRUE);
//在這裡建立代理
Object proxy = createProxy(
bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
this.proxyTypes.put(cacheKey, proxy.getClass());
return proxy;
}
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return bean;
}
```
### createProxy過程
```java
protected Object createProxy(Class> beanClass, @Nullable String beanName,
@Nullable Object[] specificInterceptors, TargetSource targetSource) {
//建立ProxyFactory例項
ProxyFactory proxyFactory = new ProxyFactory();
proxyFactory.copyFrom(this);
//在schema-based配置方式裡,可以將 proxy-target-class="true",這樣不管有沒有介面都使用cglib
if (!proxyFactory.isProxyTargetClass()) {
if (shouldProxyTargetClass(beanClass, beanName)) {
proxyFactory.setProxyTargetClass(true);
}
else {
evaluateProxyInterfaces(beanClass, proxyFactory);
}
}
//返回當前bean的advisors陣列
Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
proxyFactory.addAdvisors(advisors); //設定advisors陣列
proxyFactory.setTargetSource(targetSource);//targetSource 攜帶了真實實現的資訊
customizeProxyFactory(proxyFactory);
proxyFactory.setFrozen(this.freezeProxy);
if (advisorsPreFiltered()) {
proxyFactory.setPreFiltered(true);
}
return proxyFactory.getProxy(getProxyClassLoader());//getProxy(getProxyClassLoader())這一步建立代理
}
```
### JDK動態代理和CGLIB動態代理何時使用
這一步產生分歧的地方在ProxyFactory的getProxy方法,在getProxy之前,首先需要執行createAopProxy,而createAopProxy方法又被這個AopProxyFactory呼叫:
```java
protected final synchronized AopProxy createAopProxy() {
if (!this.active) {
activate();
}
//建立AopProxy之前,需要一個AopProxyFactory
return getAopProxyFactory().createAopProxy(this);
}
// ProxyCreatorSupport
//這個aopProxyFactory用於建立aopProxy, 之後可以用aopProxy.getProxy(classLoader)建立代理
public ProxyCreatorSupport() {
this.aopProxyFactory = new DefaultAopProxyFactory();
}
```
也就是最後會走到DefaultAopProxyFactory中
```java
@Override
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
if (!IN_NATIVE_IMAGE &&
(config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config))) {
Class> targetClass = config.getTargetClass();
if (targetClass == null) {
throw new AopConfigException();
}
//如果要代理的類本身就是介面,使用JDK動態代理
if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
return new JdkDynamicAopProxy(config);
}
//jdk動態代理基於介面,只有介面中的方法才會被增強, cglib基於類繼承,如果方法使用了final或者private修飾,也不能增強
return new ObjenesisCglibAopProxy(config);
}
else {
// 如果有介面,會跑到這個分支
return new JdkDynamicAopProxy(config);
}
}
```
總結:
- 如果被代理的目標實現了一個或多個自定義的介面,那麼就會使用JDK動態代理。
- 如果沒有實現任何介面,則使用CGLIB實現代理。
- 如果設定`proxy-target-class=true `或` `則不管有沒有實現介面都會使用CGLIB。
# 七、JDK動態代理的實現
最終的最終,都會走到真正建立代理物件的流程上:
```java
@Override
public Object getProxy(@Nullable ClassLoader classLoader) {
//獲取代理介面
Class>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true);
//查詢代理目標的介面是否定義equals和hashcode方法
findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
//使用jdk動態代理建立代理物件
return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
}
```
第一個引數:classLoader。
第二個引數:實現的介面。
第三個引數:InvocationHandler例項。
而本身JdkDynamicAopProxy本就實現了InvocationHandler,因此傳入this。至此,當呼叫被代理類的方法的時候,都會最終呼叫代理類實現的invoke方法,在這個方法中定義橫切的邏輯。
```java
public interface InvocationHandler {
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable;
}
```
- proxy:代理物件的引用。
- method:當前執行的方法。
- args:當前執行方法所需的引數。
- return:和被代理物件有相同的返回值。
```java
@Override
@Nullable
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //當生成的代理類對外提供服務的時候,都會匯入到這個invoke方法中
Object oldProxy = null;
boolean setProxyContext = false;
TargetSource targetSource = this.advised.targetSource;
Object target = null;
try {
if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {
// 對equals方法的代理
return equals(args[0]);
}
else if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {
//對hashCode()方法的代理
return hashCode();
}
//...
Object retVal;
//如果設定了exposeProxy,將proxy放入ThreadLocal中
if (this.advised.exposeProxy) {
oldProxy = AopContext.setCurrentProxy(proxy);
setProxyContext = true;
}
// Get as late as possible to minimize the time we "own" the target,
// in case it comes from a pool.
target = targetSource.getTarget();
Class> targetClass = (target != null ? target.getClass() : null);
// 獲取目標方法的攔截鏈,包含所有要執行的 advice
List