Spring AOP:日志记录和嵌套方法

Ban*_*ane 6 logging aop spring

我编写了一个简单的Spring2.5应用来演示/测试AOP; 具体来说,我想记录特定包中每个类的每个方法的进入和退出.这就是我所拥有的......

(注意:我正在使用注释控制器;我省略了与aop没有直接关系的细节,因为我的基本设置工作正常 - 我只包含与aop相关的详细信息 - 如果你需要了解更多,请告诉我)


applicationContext.xml:

(...)
<bean id="loggerInterceptor" class="aspect.LoggerInterceptor" />
(...)
Run Code Online (Sandbox Code Playgroud)

dispatcher-servlet.xml:

(...)
<aop:aspectj-autoproxy proxy-target-class="true" />
(...)
Run Code Online (Sandbox Code Playgroud)

HomeController.java:

public class HomeController() {

    public HomeController() { }

    public ModelAndView get() {
        System.out.println("In HomeController#get()...");

        this.somePrivateMethod();
        this.somePublicMethod();

        return new ModelAndView( "home" );
    }

    private void somePrivateMethod() {
        System.out.println("In HomeController#somePrivateMethod()...");
    }

    public void somePublicMethod() {
        System.out.println("In HomeController#somePublicMethod()...");
    }
}
Run Code Online (Sandbox Code Playgroud)

LoggerInterceptor.java:

public class LoggerInterceptor {

    @Pointcut("execution(* controller.*.*(..))")
    private void anyOperationInControllerPackage() {
        /* nothing to do here;
         * this just defines that we want to catch all methods
         * in the controller-package
         */
    }

    @Around("anyOperationInControllerPackage()")
    public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {

        System.out.println("Entering " + joinPoint.getSignature().getDeclaringTypeName() + "#" + joinPoint.getSignature().getName() + "() using arguments: " + Arrays.toString( joinPoint.getArgs() ) );

        try {

            Object result = joinPoint.proceed();

            System.out.println("Leaving " + joinPoint.getSignature().getDeclaringTypeName() + "#" + joinPoint.getSignature().getName() + "()." );

            return result;

        } catch (Throwable ex) {

            ex.printStackTrace();
            throw ex;

        }

    }

}
Run Code Online (Sandbox Code Playgroud)

这是我在调用HomeController#get()时得到的:

Entering controller.HomeController#get() using arguments: []
In HomeController#get()...
In HomeController#somePrivateMethod()...
In HomeController#somePublicMethod()...
Leaving controller.HomeController#get().
Run Code Online (Sandbox Code Playgroud)

如您所见,被拦截的唯一方法是HomeController#get().当#get()调用#somePrivateMethod()或#somePublicMethod()时,拦截器不会捕获它们.我希望,至少#somePublicMethod()也会被捕获(因为我正在使用cglib,我也希望#somePrivateMethod()会被捕获).

所以我想我的问题是我需要更改/添加什么才能允许(至少)控制器包中的所有公共方法被捕获,即使该包中的另一个方法调用它们并且本身首先被捕获? ??

我希望这是有道理的.:d


编辑(25APR2011 @ 1:13 PM)

applicationContext.xml:

(...)
<context:load-time-weaver />  <!-- added -->
<bean id="loggerInterceptor"... />
(...)
Run Code Online (Sandbox Code Playgroud)

aop.xml:

<!DOCTYPE aspectj PUBLIC
    "-//AspectJ//DTD//EN" "http://www.eclipse.org/aspectj/dtd/aspectj.dtd">
<aspectj>
    <weaver>
        <!-- only weave classes in this package -->
        <include within="controller.*" />
    </weaver>
    <aspects>
        <!-- use only this aspect for weaving -->
        <aspect name="aspect.LoggerInterceptor" />
    </aspects>
</aspectj>
Run Code Online (Sandbox Code Playgroud)

在Netbean的"运行"选项卡下的"项目属性"中,我将此行添加到"VM选项":

-javaagent:C:\Users\bgresham\Documents\libraries\spring-framework-2.5\dist\weaving\spring-agent.jar
Run Code Online (Sandbox Code Playgroud)

和以前一样,我没有收到任何错误 - 我只是没有得到"嵌套" - 我正在寻找的记录.

???

Sat*_*was 5

您正在使用 spring aop 来支持方面。Spring aop 仅适用于 spring beans。因此,切入点不适用于实际的类实例,即当控制器调用其任何公共或私有方法时。为了记录控制器中的所有方法,您需要通过启用您想要拦截的所有类的加载时或编译时编织来使用 AspectJ 来支持 aop。编辑:

您需要以下内容来进行加载时编织:

aop.xml

<!DOCTYPE aspectj PUBLIC "-//AspectJ//DTD//EN" "http://www.eclipse.org/aspectj/dtd/aspectj.dtd">    
<aspectj>    
    <weaver options="-Xset:weaveJavaxPackages=true -verbose -showWeaveInfo -debug">    
        <include within="*"/>
    </weaver>
    <aspects>
        <!-- weave in just this aspect -->
        <aspect name="your.logger.impl.LoggingImpl"/>
    </aspects>
  </aspectj>
Run Code Online (Sandbox Code Playgroud)

这意味着使用指定的方面编织所有文件('within=*',根据需要修改)。在加载时,您应该看到有关类编织的详细信息。

弹簧配置中的配置:

<context:load-time-weaver aspectj-weaving="autodetect" 

            weaver-class="org.springframework.instrument.classloading.ReflectiveLoadTimeWeaver"/>
Run Code Online (Sandbox Code Playgroud)

请注意,编织类必须位于服务器库路径中,而不是您的应用程序路径中。

上述配置应该可以满足您的要求。


Don*_*ows 5

如果你正在使用Spring AOP,你必须只通过Spring返回的引用调用一个已经应用了方面的方法,而不是通过this,并且我不认为你可以将切入点应用于私有方法(可能是错误的最后一部分).那是因为Spring AOP通过代理对象应用切入点,而不是通过类重写(这是AspectJ所做的).这个重要限制的好处是,它更容易使它在容器中工作(我从经验中知道Spring AOP在Tomcat中运行得很好),因为没有争论插入的位置在哪里.

这样做的最好方法是拆分类定义,以便永远不会调用方法this,但如果不可能,那么你总是可以尝试给bean一个Spring派生的引用:

private HomeController self;
@Required
public void setSelf(HomeController self) { this.self = self; }

public ModelAndView get() {
    System.out.println("In HomeController#get()...");

    self.somePrivateMethod();
    self.somePublicMethod();

    return new ModelAndView( "home" );
}
Run Code Online (Sandbox Code Playgroud)

(这非常简洁; self是许多语言中的关键字,但不是Java,所以记住你正在使用它的相对容易.)