主頁 > 後端開發 > 死磕Spring之AOP篇 - Spring AOP自動代理(二)篩選合適的通知器

死磕Spring之AOP篇 - Spring AOP自動代理(二)篩選合適的通知器

2021-04-21 06:13:01 後端開發

該系列文章是本人在學習 Spring 的程序中總結下來的,里面涉及到相關原始碼,可能對讀者不太友好,請結合我的原始碼注釋 Spring 原始碼分析 GitHub 地址 進行閱讀,

Spring 版本:5.1.14.RELEASE

在開始閱讀 Spring AOP 原始碼之前,需要對 Spring IoC 有一定的了解,可查看我的 《死磕Spring之IoC篇 - 文章導讀》 這一系列文章

了解 AOP 相關術語,可先查看 《Spring AOP 常見面試題) 》 這篇文章

該系列其他文章請查看:《死磕 Spring 之 AOP 篇 - 文章導讀》

在上一篇《Spring AOP 自動代理(一)入口》文章中,分析了 Spring AOP 自動代理的入口是 AbstractAutoProxyCreator 物件,其中自動代理的程序主要分為下面兩步:

  1. 篩選出能夠應用于當前 Bean 的 Advisor
  2. 找到了合適 Advisor 則創建一個代理物件, JDK 動態代理或者 CGLIB 動態代理

本文就接著上篇文章來分析篩選合適的通知器的處理程序,包含 @AspectJAspectJ 注解的決議程序,這里的“通知器”指的是 Advisor 物件,

回顧

// AbstractAutoProxyCreator.java
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
    /*
     * <1> 如果當前 Bean 已經創建過自定義 TargetSource 物件
     * 表示在上面的**實體化前置處理**中已經創建代理物件,那么直接回傳這個物件
     */
    if (StringUtils.hasLength(beanName)
            && this.targetSourcedBeans.contains(beanName))
    {
        return bean;
    }
    // <2> `advisedBeans` 保存了這個 Bean 沒有必要創建代理物件,則直接回傳
    if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
        return bean;
    }
    /*
     * <3> 不需要創建代理物件,則直接回傳當前 Bean
     */
    if (isInfrastructureClass(bean.getClass()) // 如果是 Spring 內部的 Bean(Advice、Pointcut、Advisor 或者 AopInfrastructureBean 標記介面)
            || shouldSkip(bean.getClass(), beanName)) // 應該跳過
    {
        // 將這個 Bean 不需要創建代理物件的結果保存起來
        this.advisedBeans.put(cacheKey, Boolean.FALSE);
        return bean;
    }

    // Create proxy if we have advice.
    // <4> 獲取能夠應用到當前 Bean 的所有 Advisor(已根據 @Order 排序)
    Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
    // <5> 如果有 Advisor,則進行下面的動態代理創建程序
    if (specificInterceptors != DO_NOT_PROXY) {
        // <5.1> 將這個 Bean 已創建代理物件的結果保存至 `advisedBeans`
        this.advisedBeans.put(cacheKey, Boolean.TRUE);
        // <5.2> 創建代理物件,JDK 動態代理或者 CGLIB 動態代理
        // 這里傳入的是 SingletonTargetSource 物件,可獲取代理物件的目標物件(當前 Bean)
        Object proxy = createProxy(bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
        // <5.3> 將代理物件的 Class 物件(目標類的子類)保存
        this.proxyTypes.put(cacheKey, proxy.getClass());
        // <5.4> 回傳代理物件
        return proxy;
    }

    // <6> 否則,將這個 Bean 不需要創建代理物件的結果保存起來
    this.advisedBeans.put(cacheKey, Boolean.FALSE);
    // <7> 回傳這個 Bean 物件
    return bean;
}

在創建代理物件的程序中,上面方法的第 4 步呼叫 getAdvicesAndAdvisorsForBean(..) 方法,獲取能夠應用到當前 Bean 的所有 Advisor(已根據 @Order 排序)

// AbstractAutoProxyCreator.java
protected abstract Object[] getAdvicesAndAdvisorsForBean(Class<?> beanClass, String beanName,
			@Nullable TargetSource customTargetSource) throws BeansException;

抽象方法,交由子類實作

篩選出合適的 Advisor 的流程

  1. 決議出當前 IoC 容器所有 Advisor 物件

    1. 獲取當前 IoC 容器所有 Advisor 型別的 Bean

    2. 決議當前 IoC 容器中所有帶有 @AspectJ 注解的 Bean,將內部帶有 @Before|@After|@Around|@AfterReturning|@AfterThrowing 注解的方法決議出對應的 PointcutAdvisor 物件,帶有 @DeclareParents 注解的欄位決議出 IntroductionAdvisor 物件

      @Around -> AspectJAroundAdvice,實作了 MethodInterceptor

      @Before -> AspectJMethodBeforeAdvice

      @After -> AspectJAfterAdvice,實作了 MethodInterceptor

      @AfterReturning -> AspectJAfterAdvice

      @AfterThrowing -> AspectJAfterThrowingAdvice,實作了 MethodInterceptor

  2. 篩選出能夠應用于這個 Bean 的 Advisor 們,主要通過 ClassFilter 類過濾器和 MethodMatcher 方法匹配器進行匹配

  3. 對篩選出來的 Advisor 進行擴展,例如子類會往首部添加一個 PointcutAdvisor 物件

  4. 對篩選出來的 Advisor 進行排序

    • 不同的 AspectJ 根據 @Order 排序

    • 同一個 AspectJ 中不同 Advisor 的排序,優先級:AspectJAfterThrowingAdvice > AspectJAfterReturningAdvice > AspectJAfterAdvice > AspectJAroundAdvice > AspectJMethodBeforeAdvice

主要涉及到下面幾個類:

  • AbstractAdvisorAutoProxyCreator:支持從當前 Spring 背景關系獲取所有 Advisor 物件
  • AnnotationAwareAspectJAutoProxyCreator:支持從帶有 @AspectJ 注解 Bean 中決議 Advisor 物件
  • BeanFactoryAspectJAdvisorsBuilder:Advisor 構建器,用于決議出當前 BeanFactory 中所有帶有 @AspectJ 注解的 Bean 中的 Advisor
  • ReflectiveAspectJAdvisorFactory:Advisor 工廠,用于決議 @AspectJ 注解的 Bean 中的 Advisor

AnnotationAwareAspectJAutoProxyCreator 繼承 AbstractAdvisorAutoProxyCreator,借助 BeanFactoryAspectJAdvisorsBuilder 構建器,這個構建器又借助 ReflectiveAspectJAdvisorFactory 工廠,

AbstractAdvisorAutoProxyCreator

org.springframework.aop.framework.autoproxy.AbstractAdvisorAutoProxyCreator:支持從當前 Spring 背景關系獲取所有 Advisor 物件,存在能應用與 Bean 的 Advisor 則創建代理物件

建構式

public abstract class AbstractAdvisorAutoProxyCreator extends AbstractAutoProxyCreator {

    /** Advisor 檢索工具類 */
	@Nullable
	private BeanFactoryAdvisorRetrievalHelper advisorRetrievalHelper;

	@Override
	public void setBeanFactory(BeanFactory beanFactory) {
		super.setBeanFactory(beanFactory);
		if (!(beanFactory instanceof ConfigurableListableBeanFactory)) {
			throw new IllegalArgumentException(
					"AdvisorAutoProxyCreator requires a ConfigurableListableBeanFactory: " + beanFactory);
		}
        // 初始化作業
		initBeanFactory((ConfigurableListableBeanFactory) beanFactory);
	}

	protected void initBeanFactory(ConfigurableListableBeanFactory beanFactory) {
        // 設定 Advisor 檢索工具類為 BeanFactoryAdvisorRetrievalHelperAdapter
		this.advisorRetrievalHelper = new BeanFactoryAdvisorRetrievalHelperAdapter(beanFactory);
	}
}

1. getAdvicesAndAdvisorsForBean 方法

getAdvicesAndAdvisorsForBean(Class<?> beanClass, String beanName, @Nullable TargetSource targetSource),篩選某個 Bean 合適的 Advisor,如下:

@Override
@Nullable
protected Object[] getAdvicesAndAdvisorsForBean(
        Class<?> beanClass, String beanName, @Nullable TargetSource targetSource) {
    // 獲取能夠應用到當前 Bean 的所有 Advisor(已根據 @Order 排序)
    List<Advisor> advisors = findEligibleAdvisors(beanClass, beanName);
    if (advisors.isEmpty()) {
        return DO_NOT_PROXY;
    }
    // 轉換成陣列并回傳
    return advisors.toArray();
}

呼叫 findEligibleAdvisors(Class<?> beanClass, String beanName) 方法,獲取能夠應用到當前 Bean 的所有 Advisor(已根據 @Order 排序)

2. findEligibleAdvisors 方法

protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
    /*
     * <1> 決議出當前 IoC 容器所有的 Advisor 物件
     * 1. 本身是 Advisor 型別的 Bean,默認情況下都會
     * 2. 從帶有 @AspectJ 注解的 Bean 中決議出來的 Advisor,子類 AnnotationAwareAspectJAutoProxyCreator 會掃描并決議
     *    PointcutAdvisor:帶有 @Before|@After|@Around|@AfterReturning|@AfterThrowing 注解的方法
     *       其中 Pointcut 為 AspectJExpressionPointcut,Advice 就是注解標注的方法
     *    IntroductionAdvisor:帶有 @DeclareParents 注解的欄位
     *
     * 未排序,獲取到的 Advisor 在同一個 AspectJ 中的順序是根據注解來的,@Around > @Before > @After > @AfterReturning > @AfterThrowing
     */
    List<Advisor> candidateAdvisors = findCandidateAdvisors();
    /*
     * <2> 篩選出能夠應用到 `beanClass` 上面的所有 Advisor 物件并回傳
     * 也就是通過 ClassFilter 進行匹配,然后再通過 MethodMatcher 對所有方法進行匹配(有一個即可)
     * AspectJExpressionPointcut 就實作了 ClassFilter 和 MethodMatcher
     */
    List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
    /*
     * <3> 抽象方法,交由子類拓展
     * 例如 AspectJAwareAdvisorAutoProxyCreator 的實作
     * 如果 `eligibleAdvisors` 中存在和 AspectJ 相關的 Advisor
     * 則會在 `eligibleAdvisors` 首部添加一個 DefaultPointcutAdvisor 物件,對應的 Advice 為 ExposeInvocationInterceptor 物件
     * 用于暴露 MethodInvocation 物件(Joinpoint 物件),存盤在 ThreadLocal 中,在其他地方則可以使用
     */
    extendAdvisors(eligibleAdvisors);
    // <4> 對 `eligibleAdvisors` 集合進行排序,根據 @Order 注解進行排序
    if (!eligibleAdvisors.isEmpty()) {
        // 不同的 AspectJ 根據 @Order 排序
        // 同一個 AspectJ 中不同 Advisor 的排序:AspectJAfterThrowingAdvice > AspectJAfterReturningAdvice > AspectJAfterAdvice > AspectJAroundAdvice > AspectJMethodBeforeAdvice
        eligibleAdvisors = sortAdvisors(eligibleAdvisors);
    }
    // <5> 回傳排序后的能夠應用到當前 Bean 的所有 Advisor
    return eligibleAdvisors;
}

該方法的處理程序如下:

  1. 呼叫 findCandidateAdvisors() 方法,決議出當前 IoC 容器所有的 Advisor 物件,得到 candidateAdvisors 集合,來源:
    • 本身是 Advisor 型別的 Bean,默認情況下都會
    • 從帶有 @AspectJ 注解的 Bean 中決議出來的 Advisor
  2. 呼叫 findAdvisorsThatCanApply(..) 方法,篩選出能夠應用到 beanClass 上面的所有 Advisor 物件并回傳,得到 eligibleAdvisors 集合
    • 通過 ClassFilter 進行匹配,然后再通過 MethodMatcher 對所有方法進行匹配(有一個即可)
  3. 呼叫 extendAdvisors(List<Advisor> candidateAdvisors) 方法,對 eligibleAdvisors 進行處理
  4. 呼叫 sortAdvisors(List<Advisor> advisors) 方法,對 eligibleAdvisors 進行排序
  5. 回傳排序后的能夠應用到當前 Bean 的所有 Advisor

接下來依次對上面的方法進行分析

2.1.1 findCandidateAdvisors 方法

findCandidateAdvisors(),該方法會去找符合條件的 Advisor 們,AbstractAdvisorAutoProxyCreator 的實作則是去找當前 IoC 容器中所有 Advisor 型別的 Bean,如下:

// AbstractAdvisorAutoProxyCreator.java
protected List<Advisor> findCandidateAdvisors() {
    Assert.state(this.advisorRetrievalHelper != null, "No BeanFactoryAdvisorRetrievalHelper available");
    // 借助 BeanFactoryAdvisorRetrievalHelperAdapter 從 IoC 容器中查找所有的 Advisor 物件
    return this.advisorRetrievalHelper.findAdvisorBeans();
}

可以看到是借助于 BeanFactoryAdvisorRetrievalHelperAdapter 去找 Advisor 型別的 Bean,如下:

// BeanFactoryAdvisorRetrievalHelperAdapter.java
public List<Advisor> findAdvisorBeans() {
    // Determine list of advisor bean names, if not cached already.
    // <1> 先從快取中獲取所有 Advisor
    String[] advisorNames = this.cachedAdvisorBeanNames;
    // <2> 沒有快取
    if (advisorNames == null) {
        // Do not initialize FactoryBeans here: We need to leave all regular beans
        // uninitialized to let the auto-proxy creator apply to them!
        // <2.1> 從當前 BeanFactory 容器中找到所有 Advisor 型別的 bean 的名稱
        advisorNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
                this.beanFactory, Advisor.class, true, false);
        // <2.2> 放入快取中
        this.cachedAdvisorBeanNames = advisorNames;
    }
    // <3> 如果沒有 Advisor,則回傳一個空集合
    if (advisorNames.length == 0) {
        return new ArrayList<>();
    }

    List<Advisor> advisors = new ArrayList<>();
    /*
     * <4> 遍歷所有的 Advisor 型別的 Bean 的名稱,獲取對應的 Bean
     */
    for (String name : advisorNames) {
        // <4.1> 判斷這個 Bean 是否有資格,默認為 true
        if (isEligibleBean(name)) {
            // <4.2> 正在初始化,則先跳過
            if (this.beanFactory.isCurrentlyInCreation(name)) {
                if (logger.isTraceEnabled()) {
                    logger.trace("Skipping currently created advisor '" + name + "'");
                }
            }
            // <4.3> 否則,獲取對應的 Bean
            else {
                try {
                    // 依賴查找到這個 Advisor 物件
                    advisors.add(this.beanFactory.getBean(name, Advisor.class));
                }
                catch (BeanCreationException ex) {
                    Throwable rootCause = ex.getMostSpecificCause();
                    // ...
                    throw ex;
                }
            }
        }
    }
    // <5> 回傳 IoC 容器中所有的 Advisor
    return advisors;
}

該方法的處理程序如下:

  1. 先從快取中獲取所有 Advisor

  2. 沒有快取

    1. 從當前 BeanFactory 容器中找到所有 Advisor 型別的 Bean 的名稱
    2. 放入快取中
  3. 如果沒有 Advisor,則回傳一個空集合

  4. 遍歷所有的 Advisor 型別的 Bean 的名稱,獲取對應的 Bean

    1. 判斷這個 Bean 是否有資格,默認為 true
    2. 正在初始化,則先跳過
    3. 否則,獲取對應的 Bean,依賴查找到這個 Advisor 物件
  5. 回傳 IoC 容器中所有的 Advisor

總結下來,就是從當前 Spring IoC 容器中找到所有 Advisor 型別的 Bean

2.2 findAdvisorsThatCanApply 方法

findAdvisorsThatCanApply(List<Advisor> candidateAdvisors, Class<?> beanClass, String beanName) 方法,從 candidateAdvisors 中找到能夠應用于 beanClass 的 Advisor,如下:

protected List<Advisor> findAdvisorsThatCanApply(
        List<Advisor> candidateAdvisors, Class<?> beanClass, String beanName) {
    ProxyCreationContext.setCurrentProxiedBeanName(beanName);
    try {
        /*
         * 篩選出能夠應用到 `beanClass` 上面的所有 Advisor 物件并回傳
         * 也就是通過 ClassFilter 進行匹配,然后再通過 MethodMatcher 對所有方法進行匹配(有一個即可)
         * AspectJExpressionPointcut 就實作了 ClassFilter 和 MethodMatcher
         */
        return AopUtils.findAdvisorsThatCanApply(candidateAdvisors, beanClass);
    }
    finally {
        ProxyCreationContext.setCurrentProxiedBeanName(null);
    }
}

可以看到是借助于 AopUtils 工具類從 candidateAdvisors 中找到能夠應用于 beanClass 的 Advisor,如下:

// AopUtils.java
public static List<Advisor> findAdvisorsThatCanApply(List<Advisor> candidateAdvisors, Class<?> clazz) {
    if (candidateAdvisors.isEmpty()) {
        return candidateAdvisors;
    }
    List<Advisor> eligibleAdvisors = new ArrayList<>();
    /*
     * <1> 遍歷所有的 Advisor 物件
     * 找到能夠應用當前 Bean 的 IntroductionAdvisor 物件,放入 `eligibleAdvisors` 集合中
     */
    for (Advisor candidate : candidateAdvisors) {
        if (candidate instanceof IntroductionAdvisor // 如果是 IntroductionAdvisor 型別
                && canApply(candidate, clazz)) // 且能夠應用到當前 Bean 中,通過其 ClassFilter 進行過濾
        {
            eligibleAdvisors.add(candidate);
        }
    }
    boolean hasIntroductions = !eligibleAdvisors.isEmpty();
    /*
     * <2> 遍歷所有的 Advisor 物件
     * 如果是 IntroductionAdvisor 型別,則會跳過,因為上面已經判斷過
     * 找到能夠應用當前 Bean 的 Advisor 物件,放入 `eligibleAdvisors` 集合中
     */
    for (Advisor candidate : candidateAdvisors) {
        if (candidate instanceof IntroductionAdvisor) {
            // already processed
            continue;
        }
        // 判斷是否能夠應用到這個 Bean 上面
        if (canApply(candidate, clazz, hasIntroductions)) {
            eligibleAdvisors.add(candidate);
        }
    }
    // <3> 回傳能夠應用到當前 Bean 的所有 Advisor 物件
    return eligibleAdvisors;
}

該方法的處理程序如下:

  1. 遍歷所有的 Advisor 物件,找到能夠應用當前 Bean 的 IntroductionAdvisor 物件,放入 eligibleAdvisors 集合中,需要滿足下面兩個條件

    • IntroductionAdvisor 型別
    • 能夠應用到當前 Bean 中,通過其 ClassFilter 進行過濾
  2. 遍歷所有的 Advisor 物件,找到能夠應用當前 Bean 的 Advisor 物件,放入 eligibleAdvisors 集合中;如果是 IntroductionAdvisor 型別,則會跳過,因為上面已經判斷過

  3. 回傳能夠應用到當前 Bean 的所有 Advisor 物件

