Spring AOP:日志和嵌套方法



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

(注意:我正在使用annotation-controllers;我省略了与aop不直接相关的细节,因为我的基本设置工作得很好——我只包括aop相关的细节——如果你需要看到更多,请告诉我)


中:

(...)
<bean id="loggerInterceptor" class="aspect.LoggerInterceptor" />
(...)

dispatcher-servlet.xml:

(...)
<aop:aspectj-autoproxy proxy-target-class="true" />
(...)

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()...");
    }
}

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;
        }
    }
}

下面是调用HomeController#get()时得到的结果:

Entering controller.HomeController#get() using arguments: []
In HomeController#get()...
In HomeController#somePrivateMethod()...
In HomeController#somePublicMethod()...
Leaving controller.HomeController#get().

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

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

我希望这有意义。: D


编辑(2011年4月25日@ 1:13PM)

中:

(...)
<context:load-time-weaver />  <!-- added -->
<bean id="loggerInterceptor"... />
(...)

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>

在Netbean的"Project Properties"下的"Run"选项卡下,我将这一行添加到"VM Options":

-javaagent:C:UsersbgreshamDocumentslibrariesspring-framework-2.5distweavingspring-agent.jar

和以前一样,我没有得到任何错误——我只是没有得到我正在寻找的"嵌套"日志记录。

? ?

如果您正在使用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" );
}

(这很简洁;self是一个关键字在许多语言,但不是Java,所以它是相对容易记住你使用它的目的。)

您正在使用spring aop来支持方面。Spring aop将只在Spring bean上工作。因此,切入点在实际的类实例上不起作用,即当控制器调用其任何公共或私有方法时。为了记录控制器中的所有方法,您需要使用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>

这意味着用指定的aspect/s编织所有文件('within=*',可以随意修改)。在加载时,您应该看到有关类编织的详细信息。

弹簧配置中的配置:

<context:load-time-weaver aspectj-weaving="autodetect" 
            weaver-class="org.springframework.instrument.classloading.ReflectiveLoadTimeWeaver"/>

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

最新更新