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

在另一个方法内的方法调用后执行某些操作

汪同
2023-03-14

我想在调用非公共方法(bar)后执行一些特定操作。此方法在另一个方法(foo)中调用。请注意,“bar”和“foo”都是在第三方jar文件中定义的。

我试图在面向方面的编程中使用Spring使用@之前注释来做到这一点。然而,我不能这样做。

在调用jar文件中的特定函数之后,有人能告诉我如何做特定的事情(调用特定函数)吗?

共有2个答案

东方修谨
2023-03-14

避免使用Spring AOP。它不允许这样做,因为Spring创建了一个代理来对bean进行体化,因此,只有对“代理”的调用才会被体化。这意味着,如果您有一个第三方类的bean(我们称之为fooBean),那么当您执行fooBean时。foo()您实际上正在通过具有方面逻辑的代理,但是一旦执行了foo()方法,则内部调用,例如对bar()的调用将不再被认为是该代理,因此,那里将没有方面。

Mybe可能会遇到一个更复杂的解决方案,因为使用纯AspectJ可能会对您有所帮助,因为它不是基于proxys的,它只是增强了编译的字节码

闻人和泽
2023-03-14

正如Gervasio Amy所建议的,您需要使用AspectJ,而不是Spring AOP。如果您在Spring环境中,可以在Spring中使用AspectJ而不是SpringAOP,这没有问题。如果您还没有使用Spring,AOP不是开始使用它的理由,AspectJ在没有Spring的简单JavaSE(或EE)版本中工作。

您需要做的是:

  • 编译你的Aspect代码与AbulJ编译器ajc。(你也可以用它编译你的整个应用程序,因为它也是Java编译器javac的替代品。)
  • 创建加载时编织配置aop.xml以便使您的应用程序能够在类加载期间动态地将方面代码编织到第三方库中。我让你自己想办法做到这一点,只是检查LTW留档。
  • 在命令行上通过-javaagent:/path/to/aspectjweaver.jar开关使用ASHOJ编织代理启动JVM或应用程序服务器。

现在你想要的方面是什么?让我们尝试几个变体,并改进切入点,使其匹配。但是首先让我们用一些第三方类(FooBar)和一个小驱动程序应用程序(Application)为我们的实验搭建舞台:

样本申请

package my.thirdparty.application;

public class Foo {
    void blah() {
        zot();
    }

    void foo() {}

    void zot() {
        foo();
    }
}
package my.thirdparty.application;

public class Bar {
    Foo foo = new Foo();

    public void doSomething() {
        someMethod();
        bar();
        anotherMethod();
    }

    private void someMethod() {
        foo.blah();
        foo.foo();
        foo.zot();
    }

    private void bar() {
        foo.blah();
        // This is the only call we want to intercept, 'foo' called by 'bar'
        foo.foo();
        foo.zot();
        anotherMethod();
    }

    private void anotherMethod() {
        foo.blah();
        foo.foo();
        foo.zot();
    }
}
package de.scrum_master.app;

import my.thirdparty.application.Bar;

public class Application {
    public static void main(String[] args) {
        new Bar().doSomething();
    }
}

如您所见,应用程序。main创建一个Bar对象并调用一个公共方法Bar。doSomething。此方法触发一系列其他方法调用,其中一些方法调用以Foo结束。foo被间接调用,但从条只进行了一次直接调用。条形图Foo。foo(根据您的问题,这是我们感兴趣的内容)。

方面,第1部分:拦截对Foo的所有调用。foo

package de.scrum_master.aspect;

import my.thirdparty.application.Foo;
import my.thirdparty.application.Bar;

public aspect MethodInterceptor {
    pointcut allCalls() :
        call(* Foo.foo(..));

    Object around(Foo fooObject) : allCalls() && target(fooObject) {
        System.out.println(thisJoinPointStaticPart + " -> caller = " + thisEnclosingJoinPointStaticPart);
        //new Exception("printing stack trace").printStackTrace(System.out);
        //System.out.println();
        return proceed(fooObject);
    }
}

控制台日志:

