欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Spring AOP源碼深入分析

 更新時間:2023年01月03日 09:59:43   作者:程序員小潘  
這篇文章主要介紹了Spring AOP源碼,AOP(Aspect Orient Programming),直譯過來就是 面向切面編程,AOP 是一種編程思想,是面向?qū)ο缶幊蹋∣OP)的一種補充

1. 前言

Spring除了IOC和DI,還有另一個殺手锏功能——Spring AOP。AOP是一種面向切面的編程思想,它的關注點是橫向的,不同于OOP的縱向。面向?qū)ο缶幊虝r,如果我們要給多個類引入同一個行為,唯一的方式就是使用繼承,否則就要在這些類里面加入大量重復的代碼,如此一來程序?qū)⒉焕诰S護。于是,AOP橫空出世,它彌補了OOP編程的弊端。Spring內(nèi)部也有大量特性是通過AOP來實現(xiàn)的,比如我們熟知的數(shù)據(jù)庫事務。

2. 術語

查看源碼前,先了解一下AOP的相關術語。

連接點(Joinpoint)

Advice執(zhí)行的位置,比如:方法前、方法后、發(fā)生異常時等等,Spring僅支持方法的連接點。

切點(Pointcut)

連接點的過濾條件,AOP通過切點定位到具體的連接點。

增強/通知(Advice)

應用在連接點的行為,增強的邏輯代碼,分為:前置、后置、環(huán)繞、異常增強。

增強器(Advisor)

通知器由一個切點(Pointcut)和一個增強(Advice)組成。

切面(Aspect)

由切點(Pointcut)和增強(Advice)組成。

織入(Weaving)

將增強應用到目標連接點的過程,可以靜態(tài)織入,也可以運行時織入。

目標(Target)

被增強的對象(方法)。

代理(Proxy)

向Target應用Advice之后創(chuàng)建的代理對象。

3. 示例

Spring AOP使用起來非常簡單,這里提供一個小示例。

1、定義切面

@Aspect
@Component
public class MyAspectConfig {
    @Pointcut("execution(* com.javap.aop.*.*(..))")
    public void pointCut() {
    }
    /**
     * 每一個增強方法,都會被封裝成Advisor對象
     *
     * @see Advisor
     */
    @Before("pointCut()")
    public void before() {
        System.err.println("before");
    }
    @After("pointCut()")
    public void after() {
        System.err.println("after");
    }
    @Around("pointCut()")
    public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        System.err.println("Around before");
        Object result = proceedingJoinPoint.proceed(proceedingJoinPoint.getArgs());
        System.err.println("Around after");
        return result;
    }
    @AfterReturning(value = "pointCut()", returning = "result")
    public void afterReturning(JoinPoint joinPoint, String result) throws Throwable {
        System.err.println("afterReturning: " + result);
    }
    @AfterThrowing(value = "pointCut()", throwing = "e")
    public void afterThrowing(Exception e) throws Throwable {
        System.err.println("afterThrowing: " + e.getMessage());
    }
}

2、定義bean,也就是需要被增強的目標對象

@Component
public class Person {
    public String say() {
        System.err.println("say...");
        return "aabbccdd";
    }
    /**
     * final方法,子類無法重寫,因此不能被增強
     */
    public final void eat() {
        System.err.println("eat...");
    }
}

3、加上@EnableAspectJAutoProxy注解,就可以啟用AOP了。

@Configuration
@ComponentScan("com.javap.aop")
@EnableAspectJAutoProxy
public class AopApplication {
    public static void main(String[] args) {
        ApplicationContext context = new AnnotationConfigApplicationContext(AopApplication.class);
        Person person = context.getBean(Person.class);
        person.say();
        person.eat();
    }
}

控制臺輸出:

Around before
before
say...
Around after
after
afterReturning: aabbccdd
eat...

4. @EnableAspectJAutoProxy

問題:為什么在啟動類上,加上**@EnableAspectJAutoProxy**注解就可以開啟AOP呢???

答案當然要在注解本身找了,該注解上有一個@Import注解,引入了AspectJAutoProxyRegistrar類。

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(AspectJAutoProxyRegistrar.class)
public @interface EnableAspectJAutoProxy {
	/**
	 * 是否強制使用類代理模式:CGLIB代理
	 */
	boolean proxyTargetClass() default false;
	/**
	 * 是否暴露代理對象,以通過AopContext獲取
	 */
	boolean exposeProxy() default false;
}

AspectJAutoProxyRegistrar類實現(xiàn)了Spring提供的ImportBeanDefinitionRegistrar接口并重寫了registerBeanDefinitions()方法,如此一來Spring在啟動時,就會觸發(fā)AspectJAutoProxyRegistrar#registerBeanDefinitions()方法,該方法會向容器內(nèi)注冊一個特別重要的類AnnotationAwareAspectJAutoProxyCreator

class AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar {
    @Override
    public void registerBeanDefinitions(
            AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        /**
         * 注冊AnnotationAwareAspectJAutoProxyCreator類
         */
        AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);
        // 獲取注解信息
        AnnotationAttributes enableAspectJAutoProxy =
                AnnotationConfigUtils.attributesFor(importingClassMetadata, EnableAspectJAutoProxy.class);
        if (enableAspectJAutoProxy != null) {
            /**
             * 將注解屬性寫入到BeanDefinition
             * proxyTargetClass:是否通過CGLIB類代理模式
             * exposeProxy:是否暴露代理類,以通過AopContext獲取
             */
            if (enableAspectJAutoProxy.getBoolean("proxyTargetClass")) {
                AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
            }
            if (enableAspectJAutoProxy.getBoolean("exposeProxy")) {
                AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);
            }
        }
    }
}

AnnotationAwareAspectJAutoProxyCreator實現(xiàn)了BeanPostProcessor接口,屬于Spring的擴展點之一,Spring在實例化bean實例后,會觸發(fā)該擴展點,對bean做擴展和增強,也就是返回織入了Advice后的代理對象。

5. AbstractAutoProxyCreator

AnnotationAwareAspectJAutoProxyCreator既然實現(xiàn)了BeanPostProcessor接口,那么必然要重寫postProcessAfterInitialization()方法來返回增強后的bean,重寫方法在父類AbstractAutoProxyCreator里。

@Override
public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
    if (bean != null) {
        Object cacheKey = getCacheKey(bean.getClass(), beanName);
        // 循環(huán)依賴,提前暴露bean時,代理對象可能已經(jīng)生成
        if (this.earlyProxyReferences.remove(cacheKey) != bean) {
            // 生成增強后的代理對象,如果有需要
            return wrapIfNecessary(bean, beanName, cacheKey);
        }
    }
    return bean;
}

Spring會調(diào)用wrapIfNecessary()方法來返回增強后的bean。**所有的bean都會被BeanPostProcessor處理,但并不是所有的bean都需要增強,Spring會根據(jù)切點表達式判斷beanClass是否需要被增強。**這個判斷過程是比較耗時的,因為要解析beanClass所有的方法去和切點表達式進行匹配,所以Spring加了一個Map緩存解析過的beanClass。

判斷bean是否要增強也很簡單,通過getAdvicesAndAdvisorsForBean()方法去解析能應用到beanClass的所有增強器Advisor,如果沒有增強器可用于該beanClass,也就不需要增強,直接原樣返回bean即可,否則基于Advisor去創(chuàng)建增強后的代理對象。

protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
    if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {
        // 已增強
        return bean;
    }
    if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
        // 已經(jīng)解析過beanClass,且判定為無需增強
        return bean;
    }
    if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
        // 無需增強
        this.advisedBeans.put(cacheKey, Boolean.FALSE);
        return bean;
    }
    /**
     * 獲取beanClass可用的Advisor
     */
    Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
    if (specificInterceptors != DO_NOT_PROXY) {
        this.advisedBeans.put(cacheKey, Boolean.TRUE);
        // 基于Advisor,創(chuàng)建代理對象
        Object proxy = createProxy(
                bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
        this.proxyTypes.put(cacheKey, proxy.getClass());
        return proxy;
    }
    // beanClass無可用的Advice,無需增強
    this.advisedBeans.put(cacheKey, Boolean.FALSE);
    return bean;
}

