使用SpringAop對方法進行增強

一、需求

前段時間項目中有一個需求需要生成編碼忽媒,編碼規(guī)則中有一個4位數(shù)字需要順序生成争拐,從1開始,當增加到10000的時候回復成1晦雨〖懿埽考慮到這個是順序生成的隘冲,同時適用于多線程環(huán)境,所以就想到使用redis存儲數(shù)據(jù)绑雄,同時使用Ression的Lock來實現(xiàn)多線程順序增加展辞。考慮到代碼的可讀性以及業(yè)務無關性万牺,所以想到使用Aop把這個邏輯代碼抽出去罗珍,利用注解的特點結(jié)合業(yè)務代碼可插拔使用。過程很順利脚粟,但是最終運行發(fā)現(xiàn)切面Aspect未生效覆旱,排查的時候發(fā)現(xiàn)是由于忘記了spring中代理類的生效規(guī)則,所以記錄此貼珊楼,總結(jié)錯誤以便后面防止出現(xiàn)這些問題通殃。

二、測試代碼

2.1 測試的業(yè)務邏輯代碼

@Component
public class MyService{

    @MyAnnotation
    public void serviceA(){
        System.out.println("serviceA");
    }
}

2.2 測試的注解

通過這個注解標識該方法是否需要被攔截

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Component
public @interface MyAnnotation{
}

2.3 切面

@Component
@Aspect
public class MyAspect{
    //定義攔截點厕宗,由于我是通過注解標識該方法是否需要被攔截的画舌,所以這里攔截點是方法時候被標注注解MyAnnotation
    @Pointcut("@annotation(com.example.demo.MyAnnotation)")
    public void pointcut(){
    }
     //攔截后執(zhí)行的環(huán)繞通知
    @Around(value = "pointcut()")
    public Object sout(ProceedingJoinPoint point) throws Throwable {
        System.out.println("run into aspect!");
        //注意這里一定要執(zhí)行被攔截的方法,否則就會導致只執(zhí)行了攔截而沒有執(zhí)行原本的業(yè)務方法
        return point.proceed();
    }
}

2..4 測試方法

@SpringBootApplication
@EnableAspectJAutoProxy
public class DemoApplication implements ApplicationContextAware {

    static ApplicationContext ctx;

    public static void main(String[] args) throws IOException {
        SpringApplication.run(DemoApplication.class, args);
        ServiceAinf = (ServiceA) ctx.getBean("serviceA ");
        inf.ServiceA();
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.ctx = applicationContext;
    }
}

2.5 測試結(jié)果如下

2021-07-27 14:25:02.623  INFO 23356 --- [  restartedMain] com.example.demo.DemoApplication         : Starting DemoApplication using Java 1.8.0_161 on DESKTOP-APRM3G5 with PID 23356 (D:\IDEA-WROKPLACE\demo2\target\classes started by ColpoCharlie in D:\IDEA-WROKPLACE\demo2)
2021-07-27 14:25:02.625  INFO 23356 --- [  restartedMain] com.example.demo.DemoApplication         : No active profile set, falling back to default profiles: default
2021-07-27 14:25:02.652  INFO 23356 --- [  restartedMain] .e.DevToolsPropertyDefaultsPostProcessor : Devtools property defaults active! Set 'spring.devtools.add-properties' to 'false' to disable
2021-07-27 14:25:03.068  INFO 23356 --- [  restartedMain] o.s.b.d.a.OptionalLiveReloadServer       : LiveReload server is running on port 35729
2021-07-27 14:25:03.079  INFO 23356 --- [  restartedMain] com.example.demo.DemoApplication         : Started DemoApplication in 0.701 seconds (JVM running for 1.239)
run into aspect!
serviceA

2.6 結(jié)果分析

可以看到方法確實被攔截下來了已慢,而且執(zhí)行完切面的around方法后曲聂,繼續(xù)執(zhí)行了原本的方法邏輯,同時也可以很明顯的看到這個ServiceA的對象inf確實是采用了CGLIB生成了代理類的子類


image.png

三佑惠、原理探索