call(void my.thirdparty.application.Foo.foo()) -> caller = execution(void my.thirdparty.application.Foo.zot())
call(void my.thirdparty.application.Foo.foo()) -> caller = execution(void my.thirdparty.application.Bar.someMethod())
call(void my.thirdparty.application.Foo.foo()) -> caller = execution(void my.thirdparty.application.Foo.zot())
call(void my.thirdparty.application.Foo.foo()) -> caller = execution(void my.thirdparty.application.Foo.zot())
call(void my.thirdparty.application.Foo.foo()) -> caller = execution(void my.thirdparty.application.Bar.bar())
call(void my.thirdparty.application.Foo.foo()) -> caller = execution(void my.thirdparty.application.Foo.zot())
call(void my.thirdparty.application.Foo.foo()) -> caller = execution(void my.thirdparty.application.Foo.zot())
call(void my.thirdparty.application.Foo.foo()) -> caller = execution(void my.thirdparty.application.Bar.anotherMethod())
call(void my.thirdparty.application.Foo.foo()) -> caller = execution(void my.thirdparty.application.Foo.zot())
call(void my.thirdparty.application.Foo.foo()) -> caller = execution(void my.thirdparty.application.Foo.zot())
call(void my.thirdparty.application.Foo.foo()) -> caller = execution(void my.thirdparty.application.Bar.anotherMethod())
call(void my.thirdparty.application.Foo.foo()) -> caller = execution(void my.thirdparty.application.Foo.zot())

这是一个很好的开始,因为现在我们已经可以拦截对Foo的所有调用。foo。但是,如何将拦截限制在从条的控制流(cflow)中发出的那些调用上呢。条形码?

方面,第#2部分:截取对Foo.foo的调用(in-)直接由Bar.bar

package de.scrum_master.aspect;

import my.thirdparty.application.Foo;
import my.thirdparty.application.Bar;

public aspect MethodInterceptor {
    pointcut indirectCalls() :
        call(* Foo.foo(..)) && cflow(execution(* Bar.bar(..)));

    Object around(Foo fooObject) : indirectCalls() && target(fooObject) {
        System.out.println(thisJoinPointStaticPart + " -> caller = " + thisEnclosingJoinPointStaticPart);
        //new Exception("printing stack trace").printStackTrace(System.out);
        //System.out.println();
        return proceed(fooObject);
    }
}

控制台日志:

call(void my.thirdparty.application.Foo.foo()) -> caller = execution(void my.thirdparty.application.Foo.zot())
call(void my.thirdparty.application.Foo.foo()) -> caller = execution(void my.thirdparty.application.Bar.bar())
call(void my.thirdparty.application.Foo.foo()) -> caller = execution(void my.thirdparty.application.Foo.zot())
call(void my.thirdparty.application.Foo.foo()) -> caller = execution(void my.thirdparty.application.Foo.zot())
call(void my.thirdparty.application.Foo.foo()) -> caller = execution(void my.thirdparty.application.Bar.anotherMethod())
call(void my.thirdparty.application.Foo.foo()) -> caller = execution(void my.thirdparty.application.Foo.zot())

现在看起来比以前好多了,我们把之前截获的12个电话的结果缩小到了6个。但是我们怎么会有像Foo这样的调用者呢。zot条。结果列表中的anotherMethod,尽管我们说过要将控制流限制为条。条形码?答案很简单:Bar也直接或间接地调用了这两种方法。因此,条形码和条形码都在控制流中。如果我们检查调用堆栈(只需取消对代码中的两条日志语句的注释),就会更清楚地看到这一点:

call(void my.thirdparty.application.Foo.foo()) -> caller = execution(void my.thirdparty.application.Foo.zot())
java.lang.Exception: printing stack trace
    at my.thirdparty.application.Foo.foo_aroundBody1$advice(Foo.java:22)
    at my.thirdparty.application.Foo.zot(Foo.java:11)
    at my.thirdparty.application.Foo.blah(Foo.java:5)
    at my.thirdparty.application.Bar.bar(Bar.java:19)
    at my.thirdparty.application.Bar.doSomething(Bar.java:8)
    at de.scrum_master.app.Application.main(Application.java:7)

call(void my.thirdparty.application.Foo.foo()) -> caller = execution(void my.thirdparty.application.Bar.bar())
java.lang.Exception: printing stack trace
    at my.thirdparty.application.Bar.foo_aroundBody3$advice(Bar.java:22)
    at my.thirdparty.application.Bar.bar(Bar.java:21)
    at my.thirdparty.application.Bar.doSomething(Bar.java:8)
    at de.scrum_master.app.Application.main(Application.java:7)

call(void my.thirdparty.application.Foo.foo()) -> caller = execution(void my.thirdparty.application.Foo.zot())
java.lang.Exception: printing stack trace
    at my.thirdparty.application.Foo.foo_aroundBody1$advice(Foo.java:22)
    at my.thirdparty.application.Foo.zot(Foo.java:11)
    at my.thirdparty.application.Bar.bar(Bar.java:22)
    at my.thirdparty.application.Bar.doSomething(Bar.java:8)
    at de.scrum_master.app.Application.main(Application.java:7)