6. 構建Advisor

bean是否要增強,是通過beanClass是否有可用的Advisor來判斷的。那么,Advisor是怎么來的呢?

查找bean可用的Advisor的方法在AbstractAdvisorAutoProxyCreator#findEligibleAdvisors(),先從容器內(nèi)找出所有的Advisor,也就是我們在@Aspect類里定義的各種增強方法,然后根據(jù)切點表達式過濾出可以應用到當前beanClass的Advisor。然后將一個特殊的攔截器ExposeInvocationInterceptor插入到首位,最后將攔截器排個序返回。

protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
    /**
     * 查找容器內(nèi)所有的Advisor:@Aspect類里定義的各種增強方法
     */
    List<Advisor> candidateAdvisors = findCandidateAdvisors();
    /**
     * 根據(jù)切點表達式,過濾出可以應用到beanClass的Advisor
     */
    List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
    /**
     * 將ExposeInvocationInterceptor攔截器插入到第一個
     */
    extendAdvisors(eligibleAdvisors);
    if (!eligibleAdvisors.isEmpty()) {
        eligibleAdvisors = sortAdvisors(eligibleAdvisors);
    }
    return eligibleAdvisors;
}

構建Advisor的職責交給了BeanFactoryAspectJAdvisorsBuilder類的buildAspectJAdvisors()方法,它首先從容器內(nèi)找出所有加了@Aspect注解的bean,然后解析bean所有加了@Pointcut、@Around@Before、@After、@AfterReturning、@AfterThrowing注解的方法,將它們封裝成Advisor對象。

/**
*將bean里面加了指定注解的方法封裝成對應的Advice
*@Before -> AspectJMethodBeforeAdvice
* Around -> AspectJAroundAdvice
*@After -> AspectJAfterAdvice
*.......
*/
List<Advisor> classAdvisors = this.advisorFactory.getAdvisors(factory);

容器內(nèi)的Advisor對象并不能都應用到目標bean對象,所以Spring還會調(diào)用findAdvisorsThatCanApply()方法過濾出當前beanClass可用的Advisor。

7. 創(chuàng)建代理對象

如果當前bean有可用的Advisor,也就意味著需要對bean做增強,此時會調(diào)用createProxy()方法創(chuàng)建增強后的代理對象。

問題:使用JDK代理還是CGLIB代理?

我們可以通過屬性proxyTargetClass強制使用CGLIB代理,如果不指定,Spring的規(guī)則是:如果beanClass實現(xiàn)了接口,且接口至少有一個自定義方法,那么就用JDK代理,否則用CGLIB代理。

protected void evaluateProxyInterfaces(Class<?> beanClass, ProxyFactory proxyFactory) {
    // 獲取beanClass實現(xiàn)的所有接口
    Class<?>[] targetInterfaces = ClassUtils.getAllInterfacesForClass(beanClass, getProxyClassLoader());
    boolean hasReasonableProxyInterface = false;
    /**
     * 遍歷接口
     * 1.非配置的回調(diào)接口,即Spring內(nèi)置的接口不算,例如InitializingBean、Aware等接口
     * 2.非內(nèi)部語言接口,例如GroovyObject
     * 3.接口至少有一個自定義方法
     */
    for (Class<?> ifc : targetInterfaces) {
        if (!isConfigurationCallbackInterface(ifc) && !isInternalLanguageInterface(ifc) &&
                ifc.getMethods().length > 0) {
            hasReasonableProxyInterface = true;
            break;
        }
    }
    if (hasReasonableProxyInterface) {
        // Must allow for introductions; can't just set interfaces to the target's interfaces only.
        for (Class<?> ifc : targetInterfaces) {
            proxyFactory.addInterface(ifc);
        }
    } else {
        // 沒有實現(xiàn)有效接口,使用CGLIB代理
        proxyFactory.setProxyTargetClass(true);
    }
}

最終,Spring會通過ProxyFactory去創(chuàng)建代理對象。