考慮到這是在原本的功能上動態(tài)添加了增強功能朋腋,所以基本上是采用了動態(tài)代理的方式,結(jié)合Spring的默認使用了CGLIB的代理方式膜楷,猜測應該是內(nèi)部使用CGLIB生成了一個代理類旭咽,之后當所有方法調(diào)用的時候就是調(diào)用了代理類的增強后的方法

3.1 容器中的代理類是哪一步生成的

1、通過debug進入getBean的方法內(nèi)部

//AbstractApplicationContext
    //---------------------------------------------------------------------
    // Implementation of BeanFactory interface
    //---------------------------------------------------------------------
    @Override
    public Object getBean(String name) throws BeansException {
        //判斷BeanFactory是否可用
        assertBeanFactoryActive();
       //通過BeanFactory調(diào)用getBean方法獲取Bean
        return getBeanFactory().getBean(name);
    }

2赌厅、進一步追蹤到AbstractAutowireCapableBeanFactory的createBean方法穷绵,這里我們主要看到resolveBeforeInstantiation方法和doCreateBean方法,這個方法提供一個通過BeanPostProcessors的遍歷調(diào)用生成代理類的途徑特愿,本次測試的是通過doCreateBean方法創(chuàng)建代理類的仲墨。

//AbstractAutowireCapableBeanFactory
    //---------------------------------------------------------------------
    // Implementation of relevant AbstractBeanFactory template methods
    //---------------------------------------------------------------------

    /**
     * Central method of this class: creates a bean instance,
     * populates the bean instance, applies post-processors, etc.
     * @see #doCreateBean
     * 用于創(chuàng)建一個Bean的實例,然后填充這個實例的屬性
     */
    @Override
    protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
            throws BeanCreationException {

        if (logger.isTraceEnabled()) {
            logger.trace("Creating instance of bean '" + beanName + "'");
        }
        //傳遞進來的beandefinition揍障,這里是ServiceA對應的beandefinition
        RootBeanDefinition mbdToUse = mbd;

        // Make sure bean class is actually resolved at this point, and
        // clone the bean definition in case of a dynamically resolved Class
        // which cannot be stored in the shared merged bean definition.
        Class<?> resolvedClass = resolveBeanClass(mbd, beanName);
        if (resolvedClass != null && !mbd.hasBeanClass() && mbd.getBeanClassName() != null) {
            mbdToUse = new RootBeanDefinition(mbd);
            mbdToUse.setBeanClass(resolvedClass);
        }

        // Prepare method overrides.
        try {
            mbdToUse.prepareMethodOverrides();
        }
        catch (BeanDefinitionValidationException ex) {
            throw new BeanDefinitionStoreException(mbdToUse.getResourceDescription(),
                    beanName, "Validation of method overrides failed", ex);
        }

        try {
            // Give BeanPostProcessors a chance to return a proxy instead of the target bean instance.
            //提供了通過調(diào)用這些BeanPostProcessor的方法目养,生成目標類的代理類
            Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
            if (bean != null) {
                return bean;
            }
        }
        catch (Throwable ex) {
            throw new BeanCreationException(mbdToUse.getResourceDescription(), beanName,
                    "BeanPostProcessor before instantiation of bean failed", ex);
        }

        try {
            //本次測試的主要生成代理類的方法
            Object beanInstance = doCreateBean(beanName, mbdToUse, args);
            if (logger.isTraceEnabled()) {
                logger.trace("Finished creating instance of bean '" + beanName + "'");
            }
            return beanInstance;
        }
        catch (BeanCreationException | ImplicitlyAppearedSingletonException ex) {
            // A previously detected exception with proper bean creation context already,
            // or illegal singleton state to be communicated up to DefaultSingletonBeanRegistry.
            throw ex;
        }
        catch (Throwable ex) {
            throw new BeanCreationException(
                    mbdToUse.getResourceDescription(), beanName, "Unexpected exception during bean creation", ex);
        }
    }

3、我們看resolveBeforeInstantiation方法的邏輯定義在哪里

