[Done]Spring @Pointcut 切點調用不到(SpringAOP嵌套方法不起作用) 注意事項


今天在開發過程中,遇到一個問題卡了很久,測試代碼如下:

package spring.pointcut;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;

/**
 * @Description: Pointcut測試
 * @Author: qionghui.fang
 * @Date: 2018/10/23 上午10:10
 */
@Aspect
public class TargetMonitor {

    // 配置方法1
//    @Pointcut("execution(* spring.pointcut.Target.onEvent(String))")
//    private void anyMethod() {}
//    @Around("anyMethod()")

    //配置方法2
    @Around("execution(* spring.pointcut.Target.onEvent(..))")
    public Object monitor(ProceedingJoinPoint point) throws Throwable {
        System.out.println("before");
        try {
            return point.proceed();
        } finally {
            System.out.println("after");
        }
    }
}

目標類:

public class Target {


    public void otherEvent(){
        System.out.println("Call otherEvent()");
    }


    public boolean onEvent(Integer type, Long Value){
        System.out.println("Call onEvent(Integer type, Long Value)");
        for (int i=0; i<=3; i++){
            onEvent("");
        }
        System.out.println("End Call onEvent(Integer type, Long Value)");
        return true;
    }

    public boolean onEvent(String type){
        System.out.println("Call onEvent(String type)");
        return true;
    }

}

Main方法:

public class Main {

    public static void main(String[] args) {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("spring/spring.xml");
        Target target = (Target) ctx.getBean("target");

        System.out.println("\n*****************************");
        target.onEvent(1,1L);
        System.out.println("\n*****************************");
        target.onEvent("");
        System.out.println("\n*****************************");
        target.otherEvent();
        System.out.println("\n*****************************");
    }
}

spring.xml

<?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:aop="http://www.springframework.org/schema/aop"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:context="http://www.springframework.org/schema/context"
           xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd">
    
    <bean id="target" class="spring.pointcut.Target"/>

    <bean id="monitor" class="spring.pointcut.TargetMonitor"/>

    <!-- 基於@AspectJ切面的驅動器 -->
    <aop:aspectj-autoproxy proxy-target-class="true"/>

</beans>

結果輸出:

22:14:25.092 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Returning cached instance of singleton bean 'target'

*****************************
22:14:25.096 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Returning cached instance of singleton bean 'monitor'
before
Call onEvent(Integer type, Long Value)
Call onEvent(String type)
Call onEvent(String type)
Call onEvent(String type)
Call onEvent(String type)
End Call onEvent(Integer type, Long Value)
after

*****************************
before
Call onEvent(String type)
after

*****************************
Call otherEvent()

*****************************

問題描述:

在目標類里有兩個同名的onEvent方法,下面這個是目標切點方法,但是系統調用時,方法入口是上面的onEvent方法,所以怎么都執行不到想要的邏輯。

其實想一想動態代理,是代理的類這一層級,關於類中方法的相互調用,是不能侵入這么深的。

注意:切點方法當前僅當在類執行入口時才能被調用,而非類內部的其他接口調用。

 

原理跟蹤,跟蹤生成的動態代理類:

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
import spring.dynamicproxy.IFace;

public final class Target1$Proxy extends Proxy implements Target {
    private static Method m1;
    private static Method m2;
    private static Method m3;
    private static Method m4;
    private static Method m5;
    private static Method m0;

    static {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            m3 = Class.forName("spring.dynamicproxy.Target").getMethod("onEvent");
            m4 = Class.forName("spring.dynamicproxy.Target").getMethod("onEvent1");
            m5 = Class.forName("spring.dynamicproxy.Target").getMethod("otherEvent");
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }

    public Target1$Proxy(InvocationHandler var1) throws  {
        super(var1);
    }

    public final boolean onEvent() throws  {
        try {
            return (Boolean)super.h.invoke(this, m3, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final boolean onEvent1() throws  {
        try {
            return (Boolean)super.h.invoke(this, m4, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final void otherEvent() throws {
        try {
            super.h.invoke(this, m5, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    

}

 

上述省略了部分其他方法,大家關注這個動態代理類內容,

JDK的動態代理核心是生成了一個新的代理類,這個代理類基礎Proxy類,實現了目標對象的接口,而在具體方法執行時,

調用super.h.invoke,這里的super.h 是指我們初始化動態代理的InvocationHandler,這里有點繞,大家要好好理解。

也就是說動態代理生效的方法是,當調用代理類Target.m的目標方法時,其實執行的是ProxyTarget.m,

這樣我們在切點中round前后的邏輯就可以執行到。

但是當執行round中的super.h.invoke時,這個方法里執行的是原始類的原生邏輯,比如執行上述例子的onEvent1,

但onEnvent1中調用onEvent時,是執行的this.onEvent,而非ProxyTarget.onEvent,

這便是為什么上述例子無法執行的底層核心原因。

 

這篇文章也描述了類似的問題:https://blog.csdn.net/bobozai86/article/details/78896487

 

解決辦法:

1、在調用點使用代理類,而非this調用:

main中獲取容器對象的測試方法:

 

 

2、思考業務層面,是否可以切在更內層的方法。

 

本質還是要理解動態代理的實現原理。

 

以上。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM