【问题标题】:Spring AOP: logging and nested-methodsSpring AOP:日志记录和嵌套方法
【发布时间】:2011-08-12 10:51:09
【问题描述】:

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

(注意:我正在使用注释控制器;我省略了与 aop 不直接相关的细节,因为我的基本设置工作正常——我只包括与 aop 相关的细节——如果您需要查看更多信息,请告诉我)


applicationContext.xml:

(...)
<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:13)

applicationContext.xml:

(...)
<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 的“运行”选项卡下的“项目属性”中,我将此行添加到“VM 选项”

-javaagent:C:\Users\bgresham\Documents\libraries\spring-framework-2.5\dist\weaving\spring-agent.jar

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

???

【问题讨论】:

    标签: spring logging aop


    【解决方案1】:

    如果您使用 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 文档中提到了另一个严重的技巧,但这涉及大量的耦合,所以我什至不建议查看。以上虽然相对整洁,但绝对有效;我已经部署好了。
    • @Donal:请原谅我,我是 Spring 新手。 :) 但是这个例子在我的环境中不太适用。我收到以下错误: 原因:java.lang.IllegalStateException: ContainerBase.addChild: start: LifecycleException: org.springframework.beans.factory.BeanInitializationException: bean 'homeController' 需要属性'self'
    • 我了解传统路线是在 xml 中配置您的 bean 并提供 -element;但我几乎只使用自动连接和注释,所以 HomeController 不会出现在我的 xml 中(我更愿意保持这种方式)。有没有办法通过注释获得对自我的引用?或者也许在 HomeController#constructor() 中从 spring 的上下文中获取自身?
    • @Bane:我实际上在我的应用程序中采取了另一种方式,并明确列出了所有内容。这让我的工具可以为我提供应用程序架构的简洁可视化,这对报告很有用。 :-) 据我所知,用 @Component 注释 bean 和用 @Autowired 注释属性设置器就足够了,但我强调我还没有尝试过。跨度>
    • @Donal:那没用。 :( 不过,无论如何,我可能会走你的路——我只有 3 个控制器,所以没什么大不了的——如果我回去这样做,我一定会在此处记录整个解决方案下一个人。非常感谢你的帮助!
    【解决方案2】:

    您正在使用 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>
    

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

    spring配置中的配置:

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

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

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

    【讨论】:

    • 首先,感谢您的回复! :) 但是,当我尝试添加该额外标签时,我注意到 NB 并未将其显示为有效选项。无论如何添加它(并重新部署)会给我这个错误:ADM5603:Event listener error [ContainerBase.addChild: start: LifecycleException: org.xml.sax.SAXParseException: cvc-complex-type.3.2.2: Attribute 'mode' is not允许出现在元素'aop:aspectj-autoproxy'中。]
    • 所以我删除了它,但根据我发现的一些在线指南添加了对 aspectJ 加载时编织的支持。但我仍然没有得到我正在寻找的嵌套日志记录。请参阅我对上面原始帖子的详细编辑。
    • @Bane 如果您在像 tomcat 这样的服务器下运行它,那么 LTW 选项将使用 tomcat 而不是 JVM 设置。 spring 代理 jvm 选项应该与 SE 代码一起使用。
    • @Turbochrgd:感谢您的反馈!我们在 glassfish 下奔跑,所以感谢您为我提出这一点。就目前而言,我们的环境非常严格——我不确定我是否会继续走这条路,因为它需要修改服务器的环境。不过,对于未来的新人来说,这是很好的信息,所以谢谢! :)
    • @Bane 在这种情况下,您应该考虑在部署应用程序之前使用编译时编织
    猜你喜欢
    • 1970-01-01
    • 2018-04-13
    • 2012-11-04
    • 2012-09-30
    • 1970-01-01
    • 1970-01-01
    • 2015-06-09
    • 1970-01-01
    • 2012-09-12
    相关资源
    最近更新 更多