[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、思考业务层面,是否可以切在更内层的方法。
本质还是要理解动态代理的实现原理。
以上。
[Done]Spring @Pointcut 切点调用不到(SpringAOP嵌套方法不起作用) 注意事项的更多相关文章
- Spring.net 间接调用被AOP拦截的方法失效(无法进入aop的拦截方法)
.下面的tx要定义 <objects xmlns="http://www.springframework.net" xmlns:db="http://www.spr ...
- 使用Spring boot开发RestFul 风格项目PUT/DELETE方法不起作用
在使用Spring boot 开发restful 风格的项目,put.delete方法不起作用,解决办法. 实体类Student @Data public class Student { privat ...
- Spring事务:调用同一个类中的方法
问题: 如果同一个类中有方法:methodA(); methodB().methodA()没有开启事务,methodB()开启了事务 且methodA()会调用methodB(). 那么,method ...
- Spring异步调用原理及SpringAop拦截器链原理
一.Spring异步调用底层原理 开启异步调用只需一个注解@EnableAsync @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTI ...
- Spring AOP 切点(pointcut)表达式
这遍文章将介绍Spring AOP切点表达式(下称表达式)语言,首先介绍两个面向切面编程中使用到的术语. 连接点(Joint Point):广义上来讲,方法.异常处理块.字段这些程序调用过程中可以抽像 ...
- Spring学习(十六)----- Spring AOP实例(Pointcut(切点),Advisor)
在上一个Spring AOP通知的例子,一个类的整个方法被自动拦截.但在大多数情况下,可能只需要一种方式来拦截一个或两个方法,这就是为什么引入'切入点'的原因.它允许你通过它的方法名来拦截方法.另外, ...
- spring框架学习笔记5:SpringAOP示例
1.导包: 导入spring中的这两个包 再导入其他包(网上下载): 2.准备目标对象: package service; public class UserServiceImpl implement ...
- Spring AOP切点表达式用法总结
1. 简介 面向对象编程,也称为OOP(即Object Oriented Programming)最大的优点在于能够将业务模块进行封装,从而达到功能复用的目的.通过面向对象编程,不同的模 ...
- CXF整合Spring之JaxWsProxyFactoryBean调用
1.见解 1.1 客户端的接口代码还一定要和服务端的接口代码一样,连注解都要一样,不够灵活 1.2 当客户端访问服务器的请求地址时,如果服务端没有对应的地址,就会报错,但是又没有cxf的异常捕获处理 ...
随机推荐
- iOS上使用自己定义ttf字体
项目中想使用第三方的字体,在stackoverflow上查询解决的方法,也折腾一会,加入成功,示比例如以下: 1.将xx.ttf字体库增加project里面 2.在project的xx-Info.pl ...
- 动态创建html元素的几种方法
可以通过以下几种方式动态创建html元素: 1.使用jQuery创建元素的语法 2.把动态内容存放到数组中,再遍历数组动态创建html元素 3.使用模版 □ 使用jQuery动态创建元素追加到jQ ...
- CentOS6.5和RedHat6.5下以rpm方式安装mysql-5.6.20
转帖;http://blog.csdn.net/mw08091020/article/details/39234207 a.检查下linux是不是已经安装了mysql rpm -qa | grep - ...
- <jsp:directive.page import=""/>的用法和解释
<jsp:directive.page import="zero.space.ch03.BookBean"/> 相当于 <%@ page import ...
- 【linux】centos7终端中文显示乱码,命令返回中文乱码
centos7终端中文显示乱码,命令返回中文乱码 1.查看服务器编码的命令 1.1 echo $LANG 1.2 locale 1.3 查看终端xshell编码 如果以上的三点依旧保持一致,而依旧乱码 ...
- 微商营销实战技巧分享,轻松月入10W
如今能够说是移动互联时代.在这个时代,微信眼下能够说是当之无愧的移动应用,依据报道,眼下微信有7个多亿的用户,怪不得那么多人看到微商的时代,一大批人開始涌入微商,导致如今微信上卖产品都已经泛滥了,导致 ...
- C++ 转型动作 尽量避免 以及 那些意想不到的威胁
看完EffectiveC++的关于转型的章节,顿时认为周围的代码都处在悬崖边上~~ C的旧式转型:inta = 10; double b = (double)a; 对于C++的四种转型函数, cons ...
- numpy转换
csv2npy cccsv=numpy.genfromtxt('/root/c.csv', delimiter = ',') buf2npy imga=numpy.frombuffer(buf,num ...
- C语言:字符串输出流输出文件中的数据。
#include<stdio.h> #include<string.h> int main() { //定义文件指针 FILE *f = NULL; //打开文件 f = fo ...
- 关于一道JS面试题的思考
题目: ; i < ; i++) { setTimeout(function() { console.log(new Date, i); }, ); } console.log(new Date ...