AopUtils#canApply 方法

如何判斷這個 Advisor 能夠應用于某個 Bean 都是呼叫 canApply(..) 方法如下:

// AopUtils.java
public static boolean canApply(Advisor advisor, Class<?> targetClass, boolean hasIntroductions) {
    if (advisor instanceof IntroductionAdvisor) {
        /*
         * 從 IntroductionAdvisor 中獲取 ClassFilter 類過濾器,判斷這個目標類是否符合條件
         */
        return ((IntroductionAdvisor) advisor).getClassFilter().matches(targetClass);
    }
    else if (advisor instanceof PointcutAdvisor) {
        /*
         * 根據 Pointcut 中的 ClassFilter 和 MethodFilter 進行過濾
         * 例如 Aspect 的實作類 AspectJExpressionPointcut
         */
        PointcutAdvisor pca = (PointcutAdvisor) advisor;
        return canApply(pca.getPointcut(), targetClass, hasIntroductions);
    }
    else {
        // It doesn't have a pointcut so we assume it applies.
        // 否則,沒有 Pointcut,也就是沒有篩選條件,則都符合條件
        return true;
    }
}

如果 IntroductionAdvisor 型別的 Advisor 則通過 ClassFilter 類過濾器進行判斷即可;如果是 PointcutAdvisor 型別的 Advisor 則需要呼叫 canApply(..) 的多載方法進行判斷;否則,沒有 Pointcut,也就是沒有篩選條件,則都符合條件

AopUtils#canApply 多載方法

如何判斷 PointcutAdvisor 型別的 Advisor 能夠應用于某個 Bean 的程序如下:

// AopUtils.java
public static boolean canApply(Pointcut pc, Class<?> targetClass, boolean hasIntroductions) {
    Assert.notNull(pc, "Pointcut must not be null");
    // <1> 使用 ClassFilter 匹配 `targetClass`
    if (!pc.getClassFilter().matches(targetClass)) {
        return false;
    }

    // <2> 獲取 MethodMatcher 方法匹配器
    MethodMatcher methodMatcher = pc.getMethodMatcher();
    // <3> 如果方法匹配器為 TrueMethodMatcher,則默認都通過
    if (methodMatcher == MethodMatcher.TRUE) {
        // No need to iterate the methods if we're matching any method anyway...
        return true;
    }

    // <4> 如果方法匹配器為 IntroductionAwareMethodMatcher,則進行轉換
    // AspectJExpressionPointcut 就是 IntroductionAwareMethodMatcher 的實作類
    IntroductionAwareMethodMatcher introductionAwareMethodMatcher = null;
    if (methodMatcher instanceof IntroductionAwareMethodMatcher) {
        introductionAwareMethodMatcher = (IntroductionAwareMethodMatcher) methodMatcher;
    }

    /*
     * <5> 獲取目標類、以及實作的所有介面,并添加至 `classes` 集合中
     */
    Set<Class<?>> classes = new LinkedHashSet<>();
    // <5.1> 如果不是 java.lang.reflect.Proxy 的子類
    if (!Proxy.isProxyClass(targetClass)) {
        // 獲取目標類的 Class 物件(如果目標類是 CGLIB 代理物件,則獲取其父類的 Class 物件,也就得到了目標類)
        classes.add(ClassUtils.getUserClass(targetClass));
    }
    // <5.2> 獲取目標類實作的所有介面,如果目標類本身是一個介面,那么就取這個目標類
    classes.addAll(ClassUtils.getAllInterfacesForClassAsSet(targetClass));

    /*
     * <6> 遍歷上面的 `classes` 集合
     */
    for (Class<?> clazz : classes) {
        // <6.1> 獲取這個 Class 物件的所有方法
        Method[] methods = ReflectionUtils.getAllDeclaredMethods(clazz);
        // <6.2> 遍歷上一步獲取到的所有方法
        for (Method method : methods) {
            // <6.3> 使用方法匹配器對該方法進行匹配,如果匹配成功則直接回傳 `true`
            // AspectJExpressionPointcut 底層就是通過 AspectJ 進行處理的
            if (introductionAwareMethodMatcher != null ?
                    introductionAwareMethodMatcher.matches(method, targetClass, hasIntroductions) :
                    methodMatcher.matches(method, targetClass)) {
                return true;
            }
        }
    }

    // <7> 一個方法都沒匹配則回傳 `false`,表示這個 Advisor 不能應用到這個 Bean 上面
    return false;
}

該方法的處理程序如下:

  1. 使用 Pointcut 的 ClassFilter 匹配 targetClass,不通過則直接回傳 false
  2. 獲取 Pointcut 的 MethodMatcher 方法匹配器,保存至 methodMatcher
  3. 如果 methodMatcher 為 TrueMethodMatcher,則默認都通過,回傳 true
  4. 如果 methodMatcher 為 IntroductionAwareMethodMatcher,則進行轉換,保存至 introductionAwareMethodMatcher
    • AspectJExpressionPointcut 就是 IntroductionAwareMethodMatcher 的實作類
  5. 獲取目標類、以及實作的所有介面,并添加至 classes 集合中
    1. 如果不是 java.lang.reflect.Proxy 的子類,則獲取 targetClass 目標類的 Class 物件(如果目標類是 CGLIB 代理物件,則獲取其父類的 Class 物件,也就得到了目標類)
    2. 獲取 targetClass 目標類實作的所有介面,如果目標類本身是一個介面,那么就取這個目標類
  6. 遍歷上面的 classes 集合
    1. 獲取這個 Class 物件的所有方法
    2. 遍歷上一步獲取到的所有方法
    3. 使用 methodMatcher 方法匹配器對該方法進行匹配,優先使用 introductionAwareMethodMatcher 方法匹配器,匹配成功則直接回傳 true,說明有一個方法滿足條件即可
      • AspectJExpressionPointcut 底層就是通過 AspectJ 的運算式處理進行處理的
  7. 一個方法都沒匹配成功則回傳 false,表示這個 Advisor 不能應用到這個 Bean 上面

總結下來,PointcutAdvisor 是根據 Pointcut 的 ClassFilter 對目標類進行過濾,如果通過的話,則通過 MethodMatcher 方法匹配器對目標類的方法進行匹配,有一個方法滿足條件就表示這個 PointcutAdvisor 可以應用于目標類

2.3 extendAdvisors 方法

extendAdvisors(List<Advisor> candidateAdvisors) 放,對篩選出來的 Advisor 進行擴展,抽象方法,我們來看到子類的實作:

// AspectJAwareAdvisorAutoProxyCreator.java
@Override
protected void extendAdvisors(List<Advisor> candidateAdvisors) {
    AspectJProxyUtils.makeAdvisorChainAspectJCapableIfNecessary(candidateAdvisors);
}

可以看到是借助于 AspectJProxyUtils 工具類進行擴展,如下:

public static boolean makeAdvisorChainAspectJCapableIfNecessary(List<Advisor> advisors) {
    // Don't add advisors to an empty list; may indicate that proxying is just not required
    if (!advisors.isEmpty()) {
        boolean foundAspectJAdvice = false;
        // 遍歷所有 Advisor
        for (Advisor advisor : advisors) {
            // Be careful not to get the Advice without a guard, as this might eagerly
            // instantiate a non-singleton AspectJ aspect...
            // 判斷這個 Advisor 是否和 AspectJ 相關
            if (isAspectJAdvice(advisor)) {
                foundAspectJAdvice = true;
                break;
            }
        }
        // 如果 `advisors` 涉及到和 AspectJ 相關的 Advisor
        // 則向其首部添加一個 DefaultPointcutAdvisor 物件,對應的 Advice 為 ExposeInvocationInterceptor 物件
        // 用于暴露 MethodInvocation 物件(Joinpoint 物件),存盤在 ThreadLocal 中,在其他地方則可以使用
        if (foundAspectJAdvice && !advisors.contains(ExposeInvocationInterceptor.ADVISOR)) {
            advisors.add(0, ExposeInvocationInterceptor.ADVISOR);
            return true;
        }
    }
    return false;
}

private static boolean isAspectJAdvice(Advisor advisor) {
    return ( advisor instanceof InstantiationModelAwarePointcutAdvisor
            || advisor.getAdvice() instanceof AbstractAspectJAdvice
            || ( advisor instanceof PointcutAdvisor && ((PointcutAdvisor) advisor).getPointcut() instanceof AspectJExpressionPointcut) );
}

處理程序很簡單,當存在和 AspectJ 相關的 Advisor(使用了 AspectJ 的注解這里都是 true),則在首部添加一個 DefaultPointcutAdvisor 物件

添加的這個 Advisor 對應的 Advice 為 ExposeInvocationInterceptor 方法攔截器,用于暴露 MethodInvocation 物件(Joinpoint 物件),存盤在 ThreadLocal 中,在其他地方則可以使用

2.4 sortAdvisors 方法

sortAdvisors(List<Advisor> advisors) 方法,對篩選出來的 Advisor 進行排序,如下:

// AspectJAwareAdvisorAutoProxyCreator.java
protected List<Advisor> sortAdvisors(List<Advisor> advisors) {
    List<PartiallyComparableAdvisorHolder> partiallyComparableAdvisors = new ArrayList<>(advisors.size());
    for (Advisor element : advisors) {
        // 使用 AspectJPrecedenceComparator 比較器
        partiallyComparableAdvisors.add(
                new PartiallyComparableAdvisorHolder(element, DEFAULT_PRECEDENCE_COMPARATOR));
    }
    List<PartiallyComparableAdvisorHolder> sorted = PartialOrder.sort(partiallyComparableAdvisors);
    if (sorted != null) {
        List<Advisor> result = new ArrayList<>(advisors.size());
        for (PartiallyComparableAdvisorHolder pcAdvisor : sorted) {
            result.add(pcAdvisor.getAdvisor());
        }
        return result;
    }
    else {
        // AbstractAdvisorAutoProxyCreator
        // 使用 AnnotationAwareOrderComparator 比較器,通過 @Order 注解
        return super.sortAdvisors(advisors);
    }
}

AspectJPrecedenceComparator 是對 AnnotationAwareOrderComparator 的包裝,進行了擴展,排序不同型別的 Advice,詳細的程序這里不展述了

我通過 Debug 打斷點得到的結論:

  • 不同的 AspectJ 根據 @Order 排序

  • 同一個 AspectJ 中不同 Advisor 的排序,優先級如下:

    AspectJAfterThrowingAdvice > AspectJAfterReturningAdvice > AspectJAfterAdvice > AspectJAroundAdvice > AspectJMethodBeforeAdvice

小結

到這里我們可以一個結論,篩選合適的通知器的總的程序在 AbstractAdvisorAutoProxyCreator#findEligibleAdvisors(..) 方法中進行,分為下面幾步:

  1. 找符合條件的 Advisor 們,在 AbstractAdvisorAutoProxyCreator 則是去找當前 IoC 容器中所有 Advisor 型別的 Bean
  2. 從上一步找到的 Advisor 篩選出能夠應用于當前 Bean 的 Advisor 們,主要是通過 Pointcut 的 ClassFilter 類過濾器和 MethodMatcher 方法匹配器進行判斷,有一個方法匹配這個 Advisor 即滿足條件
  3. 支持對找到的 Advisor 集合進行擴展,在子類中會往其首部添加一個方法攔截器為 ExposeInvocationInterceptor 的 PointcutAdvisor
  4. 對找到的合適的 Advisor 進行排序,排序結果如上所述

上面程序的第 1 步僅找到當前 IoC 容器中所有 Advisor 型別的 Bean,是不是沒有對 AspectJ 相關注解進行決議,這個程序在子類中實作,也就是接下來要講的內容

AnnotationAwareAspectJAutoProxyCreator

org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator:支持從帶有 @AspectJ 注解 Bean 中決議 Advisor 物件

建構式

public class AnnotationAwareAspectJAutoProxyCreator extends AspectJAwareAdvisorAutoProxyCreator {

	/**
	 * 用于指定哪些 Bean 能夠作為 Advisor
	 */
	@Nullable
	private List<Pattern> includePatterns;
	/**
	 * 決議 AspectJ 注解的 Advisor 工廠
	 */
	@Nullable
	private AspectJAdvisorFactory aspectJAdvisorFactory;
	/**
	 * 構建器模式,用于構建  AspectJ 注解的 Advisor
	 */
	@Nullable
	private BeanFactoryAspectJAdvisorsBuilder aspectJAdvisorsBuilder;

	@Override
	protected void initBeanFactory(ConfigurableListableBeanFactory beanFactory) {
        // 初始化 BeanFactoryAdvisorRetrievalHelperAdapter
		super.initBeanFactory(beanFactory);
        // 初始化 ReflectiveAspectJAdvisorFactory
		if (this.aspectJAdvisorFactory == null) {
			this.aspectJAdvisorFactory = new ReflectiveAspectJAdvisorFactory(beanFactory);
		}
        // 初始化 BeanFactoryAspectJAdvisorsBuilderAdapter
		this.aspectJAdvisorsBuilder =
				new BeanFactoryAspectJAdvisorsBuilderAdapter(beanFactory, this.aspectJAdvisorFactory);
	}
}

2.1.2 findCandidateAdvisors 方法

findCandidateAdvisors(),該方法會去找符合條件的 Advisor 們,通過父類找到當前 IoC 容器中所有 Advisor 型別的 Bean,這里又會決議出帶有 @AspectJ 注解的 Bean 中的 Advisor 們,如下:

protected List<Advisor> findCandidateAdvisors() {
    // Add all the Spring advisors found according to superclass rules.
    // <1> 呼叫父類方法,從 IoC 容器中查找所有的 Advisor 型別的 Bean
    List<Advisor> advisors = super.findCandidateAdvisors();
    // Build Advisors for all AspectJ aspects in the bean factory.
    // <2> 如果 AspectJ 決議器不為空,默認為 BeanFactoryAspectJAdvisorsBuilderAdapter
    if (this.aspectJAdvisorsBuilder != null) {
        // 決議所有帶有 @AspectJ 注解的 Bean
        // 其中帶有 @Before|@After|@Around|@AfterReturning|@AfterThrowing 注解的方法會被決議成一個 PointcutAdvisor 物件
        // 將決議出來的所有 Advisor 添加至 `advisors` 中
        advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors());
    }
    // <3> 回傳 `advisors` 集合(當前 IoC 容器中決議出來的所有的 Advisor 物件)
    return advisors;
}

該方法的處理程序如下:

  1. 呼叫父類方法,從 IoC 容器中查找所有的 Advisor 型別的 Bean,保存至 advisors 中,可回傳上面的 2.1.1 findCandidateAdvisors 方法 小節查看
  2. 如果 AspectJ 決議器不為空,默認為 BeanFactoryAspectJAdvisorsBuilderAdapter,則通過它決議出 Advisor 來
    • 決議所有帶有 @AspectJ 注解的 Bean
    • 其中帶有 @Before|@After|@Around|@AfterReturning|@AfterThrowing 注解的方法會被決議成一個 PointcutAdvisor 物件
    • 將決議出來的所有 Advisor 添加至 advisors
  3. 回傳 advisors 集合(當前 IoC 容器中決議出來的所有的 Advisor 物件)

BeanFactoryAspectJAdvisorsBuilderAdapter

關于 @AspectJ 注解的決議由 BeanFactoryAspectJAdvisorsBuilderAdapter 完成,如下:

// AnnotationAwareAspectJAutoProxyCreator.java
private class BeanFactoryAspectJAdvisorsBuilderAdapter extends BeanFactoryAspectJAdvisorsBuilder {

    public BeanFactoryAspectJAdvisorsBuilderAdapter(
            ListableBeanFactory beanFactory, AspectJAdvisorFactory advisorFactory) {

        super(beanFactory, advisorFactory);
    }

    @Override
    protected boolean isEligibleBean(String beanName) {
        return AnnotationAwareAspectJAutoProxyCreator.this.isEligibleAspectBean(beanName);
    }
}

protected boolean isEligibleAspectBean(String beanName) {
    if (this.includePatterns == null) {
        return true;
    } else {
        for (Pattern pattern : this.includePatterns) {
            if (pattern.matcher(beanName).matches()) {
                return true;
            }
        }
        return false;
    }
}

這里只重寫了 isEligibleBean(String) 方法,用于判斷這個 Bean 是否有資格作為一個 Advisor,可以看到是通過 includePatternsbeanName 進行判斷,匹配通過才有資格,當然,includePatterns 一般為空,都有資格,

這個類繼承了 BeanFactoryAspectJAdvisorsBuilder 構建器,我們來看到這個構建器是如何決議的,

BeanFactoryAspectJAdvisorsBuilder

org.springframework.aop.aspectj.annotation.BeanFactoryAspectJAdvisorsBuilder,Advisor 構建器,用于決議出當前 BeanFactory 中所有帶有 @AspectJ 注解的 Bean 中的 Advisor

建構式

public class BeanFactoryAspectJAdvisorsBuilder {
	/**
	 * 當前 IoC 容器,DefaultListableBeanFactory
	 */
	private final ListableBeanFactory beanFactory;
	/**
	 * Advisor 工廠,用于決議 @AspectJ 注解的 Bean 中的 Advisor
	 */
	private final AspectJAdvisorFactory advisorFactory;
	/**
	 * 用于快取帶有 @AspectJ 注解的 Bean 的名稱
	 */
	@Nullable
	private volatile List<String> aspectBeanNames;
	/**
	 * 快取 @AspectJ 注解的單例 Bean 中決議出來的 Advisor
	 * key:帶有 @AspectJ 注解的 beanName
	 * value:其內部決議出來的 Advisor 集合
	 */
	private final Map<String, List<Advisor>> advisorsCache = new ConcurrentHashMap<>();
	/**
	 * 快取 @AspectJ 注解的非單例 Bean 的元資料實體構建工廠
	 * key:帶有 @AspectJ 注解的 beanName(非單例)
	 * value:對應的元資料工廠物件
	 */
	private final Map<String, MetadataAwareAspectInstanceFactory> aspectFactoryCache = new ConcurrentHashMap<>();

	public BeanFactoryAspectJAdvisorsBuilder(ListableBeanFactory beanFactory, AspectJAdvisorFactory advisorFactory) {
		Assert.notNull(beanFactory, "ListableBeanFactory must not be null");
		Assert.notNull(advisorFactory, "AspectJAdvisorFactory must not be null");
		this.beanFactory = beanFactory;
		this.advisorFactory = advisorFactory;
	}
}

2.1.3 buildAspectJAdvisors 方法