//AbstractAutowireCapableBeanFactory
/**
     * Apply before-instantiation post-processors, resolving whether there is a
     * before-instantiation shortcut for the specified bean.
     * @param beanName the name of the bean
     * @param mbd the bean definition for the bean
     * @return the shortcut-determined bean instance, or {@code null} if none
     */
    @Nullable
    protected Object resolveBeforeInstantiation(String beanName, RootBeanDefinition mbd) {
        Object bean = null;
        if (!Boolean.FALSE.equals(mbd.beforeInstantiationResolved)) {
            // Make sure bean class is actually resolved at this point.
            if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
                Class<?> targetType = determineTargetType(beanName, mbd);
                if (targetType != null) {
                    bean = applyBeanPostProcessorsBeforeInstantiation(targetType, beanName);
                    if (bean != null) {
                        bean = applyBeanPostProcessorsAfterInitialization(bean, beanName);
                    }
                }
            }
            mbd.beforeInstantiationResolved = (bean != null);
        }
        return bean;
    }

4毒嫡、doCreateBean的內(nèi)部邏輯是

//AbstractAutowireCapableBeanFactory
/**
     * Actually create the specified bean. Pre-creation processing has already happened
     * at this point, e.g. checking {@code postProcessBeforeInstantiation} callbacks.
     * <p>Differentiates between default bean instantiation, use of a
     * factory method, and autowiring a constructor.
     * @param beanName the name of the bean
     * @param mbd the merged bean definition for the bean
     * @param args explicit arguments to use for constructor or factory method invocation
     * @return a new instance of the bean
     * @throws BeanCreationException if the bean could not be created
     * @see #instantiateBean
     * @see #instantiateUsingFactoryMethod
     * @see #autowireConstructor
     */
    protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
            throws BeanCreationException {

        // Instantiate the bean.
        BeanWrapper instanceWrapper = null;
        //判斷這個的beandifination是不是單例模式的
        if (mbd.isSingleton()) {
            //單例就從factoryBeanInstanceCache中移除
            instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
        }
        //假如為空則說明不是單例的或者緩存中不存在
        if (instanceWrapper == null) {
            //創(chuàng)建bean實例
            instanceWrapper = createBeanInstance(beanName, mbd, args);
        }
        //從生成的包裝類中獲取被包裝的對象
        Object bean = instanceWrapper.getWrappedInstance();
        //從生成的包裝類中獲取被包裝的對象的類型
        Class<?> beanType = instanceWrapper.getWrappedClass();
        if (beanType != NullBean.class) {
            mbd.resolvedTargetType = beanType;
        }

        // Allow post-processors to modify the merged bean definition.
        //允許BeanDefinitionPostProcessor對BeanDefinition進行修改但是BeanDefinition只能被修改一次
        synchronized (mbd.postProcessingLock) {
            if (!mbd.postProcessed) {
                try {
                    applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
                }
                catch (Throwable ex) {
                    throw new BeanCreationException(mbd.getResourceDescription(), beanName,
                            "Post-processing of merged bean definition failed", ex);
                }
                mbd.postProcessed = true;
            }
        }

        // Eagerly cache singletons to be able to resolve circular references
        // even when triggered by lifecycle interfaces like BeanFactoryAware.
        boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
                isSingletonCurrentlyInCreation(beanName));
        if (earlySingletonExposure) {
            if (logger.isTraceEnabled()) {
                logger.trace("Eagerly caching bean '" + beanName +
                        "' to allow for resolving potential circular references");
            }
            addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
        }

        // Initialize the bean instance.
        Object exposedObject = bean;
        try {
            //填充Beandefinition
            populateBean(beanName, mbd, instanceWrapper);
            //初始化給定的Bean對象
            exposedObject = initializeBean(beanName, exposedObject, mbd);
        }
        catch (Throwable ex) {
            if (ex instanceof BeanCreationException && beanName.equals(((BeanCreationException) ex).getBeanName())) {
                throw (BeanCreationException) ex;
            }
            else {
                throw new BeanCreationException(
                        mbd.getResourceDescription(), beanName, "Initialization of bean failed", ex);
            }
        }

        if (earlySingletonExposure) {
            Object earlySingletonReference = getSingleton(beanName, false);
            if (earlySingletonReference != null) {
                if (exposedObject == bean) {
                    exposedObject = earlySingletonReference;
                }
                else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
                    String[] dependentBeans = getDependentBeans(beanName);
                    Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length);
                    for (String dependentBean : dependentBeans) {
                        if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {
                            actualDependentBeans.add(dependentBean);
                        }
                    }
                    if (!actualDependentBeans.isEmpty()) {
                        throw new BeanCurrentlyInCreationException(beanName,
                                "Bean with name '" + beanName + "' has been injected into other beans [" +
                                StringUtils.collectionToCommaDelimitedString(actualDependentBeans) +
                                "] in its raw version as part of a circular reference, but has eventually been " +
                                "wrapped. This means that said other beans do not use the final version of the " +
                                "bean. This is often the result of over-eager type matching - consider using " +
                                "'getBeanNamesForType' with the 'allowEagerInit' flag turned off, for example.");
                    }
                }
            }
        }

