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

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. 3D单机游戏《天鹰教》源码发布(二)

    3D单机游戏<天鹰教>源码发布 作者 作者: 游蓝海 博客: http://blog.csdn.net/you_lan_hai mail:     you_lan_hai@foxmail. ...

  2. 跨境网上收款 找PayPal没错(获取Client ID 和 secret)

    原文地址:http://blog.csdn.net/qiandublog/article/details/52809731 只需一个PayPal账户,全球1.9亿网购买家触手可得 不管您有没有网站,拥 ...

  3. Node.js安装备忘录

    一.准备工作 Node.js下载地址 http://nodejs.org/download/ Current version: v0.10.29 二.平台的选择 2.1 Windows平台 根据自己平 ...

  4. 跟我学Spring3(9.1):Spring的事务之数据库事务概述

    原文出处: 张开涛 9.1 数据库事务概述 事务首先是一系列操作组成的工作单元,该工作单元内的操作是不可分割的,即要么所有操作都做,要么所有操作都不做,这就是事务. 事务必需满足ACID(原子性.一致 ...

  5. BZOJ 4145 [AMPPZ2014] The Prices 解题报告

    感觉也是一个小清新题.. 我们考虑设立状态 $Dp[i][s]$ 表示考虑了前 $i$ 个商店后,购买状态为 $s$ 的最小花费. 转移的话就枚举每个商店 $i$,首先令: $$Dp[i][s] = ...

  6. Unix/Linux环境C编程新手教程(30) 字符串操作那些事儿

    函数介绍 rindex(查找字符串中最后一个出现的指定字符) 相关函数 index,memchr,strchr,strrchr 表头文件 #include<string.h> 定义函数 c ...

  7. Dijkstra算法求最短路径(java)(转)

    原文链接:Dijkstra算法求最短路径(java) 任务描述:在一个无向图中,获取起始节点到所有其他节点的最短路径描述 Dijkstra(迪杰斯特拉)算法是典型的最短路径路由算法,用于计算一个节点到 ...

  8. Singleton 单例模式 MD

    Markdown版本笔记 我的GitHub首页 我的博客 我的微信 我的邮箱 MyAndroidBlogs baiqiantao baiqiantao bqt20094 baiqiantao@sina ...

  9. ButterKnife 注解 bindview

    简介 官网:http://jakewharton.github.io/butterknife/ github:https://github.com/JakeWharton/butterknife 注意 ...

  10. CSS 过滤器 兼容ie,火狐和谷歌

    这篇汇总主要是提供一些CSS不透明的详细介绍,代码示例和解释,以实现这项有用的CSS技术在您的项目中兼容所有浏览器. 关于CSS 透明度,有一点需要注意的是,它虽然使用了很多年,但它一直以来都不是一个 ...