简而言之,我的问题是如果带注释的方法不公开,我的注释会被忽略,但是如果同一个类中的另一个方法被注释,则会被识别。
我正在尝试编写注释以记录方法的执行时间,如this answer中所述。
这是我的注释:
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface LogExecutionTime
{
}
我的方面:
@Component
@Aspect
public class LogTimeAspect
{
@Around(value = "@annotation(annotation)")
public Object logExecutionTime(final ProceedingJoinPoint joinPoint, final LogExecutionTime annotation) throws Throwable
{
final long startingTime = System.currentTimeMillis();
try
{
System.out.println("Starting timed method");
final Object retval = joinPoint.proceed();
return retval;
}
finally
{
System.out.println("Finished. Timed method " + joinPoint.toShortString() + " took: " + (System.currentTimeMillis() - startingTime) + "ms.");
}
}
}
我使用注释的课程:
@Component("classToBeAnnotated")
public class ClassToBeAnnotated {
@LogExecutionTime
public void operate() throws InterruptedException {
System.out.println("Performing operation");
Thread.sleep(1000);
}
}
测试类:
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "/DefaultApplicationContext.xml" })
public class TestLauncher
{
@Autowired
private ClassToBeAnnotated classToBeAnnotated;
@Test
public void test() throws InterruptedException
{
classToBeAnnotated.operate();
}
}
如果我运行上面显示的代码,我会
Starting timed method
Performing operation
Finished. Timed method execution(operate) took: 1025ms.
到目前为止一切顺利。但是如果我使用注释
从方法中删除public
@LogExecutionTime
void operate() throws InterruptedException
注释被忽略,我得到:
Performing operation
没有错误,没有警告,只是没有运行。但最让我感到震惊的是,如果我将另一个方法添加到同一个类中,并将其公开并注释它,我会得到与初始条件相同的输出,即使这个额外的方法除了具有相同的注释之外,不会以任何方式调用或与原始版本相关。
@Component("classToBeAnnotated")
public class ClassToBeAnnotated {
@LogExecutionTime
public void someOtherMethod()
{
}
@LogExecutionTime
void operate() throws InterruptedException {
System.out.println("Performing operation");
Thread.sleep(1000);
}
}
输出:
Starting timed method
Performing operation
Finished. Timed method execution(operate) took: 1029ms.
有人可以解释为什么会这样吗?
答案 0 :(得分:1)
Spring AOP是一个依赖于
的“AOP lite”框架接口定义的方法定义为public,因此JDK动态代理也只能代理公共方法。
CGLIB动态代理通过子类化现有类来工作,将代理放入与基类相同的包中。因此,它可以访问公共,受保护和受包保护的方法,但不能访问私有方法。无论如何,Spring试图统一处理两个变体,仅将AOP限制为公共的非静态方法。
好消息是Spring有一个非常好的原生AspectJ集成,通常通过LTW(加载时编织)激活,参见手册部分9.8 Using AspectJ with Spring applications。但CTW(编译时编织)也是可能的。查看AspectJ手册以获取更多信息。使用完整的AspectJ,您可以拦截非公共方法,构造函数,成员读/写访问等。虽然Spring AOP仅适用于Spring Beans / Components,但AspectJ适用于任何类,并且不需要任何代理,从而提高了效率。所以你有很多选择,可以使用两全其美。
答案 1 :(得分:0)
由于 proxy-based nature of Spring AOP。但您可以使用Instrumentation。从OOD到使用注释的错误,它们应该是或包含元数据,它们不应该用作标记。
答案 2 :(得分:0)
原因是spring使用优化来检查将建议哪些类。
当实例化bean时,AopUtils.canApply会检查是否可以将任何切入点应用于bean类的任何 public 方法。
因此,当你的班级中没有使用@LogExecutionTime注释的公共方法时,spring将无法检测它。
在你的情况下,如果有一个空的公共方法,则会对类进行检测,并且由于您的类没有实现任何接口,并且无法使用jdk代理,因此您的受保护方法会被意外检测。