5癌蚁、初始化方法

//AbstractAutowireCapableBeanFactory
    /**
     * Initialize the given bean instance, applying factory callbacks
     * as well as init methods and bean post processors.
     * <p>Called from {@link #createBean} for traditionally defined beans,
     * and from {@link #initializeBean} for existing bean instances.
     * 這個方法是用于出初始化給定的Bean對象,這個方法會被factory在createBean方法或者initializeBean方法調(diào)用
     * @param beanName the bean name in the factory (for debugging purposes)
     * @param bean the new bean instance we may need to initialize
     * @param mbd the bean definition that the bean was created with
     * (can be null}, if given an existing bean instance)
     * @return the initialized bean instance (potentially wrapped)
     *  返回被包裝的Bean對象
     */
    protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) {
        if (System.getSecurityManager() != null) {
            AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
                invokeAwareMethods(beanName, bean);
                return null;
            }, getAccessControlContext());
        }
        else {
            invokeAwareMethods(beanName, bean);
        }

        Object wrappedBean = bean;
        if (mbd == null || !mbd.isSynthetic()) {
            //調(diào)用BeanPostProcessor的前置處理器對這個Bean對象進行初始化
            wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
        }

        try {
            //調(diào)用這個Bean對象的構造器方法進行初始化
            invokeInitMethods(beanName, wrappedBean, mbd);
        }
        catch (Throwable ex) {
            throw new BeanCreationException(
                    (mbd != null ? mbd.getResourceDescription() : null),
                    beanName, "Invocation of init method failed", ex);
        }
        if (mbd == null || !mbd.isSynthetic()) {
            //調(diào)用BeanPostProcessor的后置處理器對這個Bean對象進行初始化
            wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
        }

        return wrappedBean;
    }

從上方的幾個初始化方法的調(diào)用順序可以知道,我們之前記憶的Bean的創(chuàng)建流程中顯示前置處理器-》初始化方法-》后置處理器這樣的而初始化邏輯是這里確定的匈勋。

而本次生成代理類是根據(jù)后置處理器生成的
6礼旅、applyBeanPostProcessorsAfterInitialization

//AbstractAutowireCapableBeanFactory

public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)
            throws BeansException {

        Object result = existingBean;
        //可以看到是獲取所有的后置處理器然后遍歷每個后置處理器,調(diào)用后置處理器初始化對象
        for (BeanPostProcessor processor : getBeanPostProcessors()) {
            Object current = processor.postProcessAfterInitialization(result, beanName);
            if (current == null) {
                return result;
            }
            result = current;
        }
        return result;
    }
image.png

每一個后置處理器都有自己的后置處理方法洽洁,根據(jù)BeanPostProcessor的實現(xiàn)類的不同分別調(diào)用這些后置處理器的方法痘系。當一個后置處理器不重寫覆蓋默認的BeanPostProcessor的后置處理方法postProcessAfterInitialization,那么這個方法內(nèi)部是直接返回傳入對象的饿自。

這里的生成代理類的后置處理器是AnnotationAwareAspectJAutoProxyCreator汰翠,它的父類是AbstractAutoProxyCreator实蓬,這個類定義了后置處理方法

