今天在开发过程中,遇到一个问题卡了很久,测试代码如下:

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、思考业务层面,是否可以切在更内层的方法。

本质还是要理解动态代理的实现原理。

以上。

[Done]Spring @Pointcut 切点调用不到(SpringAOP嵌套方法不起作用) 注意事项的更多相关文章

  1. Spring.net 间接调用被AOP拦截的方法失效(无法进入aop的拦截方法)

    .下面的tx要定义 <objects xmlns="http://www.springframework.net" xmlns:db="http://www.spr ...

  2. 使用Spring boot开发RestFul 风格项目PUT/DELETE方法不起作用

    在使用Spring boot 开发restful 风格的项目,put.delete方法不起作用,解决办法. 实体类Student @Data public class Student { privat ...

  3. Spring事务:调用同一个类中的方法

    问题: 如果同一个类中有方法:methodA(); methodB().methodA()没有开启事务,methodB()开启了事务 且methodA()会调用methodB(). 那么,method ...

  4. Spring异步调用原理及SpringAop拦截器链原理

    一.Spring异步调用底层原理 开启异步调用只需一个注解@EnableAsync @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTI ...

  5. Spring AOP 切点(pointcut)表达式

    这遍文章将介绍Spring AOP切点表达式(下称表达式)语言,首先介绍两个面向切面编程中使用到的术语. 连接点(Joint Point):广义上来讲,方法.异常处理块.字段这些程序调用过程中可以抽像 ...

  6. Spring学习(十六)----- Spring AOP实例(Pointcut(切点),Advisor)

    在上一个Spring AOP通知的例子,一个类的整个方法被自动拦截.但在大多数情况下,可能只需要一种方式来拦截一个或两个方法,这就是为什么引入'切入点'的原因.它允许你通过它的方法名来拦截方法.另外, ...

  7. spring框架学习笔记5:SpringAOP示例

    1.导包: 导入spring中的这两个包 再导入其他包(网上下载): 2.准备目标对象: package service; public class UserServiceImpl implement ...

  8. Spring AOP切点表达式用法总结

    1. 简介        面向对象编程,也称为OOP(即Object Oriented Programming)最大的优点在于能够将业务模块进行封装,从而达到功能复用的目的.通过面向对象编程,不同的模 ...

  9. CXF整合Spring之JaxWsProxyFactoryBean调用

    1.见解 1.1 客户端的接口代码还一定要和服务端的接口代码一样,连注解都要一样,不够灵活 1.2 当客户端访问服务器的请求地址时,如果服务端没有对应的地址,就会报错,但是又没有cxf的异常捕获处理 ...

随机推荐

  1. java基础学习总结——面向对象2

    一.JAVA类的定义

  2. OpenCV学习(21) Grabcut算法详解

    grab cut算法是graph cut算法的改进.在理解grab cut算之前,应该学习一下graph cut算法的概念及实现方式. 我搜集了一些graph cut资料:http://yunpan. ...

  3. OpenCV学习(18) 细化算法(6)

    本章我们在学习一下基于索引表的细化算法. 假设要处理的图像为二值图,前景值为1,背景值为0. 索引表细化算法使用下面的8邻域表示法: 一个像素的8邻域,我们可以用8位二进制表示,比如下面的8邻域,表示 ...

  4. 我所遭遇过的游戏中间件--Scaleform

    我所遭遇过的游戏中间件---Scaleform Scaleform帮助开发人员利用现代系统的三维硬件加速性能创建电影品质的菜单.游戏内HUD,动画纹理.迷你游戏以及移动游戏与应用.Scaleform作 ...

  5. SSM(SpringMVC+Spring+Mybatis)框架程序on IDEA

    有了之前文章搭建的SSH框架之后,现在搭建基于Mybatis的框架.主要基于如下这篇文章: http://blog.csdn.net/gallenzhang/article/details/51932 ...

  6. QT笔记:数据库总结(三)之SQL模型类-QSqlTableModel模型

    QSqlTableModel类继承至QSqlQueryModel类,该类提供了一个可读写单张SQL表的可编辑数据模型,功能:修改,插入,删除,查询,和排序 常用函数 QVariant headerDa ...

  7. HTML标签 闭合还是不闭合?

    你在写 HTML5 代码的时候,是否纠结过应该写 <br /> 还是 <br>,是写 <input /> 还是写 <input>.写 <scrip ...

  8. fatal error C1853: '<filename>' is not a precompiled header file

    当编译c和c++混合的项目时,会出现如下类似的错误 fatal error C1853: '<filename>' is not a precompiled header file 解决方 ...

  9. 用vs2013开发node.js的addon.

        下载node.js的源代码. https://github.com/joyent/node 如果用svn下载,后面加上/trunk,以免把用不着的branches也下载下来,浪费时间. 安装V ...

  10. LuaCURL

    LuaCURL:http://luacurl.luaforge.net/ curl大家应该都知道吧,在linux下被广泛使用,也有windows版本,网络上还有其win32版本的源代码.它是一个命令行 ...