call(void my.thirdparty.application.Foo.foo()) -> caller = execution(void my.thirdparty.application.Foo.zot())
java.lang.Exception: printing stack trace
    at my.thirdparty.application.Foo.foo_aroundBody1$advice(Foo.java:22)
    at my.thirdparty.application.Foo.zot(Foo.java:11)
    at my.thirdparty.application.Foo.blah(Foo.java:5)
    at my.thirdparty.application.Bar.anotherMethod(Bar.java:27)
    at my.thirdparty.application.Bar.bar(Bar.java:23)
    at my.thirdparty.application.Bar.doSomething(Bar.java:8)
    at de.scrum_master.app.Application.main(Application.java:7)

call(void my.thirdparty.application.Foo.foo()) -> caller = execution(void my.thirdparty.application.Bar.anotherMethod())
java.lang.Exception: printing stack trace
    at my.thirdparty.application.Bar.foo_aroundBody5$advice(Bar.java:22)
    at my.thirdparty.application.Bar.anotherMethod(Bar.java:28)
    at my.thirdparty.application.Bar.bar(Bar.java:23)
    at my.thirdparty.application.Bar.doSomething(Bar.java:8)
    at de.scrum_master.app.Application.main(Application.java:7)

call(void my.thirdparty.application.Foo.foo()) -> caller = execution(void my.thirdparty.application.Foo.zot())
java.lang.Exception: printing stack trace
    at my.thirdparty.application.Foo.foo_aroundBody1$advice(Foo.java:22)
    at my.thirdparty.application.Foo.zot(Foo.java:11)
    at my.thirdparty.application.Bar.anotherMethod(Bar.java:29)
    at my.thirdparty.application.Bar.bar(Bar.java:23)
    at my.thirdparty.application.Bar.doSomething(Bar.java:8)
    at de.scrum_master.app.Application.main(Application.java:7)

如果您检查6个调用栈,您会在每个调用栈中找到Bar.bar。所以cflow切入点做了我们告诉它的事情。

我们能做得更好吗?告诉方面不仅将被调用方(目标)对象限制为Foo,还将调用方(此)对象限制为Bar,怎么样?

方面,第3部分:拦截对Foo的调用。foo栏直接(在-)制作。条形码,但肯定来自条形码对象

package de.scrum_master.aspect;

import my.thirdparty.application.Foo;
import my.thirdparty.application.Bar;

public aspect MethodInterceptor {
    pointcut callsFromBar(Bar barObject) :
        call(* Foo.foo(..)) && cflow(execution(* Bar.bar(..))) && this(barObject);

    Object around(Foo fooObject, Bar barObject) : callsFromBar(barObject) && target(fooObject) {
        System.out.println(thisJoinPointStaticPart + " -> caller = " + thisEnclosingJoinPointStaticPart);
        new Exception("printing stack trace").printStackTrace(System.out);
        System.out.println();
        return proceed(fooObject, barObject);
    }
}

控制台日志:

call(void my.thirdparty.application.Foo.foo()) -> caller = execution(void my.thirdparty.application.Bar.bar())
java.lang.Exception: printing stack trace
    at my.thirdparty.application.Bar.foo_aroundBody3$advice(Bar.java:22)
    at my.thirdparty.application.Bar.bar(Bar.java:21)
    at my.thirdparty.application.Bar.doSomething(Bar.java:8)
    at de.scrum_master.app.Application.main(Application.java:7)

call(void my.thirdparty.application.Foo.foo()) -> caller = execution(void my.thirdparty.application.Bar.anotherMethod())
java.lang.Exception: printing stack trace
    at my.thirdparty.application.Bar.foo_aroundBody5$advice(Bar.java:22)
    at my.thirdparty.application.Bar.anotherMethod(Bar.java:28)
    at my.thirdparty.application.Bar.bar(Bar.java:23)
    at my.thirdparty.application.Bar.doSomething(Bar.java:8)
    at de.scrum_master.app.Application.main(Application.java:7)

我们正在变得越来越好:从6次减少到2次拦截。来自栏的一个。anotherMethod仍然是不需要的,因为它只是由条间接触发的。我们的目标是只拦截直接呼叫。好的,那么让我们更准确地说:

方面,第4部分:拦截对Foo的调用。fooBar直接制作。条形码,不允许间接寻址

package de.scrum_master.aspect;

import my.thirdparty.application.Foo;
import my.thirdparty.application.Bar;

