当前位置: 首页 > 知识库问答 >
问题:

从Spring AOP到AspectJ的转换

南宫奇思
2023-03-14

我正在将一些使用Spring AOP的代码迁移到AspectJ方面(在编译时编织)。我正在寻找关于如何修改切入点以使它们在迁移后表现相同的反馈?

这让我很头疼,我想知道我是否可以更改切入点,使其仍然表现得好像它仍然是一个代理?(即在类型层次结构中的任何一点排除来自自身内部的调用,例如调用继承的函数)

共有1个答案

逑阳泽
2023-03-14

我认为Jigish混合方面样式的想法是一种很好的迁移方法,甚至是永久地继续使用Spring的方法,只要您没有任何迫在眉睫的问题(例如性能)。

现在,说到这里,我为您提供了一个变通方法,以防您出于任何原因需要在全面AspectJ中排除内部调用。我不认为它是优雅的,但它的工作。这里有一个概念证明:

两个示例应用程序类:

package de.scrum_master.app;

public class Foo {
    public void doSomethingFooish() {
        fooTwo(22);
    }

    public void fooOne(Bar bar) {
        bar.doSomethingBarish();
        fooTwo(11);
    }

    public String fooTwo(int number) {
        return fooThree("xxx");
    }

    public String fooThree(String text) {
        return text + " " + text + " " + text;
    }
}
package de.scrum_master.app;

public class Bar {
    public void doSomethingBarish() {
        barTwo(22);
    }

    public void barOne(Foo foo) {
        foo.doSomethingFooish();
        barTwo(11);
    }

    public String barTwo(int number) {
        return barThree("xxx");
    }

    public String barThree(String text) {
        return text + " " + text + " " + text;
    }
}
package de.scrum_master.app;

public class Application {
    public static void main(String[] args) {
        Foo foo = new Foo();
        Bar bar = new Bar();

        foo.fooOne(bar);
        bar.barOne(foo);
    }
}
package de.scrum_master.aspect;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;

@Aspect
public class SampleAspect {
    @Pointcut("call(public * de.scrum_master..*(..)) && !within(SampleAspect)")
    public static void publicMethodCalls() {}

    @Before("publicMethodCalls()")
    public void myPointcut(
        JoinPoint thisJoinPoint,
        JoinPoint.EnclosingStaticPart thisEnclosingJoinPointStaticPart
    ) {
        System.out.println(thisEnclosingJoinPointStaticPart + " -> " + thisJoinPoint);
    }
}
  • application同时调用foobar方法,
  • foo调用bar方法,但也在内部调用自己的方法,
  • bar调用foo方法,但也在内部调用自己的方法。
execution(void de.scrum_master.app.Application.main(String[])) -> call(void de.scrum_master.app.Foo.fooOne(Bar))
execution(void de.scrum_master.app.Foo.fooOne(Bar)) -> call(void de.scrum_master.app.Bar.doSomethingBarish())
execution(void de.scrum_master.app.Bar.doSomethingBarish()) -> call(String de.scrum_master.app.Bar.barTwo(int))
execution(String de.scrum_master.app.Bar.barTwo(int)) -> call(String de.scrum_master.app.Bar.barThree(String))
execution(void de.scrum_master.app.Foo.fooOne(Bar)) -> call(String de.scrum_master.app.Foo.fooTwo(int))
execution(String de.scrum_master.app.Foo.fooTwo(int)) -> call(String de.scrum_master.app.Foo.fooThree(String))
execution(void de.scrum_master.app.Application.main(String[])) -> call(void de.scrum_master.app.Bar.barOne(Foo))
execution(void de.scrum_master.app.Bar.barOne(Foo)) -> call(void de.scrum_master.app.Foo.doSomethingFooish())
execution(void de.scrum_master.app.Foo.doSomethingFooish()) -> call(String de.scrum_master.app.Foo.fooTwo(int))
execution(String de.scrum_master.app.Foo.fooTwo(int)) -> call(String de.scrum_master.app.Foo.fooThree(String))
execution(void de.scrum_master.app.Bar.barOne(Foo)) -> call(String de.scrum_master.app.Bar.barTwo(int))
execution(String de.scrum_master.app.Bar.barTwo(int)) -> call(String de.scrum_master.app.Bar.barThree(String))