protected Object createProxy(Class<?> beanClass, @Nullable String beanName,
                             @Nullable Object[] specificInterceptors, TargetSource targetSource) {
    if (this.beanFactory instanceof ConfigurableListableBeanFactory) {
        AutoProxyUtils.exposeTargetClass((ConfigurableListableBeanFactory) this.beanFactory, beanName, beanClass);
    }
    // 基于ProxyFactory創(chuàng)建代理對象
    ProxyFactory proxyFactory = new ProxyFactory();
    // 屬性賦值給ProxyFactory
    proxyFactory.copyFrom(this);
    /**
     * 判斷使用JDK代理還是CGLIB代理
     * 如果beanClass實現(xiàn)了接口,且接口至少有一個自定義方法,則使用JDK代理
     * 否則CGLIB代理
     */
    if (!proxyFactory.isProxyTargetClass()) {
        if (shouldProxyTargetClass(beanClass, beanName)) {
            proxyFactory.setProxyTargetClass(true);
        } else {
            evaluateProxyInterfaces(beanClass, proxyFactory);
        }
    }
    Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
    proxyFactory.addAdvisors(advisors);
    proxyFactory.setTargetSource(targetSource);
    customizeProxyFactory(proxyFactory);
    // 是否凍結(jié),也就是Advisor不可再變更
    proxyFactory.setFrozen(this.freezeProxy);
    if (advisorsPreFiltered()) {
        proxyFactory.setPreFiltered(true);
    }
    // 創(chuàng)建動態(tài)代理  兩種方式 JDK CGlib
    return proxyFactory.getProxy(getProxyClassLoader());
}

AopProxy有兩種實現(xiàn),分別是**JdkDynamicAopProxy****CglibAopProxy****,前者使用JDK動態(tài)代理的方式將Advice織入bean,后者通過CGLIB生成子類的方式將Advice織入bean。**我們以CGLIB為例,創(chuàng)建代理對象的方法是CglibAopProxy#getProxy()。

@Override
public Object getProxy(@Nullable ClassLoader classLoader) {
    if (logger.isTraceEnabled()) {
        logger.trace("Creating CGLIB proxy: " + this.advised.getTargetSource());
    }
    try {
        Class<?> rootClass = this.advised.getTargetClass();
        Assert.state(rootClass != null, "Target class must be available for creating a CGLIB proxy");

        Class<?> proxySuperClass = rootClass;
        if (ClassUtils.isCglibProxyClass(rootClass)) {
            proxySuperClass = rootClass.getSuperclass();
            Class<?>[] additionalInterfaces = rootClass.getInterfaces();
            for (Class<?> additionalInterface : additionalInterfaces) {
                this.advised.addInterface(additionalInterface);
            }
        }
        // Validate the class, writing log messages as necessary.
        validateClassIfNecessary(proxySuperClass, classLoader);
        // Configure CGLIB Enhancer...
        Enhancer enhancer = createEnhancer();
        if (classLoader != null) {
            enhancer.setClassLoader(classLoader);
            if (classLoader instanceof SmartClassLoader &&
                    ((SmartClassLoader) classLoader).isClassReloadable(proxySuperClass)) {
                enhancer.setUseCache(false);
            }
        }
        enhancer.setSuperclass(proxySuperClass);
        /**
         * 會實現(xiàn)接口
         * @see org.springframework.aop.SpringProxy
         * @see org.springframework.aop.framework.Advised
         */
        enhancer.setInterfaces(AopProxyUtils.completeProxiedInterfaces(this.advised));
        enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
        enhancer.setStrategy(new ClassLoaderAwareUndeclaredThrowableStrategy(classLoader));
        /**
         * 獲取方法回調(diào),AOP主要看
         * @see DynamicAdvisedInterceptor
         */
        Callback[] callbacks = getCallbacks(rootClass);
        Class<?>[] types = new Class<?>[callbacks.length];
        for (int x = 0; x < types.length; x++) {
            types[x] = callbacks[x].getClass();
        }
        // fixedInterceptorMap only populated at this point, after getCallbacks call above
        enhancer.setCallbackFilter(new ProxyCallbackFilter(
                this.advised.getConfigurationOnlyCopy(), this.fixedInterceptorMap, this.fixedInterceptorOffset));
        enhancer.setCallbackTypes(types);
        // Generate the proxy class and create a proxy instance.
        return createProxyClassAndInstance(enhancer, callbacks);
    } catch (CodeGenerationException | IllegalArgumentException ex) {
        throw new AopConfigException("Could not generate CGLIB subclass of " + this.advised.getTargetClass() +
                ": Common causes of this problem include using a final class or a non-visible class",
                ex);
    } catch (Throwable ex) {
        // TargetSource.getTarget() failed
        throw new AopConfigException("Unexpected AOP exception", ex);
    }
}