public aspect MethodInterceptor {
    pointcut directCalls(Bar barObject) :
        call(* Foo.foo(..)) && cflow(execution(* Bar.bar(..))) && this(barObject) &&
        if("bar".equals(thisEnclosingJoinPointStaticPart.getSignature().getName()));

    Object around(Foo fooObject, Bar barObject) : directCalls(barObject) && target(fooObject) {
        System.out.println(thisJoinPointStaticPart + " -> caller = " + thisEnclosingJoinPointStaticPart);
        new Exception("printing stack trace").printStackTrace(System.out);
        System.out.println();
        return proceed(fooObject, barObject);
    }
}

控制台日志:

call(void my.thirdparty.application.Foo.foo()) -> caller = execution(void my.thirdparty.application.Bar.bar())
java.lang.Exception: printing stack trace
    at my.thirdparty.application.Bar.foo_aroundBody3$advice(Bar.java:22)
    at my.thirdparty.application.Bar.bar(Bar.java:21)
    at my.thirdparty.application.Bar.doSomething(Bar.java:8)
    at de.scrum_master.app.Application.main(Application.java:7)

等等,瞧!这是我们最初想要的。让我们重述一下我们刚才所做的工作,以便缩小切入点:

  • 调用(*Foo.foo(...))-只调用Foo.foo
  • cflow(执行(*Bar.bar(...)))-仅在控制流中执行Bar.bar
  • this(barObject)-调用者必须是Bar对象
  • 目标(foObject)-被调用者必须是Foo对象
  • if(bar. equals(thisEnCloSingJoinPointStatic Part.getSignature(). getName()))-一个动态运行时条件检查直接调用方的方法名是否真的是bar

我希望这能解决你的问题,不要太冗长。我想用教程的方式来做,以便让您了解如何解决像这样的高级AOP问题。享受

 类似资料:
  • 我想用Java编写一个注释,它在执行带注释的方法之前和之后执行一些东西,类似于Spring中的aspects。 我已经尝试过Spring方面,但它只适用于bean(正如这家伙在这里提到的),我想保持独立于Spring框架。 将字符串写入控制台的简单类: 相关注释: 而我需要像这样的东西(由Spring相位推断) 我想要以下输出: 你好 你好吗 世界 编辑: 我创建了以下方面(没有注释),但它不起作

  • 我在我的服务层有一个方法: [1]这一行获取条形码,比如然后调用另一个类中的一个方法,该方法从数据库中检索有关该项目的信息,执行计算并返回价格。可能的值可以类似于。 [2]按价格对所有值进行升序排序。因此,如果列表包含和和,则将其排序为和。 当我尝试测试此方法时,当调用时,我得到空指针异常。我如何测试这个方法?我试着搜索并找到了一些关于使用mockito的问题和答案,但我不知道如何实现它。 我目前

  • 问题内容: 在Bruce Eckel的“ Thinking In Java,第四版”的第428页(有关类型信息的章节)中,具有以下示例: 也许我有点累,但是我看不到add()方法中对add()的调用是如何工作的。我一直认为它应该有一个引用,或者是一个静态方法(并且我在ArrayList或List中找不到静态add())。我想念什么? 我只是为自己测试,发现这可行: 问题答案: Java为这样的方法

  • 我有一个层,称为,另一个称为。我想知道我是否可以强制执行一条规则,上面写着: 在参数列表包含名为foo的参数的服务层中的任何公共类的任何公共方法中,断言它从权限层调用方法(理想情况下,确保在服务层中的任何其他方法之前调用该方法)。 ArchUnit可以这样做吗?

  • 问题内容: 我是python的新手。我试图在类中将值从一种方法传递给另一种方法。我搜索了该问题,但无法获得适当的解决方案。因为在我的代码中,“ if”正在调用类的方法“ on_any_event”,而该方法反过来应该调用我的另一个方法“ dropbox_fn”,该方法利用了“ on_any_event”中的值。如果“dropbox_fn”方法在类之外,它将起作用吗? 我将用代码说明。 这里的主要问

  • 问题内容: 我是Java的初学者,并且一直在研究各种解决方案来解决这个问题,并且使自己陷入困境。我尝试过使用Threads,然后发现了这个Timer类,到目前为止,它一直没有成功。如果您可以 使用main方法 发布 可执行代码, 这样我就可以看到它正常工作并从那里开始玩,那将很棒。 启动程序 呼叫 生成随机数并设置Timer相应的时间。 定时器关闭时,再次通话。 可能使用的是:http : //d