java - 在另一个方法中调用一个方法后执行一些操作

标签 java spring aop aspectj

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

我尝试在使用spring的面向方面的编程中使用@before批注进行此操作。 However, I could not do that

有人可以让我知道从jar文件中调用特定功能后如何执行特定操作(调用特定功能)吗?

最佳答案

正如Gervasio Amy所建议的那样,您需要使用AspectJ,而不是Spring AOP。如果您在Spring环境中,可以使用use AspectJ within Spring代替Spring AOP,这没有问题。如果尚未使用Spring,则不是开始使用AOP的理由,AspectJ可以在没有Spring的简单Java SE(或EE)版本中使用。

您需要做的是:


使用AspectJ编译器ajc编译Aspect代码。 (您还可以使用它编译整个应用程序,因为它也是Java编译器javac的替代。)
创建一个加载时编织配置aop.xml,以使您的应用程序可以在类加载期间将方面代码实时地编织到第三方库中。我要由您自己决定如何执行此操作,只需检查LTW documentation
通过-javaagent:/path/to/aspectjweaver.jar开关在命令行上使用AspectJ编织代理启动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();
    }
}


如您所见,Application.main创建一个Bar对象并调用公共方法Bar.doSomething。该方法触发了一系列其他方法调用,其中一些最终以Foo.foo间接调用,但是从Bar.barFoo.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的控制流(Bar.bar)中进行的那些调用呢?

方面,第2部分:拦截由Foo.foo直接(在内部)对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.zotBar.anotherMethod这样的调用者又会如何发生呢?将控制流限制为Bar.bar?答案很简单:这两个方法也被Bar.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直接(内)进行的Bar.bar调用,但肯定是来自Bar对象

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截获。Bar.anotherMethod仍然是不需要的,因为它只是由Bar.bar间接触发的,我们的目标是仅截获直接呼叫。好吧,接下来让我们变得更加精确:

方面,第4部分:拦截由Foo.foo直接进行的对Bar.bar的调用,不允许间接访问

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)


等等!这是我们首先要的。让我们概括一下我们为缩小切入点所做的工作:


call(* Foo.foo(..))-仅呼叫Foo.foo
cflow(execution(* Bar.bar(..)))-仅在控制流中执行Bar.bar
this(barObject)-调用者必须是Bar对象
target(fooObject)-被调用方必须是Foo对象
if("bar".equals(thisEnclosingJoinPointStaticPart.getSignature().getName()))-动态运行时条件检查直接调用者的方法名称是否真的bar


我希望这可以解决您的问题,并且不要太冗长。我想按照教程的方式进行操作,以便使您了解如何解决像这样的高级AOP问题。请享用!

关于java - 在另一个方法中调用一个方法后执行一些操作,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33909874/

相关文章:

vb.net - Ninject:如何使用约定绑定(bind)的拦截

caSTLe-windsor - Windsor容器中的CaSTLe动态代理

java - 是否必须在aspectJ中实现具体方面

java - 如何结合 artemis-odb + Glenn Fiedler 游戏循环

java - Spring、Hibernate 和 .xml 错误

java - 正则表达式 : who's greedier?

java - Spring Boot 桌面独立应用程序中的 Apache Shiro

java - 为什么 StringBuilder 会抛出 NullPointerException

spring - 使用logback记录spring JdbcTemplate的sql参数

Spring-JMS(Websphere MQ)