【问题标题】:Inconsistent build results when using AspectJ Load Time Weaving in a spring boot application在 Spring Boot 应用程序中使用 AspectJ Load Time Weaving 时构建结果不一致
【发布时间】:2020-01-07 11:53:38
【问题描述】:

目前我正在使用 AspectJ 加载时间编织来拦截基础实体的构造函数以进行审计。然而,在运行应用程序时,我得到了令人难以置信的不一致结果,这些结果围绕 aspectJ 编织到 LTW 类中的 aspectOf() 方法。

在某些情况下,应用程序运行,编织正确完成,代码按预期运行。其他时候我会遇到:

java.lang.NoSuchMethodException: ca.gc.cfp.core.cfpws.repository.aspect.BaseEntityAspect.aspectOf() 

目前我正在使用 https://github.com/subes/invesdwin-instrument 将检测代理动态附加到 JVM,因此我们的部署人员不需要进行任何额外的配置。

我的 Spring 应用主程序:

@SpringBootApplication
@EntityScan(basePackages = {"ca.gc.cfp.model"})
public class CfpWsApplication {

  public static void main(final String[] args) {

    DynamicInstrumentationLoader.waitForInitialized();
    DynamicInstrumentationLoader.initLoadTimeWeavingContext();

    if (!InstrumentationLoadTimeWeaver.isInstrumentationAvailable()) {
      throw new IllegalStateException(
          "Instrumentation is not available AspectJ weaver will not function.");
    }

    SpringApplication.run(CfpWsApplication.class, args);
  }

LTW 方面:

@Aspect
public class BaseEntityAspect {
  Logger logger = LoggerFactory.getLogger(BaseEntityAspect.class);

  /** Application context property needed to fetch the AuditDate bean */
  @Autowired private ApplicationContext context;

  @AfterReturning(
      "onBaseEntityCreated() && !within(ca.gc.cfp.core.cfpws.repository.aspect.BaseEntityAspect)")
  public void injectAuditTimeStamp(JoinPoint joinPoint) {
    try {
      AuditDate auditDate = context.getBean(AuditDate.class);
      Object entityTarget = joinPoint.getTarget();

      // Inject the auditing date for this Entity instance
      if (entityTarget instanceof BaseEntity) {
        BaseEntity baseEnt = (BaseEntity) entityTarget;
        baseEnt.setAuditDate(auditDate.getAuditingTimeStamp());
      }
    } catch (NullPointerException e) {
      logger.error(
          e.getMessage()
              + " Not yet in the conext of an httpRequest, the AuditDate bean has not yet been instantiated.");
    }
  }

  @Pointcut(
      "execution(ca.gc.cfp.model.entity.BaseEntity.new(..)) && !within(ca.gc.cfp.core.cfpws.repository.aspect.BaseEntityAspect)")
  public void onBaseEntityCreated() {}
}

具有临时工厂方法的 Aspect 配置类使用 Aspects 实用程序让 spring 知道它应该向 aspectJ 询问编织的 Aspect:

@Configuration
@EnableAspectJAutoProxy
public class AspectConfig {

  /**
   * Static factory for access to the load time woven aspect. This allows the aspect to be injected
   * with the application context and beans from the spring IoC
   */
  @Bean
  public BaseEntityAspect getBaseEntityAspect() {
    return Aspects.aspectOf(BaseEntityAspect.class);
  }
}

aop.xml:

<aspectj>
    <weaver options="-verbose -showWeaveInfo -Xreweavable -debug">
        <include within="ca.gc.cfp.model" />
        <include within="ca.gc.cfp.model..*" /> 
        <include within="ca.gc.cfp.core.cfpws.repository.aspect..*"/>
    </weaver>
    <aspects>
        <aspect name="ca.gc.cfp.core.cfpws.repository.aspect.BaseEntityAspect" />
    </aspects>
</aspectj>

在很多情况下,当我使用此配置运行时,我会得到以下信息:

Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [ca.gc.cfp.core.cfpws.repository.aspect.BaseEntityAspect]: Factory method 'getBaseEntityAspect' threw exception; nested exception is org.aspectj.lang.NoAspectBoundException: Exception while initializing ca.gc.cfp.core.cfpws.repository.aspect.BaseEntityAspect: java.lang.NoSuchMethodException: ca.gc.cfp.core.cfpws.repository.aspect.BaseEntityAspect.aspectOf()
    at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:185) ~[spring-beans-5.1.4.RELEASE.jar:5.1.4.RELEASE]
    at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:622) ~[spring-beans-5.1.4.RELEASE.jar:5.1.4.RELEASE]
    ... 19 common frames omitted
Caused by: org.aspectj.lang.NoAspectBoundException: Exception while initializing ca.gc.cfp.core.cfpws.repository.aspect.BaseEntityAspect: java.lang.NoSuchMethodException: ca.gc.cfp.core.cfpws.repository.aspect.BaseEntityAspect.aspectOf()
    at org.aspectj.lang.Aspects.aspectOf(Aspects.java:50) ~[aspectjrt-1.9.4.jar:1.9.4]
    at ca.gc.cfp.core.cfpws.configuration.AspectConfig.getBaseEntityAspect(AspectConfig.java:22) ~[classes/:na]
    at ca.gc.cfp.core.cfpws.configuration.AspectConfig$$EnhancerBySpringCGLIB$$1cae4c58.CGLIB$getBaseEntityAspect$0(<generated>) ~[classes/:na]
    at ca.gc.cfp.core.cfpws.configuration.AspectConfig$$EnhancerBySpringCGLIB$$1cae4c58$$FastClassBySpringCGLIB$$84edb9e.invoke(<generated>) ~[classes/:na]
    at org.springframework.cglib.proxy.MethodProxy.invokeSuper(MethodProxy.java:244) ~[spring-core-5.1.4.RELEASE.jar:5.1.4.RELEASE]
    at org.springframework.context.annotation.ConfigurationClassEnhancer$BeanMethodInterceptor.intercept(ConfigurationClassEnhancer.java:363) ~[spring-context-5.1.4.RELEASE.jar:5.1.4.RELEASE]
    at ca.gc.cfp.core.cfpws.configuration.AspectConfig$$EnhancerBySpringCGLIB$$1cae4c58.getBaseEntityAspect(<generated>) ~[classes/:na]
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_211]
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_211]
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_211]
    at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_211]
    at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:154) ~[spring-beans-5.1.4.RELEASE.jar:5.1.4.RELEASE]
    ... 20 common frames omitted