Spring會通過Enhancer去創(chuàng)建子類對象來增強目標bean,生成的子類默認會實現(xiàn)org.springframework.aop.SpringProxy接口用來標記它是一個Spring生成的代理類。我們重點看給Enhancer對象設置的Callback,因為它會對方法做攔截,也就是說,我們調(diào)用子類的增強方法,其實就是在調(diào)用Callback#intercept()。Spring考慮的比較全面,會針對main、equals、hashCode等方法做處理,針對AOP我們主要看AopInterceptor即可。

/**
* Spring考慮了很多情況,
*比如對main、equals、hashCode方法的攔截
*AOP主要看aopInterceptor
*/
Callback[] mainCallbacks = new Callback[]{
aopInterceptor,ll for normal advice
targetInterceptor,// invoke target without considering advice,if optimized
new SerializableNo0p(),l/ no override for methods mapped to this
targetDispatcher, this.advisedDispatcher,
new EqualsInterceptor(this.advised),
new HashCodeInterceptor(this.advised)
};

aopInterceptor是DynamicAdvisedInterceptor子類對象,用于處理AOP方法調(diào)用,也就是說,AOP增強邏輯就在DynamicAdvisedInterceptor#intercept()里。

// Choose an "aop" interceptor (used for AOP calls).
Callback aopInterceptor = new DynamicAdvisedInterceptor(this.advised);

8. DynamicAdvisedInterceptor

調(diào)用CGLIB增強子類的方法,其實會觸發(fā)DynamicAdvisedInterceptor#intercept()方法,看看Spring增強的邏輯。

public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
    Object oldProxy = null;
    boolean setProxyContext = false;
    Object target = null;
    TargetSource targetSource = this.advised.getTargetSource();
    try {
        /**
         * 是否要暴露代理對象,以通過AopContext獲取
         * 寫入ThreadLocal
         */
        if (this.advised.exposeProxy) {
            oldProxy = AopContext.setCurrentProxy(proxy);
            setProxyContext = true;
        }
        /**
         * 獲取目標Bean實例
         * 如果是prototype 此時會創(chuàng)建新的實例
         */
        target = targetSource.getTarget();
        Class<?> targetClass = (target != null ? target.getClass() : null);
        /**
         * 獲取方法能應用到的Advice,攔截器調(diào)用鏈
         * @see org.springframework.aop.interceptor.ExposeInvocationInterceptor
         * @see org.springframework.aop.aspectj.AspectJAfterThrowingAdvice
         * @see org.springframework.aop.framework.adapter.AfterReturningAdviceInterceptor
         * @see org.springframework.aop.aspectj.AspectJAfterAdvice
         * @see org.springframework.aop.aspectj.AspectJAroundAdvice
         * @see org.springframework.aop.framework.adapter.MethodBeforeAdviceInterceptor
         */
        List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
        Object retVal;
        if (chain.isEmpty() && Modifier.isPublic(method.getModifiers())) {
            /**
             * 沒有Advice,直接調(diào)用父類方法
             */
            Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
            retVal = methodProxy.invoke(target, argsToUse);
        } else {
            retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();
        }
        // 處理一下返回結(jié)果的類型
        retVal = processReturnType(proxy, target, method, retVal);
        return retVal;
    } finally {
        if (target != null && !targetSource.isStatic()) {
            targetSource.releaseTarget(target);
        }
        if (setProxyContext) {
            AopContext.setCurrentProxy(oldProxy);
        }
    }
}

方法攔截器主要做了三件事:

  • 判斷是否要暴露代理對象,如果要則寫入ThreadLocal
  • 獲取方法能應用到的Advice,構建攔截器調(diào)用鏈
  • 觸發(fā)攔截器調(diào)用鏈