改进的动态排除内部调用的方面:

现在我们需要比较调用方(this()切入点在本机aspectJ语法中)是否与被调用方(target()切入点)相同。如果是这样,我们希望跳过建议执行。在AspectJ中有两种获取调用方/被调用方引用的方法:

    null
package de.scrum_master.aspect;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;

@Aspect
public class SampleAspect {
    @Pointcut("call(public * de.scrum_master..*(..)) && !within(SampleAspect)")
    public static void publicMethodCalls() {}

    @Before("publicMethodCalls() && target(callee)")
    public void myPointcut(
        Object callee,
        JoinPoint thisJoinPoint,
        JoinPoint.EnclosingStaticPart thisEnclosingJoinPointStaticPart
    ) {
        Object caller = thisJoinPoint.getThis();
        if (caller == callee)
            return;
        System.out.println(thisEnclosingJoinPointStaticPart + " -> " + thisJoinPoint);
        System.out.println("  caller = " + caller);
        System.out.println("  callee = " + callee);
    }
}
execution(void de.scrum_master.app.Application.main(String[])) -> call(void de.scrum_master.app.Foo.fooOne(Bar))
  caller = null
  callee = de.scrum_master.app.Foo@6a5c2445
execution(void de.scrum_master.app.Foo.fooOne(Bar)) -> call(void de.scrum_master.app.Bar.doSomethingBarish())
  caller = de.scrum_master.app.Foo@6a5c2445
  callee = de.scrum_master.app.Bar@47516490
execution(void de.scrum_master.app.Application.main(String[])) -> call(void de.scrum_master.app.Bar.barOne(Foo))
  caller = null
  callee = de.scrum_master.app.Bar@47516490
execution(void de.scrum_master.app.Bar.barOne(Foo)) -> call(void de.scrum_master.app.Foo.doSomethingFooish())
  caller = de.scrum_master.app.Bar@47516490
  callee = de.scrum_master.app.Foo@6a5c2445

在特殊情况下,如果您知道某个建议所针对的确切类名,那么您可能可以使用cflow()来排除内部调用,而不会构造出难看的If结构,但我没有考虑清楚,也没有尝试过。它在一般情况下不起作用。

更新1:

我又玩了一些。下面是使用execution()代替call()的替代方案。因此,它不能依赖于封闭的连接点,而需要分析当前的调用堆栈。我没有对照上面描述的解决方案对性能进行基准测试,但它肯定会编织更少的连接点。此外,它在其切入点中使用if(),而不是在通知中使用if语句。条件仍然是在运行时动态确定的,而不是在编织代码时静态确定的,但我猜在这里无论如何都是不可能的,因为它依赖于控制流。

package de.scrum_master.aspect;

public aspect DemoAspect {
    pointcut publicMethodCalls() :
        execution(public !static * de.scrum_master..*(..)) &&
        if(Thread.currentThread().getStackTrace()[3].getClassName() != thisJoinPointStaticPart.getSignature().getDeclaringTypeName()); 

    before() : publicMethodCalls() {
        System.out.println(thisJoinPoint);
    }
}
package de.scrum_master.aspect;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;

@Aspect
public class SampleAspect {
    @Pointcut("execution(public !static * de.scrum_master..*(..)) && if()")
    public static boolean publicMethodCalls(JoinPoint thisJoinPoint) {
        return Thread.currentThread().getStackTrace()[3].getClassName() != thisJoinPoint.getSignature().getDeclaringTypeName();
    }

