我写了一个简单的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"/>
注意,编织类必须位于服务器库路径中,而不是您的应用程序路径中。