【问题标题】:Why do I need a public method to make my annotation work?为什么我需要一个公共方法来使我的注释工作?
【发布时间】:2015-03-12 19:43:56
【问题描述】:

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

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

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

【问题讨论】:

    标签: java spring annotations aop


    【解决方案1】:

    Spring AOP 是一个“AOP lite”框架,依赖于

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

    由接口定义的方法根据定义是公共的,因此 JDK 动态代理也只能代理公共方法。

    CGLIB 动态代理通过子类化现有类来工作,将代理放入与基类相同的包中。所以它可以访问公共的、受保护的和包保护的方法,但不能访问私有的。无论如何,Spring 尝试统一处理这两种变体,将 AOP 限制为仅限公共的、非静态方法。

    好消息是 Spring 有一个非常好的原生 AspectJ 集成,通常通过 LTW(加载时编织)激活,请参阅手册部分 9.8 Using AspectJ with Spring applications。但是CTW(编译时编织)也是可能的。查看 AspectJ 手册以获取更多信息。使用完整的 AspectJ,您可以拦截非公共方法、构造函数、成员读/写访问等。虽然 Spring AOP 仅适用于 Spring Beans/Components,但 AspectJ 适用于任何类并且不需要任何代理,从而提高了效率。所以你有很多选择,可以两全其美。

    【讨论】:

      【解决方案2】:

      因为 proxy-based nature of Spring AOP。但是你可以使用Instrumentation。从OOD来看,使用注释是错误的,它们应该是或包含元数据,它们不应该用作标记。

      【讨论】:

      • 从 OOP 的角度来看,您也可以说 APO 是错误的。基于注释的 AOP 是定义方面的唯一合理方式。当您看到 @Transactional 时,它会清楚它代表什么。也很容易找到要检测的所有类。不像@Before("com.myapp.System.operation() && args(account,..)")。 Spring 不能很好地处理许多切入点。加载时间和内存消耗严重受损(spring 尝试所有 bean 上的所有 pointcat,因此您获得 n^2 复杂度)。所以一个注解有一个切入点和许多被注解的类比有很多切入点要好。
      • @PeterRader 排名?
      • @woru 你误会了,AOP 在 OOD 和 OOP 中都不是错的。但是 OOD/OOP 中的仅标记注释是错误的! @Transactional 具有元数据,并且不是标记注释!但是@LogExecutionTime 是一件坏事,它没有 OO 数量,因为如果您删除该注释,OOD 既不会变成坏也不会变成好。嘿,注释及其切入点到底是什么?它记录执行时间并提供有关性能泄漏的想法,它可能有助于决定是否使用线程,因此它更像是一个偶尔的工作。
      【解决方案3】:

      原因是 spring 使用优化来检查哪些类将被建议。

      当一个 bean 被实例化时,AopUtils.canApply 检查是否可以将任何切入点应用于 bean 类的任何 public 方法。

      因此,当您的类中没有使用 @LogExecutionTime 注释的公共方法时,spring 不会对其进行检测。

      在您的情况下,当有一个空的公共方法时,会检测类,并且由于您的类没有实现任何接口并且无法使用 jdk 代理,因此您的受保护方法被意外检测了。

      【讨论】:

        猜你喜欢
        • 2015-04-30
        • 2022-01-06
        • 2015-04-27
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-08-09
        • 2020-10-13
        相关资源
        最近更新 更多