文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

你了解Spring AOP的这个技能点吗?有什么应用场景?

2024-11-30 03:42

关注

1. 介绍

今天看Spring文档看到这么一个知识点《Control Flow Pointcuts》都不好翻译

官方原文:

Spring control flow pointcuts are conceptually similar to AspectJ cflow pointcuts, although less powerful. (There is currently no way to specify that a pointcut runs below a join point matched by another pointcut.) A control flow pointcut matches the current call stack. For example, it might fire if the join point was invoked by a method in the com.mycompany.web package or by the SomeCaller class. Control flow pointcuts are specified by using the org.springframework.aop.support.ControlFlowPointcut class.

大意:Spring控制流切入点在概念上类似于aspectj cflow切入点,尽管功能不那么强大。(目前还没有办法指定一个切入点在与另一个切入点匹配的连接点下面运行。)控制流切入点与当前调用堆栈匹配。例如,如果连接点由com.mycompany.web包中的方法或someecaller类调用,则可能会触发该连接点。控制流切入点是通过使用org.springframework.aop.support.ControlFlowPointcut类指定的。

其实看完这个,可能你还是不懂什么意思,接下来我们来跑一个实例,就能明白撒意思了。

2. Control Flow实例

准备几个方法嵌套调用的类

static class PersonDAO {
  public void save(String name) {
    System.out.println("PersonDAO save method invoke...") ;
  }
}
static class PersonService {
  private PersonDAO dao ;
  public PersonService(PersonDAO dao) {
    this.dao = dao ;
  }
  public void save(String name) {
    System.out.println("PersonService save method inovke...") ;
    this.dao.save(name) ;
  }
}


static class PersonManager {
  private PersonService ps ;
  public void setPs(PersonService ps) {
    this.ps = ps ;
  }
  public void index(String name) {
    System.out.println("PersonManager index method invoke...") ;
    this.ps.save(name) ;
  }
}

上面的类及方法调用非常简单:PersonManager ---> PersonService ---> PersonDAO。接下来是通过编程的方式创建PersonService代理对象。

// 实例化上面的类
PersonDAO dao = new PersonDAO() ;
PersonService target = new PersonService(dao) ;


PersonManager pm = new PersonManager() ;


Class clazz = PersonManager.class ;
String methodName = "index" ;
// 定义切入点
ControlFlowPointcut pointcut = new ControlFlowPointcut(clazz, methodName) ;
// 定义通知
MethodInterceptor logInterceptor = invocation -> {
  System.out.println("before log...") ;
  Object ret = invocation.proceed() ;
  System.out.println("after log...") ;
  return ret ;
} ;
// 定义切面


DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor(pointcut, logInterceptor) ;
// 通过ProxyFactory创建代理对象,创建的是PersonService对象的代理
ProxyFactory factory = new ProxyFactory(target) ;
factory.addAdvisor(advisor) ;
// 基于CGLIB生成代理
factory.setProxyTargetClass(true) ;
PersonService ps = (PersonService) factory.getProxy() ;


pm.setPs(ps) ;


pm.index("张三") ;

控制台输出

PersonManager index method invoke...
before log...
PersonService save method inovke...
PersonDAO save method invoke...
after log...

从输出的结果发现,在PersonService#save方法之前之前和之后分别打印了日志信息。原理是什么呢?这里我们需要先看ControlFlowPointcut 切入点是如何工作的。

ControlFlowPointcut核心方法

这里只列出了几个重要的方法,在spring中只支持方法级别的拦截。

public class ControlFlowPointcut implements Pointcut, ClassFilter, MethodMatcher, Serializable {
  private final Class clazz;
  @Nullable
  private final String methodName;
  // 对应类级别的匹配全部返回true,就是都匹配
  @Override
  public boolean matches(Class clazz) {
    return true;
  }
  // 方法匹配,直接true
  @Override
  public boolean matches(Method method, Class targetClass) {
    return true;
  }
  // 这里是关键,只有isRuntime返回了true才有可能调用下面3个参数的matches方法
  @Override
  public boolean isRuntime() {
    return true;
  }
  // 该方法的调用需要上面2个参数的matches方法返回true且isRuntime方法也返回true才会调用这里
  @Override
  public boolean matches(Method method, Class targetClass, Object... args) {
    // 遍历当前线程的执行栈情况(也就是当前方法的调用栈情况)
    for (StackTraceElement element : new Throwable().getStackTrace()) {
      // 这里就开始判断当前执行的类是否与给定的类相同 && 当前设置的methodName为空或者当前栈执行的方法名与给定的方法名相同
      if (element.getClassName().equals(this.clazz.getName()) &&
          (this.methodName == null || element.getMethodName().equals(this.methodName))) {
        // 最终这里只有返回了true,我们上面的通知MethodInterceptor才会被执行
        return true;
      }
    }
    return false;
  }
}

有了上面源码的分析后,我们再来看看上面的示例代码:

// 指明要匹配的类
Class clazz = PersonManager.class ;
// 指明要匹配的方法名
String methodName = "index" ;

ControlFlowPointcut pointcut = new ControlFlowPointcut(clazz, methodName) ;

分析到这你应该知道这个Control Flow有撒用了吧,总结:

Control Flow就是用来判断当前执行的线程栈中(所有方法的调用)是否与你给定的类及方法匹配,只有匹配了才能执行我们的增强(通知)代码。

简单说:我PersonService想监控PersonManager中的index方法是否调用了我。

官方有这段说明:

Dynamic pointcuts are costlier to evaluate than static pointcuts. They take into account method arguments as well as static information. This means that they must be evaluated with every method invocation and that the result cannot be cached, as arguments will vary.

The main example is the control flow pointcut.

大意:与静态快捷方式相比,动态快捷方式的评估成本更高。它们会考虑方法参数和静态信息。这意味着每次调用方法时都必须对其进行评估,而且由于参数会发生变化,因此无法缓存评估结果。控制流快捷方式就是一个主要的例子。

3. Control Flow性能

同样来自官方说明:

Control flow pointcuts are significantly more expensive to evaluate at runtime than even other dynamic pointcuts. In Java 1.4, the cost is about five times that of other dynamic pointcuts.

大意:与其他动态切入点相比,控制流切入点在运行时评估的成本要高得多。在Java1.4中,成本大约是其他动态切入点的五倍。

知道了Control Flow怎么一回事,那它有什么使用场景吗?有使用过的还望能分享下,欢迎大家留言讨论。图片

来源:Spring全家桶实战案例源码内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