buildAspectJAdvisors() 方法,決議出當前 BeanFactory 中所有帶有 @AspectJ 注解的 Bean 中的 Advisor,如下:

public List<Advisor> buildAspectJAdvisors() {
    // <1> 從快取中獲取所有帶有 @AspectJ 注解的 Bean,保存至 `aspectNames` 集合中
    List<String> aspectNames = this.aspectBeanNames;

    // <2> 快取中沒有,則對當前物件加鎖再判斷快取中是否有資料
    if (aspectNames == null) {
        synchronized (this) {
            aspectNames = this.aspectBeanNames;
            // <3> 還是沒有快取,則進行接下來的處理
            if (aspectNames == null) {
                List<Advisor> advisors = new ArrayList<>();
                aspectNames = new ArrayList<>();
                // <3.1> 獲取當前 IoC 容器中所有的 Bean 的名稱集合
                String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
                        this.beanFactory, Object.class, true, false);
                // <3.2> 遍歷所有的 Bean 的名稱,進行處理
                for (String beanName : beanNames) {
                    // <3.3> 判斷這個 Bean 是否有資格,默認都為 true
                    if (!isEligibleBean(beanName)) {
                        // 如果沒有資格則跳過
                        continue;
                    }
                    // We must be careful not to instantiate beans eagerly as in this case they
                    // would be cached by the Spring container but would not have been weaved.
                    // <3.4> 獲取這個 Bean 的 Class 物件,如果為空則跳過
                    Class<?> beanType = this.beanFactory.getType(beanName);
                    if (beanType == null) {
                        continue;
                    }
                    // <3.5> 如果這個 Bean 帶有 @Aspect 注解,且沒有以 `ajc$` 開頭的欄位,那么進行接下來的決議程序
                    if (this.advisorFactory.isAspect(beanType)) {
                        // <3.5.1>  將這個 Bean 的名稱保存至 `aspectNames` 集合中
                        aspectNames.add(beanName);
                        AspectMetadata amd = new AspectMetadata(beanType, beanName);
                        // <3.5.2> 判斷 @AspectJ 注解的類別是否為 `singleton`,默認空的情況就是這個
                        if (amd.getAjType().getPerClause().getKind() == PerClauseKind.SINGLETON) {
                            MetadataAwareAspectInstanceFactory factory =
                                    new BeanFactoryAspectInstanceFactory(this.beanFactory, beanName);
                            // <3.5.2.1> 決議這個 Bean 中帶有 @Before|@After|@Around|@AfterReturning|@AfterThrowing 注解的方法
                            // 會決議成對應的 InstantiationModelAwarePointcutAdvisorImpl 物件(PointcutAdvisor)
                            List<Advisor> classAdvisors = this.advisorFactory.getAdvisors(factory);
                            // <3.5.2.2> 如果這個 Bean 是單例模式,則將決議出來的 Advisor 全部快取至 `advisorsCache` 中
                            if (this.beanFactory.isSingleton(beanName)) {
                                this.advisorsCache.put(beanName, classAdvisors);
                            }
                            // <3.5.2.3> 否則,將這個 Bean 對應的 MetadataAwareAspectInstanceFactory(AspectJ 元資料實體構建工廠)快取至 `aspectFactoryCache` 中
                            else {
                                this.aspectFactoryCache.put(beanName, factory);
                            }
                            // <3.5.2.4> 將決議出來的 Advisor 添加至 `advisors` 中
                            advisors.addAll(classAdvisors);
                        }
                        // <3.5.3> 否則,這個 AspectJ 不是單例模式,不能將決議出來的 Advisor 快取,其他的處理程序都和上面一樣
                        else {
                            // Per target or per this.
                            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);
                            // 將這個 Bean 對應的 MetadataAwareAspectInstanceFactory(AspectJ 元資料實體構建工廠)快取至 `aspectFactoryCache` 中
                            this.aspectFactoryCache.put(beanName, factory);
                            // 決議出這個 Bean 中所有的 Advisor,并添加至 `advisors` 中
                            advisors.addAll(this.advisorFactory.getAdvisors(factory));
                        }
                    }
                }
                // <3.6> 對 `aspectNames` 進行快取
                this.aspectBeanNames = aspectNames;
                // <3.7> 回傳所有 AspectJ 的所有的 Advisor 物件們
                return advisors;
            }
        }
    }

    if (aspectNames.isEmpty()) {
        return Collections.emptyList();
    }
    List<Advisor> advisors = new ArrayList<>();
    /*
     * <4> 否則,遍歷快取中的 AspectJ 的 beanName
     */
    for (String aspectName : aspectNames) {
        // <4.1> 嘗試從 `advisorsCache` 快取中獲取這個 beanName 對應的所有 Advisor 們,并添加至 `advisors` 中
        List<Advisor> cachedAdvisors = this.advisorsCache.get(aspectName);
        if (cachedAdvisors != null) {
            advisors.addAll(cachedAdvisors);
        }
        // <4.2> `advisorsCache` 快取中沒有,
        // 則根據 `aspectFactoryCache` 快取中對應的 MetadataAwareAspectInstanceFactory(AspectJ 元資料實體構建工廠)決議出所有的 Advisor 們,并添加至 `advisors` 中
        else {
            MetadataAwareAspectInstanceFactory factory = this.aspectFactoryCache.get(aspectName);
            advisors.addAll(this.advisorFactory.getAdvisors(factory));
        }
    }
    // <5> 回傳所有 AspectJ 的所有的 Advisor 物件們
    return advisors;
}

該方法的處理程序稍微有點復雜,如下:

  1. 從快取中獲取所有帶有 @AspectJ 注解的 Bean,保存至 aspectNames 集合中
  2. 快取中沒有,則對當前物件加鎖再判斷快取中是否有資料
  3. 還是沒有快取,則進行接下來的處理
    1. 獲取當前 IoC 容器中所有的 Bean 的名稱集合
    2. 遍歷所有的 Bean 的名稱,進行處理
    3. 判斷這個 Bean 是否有資格,默認都為 true
    4. 獲取這個 Bean 的 Class 物件,如果為空則跳過
    5. 如果這個 Bean 帶有 @Aspect 注解,且沒有以 ajc$ 開頭的欄位,那么進行接下來的決議程序
      1. 將這個 Bean 的名稱保存至 aspectNames 集合中
      2. 判斷 @AspectJ 注解的類別是否為 singleton,默認空的情況就是這個
        1. 通過 AspectJAdvisorFactory#getAdvisors(..) 方法決議出這個 Bean 中帶有 @Before|@After|@Around|@AfterReturning|@AfterThrowing 注解的方法,例如會決議成 InstantiationModelAwarePointcutAdvisorImpl 物件(PointcutAdvisor)
        2. 如果這個 Bean 是單例模式,則將決議出來的 Advisor 全部快取至 advisorsCache
        3. 否則,將這個 Bean 對應的 MetadataAwareAspectInstanceFactory(AspectJ 元資料實體構建工廠)快取至 aspectFactoryCache
        4. 將決議出來的 Advisor 添加至 advisors
      3. 否則,這個 AspectJ 不是單例模式,不能將決議出來的 Advisor 快取,其他的處理程序都和上面一樣
    6. aspectNames 進行快取
    7. 回傳所有 AspectJ 的所有的 Advisor 物件們
  4. 否則,遍歷快取中的 AspectJ 的 beanName,進行處理
    1. 嘗試從 advisorsCache 快取中獲取這個 beanName 對應的所有 Advisor 們,并添加至 advisors
    2. advisorsCache 快取中沒有,則根據 aspectFactoryCache 快取中對應的 MetadataAwareAspectInstanceFactory(AspectJ 元資料實體構建工廠)決議出所有的 Advisor 們,并添加至 advisors
  5. 回傳所有 AspectJ 的所有的 Advisor 物件們

做個小結,整個程序稍微復雜一點,會嘗試從快取中獲取 Advisor,快取中沒有資料則先獲取到所有帶有 @AspectJ 注解的 Bean,通過 ReflectiveAspectJAdvisorFactory 對這些 Bean 中帶有 AspectJ 相關注解的方法進行處理,生成對應的 PointcutAdvisor 物件,

接下來,我們來看看 ReflectiveAspectJAdvisorFactory 決議 @AspectJ 注解的 Bean 的程序

ReflectiveAspectJAdvisorFactory

org.springframework.aop.aspectj.annotation.ReflectiveAspectJAdvisorFactory,Advisor 工廠,用于決議 @AspectJ 注解的 Bean 中的 Advisor

建構式

public class ReflectiveAspectJAdvisorFactory extends AbstractAspectJAdvisorFactory implements Serializable {
	/**
	 * 方法比較器
	 */
	private static final Comparator<Method> METHOD_COMPARATOR;

	static {
		Comparator<Method> adviceKindComparator = new ConvertingComparator<>(
				new InstanceComparator<>( Around.class, Before.class, After.class, AfterReturning.class, AfterThrowing.class),
				(Converter<Method, Annotation>) method -> {
					AspectJAnnotation<?> annotation = AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(method);
					return (annotation != null ? annotation.getAnnotation() : null);
				});
		Comparator<Method> methodNameComparator = new ConvertingComparator<>(Method::getName);
		METHOD_COMPARATOR = adviceKindComparator.thenComparing(methodNameComparator);
	}
}

可以看到有一個 Comparator 方法比較器,順序是 @Around > @Before > @After > @AfterReturning > @AfterThrowing,注意獲取到應用于某個 Bean 的 Advisor 的順序不是這樣子,可以回到前面的 2.4 sortAdvisors 方法 小節看看

2.1.3.1 getAdvisors 方法