//AbstractAutoProxyCreator
    /**
     * Create a proxy with the configured interceptors if the bean is
     * identified as one to proxy by the subclass.
     * @see #getAdvicesAndAdvisorsForBean
     */
    @Override
    public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
        if (bean != null) {
            Object cacheKey = getCacheKey(bean.getClass(), beanName);
            if (this.earlyProxyReferences.remove(cacheKey) != bean) {
                return wrapIfNecessary(bean, beanName, cacheKey);
            }
        }
        return bean;
    }
    /**
     * Wrap the given bean if necessary, i.e. if it is eligible for being proxied.
     * @param bean the raw bean instance
     * @param beanName the name of the bean
     * @param cacheKey the cache key for metadata access
     * 這里有一個疑問這個類的名稱是駝峰命名的袋励,也就是serivceA装畅,這樣不會在不同包下面有相同的key嗎
     * @return a proxy wrapping the bean, or the raw bean instance as-is
     */
    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))) {
            return bean;
        }
        if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
            this.advisedBeans.put(cacheKey, Boolean.FALSE);
            return bean;
        }

        // Create proxy if we have advice.
        Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
        //如果這個類有關聯(lián)到切面挣郭,則為這個傳進的Bean生成一個代理類并返回
        if (specificInterceptors != DO_NOT_PROXY) {
            this.advisedBeans.put(cacheKey, Boolean.TRUE);
            Object proxy = createProxy(
                    bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
            this.proxyTypes.put(cacheKey, proxy.getClass());
            return proxy;
        }

        this.advisedBeans.put(cacheKey, Boolean.FALSE);
        return bean;
    }

可以看到這里有一個wrapIfNecessary方法,該內(nèi)部有一個createProxy方法用于創(chuàng)建代理類后并返回予颤。我們看看我們是否能夠拿到這個代理類残吩。


image.png

可以看到已經(jīng)能夠獲得代理類了萌焰,而且嘗試通過代理類調(diào)用方法已經(jīng)能夠被攔截下來的总放。

2021-08-03 16:22:58.329  INFO 29984 --- [  restartedMain] com.example.demo.DemoApplication         : No active profile set, falling back to default profiles: default
2021-08-03 16:22:58.354  INFO 29984 --- [  restartedMain] .e.DevToolsPropertyDefaultsPostProcessor : Devtools property defaults active! Set 'spring.devtools.add-properties' to 'false' to disable
2021-08-03 16:22:59.046  INFO 29984 --- [  restartedMain] o.s.b.d.a.OptionalLiveReloadServer       : LiveReload server is running on port 35729
2021-08-03 16:22:59.064  INFO 29984 --- [  restartedMain] com.example.demo.DemoApplication         : Started DemoApplication in 1.013 seconds (JVM running for 1.485)
run into aspect!
ServiceA
Disconnected from the target VM, address: '127.0.0.1:52801', transport: 'socket'

四呈宇、容易踩坑的點

4.1、嵌套方法的增強

由于業(yè)務的復雜性局雄,可能是多個方法一起互相調(diào)用的甥啄,所以存在很多方法嵌套的情況,例如事務的嵌套炬搭。對于這種方法的嵌套蜈漓,假如說我希望這些互相調(diào)用的方法都需要被攔截到,對于不熟悉代理的人來說大部分人的代碼是這樣寫的宫盔。

@Component
public class ServiceA {

    @MyAnnotation
    public void ServiceA(){
        System.out.println("ServiceA");
        ServiceB();
    }
    
    public void ServiceB(){
        System.out.println("ServiceB");
    }
}

或者是這樣寫的

@Component
public class ServiceA {

    @MyAnnotation
    public void ServiceA(){
        System.out.println("ServiceA");
        ServiceB();
    }

    @MyAnnotation
    public void ServiceB(){
        System.out.println("ServiceB");
    }
}

我們來看一下這樣寫是否會有效果的融虽,運行結(jié)果如下

2021-08-03 15:15:17.847  INFO 22684 --- [  restartedMain] com.example.demo.DemoApplication         : No active profile set, falling back to default profiles: default
2021-08-03 15:15:17.873  INFO 22684 --- [  restartedMain] .e.DevToolsPropertyDefaultsPostProcessor : Devtools property defaults active! Set 'spring.devtools.add-properties' to 'false' to disable
2021-08-03 15:15:18.373  INFO 22684 --- [  restartedMain] o.s.b.d.a.OptionalLiveReloadServer       : LiveReload server is running on port 35729
2021-08-03 15:15:18.384  INFO 22684 --- [  restartedMain] com.example.demo.DemoApplication         : Started DemoApplication in 0.794 seconds (JVM running for 1.252)
inf = class com.example.demo.ServiceA$$EnhancerBySpringCGLIB$$b121f309
run into aspect!
ServiceA
ServiceB
Disconnected from the target VM, address: '127.0.0.1:58960', transport: 'socket'