如果方法沒有可用的Advice,也就不需要增強,直接調(diào)用父類方法即可。反之方法需要增強,Spring會new一個CglibMethodInvocation對象,觸發(fā)攔截器調(diào)用鏈。

9. CglibMethodInvocation

CglibMethodInvocation#proceed()方法會觸發(fā)攔截器調(diào)用鏈,Spring通過一個int變量currentInterceptorIndex來記錄當前執(zhí)行的攔截器索引,通過和總的攔截器數(shù)量判斷是否調(diào)用完所有的攔截器,如果是則通過invokeJoinpoint()方法去調(diào)用目標方法,反之則去執(zhí)行Advice增強。

public Object proceed() throws Throwable {
    /**
     * currentInterceptorIndex 當前執(zhí)行的攔截器索引
     * interceptorsAndDynamicMethodMatchers.size() 所有攔擊器個數(shù)
     * 執(zhí)行完最后一個攔截器,就要執(zhí)行目標方法了
     */
    if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
        // 最終反射調(diào)用目標方法
        return invokeJoinpoint();
    }
	// 觸發(fā)Advice增強
    Object interceptorOrInterceptionAdvice =
            this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
    if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
        InterceptorAndDynamicMethodMatcher dm =
                (InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
        Class<?> targetClass = (this.targetClass != null ? this.targetClass : this.method.getDeclaringClass());
        if (dm.methodMatcher.matches(this.method, targetClass, this.arguments)) {
            return dm.interceptor.invoke(this);
        } else {
            return proceed();
        }
    } else {
        return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
    }
}

10. Advice子類

Spring AOP提供了五種Advice增強,分別是通過@Around、@Before、@After@AfterReturning、@AfterThrowing注解標記的方法。這五種Advice分別對應五個Advice子類實現(xiàn)。

1、AspectJAfterThrowingAdvice

先走下一個攔截器,只有在發(fā)生異常時,才觸發(fā)的Advice。

@Override
public Object invoke(MethodInvocation mi) throws Throwable {
    try {
        // 先走下一個攔截器
        return mi.proceed();
    } catch (Throwable ex) {
        if (shouldInvokeOnThrowing(ex)) {
            /**
             * 只有發(fā)生異常了,才會執(zhí)行@AfterThrowing Advice
             */
            invokeAdviceMethod(getJoinPointMatch(), null, ex);
        }
        throw ex;
    }
}

2、AfterReturningAdviceInterceptor

走剩下所有的攔截器,拿到返回結(jié)果。正常執(zhí)行完畢才觸發(fā)的Advice,如果發(fā)生異常則不觸發(fā)。

@Override
public Object invoke(MethodInvocation mi) throws Throwable {
    // 直接走剩下的攔截器,拿到返回結(jié)果
    Object retVal = mi.proceed();
    /**
     * 正常執(zhí)行完畢,才觸發(fā) @AfterReturning Advice
     * 如果期間發(fā)生異常,則不觸發(fā)
     */
    this.advice.afterReturning(retVal, mi.getMethod(), mi.getArguments(), mi.getThis());
    return retVal;
}

3、AspectJAfterAdvice

先走下一個攔截器,執(zhí)行完畢再最終調(diào)用的Advice,不論是否發(fā)生異常。

@Override
public Object invoke(MethodInvocation mi) throws Throwable {
    try {
        /**
         * 先走下一個攔截器,執(zhí)行完畢再最終調(diào)用@After Advice
         */
        return mi.proceed();
    } finally {
        /**
         * 執(zhí)行@After Advice方法
         * @After Advice一定會被調(diào)用,不管是否發(fā)生異常,因為在finally
         */
        invokeAdviceMethod(getJoinPointMatch(), null, null);
    }
}

4、AspectJAroundAdvice

直接觸發(fā)Advice,是否繼續(xù)調(diào)用后續(xù)Advice由我們自己決定。

