如何使用spring aop记录方法链接 [英] how to do logging of method chaining using spring aop

查看:154
本文介绍了如何使用spring aop记录方法链接的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用带有Spring AOP的slf4j进行日志记录和异常目的。在某些类中有一些方法形成了一个方法链接。我可以在第一个方法的入口和出口点登录,但是当这个方法调用另一个方法时,AOP只记录第一个方法的入口和出口点。我想记录每个方法的入口和退出点,使用 @这里的注释是伪代码来解释我想要的东西

I am using slf4j with Spring AOP for logging and Exception purpose.there are some methods in some classes which formed a method chaining. I am able to log at first method's entry and exit point but when this method called another method then AOP is logging only first method's entry and exit point.I want to log every method's entry and and exit point using @Around annotation here is Pseudo code to explain what i want

package com.sample;
public class Test implements T{

@Override
 public void show() {
   System.out.println("Test.show()");
   test();
  }
  void Test(){
   //Want to log entry and exit point of this method whenever this method called by any other method
   //The method may belongs to same class or different package's different class
  }

spring.xml 是这样的

<bean id="exceptionAspect" class="com.sample.ExceptionAspect"/>
<bean id="test" class="com.sample.Test"/>

我的建议类看起来像

@Aspect
public class LoggingAspect {
@Around(value="execution (* com.sample.*.*(..))||"+
              "execution(* some other package.*.*(..))")
public void logAround(ProceedingJoinPoint joinPoint) throws Throwable {
      final Logger logger = LoggerFactory.getLogger(joinPoint.getTarget().getClass());
      logger.info("Execution of : " + joinPoint.getSignature() + " Started");
      joinPoint.proceed();
      logger.info("Execution of : " + joinPoint.getSignature() + " completed");
  }

}

客户等级

package com.test;
public class App {
   public static void main(String[] args) throws Exception {

    ApplicationContext appContext = new ClassPathXmlApplicationContext(
                "classpath:/META-INF/spring.xml");

        T test=(T) appContext.getBean("test");
        test.show();
    }

非常感谢任何帮助..

Any Help is greatly appreciated..

推荐答案

Spring AOP 是不可能做到的(至少没有重构的建议的方法)。原因是Spring AOP是基于代理的,这意味着它为每个bean创建一个代理类并注入它而不是你的实现。代理具有bean的所有方法,并添加了方面功能。因此,当您调用bean的方法(实际上是bean的代理)时,将执行方面代码,然后通过委托调用您的方法。因此,当您的方法调用其他方法时,调用是使用真实bean执行的,而不是那些代理 - 如果存在的话 - 因此您无法得到您期望的输出。

What you are trying to do is not possible with Spring AOP (at least not without some refactoring of the advised methods). The reason for that is that Spring AOP is proxy based, which means for each bean it creates a proxy class and injects it instead of your implementation. A proxy has all the methods of the bean with added aspect functionality. So When you call a method of a bean (actually the proxy of the bean), the aspect code is executed and your method is then called by delegation. So when your method calls other methods the call are performed using the real beans, not the proxies of those - if any are present - hence you don't get the output you are expecting.

你可以想到它看起来像这样的代理:

You can think of a proxy it looks somehow like that:

class MyBeanProxy implements MyBean {

    MyBeanImpl theBean;

    public void foo() {
        // aspect code
        theBean.foo();
    }
    public void bar() {
        // aspect code
        theBean.bar();
    }
}

你的bean在哪里

interface MyBean {
    foo();
    bar();
}

@Component("my_bean")
class MyBeanImpl implements MyBean {
    public void foo() {
        System.out.println("foo");
        bar();
    }
    public void bar() {
        System.out.println("bar");
    }
}

在上面的示例中,当您调用<$ c时$ c> foo()然后通过代理执行方面代码,并发生 MyBeanImpl#foo()的委托,其中<$正在调用c $ c> bar()。现在很明显, bar()的方面代码将不会被执行。

In the example above, when you call foo() via the proxy then the aspect code is executed, and the delegation to MyBeanImpl#foo() happens, where bar() is being called. Now it becomes obvious that the aspect code for bar() will not be executed.

现在你怎么做它有效吗?

Now how can you make it work?

1 - 以这样的方式重构你的代码:对于你想要为它们执行方面代码的方法,调用会发生在代理对象上而不是bean本身。为此,您可以获得实际的代理并使用它来调用您的方法。

1 - Refactor your code in such a way that for methods you want to have the aspect code executed for them the calls happen on the proxy object not on the bean itself. For that you can get the actual proxy and use it to call your methods.

public void foo() {
    System.out.println("foo");
    MyBean myBeanProxy = (MyBean) AopContext.currentProxy();
    myBeanProxy.bar();
}

注意这种方法更像是黑客攻击比干净的方式做这项工作。例如,显然 myBeanProxy 不知道当前对象的状态。

Note that this method is more of a hack than a clean way to do the job. For example it is obvious that myBeanProxy has no clue of the state of your current object.

2 - 重构代码,使 bar()在另一个bean中,您可以使用 appContext

2 - Refactor you code in such a way that bar() is in another bean which you can retrieve using your appContext.

3 - 使用 AspectJ :Aspect代码被注入目标类本身(真实的东西!)

3- Use AspectJ: Aspect code is injected into the target classes themselves (The real thing!)

以下是使用AspectJ的小例子

Here is small example using AspectJ

Aspect

package com.aj;

import java.util.Arrays;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;

@Aspect
public class MyAspect {

    @Around("execution( * com.app.services.*.* (..) )")
    public Object callDurationAdvice(ProceedingJoinPoint pjp) throws Throwable {
        Signature signature = pjp.getSignature();
        Object[] args = pjp.getArgs();
        String argList = Arrays.toString(args);
        System.out.println(signature.getDeclaringTypeName() +
                "." + signature.getName() + "(" + argList + ") started");
        long s = System.nanoTime();
        Object proceed = pjp.proceed(args);
        long e = System.nanoTime();
        System.out.println(signature.getDeclaringTypeName() +
                "." + signature.getName() + "(" + argList + ") ended after " +
                ((double)(e-s)/1000000) + " ms");
        return proceed;
    }
}

某个包中的一个类应该成为目标方面

One class in some package witch should be target for the aspect

package com.app.services;

public class ServicesVersionInfo {

    public static String getVersion() {
        return getVersionNumber() + " " + getVersionStage();
    }

    public static String getVersionNumber() {
        return "1.0.0";
    }

    public static String getVersionStage() {
        return "ALPHA";
    }
}

应用

package com.app;

import com.app.services.ServicesVersionInfo;


public class App {

    public static void main(String[] args) {
        System.out.println("App services version: " + 
            ServicesVersionInfo.getVersion());
    }
}

冉,这应该输出一些谎言

Ran, this should output something lie that

com.app.services.ServicesVersionInfo.getVersion([]) started
com.app.services.ServicesVersionInfo.getVersionNumber([]) started
com.app.services.ServicesVersionInfo.getVersionNumber([]) ended after 0.004862 ms
com.app.services.ServicesVersionInfo.getVersionStage([]) started
com.app.services.ServicesVersionInfo.getVersionStage([]) ended after 0.005673 ms
com.app.services.ServicesVersionInfo.getVersion([]) ended after 0.378877 ms
App services version: 1.0.0 ALPHA

最后,这里有一些类似的问题和进一步的读物:

Finally here are some similar questions and further readings:

Spring AOP无法在其他方法中进行方法调用

从对象本身获取AOP代理

Spring AOP热门问题#1 - 方面未应用

这篇关于如何使用spring aop记录方法链接的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