可以看到對于嵌套的方法SericeB并沒有被攔截到的,所以對于嵌套的那個方法ServiceB的調(diào)用就沒有被增強灼芭。

4.2衣形、代理失效原因

我們先來想一下無論是JDK的代理還是CGLIB的代理過程,都是為被代理的對象生成一個代理對象姿鸿,而且這個代理對象還持有被代理對象的引用。當通過代理對象去調(diào)用方法的時候倒源,基本都是先執(zhí)行被增強的邏輯代碼苛预,然后再調(diào)用持有的被代理的對象的應用,通過這個引用去反射調(diào)用被代理對象的對應方法的笋熬,所以對于這個

inf.ServiceA()

調(diào)用的而動作來說热某,實際上在執(zhí)行完增強邏輯后,還是使用了ServiceA對象來調(diào)用方法ServiceA()的,而不是ServiceAProxy來調(diào)用ServiceA()的昔馋。這就會導致一個問題筹吐。我們使用的是ServiceA.ServiceA(),也就是對于ServiceA()方法內(nèi)部調(diào)用ServiceB()方法來說的話秘遏,就是ServiceA.ServiceB()丘薛,所以是無法被代理增強的。我們來看看實際上是不是這樣的邦危。

1洋侨、再進入方法調(diào)用前我們可以看到ServiceA對象經(jīng)過Spring的處理已經(jīng)變成了代理對象的,然后通過代理對象調(diào)用方法

image-20210803152739959.png

由于我們的切面是定義了Before切面的倦蚪,所以在進入方法前就應該執(zhí)行了增強希坚。

2021-08-03 15:27:01.083  INFO 22588 --- [  restartedMain] com.example.demo.DemoApplication         : No active profile set, falling back to default profiles: default
2021-08-03 15:27:01.109  INFO 22588 --- [  restartedMain] .e.DevToolsPropertyDefaultsPostProcessor : Devtools property defaults active! Set 'spring.devtools.add-properties' to 'false' to disable
2021-08-03 15:27:01.807  INFO 22588 --- [  restartedMain] o.s.b.d.a.OptionalLiveReloadServer       : LiveReload server is running on port 35729
2021-08-03 15:27:01.826  INFO 22588 --- [  restartedMain] com.example.demo.DemoApplication         : Started DemoApplication in 1.128 seconds (JVM running for 1.623)
inf = class com.example.demo.ServiceA$$EnhancerBySpringCGLIB$$87ee8547
run into aspect!
ServiceA

可以看到確實是執(zhí)行了增強,然后再進入了實際的ServiceA方法的調(diào)用陵且,我們再仔細看一下這個方法調(diào)用的棧幀裁僧。

image-20210803153107018.png
image-20210803160317176.png

可以看到這里的棧幀是ServiceA的棧幀,所以這里的存在于ServiceA方法中的方法調(diào)用ServiceB()實際上調(diào)用的是ServiceA對象的ServiceB方法慕购,也就是不是經(jīng)過代理類調(diào)用的聊疲,無增強邏輯的方法,所以可以看到即使ServiceB方法的上方標注了注解脓钾,但實際上沒有生效售睹。

4.3、針對以上無法代理的修改方法

既然知道了為啥方法之間的調(diào)用代理不生效(由于方法的嵌套方法不是經(jīng)過代理對象調(diào)用的)可训,那么我們只要想辦法讓嵌套方法也是經(jīng)過代理對象調(diào)用就可以增強嵌套方法的執(zhí)行昌妹。

這里有兩方法可以有效避免。

①注入ServiceA類對象握截,通過這個對象來調(diào)用嵌套方法的ServiceB就可以了

這里的原理是因為在生成ServiceA對象的時候飞崖,也就是調(diào)用getBean的方法的時候Spring發(fā)現(xiàn)這個類是需要被代理的Java類,那么內(nèi)部就會默認(未指定Spring代理方式的話)使用CGLIB生成代理對象然后把這個對象以 (ServiceA的對應的key:ServiceA對象的代理對象)的形式存儲在BeanFactory中谨胞,當我們使用自動注入ServiceA的對象的時候固歪,這里注入的就是ServiceA的代理對象的。