getAdvisors(MetadataAwareAspectInstanceFactory aspectInstanceFactory) 方法,決議出這個 Bean(帶有 @AspectJ 注解)中所有的 Advisor,方法入參是 Bean 的元資料實體構建工廠,方法如下:

public List<Advisor> getAdvisors(MetadataAwareAspectInstanceFactory aspectInstanceFactory) {
    // <1> 獲取這個 Bean 的 Class 物件和 beanName
    Class<?> aspectClass = aspectInstanceFactory.getAspectMetadata().getAspectClass();
    String aspectName = aspectInstanceFactory.getAspectMetadata().getAspectName();
    validate(aspectClass);

    // We need to wrap the MetadataAwareAspectInstanceFactory with a decorator
    // so that it will only instantiate once.
    MetadataAwareAspectInstanceFactory lazySingletonAspectInstanceFactory =
            new LazySingletonAspectInstanceFactoryDecorator(aspectInstanceFactory);

    List<Advisor> advisors = new ArrayList<>();

    /*
     * <2> 遍歷沒有標注 @Pointcut 注解的方法(順序:@Around > @Before > @After > @AfterReturning > @AfterThrowing)
     */
    for (Method method : getAdvisorMethods(aspectClass)) {
        /*
         * <2.1> 如果這個方法帶有 @Before|@After|@Around|@AfterReturning|@AfterThrowing 注解
         * 則根據注解資訊創建一個 InstantiationModelAwarePointcutAdvisorImpl 物件
         * 這個物件就是 PointcutAdvisor 型別,包含了 Pointcut 和 Advice
         */
        Advisor advisor = getAdvisor(method, lazySingletonAspectInstanceFactory, advisors.size(), aspectName);
        // <2.2> 生成了 PointcutAdvisor 則添加至 `advisor` 集合中
        if (advisor != null) {
            advisors.add(advisor);
        }
    }

    // If it's a per target aspect, emit the dummy instantiating aspect.
    // <3> 如果這個 Aspect 需要延遲初始化,則往首部添加一個 PointcutAdvisor
    if (!advisors.isEmpty() && lazySingletonAspectInstanceFactory.getAspectMetadata().isLazilyInstantiated()) {
        Advisor instantiationAdvisor = new SyntheticInstantiationAdvisor(lazySingletonAspectInstanceFactory);
        advisors.add(0, instantiationAdvisor);
    }

    // Find introduction fields.
    // <4> 根據帶有 @DeclareParents 注解的欄位生成 IntroductionAdvisor 物件,并添加至 `advisor` 集合中
    for (Field field : aspectClass.getDeclaredFields()) {
        Advisor advisor = getDeclareParentsAdvisor(field);
        if (advisor != null) {
            advisors.add(advisor);
        }
    }

    // <5> 回傳這個 Aspect 中所有的 Advisor 物件
    return advisors;
}