Caused by: java.lang.NoSuchMethodException: ca.gc.cfp.core.cfpws.repository.aspect.BaseEntityAspect.aspectOf()
    at java.lang.Class.getDeclaredMethod(Class.java:2130) ~[na:1.8.0_211]
    at org.aspectj.lang.Aspects.getSingletonOrThreadAspectOf(Aspects.java:134) ~[aspectjrt-1.9.4.jar:1.9.4]
    at org.aspectj.lang.Aspects.aspectOf(Aspects.java:45) ~[aspectjrt-1.9.4.jar:1.9.4]
    ... 31 common frames omitted

但情况并非总是如此,有时应用程序运行良好,代码按预期执行并且没有问题。这已经困扰我几个星期了,我似乎无法弄清楚是什么导致这段代码偶尔工作,偶尔不工作。会不会是 CGLIB 代理对编译器隐藏了 aspectOf() 方法??

编辑/更新:我能够删除使用上述第三方依赖项将 java 代理动态加载到 Spring 应用程序上下文中。我改为使用 javaagent 参数,它在我的 IDE 中运行良好。但是,在终端中通过 Maven 构建和运行仍然会导致问题。我已经通过环境变量指定了 javaagent 参数:MAVEN_OPTS。这样做之后,maven 似乎正在接受它,但我的课程仍然没有被编织。

【问题讨论】:

  • 或者您的 IDE 构建和 maven 构建相互冲突?一个配置正确,一个不正确?

标签: java spring spring-boot aspectj load-time-weaving


【解决方案1】:

关于您自己的答案:AspectJ Maven 插件可帮助您进行编译时编织 (CTW),而不是加载时编织 (LTW)。对于 LTW,您需要确保在加载任何目标类之前 编织代理处于活动状态,因为 LTW 在类加载器级别工作。 AspectJ Maven 仅在您使用本机语法(不是基于注释)的方面或想要使用 CTW 时才需要。

我不知道这个 invesdwin-instrument 工具,但是 AspectJ 编织器提供了在运行时附加它的自己的功能。不需要第三方工具。无论如何,我确实建议修改 Java 命令行,以确保在容器中加载任何其他内容之前编织代理就位。您的部署人员应该帮助您确保-javaagent:... 参数存在。这是他们的工作!解决这个问题是为了让他们的生活更轻松,但您的应用程序可能无法预测的行为并不会提高其稳定性。

【讨论】:

  • 很有趣,感谢您提供更多详细信息。 aspectJrt、weaver 和工具都在我的类路径中。启用 LoadTimeWeaving 后,您的意思是我不需要任何其他工具来确保代理在运行时可用,只要我在命令行上指定 -javaagent 参数?
  • 还有一件事,就对开发团队其他成员的影响而言,我认为他们还需要重新配置 Eclipse 环境以应对这种变化。另一个问题是有些人从终端使用 Maven,这似乎不知道或加载了适当的 javaagent。我觉得这样的东西可能有用:stackoverflow.com/questions/14777909/…
  • (1) 您只需要类路径中的aspectjweaver.jar,不需要其他两个。背景:Weaverruntime 的超集,toolsweaver 的超集,在这种情况下你不需要'不需要,因为您不想使用其中包含的编译器。 (2) 如果配置正确,不需要额外的工具,请参阅Spring manual。 (3) 是的,团队需要调整他们的 IDE 运行配置。 (4) 可以从 Maven 使用 Java 代理启动测试或应用程序。
  • 简单安全的替代方法是通过 AspectJ Maven 插件使用编译时编织并完全摆脱加载时编织。然后您的应用程序也将启动得更快,因为在加载期间不需要编织。此外,您只需要类路径上的 aspectjrt.jar(AspectJ 运行时),这是一个只有 120K 大小的非常小的 JAR。对于应该始终编织的生产方面,这始终是我的选择。只有在您想要分析问题时才应该处于活动状态的调试或跟踪方面表明使用 LTW。
  • 感谢您对这个 kriegaex 的深入回答,他们非常有见地。我将看一下编译时编织,如果它更快,我认为这将是需要考虑的重要事项。随着我们的应用程序一直在增长,启动时间也在增加,我们希望将其降低。我将接受您的帖子作为答案,因为我们的讨论已经足够深入,可以让我朝着正确的方向前进并更好地掌握事情。
【解决方案2】:

根据 Kieveli 的评论。我的 TL 也有类似的想法。我是 Maven、Spring boot 和 AOP 的新手。研究它在外部运行的 Maven 和 Eclipse 内部的构建似乎相互“破坏”。

我一直通过 MINGW64 使用 maven 来清理/安装/构建,并且该项目没有指定 AspectJ 编译器的使用,这可能是工厂方法没有被编织到类中的原因。

我们正在讨论解决方案,因为我们使用 Jenkins 在我们的服务器上构建自动化。我在想这个 Maven 插件可能是解决方案。 https://www.mojohaus.org/aspectj-maven-plugin/

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-06-18
    • 1970-01-01
    • 2019-07-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多