    @Before("publicMethodCalls(thisJoinPoint)")
    public void myPointcut(JoinPoint thisJoinPoint) {
        System.out.println(thisJoinPoint);
    }
}
execution(void de.scrum_master.app.Foo.fooOne(Bar))
execution(void de.scrum_master.app.Bar.doSomethingBarish())
execution(void de.scrum_master.app.Bar.barOne(Foo))
execution(void de.scrum_master.app.Foo.doSomethingFooish())

更新2:

下面是使用execution()加上通过Class.IsAssignableFrom(Class)的一些反射的另一个变体,它也应该与类层次结构和接口一起工作:

package de.scrum_master.aspect;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.SoftException;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;

@Aspect
public class SampleAspect {
    @Pointcut("execution(public !static * de.scrum_master..*(..)) && target(callee) && if()")
    public static boolean publicMethodCalls(Object callee, JoinPoint thisJoinPoint) {
        Class<?> callerClass;
        try {
            callerClass = Class.forName(
                Thread.currentThread().getStackTrace()[3].getClassName()
            );
        } catch (Exception e) {
            throw new SoftException(e);
        }
        Class<?> calleeClass = callee.getClass();
        return !callerClass.isAssignableFrom(calleeClass);
    }

    @Before("publicMethodCalls(callee, thisJoinPoint)")
    public void myPointcut(Object callee, JoinPoint thisJoinPoint) {
        System.out.println(thisJoinPoint);
    }
}
 类似资料:
  • Java+Spring+Maven应用程序: 有人能给我提供链接或者建议我一个纯AspectJ实现,不使用基于代理的Spring AOP吗? 如果我试图从同一中的访问,则不支持此操作。 我想知道:1)如何用切入点编写一个支持类内方法调用的aspectj?2)如何将其配置到我当前的Spring,maven项目中,使用aspectj加载时编织?3)如何配置aspectj maven插件,以便在Tomc

  • 我是一个新的bee-to-Spring框架,我指的是Spring项目中可用的文档。 在这个过程中,我还学习了一个新概念AOP。我遵循spring文档来尝试一些示例http://docs.spring.io/spring/docs/current/spring-framework-reference/html/aop.html 我尝试使用Spring AOP为我的第一个Aspect Hellowor

  • 主要内容:读者,前提条件,Spring AOP 概述Spring框架的关键组件之一是面向方面编程(AOP)框架。 面向方面的编程需要将程序逻辑分解成不同的部分。 此教程将通过简单实用的方法来学习Spring框架提供的AOP/面向方面编程。 读者 本教程主要是为Spring 面向方面编程(AOP)初学者准备的,帮助他们了解与Spring的AOP框架相关的基础到高级概念。 前提条件 在开始练习本教程系列文章中给出的各种类型的示例之前,我们假设您已经了解

  • 我将获得一个AWS S3图像资源作为S3ObjectInputStream,并希望将其放入Vaadin图像HTML组件中。Vaadin图像只有两种初始化方式。第一个是通过一个表示文件路径的字符串,这意味着将S3映像写入磁盘,由于数据安全原因,我无法这样做,第二个是通过实现“AbstractStreamResource”的东西。现在的问题是,如何将S3ObjectInputStream转换为实现“A

  • 我有一个自定义注释, 我正在将这个注释用于以下方法, 我在以下方面捕捉事件, @Around建议仅适用于“进程连接点”参数。如果将 XAudit 作为第二个参数传递,则会引发以下错误: 我需要在方面中使用xaud才能访问Xaud的操作。请分享一些关于如何在@周围方面中访问@Xaud值的指针。

  • 问题内容: 我希望将标准JSON对象处理为一个对象,其中每行必须包含一个单独的,自包含的有效JSON对象。查看JSON行 : 我当前的解决方案是将JSON文件读取为文本文件,并从开头和结尾删除。因此,在每行上创建一个有效的JSON对象,而不是包含行的嵌套对象。 我想知道是否有更优雅的解决方案?我怀疑在文件上使用字符串操作可能会出错。 目的是在Spark上将文件读入RDD。查看相关问题-使用Apac