Spring 由緩存切點驅動的通知者

Spring 緩存通知者和切點

緩存切點

/**
 *  Spring 核心切點抽象
 */
public interface Pointcut {

    /**
     *  類過濾器,當前切點是否需要織入在指定的類上
     */
    ClassFilter getClassFilter();

    /**
     *  方法匹配器,當前切點是否需要織入在指定的方法上
     */
    MethodMatcher getMethodMatcher();

    Pointcut TRUE = TruePointcut.INSTANCE;
}

/**
 *  檢查目標方法是否需要獲得通知
 */
public interface MethodMatcher {

    /**
     *  目標方法是否需要獲得通知,
     *  isRuntime() 和此方法返回 false 時,不執行通知。
     */
    boolean matches(Method method, Class<?> targetClass);

    /**
     *  是否需要執行運行時匹配【false 表示只需要執行靜態匹配即可】
     *  如果 isRuntime() 返回 true,則
     *  matches(Method method, Class<?> targetClass)
     *  && matches(Method method, Class<?> targetClass, Object... args)
     *  都返回 true 時才執行通知
     */
    boolean isRuntime();

    /**
     *  當 matches(Method method, Class<?> targetClass) 和 isRuntime() 都返回 true 時,
     *  在通知執行前再次進行匹配
     */
    boolean matches(Method method, Class<?> targetClass, Object... args);

    /**
     *  匹配所有方法
     */
    MethodMatcher TRUE = TrueMethodMatcher.INSTANCE;
}

/**
 *  不關心運行時參數的靜態方法匹配器
 */
public abstract class StaticMethodMatcher implements MethodMatcher {
    @Override
    public final boolean isRuntime() {
        return false;
    }

    @Override
    public final boolean matches(Method method, Class<?> targetClass, Object... args) {
        // should never be invoked because isRuntime() returns false
        throw new UnsupportedOperationException("Illegal MethodMatcher usage");
    }
}

/**
 *  緩存操作切點抽象
 */
@SuppressWarnings("serial")
abstract class CacheOperationSourcePointcut extends StaticMethodMatcherPointcut implements Serializable {
    /**
     *  目標類的指定方法是否需要通知
     */
    @Override
    public boolean matches(Method method, Class<?> targetClass) {
        // 排除緩存管理器
        if (CacheManager.class.isAssignableFrom(targetClass)) {
            return false;
        }
        final CacheOperationSource cas = getCacheOperationSource();
        // 存在緩存操作源 && 指定方法上能解析到緩存操作
        return cas != null && !CollectionUtils.isEmpty(cas.getCacheOperations(method, targetClass));
    }

    @Override
    public boolean equals(Object other) {
        if (this == other) {
            return true;
        }
        if (!(other instanceof CacheOperationSourcePointcut)) {
            return false;
        }
        final CacheOperationSourcePointcut otherPc = (CacheOperationSourcePointcut) other;
        return ObjectUtils.nullSafeEquals(getCacheOperationSource(), otherPc.getCacheOperationSource());
    }

    @Override
    public int hashCode() {
        return CacheOperationSourcePointcut.class.hashCode();
    }

    @Override
    public String toString() {
        return getClass().getName() + ": " + getCacheOperationSource();
    }

    /**
     * Obtain the underlying {@link CacheOperationSource} (may be {@code null}).
     * To be implemented by subclasses.
     */
    @Nullable
    protected abstract CacheOperationSource getCacheOperationSource();
}

緩存通知者

/**
 *  持有 AOP 通知的基礎接口
 */
public interface Advisor {
    /**
     *  如果沒有正確配置通知,則返回一個空通知
     * @since 5.0
     */
    Advice EMPTY_ADVICE = new Advice() {};

    /**
     *  返回切面的通知
     */
    Advice getAdvice();

    /**
     *  此通知是否與具體的實例關聯【不可共享】
     */
    boolean isPerInstance();
}

/**
 *  由切入點驅動的通知者接口
 */
public interface PointcutAdvisor extends Advisor {
    /**
     *  獲取驅動此 Advisor 的切入點
     */
    Pointcut getPointcut();
}

@SuppressWarnings("serial")
public abstract class AbstractPointcutAdvisor implements PointcutAdvisor, Ordered, Serializable {
    /**
     *  此 Advisor 關聯切面的順序值:值越小,越先執行
     */
    @Nullable
    private Integer order;

    public void setOrder(int order) {
        this.order = order;
    }

