首页 > 解决方案 > Java Spring Boot 应用程序中“私有”辅助方法的替代方案

问题描述

我有一个具体的,我们可以调用ServiceImpl谁实现了一个名为Service. 我正在使用 Spring AOP/Aspect-J 来记录方法执行时间,目前该方法在ServiceImpl该类中的公共方法上工作得很好(知道代理如何工作是合乎逻辑的)。我想立即说明的一件事是,我正在使用swagger-codegen-maven-plugin插件,它基本上在编译时为您生成代码,就我而言,它正在生成一些我正在使用的自定义数据类型我的公共输入方法的返回类型ServiceImpl它调用了我无法使用 SpringAOP 记录/跟踪的嵌套私有方法(为什么我要创建这个线程)。另外,我正在使用具有以下结构的多模块 maven 项目:

现在,在你告诉我 Spring AOP 不能在没有在 Spring AOP 中启用加载时间编织的情况下拦截嵌套私有方法之前,我在尝试自我调用并调用该引用上的嵌套私有方法(类型为)。我每次都遇到空指针异常(NPE),因为无论我是自动装配bean 还是从ApplicationContext (ie )获取它,引用 ( )似乎都是空的selfServiceImpl@Autowiredprivate ServiceImpl selfapplicationContext.get(ServiceImpl.class)

但是,我的问题只是私有方法是否有任何替代方法;是否可以对我的逻辑进行一些更改,以最简洁的方式解决这个问题,并最终成功地在我的 Spring Boot 应用程序中记录我的公共和嵌套私有方法的响应/执行时间。

下面是我的代码是如何定义的。

@Service
public class ServiceImpl implements Service {

@MySpringAOPAnnotationThatWorks
public SwaggerGeneratedDataType1 myPublicMethod(SwaggerGeneratedDataType2 myMethodArgument) {

    // ...

    // I WANT TO TRACK THESE NESTED PRIVATE METHOD EXECUTION TIMES 
    myNestedPrivateMethod1(myMethodArgument.getSomeAttribute1()); // NULL POINTER EXCEPTION when I tried using Self Invocation and using "self" instance to call method on
   
    // ...

    // I WANT TO TRACK THESE NESTED PRIVATE METHOD EXECUTION TIMES 
    myNestedPrivateMethod2(myMethodArgument.getSomeAttribute2());
}


private void myNestedPrivateMethod1(String myNestedMethodArgument1) {
    // some logic here
}


private void myNestedPrivateMethod2(String myNestedMethodArgument2) {
    // some logic here
}

简单记录嵌套私有方法的执行时间的最简单方法是什么?

标签: javaspring-bootaspectjspring-aopintercept

解决方案


为什么它不能按您期望的方式工作

在您创建这个问题之前,您已经在这里评论了我的答案,即您已经知道 Spring 代理在内部是如何工作的:

  • 代理是目标对象类的子类,实现与该类相同的接口(如果有)。
  • 代理在内部持有对目标对象的引用,并将截获的方法调用委托给它(除非方面决定不调用原始方法)。

无论如何,与动态代理完全无关的是,在 Java 中,private方法只能由原始声明类调用,

  • 不是通过子类(为此我们使用protected),
  • 不是同一个包中的其他类(因为我们使用包保护),
  • 不是通过其他包中不相关的类(为此我们使用public)。

现在,由于代理既是子类实例又是外部对象,您会看到不可能在代理上调用父类的私有方法,即即使您将代理自动连接到目标类,然后尝试调用PROXY.privateMethod(),不管有没有 Spring AOP,它都不起作用。这只是一个 JVM 限制。私人是私人是私人的。

你可以做什么

1.使用原生AspectJ

如果您使用本机 AspectJ,则不再有代理。该代码直接编织到原始字节码中。如果操作正确,您可以拦截私有方法。

但是为什么要测量私有方法运行时呢?您应该专注于衡量公共的,因为从用户的角度来看,这些才是重要的。对于内部优化,请使用分析器。

2. 使私有方法(包)受保护

如果您至少创建目标方法protected,它们可以被子类调用,即也可以被动态代理调用。但为了做对,你需要

  • @Autowire MyComponent INSTANCE进入目标班级,
  • 将您以前私有的、现在受保护的方法调用从 simpleinternalMethod()this.internalMethod()to 更改INSTANCE.internalMethod()为以使调用能够被 Spring AOP 方面拦截,
  • 确保使用 CGLIB 代理而不是 JDK 动态代理。Spring Boot 默认情况下会这样做,即使对于作为接口而不是实现类的 bean 也是如此。然而,Spring Core 默认为使用appContext.getBean(MyInterface.class). 因此,在这种情况下,您需要激活目标类自动代理。原因是设计上的接口只有公共方法,即受保护的方法不是由代理实现的,因此不能调用它。

这将起作用,并且使方法受到保护也记录了它们打算由子类使用。然而,将代理实例自动连接到目标类是丑陋的 IMO,因为它使目标类能够感知 AOP。实际上,方面是横切关注点,目标类应该不知道它们,并且无需任何额外的体操即可工作。

我的建议与 Spring 手册的建议相同:如果您绝对认为需要对方面的自调用支持,请使用本机 AspectJ。

3. 将私有方法分解为额外组件的公共方法

如果您绝对想坚持使用 Spring AOP 并希望避免将代理自动连接到目标类中,最干净的解决方案是创建一个单独的 Spring bean/组件,将以前的私有方法公开为公共方法,连接该助手组件到您的原始目标类中,并从组件 A 调用方法到 B。然后 Spring AOP 方面就可以工作了。如果像这样公开以前的私有方法是有意义的,那是一个只能根据具体情况做出的设计决策。如果您之前有充分的理由从 API 中隐藏这些方法,那么答案可能是否定的。否则,它可能是肯定的。如果新提取的组件也可以被其他类使用,那么使您的组件更细粒度甚至可能是有益的。


推荐阅读