当前位置: 首页 > 知识库问答 >
问题:

在spring boot应用程序中使用AspectJ加载时编织时生成结果不一致

万修为
2023-03-14

目前,我正在使用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 config类使用Aspects utils的临时工厂方法让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构建和运行仍然会导致问题。我已经通过环境变量:MAVEN_OPTS指定了javaagent参数。这样做之后,maven似乎正在捡起它,但我的类仍然没有被编织。

共有2个答案

薄瑞
2023-03-14

根据基维利的评论。我的TL也有同样的想法。我不熟悉Maven、Spring boot和AOP。从外部来看,运行Maven和Eclipse内部的构建似乎正在相互“碰撞”。

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

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

陶成济
2023-03-14

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

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

 类似资料:
  • 和位于同一个包中,编织工作在直接实例化的包上,而不是bean返回的包上。 我搜索了Spring AOP文档,但似乎找不到任何与此相关的内容。对于自动代理,您需要做一些魔术,对于SpringAOP也需要做一些限制,但是加载时间编织就我所知应该可以工作--例如,我已经尝试过私有方法,它可以工作。

  • 当我设置Spring Ashej加载时间编织并通过关键字创建一个实例时,如下所示(下图)。事实证明,我无法访问构造函数中的依赖项。正如预期的那样,这一切都很好。执行顺序是 。现在,我可以访问构造函数中的依赖项。但问题是执行顺序:

  • 我参与了一个较老项目的审查任务。任务是将某些库更新到更新的版本。这个项目成功地使用了spring(4.3.14.Release),以及JDK 8下的AspectJ(1.9.0)和Tomcat8.0.20。现在spring将更新到最新版本(目前为5.3.3),Tomcat版本也将提升到最新版本(目前为9.0.37)。服务器应该在JDK11下运行。升级库后,我们意识到AspectJ不再工作了。所以我开

  • 我构建了一个应用程序来测试围绕类注入日志信息(进入和退出)。我使用Spring构建了它,并使用以下示例构建它。 http://static.springsource.org/spring/docs/2.5.5/reference/aop.html#aop-aj ltw公司 它现在工作得很好,但我有2个问题: 当日志围绕方法编织时,不包括私有方法。在Spring的xml设置中是否有允许编织私有方法的