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

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. [翻译] Core Text Objective-C Wrapper

    Core Text Objective-C Wrapper https://github.com/akosma/CoreTextWrapper Introduction(介绍) One of the ...

  2. 四边形优化dp

    理解: http://blog.renren.com/share/263498909/1064362501 http://www.cnblogs.com/ronaflx/archive/2011/03 ...

  3. @Java虚拟机之对象访问

    建立对象是为了使用对象,我们的java程序需要通过栈上的reference数据来操作堆上的具体对象. 对象访问会涉及到Java栈.Java堆.方法区这三个内存区域. 如下面这句代码: Object o ...

  4. 字符串转换成JSON的三种方式

    采用Ajax的项目开发过程中,经常需要将JSON格式的字符串返回到前端,前端解析成JS对象(JSON ).ECMA-262(E3) 中没有将JSON概念写到标准中,但在 ECMA-262(E5) 中J ...

  5. 【转】QT CEF3 消息循环处理

    初次写博客,可能有点乱, 按照自己的实际经历谈一下CEF3钟遇到的一些坑,希望对以后的小伙有些帮助. 先说一下经历,当初第一次接触CEF3的时候,没做特殊处理,直接将cef3封装成控件,嵌入到QT程序 ...

  6. go语言基础之冒泡排序原理

    1.冒泡排序原理 示例: package main //必须有个main包 import "fmt" import "math/rand" import &qu ...

  7. c#递归生成XML

    递归方法大家应该都很熟悉了,简而言之就是方法内部调用自己,就这样不断重复重复再重复的执行, 不过要担心死循环哟... 当我们系统需要动态生成菜单时,也就是说我们系统的菜单是存在数据库中的,数据库结构类 ...

  8. Web开发者不容错过的10段CSS代码

    Web开发技术每年都在革新,浏览器已逐渐支持CSS3特性,并且网站设计师和前端开发者普遍采用这种新技术进行设计与开发.但仍然有一些开发者迷恋着一些CSS2代码. 本文将分享20段非常专业的CSS2/C ...

  9. 无法执行 varchar 值到 varchar 的隐式转换,原因是,由于排序规则冲突,该值的排序规则未经解析。

    SELECT CONVERT(VARCHAR(100), 列名) FROM Table 提示错误: 无法执行 varchar 值到 varchar 的隐式转换,原因是,由于排序规则冲突,该值的排序规则 ...

  10. 宿主机为linux、windows分别实现VMware三种方式上网(转)

    一.VMware三种方式工作原理1 Host-only连接方式  让虚机具有与宿主机不同的各自独立IP地址,但与宿主机位于不同网段,同时为宿主主机新增一个IP地址,且保证该IP地址与各虚机IP地址位于 ...