該方法的處理程序如下:

  1. 獲取這個 Bean 的 Class 物件和 beanName

  2. 遍歷沒有標注 @Pointcut 注解的方法(順序:@Around > @Before > @After > @AfterReturning > @AfterThrowing

    private List<Method> getAdvisorMethods(Class<?> aspectClass) {
       final List<Method> methods = new ArrayList<>();
       ReflectionUtils.doWithMethods(aspectClass, method -> {
          // Exclude pointcuts
          // 排除 @Pointcut 注解標注的方法
          if (AnnotationUtils.getAnnotation(method, Pointcut.class) == null) {
             methods.add(method);
          }
       });
       // 進行排序
       methods.sort(METHOD_COMPARATOR);
       return methods;
    }
    
    1. 呼叫 getAdvisor(..) 方法,將這個方法決議成 InstantiationModelAwarePointcutAdvisorImpl 物件(PointcutAdvisor),這個方法必須帶有 @Before|@After|@Around|@AfterReturning|@AfterThrowing 注解
    2. 如果上一步決議出了 Advisor 物件,則添加至 advisor 集合中
  3. 如果這個 Aspect 需要延遲初始化,則往首部添加一個 SyntheticInstantiationAdvisor(PointcutAdvisor),暫時忽略

  4. 呼叫 getDeclareParentsAdvisor(..) 方法,根據帶有 @DeclareParents 注解的欄位生成 IntroductionAdvisor 物件,并添加至 advisor 集合中

    private Advisor getDeclareParentsAdvisor(Field introductionField) {
        DeclareParents declareParents = introductionField.getAnnotation(DeclareParents.class);
        if (declareParents == null) {
            return null;
        }
        if (DeclareParents.class == declareParents.defaultImpl()) {
            throw new IllegalStateException("'defaultImpl' attribute must be set on DeclareParents");
        }
        return new DeclareParentsAdvisor(
                introductionField.getType(), declareParents.value(), declareParents.defaultImpl());
    }
    
  5. 回傳這個 Aspect 中所有的 Advisor 物件,也就是回傳 advisor 集合

可以看到 @AspectJ 注解的 Bean 中的 @Before|@After|@Around|@AfterReturning|@AfterThrowing 注解方法在 getAdvisor(..) 方法中進行

2.1.3.2 getAdvisor 方法

getAdvisor(..) 方法,決議 @Before|@After|@Around|@AfterReturning|@AfterThrowing 注解方法,生成 PointcutAdvisor 物件,如下:

public Advisor getAdvisor(Method candidateAdviceMethod, MetadataAwareAspectInstanceFactory aspectInstanceFactory,
        int declarationOrderInAspect, String aspectName) {

    validate(aspectInstanceFactory.getAspectMetadata().getAspectClass());

    /*
     * <1> 嘗試根據該方法生成一個 AspectJExpressionPointcut 物件
     * 根據 @Before|@After|@Around|@AfterReturning|@AfterThrowing 注解資訊進行創建,沒有的話則回傳 null
     */
    AspectJExpressionPointcut expressionPointcut = getPointcut(
            candidateAdviceMethod, aspectInstanceFactory.getAspectMetadata().getAspectClass());
    if (expressionPointcut == null) {
        return null;
    }

    /*
     * <2> 如果存在上面其中一個注解,則將創建的 AspectJExpressionPointcut 封裝成 InstantiationModelAwarePointcutAdvisorImpl 物件
     * 也就是封裝成了 PointcutAdvisor 物件,會初始化一個 Advice,也就是注解標注的方法
     * 那么這個物件中就包含了 Pointcut 和 Advice,就可以判斷某個方法是否被攔截,攔截后應該如何處理
     */
    return new InstantiationModelAwarePointcutAdvisorImpl(expressionPointcut, candidateAdviceMethod,
            this, aspectInstanceFactory, declarationOrderInAspect, aspectName);
}

該方法的處理程序如下:

  1. 呼叫 getPointcut(..) 方法,嘗試根據該方法生成一個 AspectJExpressionPointcut 物件,根據 @Before|@After|@Around|@AfterReturning|@AfterThrowing 注解資訊進行創建,沒有的話則回傳 null
  2. 如果存在上面其中一個注解,則將創建的 AspectJExpressionPointcut 封裝成 InstantiationModelAwarePointcutAdvisorImpl 物件,也就是封裝成了 PointcutAdvisor 物件,會初始化一個 Advice,也就是注解標注的方法,那么這個物件中就包含了 Pointcut 和 Advice,就可以判斷某個方法是否被攔截,攔截后應該如何處理

getPointcut 方法

getPointcut(Method candidateAdviceMethod, Class<?> candidateAspectClass) 方法,嘗試將方法決議成 AspectJExpressionPointcut 物件,如下:

private AspectJExpressionPointcut getPointcut(Method candidateAdviceMethod, Class<?> candidateAspectClass) {
    // <1> 找到這個方法的 @Before|@After|@Around|@AfterReturning|@AfterThrowing 注解資訊
    AspectJAnnotation<?> aspectJAnnotation =
            AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(candidateAdviceMethod);
    if (aspectJAnnotation == null) {
        return null;
    }

    // <2> 如果帶有上面其中一個注解,則創建一個 AspectJExpressionPointcut 物件
    AspectJExpressionPointcut ajexp =
            new AspectJExpressionPointcut(candidateAspectClass, new String[0], new Class<?>[0]);
    // <3> 設定 Pointcut 的運算式
    ajexp.setExpression(aspectJAnnotation.getPointcutExpression());
    if (this.beanFactory != null) {
        ajexp.setBeanFactory(this.beanFactory);
    }
    // <4> 回傳 AspectJExpressionPointcut 物件
    return ajexp;
}

該方法的處理程序如下:

  1. 找到這個方法的 @Before|@After|@Around|@AfterReturning|@AfterThrowing 注解資訊
  2. 如果帶有上面其中一個注解,則創建一個 AspectJExpressionPointcut 物件
  3. 設定 Pointcut 的運算式
  4. 回傳 AspectJExpressionPointcut 物件

getAdvice 方法

getAdvice(..) 方法,主要根據 AspectJExpressionPointcut 初始化一個 Advice 物件,如下:

public Advice getAdvice(Method candidateAdviceMethod, AspectJExpressionPointcut expressionPointcut,
        MetadataAwareAspectInstanceFactory aspectInstanceFactory, int declarationOrder, String aspectName) {

    Class<?> candidateAspectClass = aspectInstanceFactory.getAspectMetadata().getAspectClass();
    validate(candidateAspectClass);

    // 獲取 @Pointcut、@Around、@Before、@After、@AfterReturning、@AfterThrowing 注解
    AspectJAnnotation<?> aspectJAnnotation =
            AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(candidateAdviceMethod);
    if (aspectJAnnotation == null) {
        return null;
    }

    // If we get here, we know we have an AspectJ method.
    // Check that it's an AspectJ-annotated class
    if (!isAspect(candidateAspectClass)) {
        throw new AopConfigException("Advice must be declared inside an aspect type: " +
                "Offending method '" + candidateAdviceMethod + "' in class [" +
                candidateAspectClass.getName() + "]");
    }

    if (logger.isDebugEnabled()) {
        logger.debug("Found AspectJ method: " + candidateAdviceMethod);
    }

    AbstractAspectJAdvice springAdvice;

    switch (aspectJAnnotation.getAnnotationType()) {
        case AtPointcut:
            if (logger.isDebugEnabled()) {
                logger.debug("Processing pointcut '" + candidateAdviceMethod.getName() + "'");
            }
            return null;
        case AtAround: // @Around -> AspectJAroundAdvice
            springAdvice = new AspectJAroundAdvice(
                    candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
            break;
        case AtBefore: // @Before -> AspectJMethodBeforeAdvice
            springAdvice = new AspectJMethodBeforeAdvice(
                    candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
            break;
        case AtAfter: // @After -> AspectJAfterAdvice
            springAdvice = new AspectJAfterAdvice(
                    candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
            break;
        case AtAfterReturning: // @AfterReturning -> AspectJAfterAdvice
            springAdvice = new AspectJAfterReturningAdvice(
                    candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
            AfterReturning afterReturningAnnotation = (AfterReturning) aspectJAnnotation.getAnnotation();
            if (StringUtils.hasText(afterReturningAnnotation.returning())) {
                springAdvice.setReturningName(afterReturningAnnotation.returning());
            }
            break;
        case AtAfterThrowing: // @AfterThrowing -> AspectJAfterThrowingAdvice
            springAdvice = new AspectJAfterThrowingAdvice(
                    candidateAdviceMethod, expressionPointcut, aspectInstanceFactory);
            AfterThrowing afterThrowingAnnotation = (AfterThrowing) aspectJAnnotation.getAnnotation();
            if (StringUtils.hasText(afterThrowingAnnotation.throwing())) {
                springAdvice.setThrowingName(afterThrowingAnnotation.throwing());
            }
            break;
        default:
            throw new UnsupportedOperationException(
                    "Unsupported advice type on method: " + candidateAdviceMethod);
    }

    // Now to configure the advice...
    springAdvice.setAspectName(aspectName);
    springAdvice.setDeclarationOrder(declarationOrder);
    /*
     * 獲取方法的引數名串列
     */
    String[] argNames = this.parameterNameDiscoverer.getParameterNames(candidateAdviceMethod);
    if (argNames != null) {
        // 設定引數名
        springAdvice.setArgumentNamesFromStringArray(argNames);
    }
    springAdvice.calculateArgumentBindings();

    return springAdvice;
}

根據注解的型別創建對應的 Advice 型別,如下:

  • @Around:AspectJAroundAdvice,實作了 MethodInterceptor
  • @Before:AspectJMethodBeforeAdvice
  • @After:AspectJAfterAdvice,實作了 MethodInterceptor
  • @AfterReturning: AspectJAfterAdvice
  • @AfterThrowing:AspectJAfterThrowingAdvice,實作了 MethodInterceptor

InstantiationModelAwarePointcutAdvisorImpl

org.springframework.aop.aspectj.annotation.InstantiationModelAwarePointcutAdvisorImpl,AspectJ 注解方法決議后的物件,實作了 PointcutAdvisor,包含 Pointcut 和 Advice

final class InstantiationModelAwarePointcutAdvisorImpl
		implements InstantiationModelAwarePointcutAdvisor, AspectJPrecedenceInformation, Serializable {

	private static final Advice EMPTY_ADVICE = new Advice() {};

	private final AspectJExpressionPointcut declaredPointcut;

	private final Class<?> declaringClass;

	private final String methodName;

	private final Class<?>[] parameterTypes;

	private transient Method aspectJAdviceMethod;

	private final AspectJAdvisorFactory aspectJAdvisorFactory;

	private final MetadataAwareAspectInstanceFactory aspectInstanceFactory;

	private final int declarationOrder;

	private final String aspectName;

	private final Pointcut pointcut;

	private final boolean lazy;

	@Nullable
	private Advice instantiatedAdvice;

	@Nullable
	private Boolean isBeforeAdvice;

	@Nullable
	private Boolean isAfterAdvice;

	public InstantiationModelAwarePointcutAdvisorImpl(AspectJExpressionPointcut declaredPointcut,
			Method aspectJAdviceMethod, AspectJAdvisorFactory aspectJAdvisorFactory,
			MetadataAwareAspectInstanceFactory aspectInstanceFactory, int declarationOrder, String aspectName) {

		// AspectJExpressionPointcut 物件
		this.declaredPointcut = declaredPointcut;
		// Advice 所在的 Class 物件
		this.declaringClass = aspectJAdviceMethod.getDeclaringClass();
		// Advice 對應的方法名稱
		this.methodName = aspectJAdviceMethod.getName();
		// Advice 對應的方法引數型別
		this.parameterTypes = aspectJAdviceMethod.getParameterTypes();
		// Advice 對應的方法物件
		this.aspectJAdviceMethod = aspectJAdviceMethod;
		// Advisor 工廠,用于決議 @AspectJ 注解的 Bean 中的 Advisor
		this.aspectJAdvisorFactory = aspectJAdvisorFactory;
		// 元資料實體構建工廠
		this.aspectInstanceFactory = aspectInstanceFactory;
		// 定義的順序
		this.declarationOrder = declarationOrder;
		// Advice 所在 Bean 的名稱
		this.aspectName = aspectName;

		// 如果需要延遲初始化,則不立即初始化 Advice 物件
		if (aspectInstanceFactory.getAspectMetadata().isLazilyInstantiated()) {
			// Static part of the pointcut is a lazy type.
			Pointcut preInstantiationPointcut = Pointcuts.union(
					aspectInstanceFactory.getAspectMetadata().getPerClausePointcut(), this.declaredPointcut);

			// Make it dynamic: must mutate from pre-instantiation to post-instantiation state.
			// If it's not a dynamic pointcut, it may be optimized out
			// by the Spring AOP infrastructure after the first evaluation.
			this.pointcut = new PerTargetInstantiationModelPointcut(
					this.declaredPointcut, preInstantiationPointcut, aspectInstanceFactory);
			this.lazy = true;
		}
		// 否則,初始化 Advice 物件
		else {
			// A singleton aspect.
			// AspectJExpressionPointcut 物件
			this.pointcut = this.declaredPointcut;
			this.lazy = false;
			// 根據 AspectJExpressionPointcut 初始化一個 Advice 物件
			// `@Around`:AspectJAroundAdvice,實作了 MethodInterceptor
			// `@Before`:AspectJMethodBeforeAdvice
			// `@After`:AspectJAfterAdvice,實作了 MethodInterceptor
			// `@AfterReturning`: AspectJAfterAdvice
			// `@AfterThrowing`:AspectJAfterThrowingAdvice,實作了 MethodInterceptor
			this.instantiatedAdvice = instantiateAdvice(this.declaredPointcut);
		}
	}
    
    private Advice instantiateAdvice(AspectJExpressionPointcut pointcut) {
		Advice advice = this.aspectJAdvisorFactory.getAdvice(this.aspectJAdviceMethod, pointcut,
				this.aspectInstanceFactory, this.declarationOrder, this.aspectName);
		return (advice != null ? advice : EMPTY_ADVICE);
	}
}

總結

在上一篇《Spring AOP 自動代理(一)入口》文章講述了 Spring AOP 自動代理的入口,主要對 AbstractAutoProxyCreator 這個類進行了分析,本文接著上一篇文章分析了在 Spring AOP 自動代理的的程序中,如何從 Spring 背景關系篩選出能夠應用于某個 Bean 的 Advisor 們,大致的流程如下:

  1. 決議出當前 IoC 容器所有 Advisor 物件

    1. 獲取當前 IoC 容器所有 Advisor 型別的 Bean
    2. 決議當前 IoC 容器中所有帶有 @AspectJ 注解的 Bean,將內部帶有 @Before|@After|@Around|@AfterReturning|@AfterThrowing 注解的方法決議出對應的 PointcutAdvisor 物件,帶有 @DeclareParents 注解的欄位決議出 IntroductionAdvisor 物件
  2. 篩選出能夠應用于這個 Bean 的 Advisor 們,主要通過 ClassFilter 類過濾器和 MethodMatcher 方法匹配器進行匹配

  3. 對篩選出來的 Advisor 進行擴展,例如子類會往首部添加一個 PointcutAdvisor 物件

  4. 對篩選出來的 Advisor 進行排序

    • 不同的 AspectJ 根據 @Order 排序

    • 同一個 AspectJ 中不同 Advisor 的排序,優先級:AspectJAfterThrowingAdvice > AspectJAfterReturningAdvice > AspectJAfterAdvice > AspectJAroundAdvice > AspectJMethodBeforeAdvice

AspectJ 中的注解對應的 Advice 型別如下:

  • @Around -> AspectJAroundAdvice,實作了 MethodInterceptor
  • @Before -> AspectJMethodBeforeAdvice
  • @After -> AspectJAfterAdvice,實作了 MethodInterceptor
  • @AfterReturning -> AspectJAfterAdvice
  • @AfterThrowing -> AspectJAfterThrowingAdvice,實作了 MethodInterceptor

好了,本篇文章就到這里了,如果獲取到能夠應用于某個 Bean 的 Advisor,那么接下來要做的就是為這個 Bean 創建一個代理物件,通過 JDK 動態代理或者 CGLIB 動態代理,將在下篇文章進行分析,

轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/278345.html

標籤:Java

上一篇:Java 在Excel中添加水印(單一水印、平鋪水印)

下一篇:物件存盤服務MinIO安裝部署分布式及Spring Boot專案實作檔案上傳下載

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 【C++】Microsoft C++、C 和匯編程式檔案

    ......

    uj5u.com 2020-09-10 00:57:23 more
  • 例外宣告

    相比于斷言適用于排除邏輯上不可能存在的狀態,例外通常是用于邏輯上可能發生的錯誤。 例外宣告 Item 1:當函式不可能拋出例外或不能接受拋出例外時,使用noexcept 理由 如果不打算拋出例外的話,程式就會認為無法處理這種錯誤,并且應當盡早終止,如此可以有效地阻止例外的傳播與擴散。 示例 //不可 ......

    uj5u.com 2020-09-10 00:57:27 more
  • Codeforces 1400E Clear the Multiset(貪心 + 分治)

    鏈接:https://codeforces.com/problemset/problem/1400/E 來源:Codeforces 思路:給你一個陣列,現在你可以進行兩種操作,操作1:將一段沒有 0 的區間進行減一的操作,操作2:將 i 位置上的元素歸零。最終問:將這個陣列的全部元素歸零后操作的最少 ......

    uj5u.com 2020-09-10 00:57:30 more
  • UVA11610 【Reverse Prime】

    本人看到此題沒有翻譯,就附帶了一個自己的翻譯版本 思考 這一題,它的第一個要求是找出所有 $7$ 位反向質數及其質因數的個數。 我們應該需要質數篩篩選1~$10^{7}$的所有數,這里就不慢慢介紹了。但是,重讀題,我們突然發現反向質數都是 $7$ 位,而將它反過來后的數字卻是 $6$ 位數,這就說明 ......

    uj5u.com 2020-09-10 00:57:36 more
  • 統計區間素數數量

    1 #pragma GCC optimize(2) 2 #include <bits/stdc++.h> 3 using namespace std; 4 bool isprime[1000000010]; 5 vector<int> prime; 6 inline int getlist(int ......

    uj5u.com 2020-09-10 00:57:47 more
  • C/C++編程筆記:C++中的 const 變數詳解,教你正確認識const用法

    1、C中的const 1、區域const變數存放在堆疊區中,會分配記憶體(也就是說可以通過地址間接修改變數的值)。測驗代碼如下: 運行結果: 2、全域const變數存放在只讀資料段(不能通過地址修改,會發生寫入錯誤), 默認為外部聯編,可以給其他源檔案使用(需要用extern關鍵字修飾) 運行結果: ......

    uj5u.com 2020-09-10 00:58:04 more
  • 【C++犯錯記錄】VS2019 MFC添加資源不懂如何修改資源宏ID

    1. 首先在資源視圖中,添加資源 2. 點擊新添加的資源,復制自動生成的ID 3. 在解決方案資源管理器中找到Resource.h檔案,編輯,使用整個專案搜索和替換的方式快速替換 宏宣告 4. Ctrl+Shift+F 全域搜索,點擊查找全部,然后逐個替換 5. 為什么使用搜索替換而不使用屬性視窗直 ......

    uj5u.com 2020-09-10 00:59:11 more
  • 【C++犯錯記錄】VS2019 MFC不懂的批量添加資源

    1. 打開資源頭檔案Resource.h,在其中預先定義好宏 ID(不清楚其實ID值應該設定多少,可以先新建一個相同的資源項,再在這個資源的ID值的基礎上遞增即可) 2. 在資源視圖中選中專案資源,按F7編輯資源檔案,按 ID 型別 相對路徑的形式添加 資源。(別忘了先把檔案拷貝到專案中的res檔案 ......

    uj5u.com 2020-09-10 01:00:19 more
  • C/C++編程筆記:關于C++的參考型別,專供新手入門使用

    今天要講的是C++中我最喜歡的一個用法——參考,也叫別名。 參考就是給一個變數名取一個變數名,方便我們間接地使用這個變數。我們可以給一個變數創建N個參考,這N + 1個變數共享了同一塊記憶體區域。(參考型別的變數會占用記憶體空間,占用的記憶體空間的大小和指標型別的大小是相同的。雖然參考是一個物件的別名,但 ......

    uj5u.com 2020-09-10 01:00:22 more
  • 【C/C++編程筆記】從頭開始學習C ++:初學者完整指南

    眾所周知,C ++的學習曲線陡峭,但是花時間學習這種語言將為您的職業帶來奇跡,并使您與其他開發人員區分開。您會更輕松地學習新語言,形成真正的解決問題的技能,并在編程的基礎上打下堅實的基礎。 C ++將幫助您養成良好的編程習慣(即清晰一致的編碼風格,在撰寫代碼時注釋代碼,并限制類內部的可見性),并且由 ......

    uj5u.com 2020-09-10 01:00:41 more
最新发布
  • Rust中的智能指標:Box<T> Rc<T> Arc<T> Cell<T> RefCell<T> Weak

    Rust中的智能指標是什么 智能指標(smart pointers)是一類資料結構,是擁有資料所有權和額外功能的指標。是指標的進一步發展 指標(pointer)是一個包含記憶體地址的變數的通用概念。這個地址參考,或 ” 指向”(points at)一些其 他資料 。參考以 & 符號為標志并借用了他們所 ......

    uj5u.com 2023-04-20 07:24:10 more
  • Java的值傳遞和參考傳遞

    值傳遞不會改變本身,參考傳遞(如果傳遞的值需要實體化到堆里)如果發生修改了會改變本身。 1.基本資料型別都是值傳遞 package com.example.basic; public class Test { public static void main(String[] args) { int ......

    uj5u.com 2023-04-20 07:24:04 more
  • [2]SpinalHDL教程——Scala簡單入門

    第一個 Scala 程式 shell里面輸入 $ scala scala> 1 + 1 res0: Int = 2 scala> println("Hello World!") Hello World! 檔案形式 object HelloWorld { /* 這是我的第一個 Scala 程式 * 以 ......

    uj5u.com 2023-04-20 07:23:58 more
  • 理解函式指標和回呼函式

    理解 函式指標 指向函式的指標。比如: 理解函式指標的偽代碼 void (*p)(int type, char *data); // 定義一個函式指標p void func(int type, char *data); // 宣告一個函式func p = func; // 將指標p指向函式func ......

    uj5u.com 2023-04-20 07:23:52 more
  • Django筆記二十五之資料庫函式之日期函式

    本文首發于公眾號:Hunter后端 原文鏈接:Django筆記二十五之資料庫函式之日期函式 日期函式主要介紹兩個大類,Extract() 和 Trunc() Extract() 函式作用是提取日期,比如我們可以提取一個日期欄位的年份,月份,日等資料 Trunc() 的作用則是截取,比如 2022-0 ......

    uj5u.com 2023-04-20 07:23:45 more
  • 一天吃透JVM面試八股文

    什么是JVM? JVM,全稱Java Virtual Machine(Java虛擬機),是通過在實際的計算機上仿真模擬各種計算機功能來實作的。由一套位元組碼指令集、一組暫存器、一個堆疊、一個垃圾回收堆和一個存盤方法域等組成。JVM屏蔽了與作業系統平臺相關的資訊,使得Java程式只需要生成在Java虛擬機 ......

    uj5u.com 2023-04-20 07:23:31 more
  • 使用Java接入小程式訂閱訊息!

    更新完微信服務號的模板訊息之后,我又趕緊把微信小程式的訂閱訊息給實作了!之前我一直以為微信小程式也是要企業才能申請,沒想到小程式個人就能申請。 訊息推送平臺🔥推送下發【郵件】【短信】【微信服務號】【微信小程式】【企業微信】【釘釘】等訊息型別。 https://gitee.com/zhongfuch ......

    uj5u.com 2023-04-20 07:22:59 more
  • java -- 緩沖流、轉換流、序列化流

    緩沖流 緩沖流, 也叫高效流, 按照資料型別分類: 位元組緩沖流:BufferedInputStream,BufferedOutputStream 字符緩沖流:BufferedReader,BufferedWriter 緩沖流的基本原理,是在創建流物件時,會創建一個內置的默認大小的緩沖區陣列,通過緩沖 ......

    uj5u.com 2023-04-20 07:22:49 more
  • Java-SpringBoot-Range請求頭設定實作視頻分段傳輸

    老實說,人太懶了,現在基本都不喜歡寫筆記了,但是網上有關Range請求頭的文章都太水了 下面是抄的一段StackOverflow的代碼...自己大修改過的,寫的注釋挺全的,應該直接看得懂,就不解釋了 寫的不好...只是希望能給視頻網站開發的新手一點點幫助吧. 業務場景:視頻分段傳輸、視頻多段傳輸(理 ......

    uj5u.com 2023-04-20 07:22:42 more
  • Windows 10開發教程_編程入門自學教程_菜鳥教程-免費教程分享

    教程簡介 Windows 10開發入門教程 - 從簡單的步驟了解Windows 10開發,從基本到高級概念,包括簡介,UWP,第一個應用程式,商店,XAML控制元件,資料系結,XAML性能,自適應設計,自適應UI,自適應代碼,檔案管理,SQLite資料庫,應用程式到應用程式通信,應用程式本地化,應用程式 ......

    uj5u.com 2023-04-20 07:22:35 more