image-20210803160317176.png
image-20210803160422123.png

②通過AopContext類獲取存儲在AopContext中的代理對象

這個方法需要在啟動配置類上標注的@EnableAspectJAutoProxy(exposeProxy = true)注解里面設置exposeProxy屬性設置為true胯努,當這里設置為true的時候牢裳,在生成代理類的時候會把這個代理類存儲在AopContext中,默認exposeProxy是false叶沛,只有設置為真才會把代理類暴露出來的蒲讯。

image-20210803160706425.png
image-20210803160732865.png

可以看到以上兩種方法都能夠?qū)崿F(xiàn)嵌套方法的代理。

五灰署、總結(jié)

通過大概的代理對象的生成步驟大概了解了嵌套方法的代理失效的原因判帮,還了解在Spring中如何獲得一個類的代理對象
1局嘁、通過注入獲得代理對象
2、通過AopContext獲得代理對象
存在的疑惑
1晦墙、對于代理類的生成步驟不是很熟悉
2悦昵、對于代理類如何做方法增強的邏輯不明確

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市晌畅,隨后出現(xiàn)的幾起案子但指,更是在濱河造成了極大的恐慌,老刑警劉巖踩麦,帶你破解...
    沈念sama閱讀 216,651評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件枚赡,死亡現(xiàn)場離奇詭異,居然都是意外死亡谓谦,警方通過查閱死者的電腦和手機贫橙,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,468評論 3 392
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來反粥,“玉大人卢肃,你說我怎么就攤上這事〔哦伲” “怎么了莫湘?”我有些...
    開封第一講書人閱讀 162,931評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長郑气。 經(jīng)常有香客問我幅垮,道長,這世上最難降的妖魔是什么尾组? 我笑而不...
    開封第一講書人閱讀 58,218評論 1 292
  • 正文 為了忘掉前任忙芒,我火速辦了婚禮,結(jié)果婚禮上讳侨,老公的妹妹穿的比我還像新娘呵萨。我一直安慰自己,他們只是感情好跨跨,可當我...
    茶點故事閱讀 67,234評論 6 388
  • 文/花漫 我一把揭開白布潮峦。 她就那樣靜靜地躺著,像睡著了一般勇婴。 火紅的嫁衣襯著肌膚如雪忱嘹。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,198評論 1 299
  • 那天耕渴,我揣著相機與錄音德谅,去河邊找鬼。 笑死萨螺,一個胖子當著我的面吹牛窄做,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播慰技,決...
    沈念sama閱讀 40,084評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼椭盏,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了吻商?” 一聲冷哼從身側(cè)響起掏颊,我...
    開封第一講書人閱讀 38,926評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎艾帐,沒想到半個月后乌叶,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,341評論 1 311
  • 正文 獨居荒郊野嶺守林人離奇死亡柒爸,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,563評論 2 333
  • 正文 我和宋清朗相戀三年准浴,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片捎稚。...
    茶點故事閱讀 39,731評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡乐横,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出今野,到底是詐尸還是另有隱情葡公,我是刑警寧澤,帶...
    沈念sama閱讀 35,430評論 5 343
  • 正文 年R本政府宣布条霜,位于F島的核電站催什,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏宰睡。R本人自食惡果不足惜蒲凶,卻給世界環(huán)境...
    茶點故事閱讀 41,036評論 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望夹厌。 院中可真熱鬧豹爹,春花似錦、人聲如沸矛纹。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,676評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽或南。三九已至孩等,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間采够,已是汗流浹背肄方。 一陣腳步聲響...
    開封第一講書人閱讀 32,829評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留蹬癌,地道東北人权她。 一個月前我還...
    沈念sama閱讀 47,743評論 2 368
  • 正文 我出身青樓虹茶,卻偏偏與公主長得像,于是被迫代替她去往敵國和親隅要。 傳聞我的和親對象是個殘疾皇子蝴罪,可洞房花燭夜當晚...
    茶點故事閱讀 44,629評論 2 354