    @Override
    public int getOrder() {
        if (order != null) {
            return order;
        }
        final Advice advice = getAdvice();
        if (advice instanceof Ordered) {
            return ((Ordered) advice).getOrder();
        }
        return Ordered.LOWEST_PRECEDENCE;
    }

    @Override
    public boolean isPerInstance() {
        return true;
    }

    @Override
    public boolean equals(Object other) {
        if (this == other) {
            return true;
        }
        if (!(other instanceof PointcutAdvisor)) {
            return false;
        }
        final PointcutAdvisor otherAdvisor = (PointcutAdvisor) other;
        return ObjectUtils.nullSafeEquals(getAdvice(), otherAdvisor.getAdvice()) &&
                ObjectUtils.nullSafeEquals(getPointcut(), otherAdvisor.getPointcut());
    }

    @Override
    public int hashCode() {
        return PointcutAdvisor.class.hashCode();
    }
}

/**
 *  以 BeanFactory 爲基礎的切點通知者,通知可以配置爲 BeanFactory 中的 bean。
 */
@SuppressWarnings("serial")
public abstract class AbstractBeanFactoryPointcutAdvisor extends AbstractPointcutAdvisor implements BeanFactoryAware {
    /**
     *  通知 Bean 的名稱
     */
    @Nullable
    private String adviceBeanName;
    /**
     *  Bean 工廠
     */
    @Nullable
    private BeanFactory beanFactory;
    /**
     *  延遲初始化的通知對象
     */
    @Nullable
    private transient volatile Advice advice;
    /**
     *  鎖
     */
    private transient volatile Object adviceMonitor = new Object();

    public void setAdviceBeanName(@Nullable String adviceBeanName) {
        this.adviceBeanName = adviceBeanName;
    }

    @Nullable
    public String getAdviceBeanName() {
        return adviceBeanName;
    }

    @Override
    public void setBeanFactory(BeanFactory beanFactory) {
        this.beanFactory = beanFactory;
        resetAdviceMonitor();
    }

    private void resetAdviceMonitor() {
        if (beanFactory instanceof ConfigurableBeanFactory) {
            adviceMonitor = ((ConfigurableBeanFactory) beanFactory).getSingletonMutex();
        }
        else {
            adviceMonitor = new Object();
        }
    }

    public void setAdvice(Advice advice) {
        synchronized (adviceMonitor) {
            this.advice = advice;
        }
    }

    /**
     *  從 beanFactory 中讀取通知實例
     */
    @Override
    public Advice getAdvice() {
        Advice advice = this.advice;
        // 通知已經初始化,則直接返回
        if (advice != null) {
            return advice;
        }

        Assert.state(adviceBeanName != null, "'adviceBeanName' must be specified");
        Assert.state(beanFactory != null, "BeanFactory must be set to resolve 'adviceBeanName'");

        // 通知 bean 是單例
        if (beanFactory.isSingleton(adviceBeanName)) {
            // 依賴於 Bean 工廠提供的單例語義
            advice = beanFactory.getBean(adviceBeanName, Advice.class);
            this.advice = advice;
            return advice;
        }
        else {
            synchronized (adviceMonitor) {
                advice = this.advice;
                if (advice == null) {
                    advice = beanFactory.getBean(adviceBeanName, Advice.class);
                    this.advice = advice;
                }
                return advice;
            }
        }
    }

    @Override
    public String toString() {
        final StringBuilder sb = new StringBuilder(getClass().getName());
        sb.append(": advice ");
        if (adviceBeanName != null) {
            sb.append("bean '").append(adviceBeanName).append("'");
        }
        else {
            sb.append(advice);
        }
        return sb.toString();
    }
}

/**
 *  由 CacheOperationSource 驅動的 Advisor
 */
@SuppressWarnings("serial")
public class BeanFactoryCacheOperationSourceAdvisor extends AbstractBeanFactoryPointcutAdvisor {
    @Nullable
    private CacheOperationSource cacheOperationSource;
    /**
     *  緩存操作切點
     */
    private final CacheOperationSourcePointcut pointcut = new CacheOperationSourcePointcut() {
        @Override
        @Nullable
        protected CacheOperationSource getCacheOperationSource() {
            return cacheOperationSource;
        }
    };

    public void setCacheOperationSource(CacheOperationSource cacheOperationSource) {
        this.cacheOperationSource = cacheOperationSource;
    }

    public void setClassFilter(ClassFilter classFilter) {
        pointcut.setClassFilter(classFilter);
    }

    @Override
    public Pointcut getPointcut() {
        return pointcut;
    }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章