a 一. AOP介紹
AOP(Aspect Oriented Programming) 面向切面編程, 是OOP(Object Oriented Programming)面向對象編程的 補充,是一種思想。 OOP的特性(封裝,繼承,多態,抽象),主要強調的 對象或者類之間的縱向關係, 而橫向關係卻無法得到確保。 AOP正是補充的橫向關係。
一個非常簡單的小例子,例如我們當前有三個類(User,Admin,Employee),都有findOne查詢, 我們需要統計查詢消耗的時間,在只有OOP的時候,我們就要這麼寫(如下圖)。 亦或是根據繼承,可以自行在BaseService裏封裝兩個方法,一個beforeExecute(),一個afterExecute(), 執行時候換成 super.beforeExecute()和 super.afterExecute()。大同小異。
import ..... // 省略
@Service
public class AdminService extends BaseService{
@Resource
private AdminDAO adminDAO;
// Slf4j日誌
private final Logger logger = LoggerFactory.getLogger(ScheduledService.class);
public Admin findOne() {
logger.info("查詢開始了" + System.currentTimeMillis());
return adminDAO.findOne(); // "SELECT * FROM admin limit 1"
logger.info("查詢結束了" + System.currentTimeMillis());
}
}
Employee和User同理,不重複了
可以看到,每一個實現最起碼要調用一次,(大項目成百上千的model,service),重複量就很大, 如果是想修改其中的邏輯,就需要每一個都修改一次,重複工作量很大。
AOP利用 "橫切"思想,橫向擴展類,並將公共行爲封裝到一起(所謂的Aspect,切面),減少系統的重複代碼,降低模塊之間的耦合度,提升可維護度。 二. AOP一些常用概念
1. advice : 直譯 爲 "增強",或者"通知"。 是aop執行的具體邏輯操作, spring定義了 前置(beforeAdvice),後置(afterAdvice),異常(ThrowsAdvice),引入(DynamicIntroductionAdvice)四種,具體目錄爲org.springframework.aop。 需要注意的是, 第五種, 環繞(AspectJAroundAdvice)不是spring定義的,而是AspectJ定義的,具體路徑爲org.springframework.aop.aspectj。
2. join point : 連接點。 是在邏輯執行時可插入切面的一個點。這個點可以是調用方法時、拋出異常時或者其他行爲。切面代碼可以利用這些點插入到應用的正常流程之中,並添加新的行爲。
3. pointcut : 切點。切點的定義會匹配通知所要織入的一個或多個連接點。我們通常使用明確的類和方法名稱,或是通過表達式匹配的一組類或方法。
關於切點和連接點,查資料查的我一度非常混淆這兩個甚至覺得這就是一個概念。 在官方搜索的一個小博客裏,看到了這樣一句話 "pointcut that allows selecting join points in beans with a matching name pattern". 大概我自己的理解就是 切點一定是連接點,而連接點不一定是切點。
4.aspect : 切面。 是一個大的概念,切面應該是pointcut + advice ,是一個整體的東西。
5.weaving : 織入。 構建新對象(代理對象)的方法,spring選擇在運行時動態注入,而aspectj選擇在編譯器或者類裝載時,根據類的修飾決定是jdk代理還是cglib代理。一般都是由ProxyFactory來完成
總的來說,AOP通過切點進行織入, 通過代理的方式構造出代理對象,來替代原對象進行邏輯的執行。
三. aop的源碼分析
使用過springboot的童鞋們都知道,利用註解@EnableAspectJAutoProxy則可以開啓aop模式(記得加載aop的jar包)。 而早些版本的spring則需要在xml文件中配置 <aop:config>或者是<aop:aspectj-autoproxy>來開啓。
我們從@EnableAspectJAutoProxy 追溯進去,到達註解類的源碼。
package org.springframework.context.annotation; // 具體的包位置 有興趣的可以看看
import ...// import省略
@Target({ElementType.TYPE}) // 元註解之一, 表示該註解的修飾範圍
@Retention(RetentionPolicy.RUNTIME) // 元註解之一, 表示該類保留的階段
@Documented // 元註解之一,標記爲公衆api,可以javadoc文檔話
@Import({AspectJAutoProxyRegistrar.class}) // 導入註解,類似於組合,將參數類(可以爲多個)注入到當前容器中
public @interface EnableAspectJAutoProxy {
// 代理類標誌,爲true代表直接代理類,需要利用cglib代理。 爲false則直接使用傳統的jdk代理方式
boolean proxyTargetClass() default false;
// 是否將該代理對象暴露爲線程共享,爲true則暴露,使用threadLocal方式,爲false則忽略
boolean exposeProxy() default false;
}
ps: 關於元註解,其實是修飾註解的註解,大概所有@Enable開頭的註解類都有這幾個修飾。 可以參考這篇文章 : java元註解
ps_2: 關於@Import,個人更容易理解爲組合的模式,沒有繼承或者實現的關係,更像是一種內部類的感覺,類似於php的trait,或是python的imp.load_source。
看一下AspectJAutoProxyRegisterar的源碼,發現只有一個方法
package org.springframework.context.annotation;
import ... // import省略
class AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar {
AspectJAutoProxyRegistrar() {
}
/*
* @param AnnotationMetadata 導入數據的配置類
* @param BeanDefinitionRegistry 一種bean的定義格式,包含一些註冊的接口,通常爲beanFactory
*/
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
// 註冊成spring使用的beanDefinition格式 --- 1
AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);
// 將配置數據註冊到 AnnotationAttributes格式(封裝的特殊map) 的屬性列表
AnnotationAttributes enableAspectJAutoProxy = AnnotationConfigUtils.attributesFor(importingClassMetadata,
EnableAspectJAutoProxy.class);
// 判斷配置數據的map
if (enableAspectJAutoProxy != null) {
// 判斷對應的屬性值(該屬性值上文已講), 爲true則直接使用cglib代理 ---2
if (enableAspectJAutoProxy.getBoolean("proxyTargetClass")) {
AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
}
// 判斷對應的屬性值(該屬性值上文已講), 爲true則直接使用暴露性接口 ---3
if (enableAspectJAutoProxy.getBoolean("exposeProxy")) {
AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);
}
}
}
}
重點就是 1的方法,註冊成beanDefinitition,包含了整體的核心邏輯。 其中 最重要的則是被調用的重載方法, 將一個很核心的類AnnotationAwareAspectJAutoProxyCreator注入。
public abstract class AopConfigUtils {
//外部調用的註冊方法,實際調用的是內部對應的重載方法
@Nullable // 參數可爲null的註解
public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry) {
return registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry, (Object)null);
}
// 被調用的重載方法,此步是重點
@Nullable
public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry, @Nullable Object source) {
// 在此地方注入AnnotationAwareAspectJAutoProxyCreator 這個類,是aop的關鍵類
return registerOrEscalateApcAsRequired(AnnotationAwareAspectJAutoProxyCreator.class, registry, source);
}
// 執行註冊
@Nullable
private static BeanDefinition registerOrEscalateApcAsRequired(Class<?> cls, BeanDefinitionRegistry registry, @Nullable Object source) {
Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
// 實際註冊的org.springframework.aop.config.internalAutoProxyCreator
if (registry.containsBeanDefinition("org.springframework.aop.config.internalAutoProxyCreator")) {
// 獲取internalAutoProxyCreator的定義
BeanDefinition apcDefinition = registry.getBeanDefinition("org.springframework.aop.config.internalAutoProxyCreator");
// 與我們傳入的類名做對比, 如果一致則不做處理。 如果不一致,則需要決定優先級問題
if (!cls.getName().equals(apcDefinition.getBeanClassName())) {
// 獲取優先級並進行對比
int currentPriority = findPriorityForClass(apcDefinition.getBeanClassName());
int requiredPriority = findPriorityForClass(cls);
if (currentPriority < requiredPriority) {
// 優先級被更高的取代時, 設置beanClassName。 定義的名稱,不等同於最終的beanName
apcDefinition.setBeanClassName(cls.getName());
}
}
return null;
} else {
// else分支表示目前沒有internalAutoProxyCreator的定義,則直接新建
RootBeanDefinition beanDefinition = new RootBeanDefinition(cls);
beanDefinition.setSource(source);
beanDefinition.getPropertyValues().add("order", -2147483648);
beanDefinition.setRole(2);
// 註冊定義
registry.registerBeanDefinition("org.springframework.aop.config.internalAutoProxyCreator", beanDefinition);
return beanDefinition;
}
}
}
// 優先級隊列獲取,可以看到,這個類維護了一個優先級的數組,使用static代碼塊進行初始化,越靠後的優先級就越高
// 直接根據index的索引定優先級也是很暴力的操作。
private static int findPriorityForClass(Class<?> clazz) {
return APC_PRIORITY_LIST.indexOf(clazz);
}
// 靜態代碼塊初始化
static {
APC_PRIORITY_LIST.add(InfrastructureAdvisorAutoProxyCreator.class);
APC_PRIORITY_LIST.add(AspectJAwareAdvisorAutoProxyCreator.class);
APC_PRIORITY_LIST.add(AnnotationAwareAspectJAutoProxyCreator.class);
}
補充一點 : 在registerOrEscalateApcAsRequired方法中 註冊了"org.springframework.aop.config.internalAutoProxyCreator"這個類。查了一些資料沒有什麼有價值的, 我翻了一下老版本的源碼的發現是個預定義的常量,名爲"AUTO_PROXY_CREATOR_BEAN_NAME"。 然而 "org.springframework.aop.config.internalAutoProxyCreator"只是BeanDefintion的定義的className而已,並非真的類名.其對應的真實類是org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator。 具體在哪裏進行的alias別名沒有找到,暫時留一個坑吧。
如果通讀這段邏輯會發現,我們明明註冊的是"AnnotationAwareAspectJAutoProxyCreator",在首次進入的時候,卻直接註冊"org.springframework.aop.config.internalAutoProxyCreator"。(第一個if判斷的時候已經走的else)
我打開了AnnotationAwareAspectJAutoProxyCreator類的繼承圖,說明先註冊的是父類,所以並沒有什麼問題,但是具體爲什麼這麼設計就不得而知了。即使再次註冊到子類也會因爲優先級的原因,將類變爲對應的子類。
繼而我們可以繼續分析其源碼,打開AnnotationAwareAspectJAutoProxyCreator,發現只有簡單的幾個屬性和一些setter。
<--插播一些spring bean生命週期的知識--> bean在初始化時一般會執行
postProcessBeforeInstantiation() -> constructor() -> postProcessPropertyValues() 這幾步,而代理對象一般也在這三步中產生。所以我們向上查找直到AbstractAutoProxyCreator 。 找到了postProcessBeforeInstantiation()方法
查看AbstractAutoProxyCreator的postProcessBeforeInstantiation方法。 AbstractAutoProxyCreator定義許多屬性,有數組,map甚至bool來輔助記錄各種狀態的bean,方便後續使用,不貼這部分的代碼了,有興趣可以去看一下
// AbstractAutoProxyCreator.class
public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) {
// 拿取緩存的key
Object cacheKey = this.getCacheKey(beanClass, beanName);
if (!StringUtils.hasLength(beanName) || !this.targetSourcedBeans.contains(beanName)) {
// 如果是增強類。直接忽略
if (this.advisedBeans.containsKey(cacheKey)) {
return null;
}
// 判斷 特殊bean,判斷是否和切面、切點bean等 aop相關類或實現其接口的類
// shouldSkip則判斷是否是 original類,也就是原始類
if (this.isInfrastructureClass(beanClass) || this.shouldSkip(beanClass, beanName)) {
// 則直接放入屬性advisedBeans(concurrentHashMap)中, 並打上對應的tag,標記其爲特殊類
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return null;
}
}
// 判斷自定義的targetSource方法
TargetSource targetSource = this.getCustomTargetSource(beanClass, beanName);
// 如果有自定義target方法則加入對應的屬性(map)中
if (targetSource != null) {
if (StringUtils.hasLength(beanName)) {
this.targetSourcedBeans.add(beanName);
}
// 獲取增強的方法 --- 重點1
Object[] specificInterceptors = this.getAdvicesAndAdvisorsForBean(beanClass, beanName, targetSource);
// 創建代理對象 --- 重點2
Object proxy = this.createProxy(beanClass, beanName, specificInterceptors, targetSource);
// 將對象標記並存入自身屬性(Map)中
this.proxyTypes.put(cacheKey, proxy.getClass());
return proxy;
} else {
return null;
}
}
上述比較重要的方法就是獲取對應的增強getAdvicesAndAdvisorsForBean()方法,和 獲取代理對象的方法 createProxy()。會發現AbstractAutoProxyCreator的getAdvicesAndAdvisorsForBean()是個抽象方法,交由子類實現。 實現地點在子類AbstractAdvisorAutoProxyCreator中(上述類圖的第一個子類)
// AbstractAdvisorAutoProxyCreator.class
@Nullable
protected Object[] getAdvicesAndAdvisorsForBean(Class<?> beanClass, String beanName, @Nullable TargetSource targetSource) {
// 獲取 對應beanClass的所有增強
List<Advisor> advisors = this.findEligibleAdvisors(beanClass, beanName);
// DO_NOT_PROXY是一個空數組常量。 如果找不到則返回空數組,否則返回轉到數組狀態的增強類
return advisors.isEmpty() ? DO_NOT_PROXY : advisors.toArray();
}
protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
// 尋找目前所有的增強類
List<Advisor> candidateAdvisors = this.findCandidateAdvisors();
// 所有增強類進行處理,篩選 來確定使用的
List<Advisor> eligibleAdvisors = this.findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
// 將增強加入對應的屬性
this.extendAdvisors(eligibleAdvisors);
if (!eligibleAdvisors.isEmpty()) { // 進行一下排序
eligibleAdvisors = this.sortAdvisors(eligibleAdvisors);
}
return eligibleAdvisors;
}
findCandidateAdvisors由子類進行了實現,擴展了父類的該方法。 算法還是有些複雜的,整體流程還是比較乾淨清晰的。大概流程就是通過遞歸方式遍歷所有的bean,如果有@Aspect修飾就加入到數組中進行標記。
// AnnotationAwareAspectJAutoProxyCreator.class
protected List<Advisor> findCandidateAdvisors() {
// 調用的實際是 父類的方法
List<Advisor> advisors = super.findCandidateAdvisors();
// 擴展後續的邏輯
if (this.aspectJAdvisorsBuilder != null) {
// 尋找所有註解爲@ASpect的增強類
advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors());
}
return advisors;
}
// org.springframework.aop.aspectj.annotation.BeanFactoryAspectJAdvisorsBuilder.class
// 尋找註解爲@Aspect的增強器,包含父類的遞歸查詢
public List<Advisor> buildAspectJAdvisors() {
// 所有的增強類
List<String> aspectNames = this.aspectBeanNames;
if (aspectNames == null) {
synchronized(this) {
aspectNames = this.aspectBeanNames;
// 如果還沒定義切面的beanName
if (aspectNames == null) {
List<Advisor> advisors = new ArrayList();
List<String> aspectNames = new ArrayList();
// 遞歸方法,從下到上查找所有的bean,
String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(this.beanFactory, Object.class, true, false);
String[] var18 = beanNames;
int var19 = beanNames.length;
for(int var7 = 0; var7 < var19; ++var7) {
String beanName = var18[var7];
// isEligibleBean交由實現類實現,判斷一些類的條件,當前類默認返回true
if (this.isEligibleBean(beanName)) {
// 直接從beanFactory拿對應的bean
Class<?> beanType = this.beanFactory.getType(beanName);
// isAspect()判斷該bean是否由 @Aspect註解 有則返回true
if (beanType != null && this.advisorFactory.isAspect(beanType)) {
// 加入數組中
aspectNames.add(beanName);
// 創建一個AspectMetadata類(Aspect註解類的實例化模型),判斷是否爲Aspect的實現。 一層一層的遍歷,從子找到父, 直到找到不爲Object子類的 Aspect對象
AspectMetadata amd = new AspectMetadata(beanType, beanName);
// 判斷切面的實例模式 -- 後邊會引入文章介紹
if (amd.getAjType().getPerClause().getKind() == PerClauseKind.SINGLETON) {
// 創建Aspect的元數據
MetadataAwareAspectInstanceFactory factory = new BeanFactoryAspectInstanceFactory(this.beanFactory, beanName);
// 通過反射,獲取該增強內所有的方法
List<Advisor> classAdvisors = this.advisorFactory.getAdvisors(factory);
// 對應不同的模式 進行不同的標記處理
if (this.beanFactory.isSingleton(beanName)) {
this.advisorsCache.put(beanName, classAdvisors);
} else {
this.aspectFactoryCache.put(beanName, factory);
}
// 將獲取到的增強加入List中
advisors.addAll(classAdvisors);
} else {
if (this.beanFactory.isSingleton(beanName)) {
throw new IllegalArgumentException("Bean with name '" + beanName + "' is a singleton, but aspect instantiation model is not singleton");
}
// 非單例模式的方法, 直接加入對應的工廠中和數組中,
MetadataAwareAspectInstanceFactory factory = new PrototypeAspectInstanceFactory(this.beanFactory, beanName);
this.aspectFactoryCache.put(beanName, factory);
advisors.addAll(this.advisorFactory.getAdvisors(factory));
}
}
}
}
this.aspectBeanNames = aspectNames;
return advisors;
}
}
}
// 應對aspectName數組不爲空的時候
if (aspectNames.isEmpty()) {
return Collections.emptyList();
} else {
// 通過迭代的方法,直接遍歷當前的aspectName
List<Advisor> advisors = new ArrayList();
Iterator var3 = aspectNames.iterator();
while(var3.hasNext()) {
String aspectName = (String)var3.next();
List<Advisor> cachedAdvisors = (List)this.advisorsCache.get(aspectName);
// 在Cache的查找 如果沒有則新建 和上述操作差不多
if (cachedAdvisors != null) {
advisors.addAll(cachedAdvisors);
} else {
MetadataAwareAspectInstanceFactory factory = (MetadataAwareAspectInstanceFactory)this.aspectFactoryCache.get(aspectName);
advisors.addAll(this.advisorFactory.getAdvisors(factory));
}
}
return advisors;
}
}
// org.springframework.beans.factory.BeanFactoryUtils.class
// 通過遞歸的方法 從下之上搜索拿去所有的bean
public static String[] beanNamesForTypeIncludingAncestors(ListableBeanFactory lbf, Class<?> type, boolean includeNonSingletons, boolean allowEagerInit) {
Assert.notNull(lbf, "ListableBeanFactory must not be null");
String[] result = lbf.getBeanNamesForType(type, includeNonSingletons, allowEagerInit);
if (lbf instanceof HierarchicalBeanFactory) {
HierarchicalBeanFactory hbf = (HierarchicalBeanFactory)lbf;
if (hbf.getParentBeanFactory() instanceof ListableBeanFactory) {
// 遞歸的拿取父節點
String[] parentResult = beanNamesForTypeIncludingAncestors((ListableBeanFactory)hbf.getParentBeanFactory(), type, includeNonSingletons, allowEagerInit);
result = mergeNamesWithParent(result, parentResult, hbf);
}
}
return result;
}
關於amd.getAjType().getPerClause().getKind(). 其實是Aspect的AspectMetadata類提供的一些方式,目前有四種,默認爲singleton(和spring一樣的單例),其他的三種是pertarget、perthis、pertypewithin。找到這麼一篇帖子, 實例化切面模式
// todo
後面繼續寫
findAdvisorsThatCanApply
和 getProxy....