为什么我需要一个公共方法来使我的注释工作?

时间:2015-01-14 15:08:46

标签: java spring annotations aop

简而言之,我的问题是如果带注释的方法不公开,我的注释会被忽略,但是如果同一个类中的另一个方法被注释,则会被识别。

我正在尝试编写注释以记录方法的执行时间,如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.

有人可以解释为什么会这样吗?

3 个答案:

答案 0 :(得分:1)

Spring AOP是一个依赖于

的“AOP lite”框架
  • JDK动态代理(适用于实现一个或多个接口的所有类)或
  • CGLIB动态代理(也适用于未实现任何接口的类)。

接口定义的方法定义为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代理,因此您的受保护方法会被意外检测。