@Override
public Object invoke(MethodInvocation mi) throws Throwable {
    if (!(mi instanceof ProxyMethodInvocation)) {
        throw new IllegalStateException("MethodInvocation is not a Spring ProxyMethodInvocation: " + mi);
    }
    /**
     * 直接觸發(fā)Advice,是否繼續(xù)調(diào)用后續(xù)Advice,由@Around Advice自己決定
     */
    ProxyMethodInvocation pmi = (ProxyMethodInvocation) mi;
    ProceedingJoinPoint pjp = lazyGetProceedingJoinPoint(pmi);
    JoinPointMatch jpm = getJoinPointMatch(pmi);
    return invokeAdviceMethod(pjp, jpm, null, null);
}

5、MethodBeforeAdviceInterceptor

先觸發(fā)當前Advice,再調(diào)用下一個Advice。

@Override
public Object invoke(MethodInvocation mi) throws Throwable {
    /**
     * 先觸發(fā) @Before Advice
     * 再調(diào)用下一個Advice
     */
    this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis());
    return mi.proceed();
}

到此這篇關于Spring AOP源碼深入分析的文章就介紹到這了,更多相關Spring AOP內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • application.yml的格式寫法和pom.xml讀取配置插件方式

    application.yml的格式寫法和pom.xml讀取配置插件方式

    這篇文章主要介紹了application.yml的格式寫法和pom.xml讀取配置插件方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-07-07
  • 封裝jndi操作ldap服務器的工具類

    封裝jndi操作ldap服務器的工具類

    這篇文章主要介紹了封裝JNDI操作LDAP服務器的工具類,使用者只需要會使用List,Map 數(shù)據(jù)結(jié)構,大家參考使用吧
    2014-01-01
  • SpringBoot統(tǒng)計、監(jiān)控SQL運行情況的方法詳解

    SpringBoot統(tǒng)計、監(jiān)控SQL運行情況的方法詳解

    這篇文章主要給大家介紹了關于SpringBoot統(tǒng)計、監(jiān)控SQL運行情況的相關資料,文中通過實例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
    2022-02-02
  • 詳解基于Mybatis-plus多租戶實現(xiàn)方案

    詳解基于Mybatis-plus多租戶實現(xiàn)方案

    這篇文章主要介紹了詳解基于Mybatis-plus多租戶實現(xiàn)方案,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2020-04-04
  • 詳解SpringBoot之集成Spring AOP

    詳解SpringBoot之集成Spring AOP

    本篇文章主要介紹了詳解SpringBoot之集成Spring AOP,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-07-07
  • 在SpringBoot當中使用Thymeleaf視圖解析器的詳細教程

    在SpringBoot當中使用Thymeleaf視圖解析器的詳細教程

    Thymeleaf是一款開源的模板引擎,它允許前端開發(fā)者使用HTML與XML編寫動態(tài)網(wǎng)頁,hymeleaf的主要特點是將表達式語言嵌入到HTML結(jié)構中,它支持Spring框架,使得在Spring MVC應用中集成非常方便,本文給大家介紹了在SpringBoot當中使用Thymeleaf視圖解析器的詳細教程
    2024-09-09
  • Java中Parser的用法

    Java中Parser的用法

    這篇文章主要介紹了Java?Parser使用指南,本文通過實例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2023-05-05
  • Redis分布式鎖介紹與使用

    Redis分布式鎖介紹與使用

    服務器集群項目中的鎖是無法精準的鎖住線程資源的,于是我們就是需要使用分布式鎖,分布式鎖該如何使用又有什么注意點呢?就讓我們進入接下來的學習
    2022-09-09
  • Java里volatile關鍵字是什么意思

    Java里volatile關鍵字是什么意思

    volatile具有synchronized關鍵字的“可見性”,但是沒有synchronized關鍵字的“并發(fā)正確性”,也就是說不保證線程執(zhí)行的有序性。這篇文章主要介紹了Java里volatile關鍵字是什么意思的相關資料,需要的朋友可以參考下
    2016-11-11
  • 通過面試題解析 Java 類加載機制

    通過面試題解析 Java 類加載機制

    類加載是 Java 語言的一個創(chuàng)新,也是 Java 語言流行的重要原因之一。它使得 Java 類可以被動態(tài)加載到 Java 虛擬機中并執(zhí)行。下面小編和大家來一起學習一下吧
    2019-05-05

最新評論