【问题标题】:bytebuddy with aop in springboot not workspringboot中带有aop的bytebuddy不起作用
【发布时间】:2020-09-01 09:13:53
【问题描述】:

我尝试在springboot中使用bytebuddy实现aop。代码如下:

package klordy.learning.annotation;
@Target(METHOD)
@Retention(RUNTIME)
@Documented
public @interface CostTime {
}
package klordy.learning.agent;
public class Agent {
private static Agent instance = new Agent();
    private Logger logger = LoggerFactory.getLogger(Agent.class);
    private Agent() {}
    public static Agent getInstance(){ return instance; }
    public void install() {
        ByteBuddyAgent.install();
        AgentBuilder.Listener listener = new AgentBuilder.Listener() {
            // do nothing
            ...
        };

        new AgentBuilder.Default()
                .type(ElementMatchers.nameStartsWith("klordy.learning"))
                .transform((builder, typeDescription, classLoader, module) ->
                        builder.visit(Advice.to(TimeAdvice.class).on(ElementMatchers.isAnnotatedWith(named("klordy.learning.annotation.CostTime")))))
                .with(listener)
                // *** added as supposed, but still seems not work.
                .with(AgentBuilder.RedefinitionStrategy.RETRANSFORMATION)
                .installOnByteBuddyAgent();
         logger.info("byte buddy modification done.");
     }
}
public class TimeAdvice {
    @Advice.OnMethodEnter
    static long enter(@Advice.AllArguments Object args[], @Advice.Origin Method method){
        return System.currentTimeMillis();
    }

    @Advice.OnMethodExit
    static void exit(@Advice.Enter long startTime,
                     @Advice.Return(typing = Assigner.Typing.DYNAMIC) Object result,
                     @Advice.Origin Method method,
                     @Advice.Thrown Throwable throwable){
        if(throwable != null){
            System.out.println("error func " + System.currentTimeMillis());
        }else {
            System.out.println("func takes " + (System.currentTimeMillis() - startTime));
        }
    }
}

springboot的监听器如下:

public class AppEnvListener implements ApplicationListener<ApplicationEnvironmentPreparedEvent> {
    private Logger logger = LoggerFactory.getLogger(AppEnvListener.class);
    @Override
    public void onApplicationEvent(ApplicationEnvironmentPreparedEvent applicationEnvironmentPreparedEvent) {
        Agent.getInstance().install();
        logger.info("finished byte buddy installation.");
    }
}

最后在springboot启动时注册监听器:

@SpringBootApplication
@ComponentScan(basePackages = "klordy.learning")
public class SpringBootDemoApplication {

    public static void main(String[] args) {
       SpringApplication application = new SpringApplication(SpringBootDemoApplication.class);
       // register listener
       application.addListeners(new AppEnvListener());
       application.run(args);
    }
}

当我启动应用程序时,调试记录器显示正常。但是,处理请求时 aop 不起作用。我究竟做错了什么?我很困惑……

【问题讨论】:

  • 像往常一样,Rafael 关于 BB 的回答是正确的。只是出于好奇:为什么在 Spring Boot 中您已经拥有两个成熟的 AOP 框架(Spring AOP 和 AspectJ)可供使用时使用 BB?
  • 有兴趣和新来的BB。只是为了学习。 @kriegaex
  • 如果你想专心学习BB,不妨从更简单的Java SE环境入手,尝试有无Java代理,了解API后再切换到容器或应用服务器环境,以减少系统中变量或“移动部件”的数量。 ??????
  • ????感谢您的建议,我会接受并尝试了解有关 API 的更多信息。

标签: java spring-boot aop byte-buddy


【解决方案1】:

好的,我想我通过使用 Spring Boot 重新创建您的情况找到了您的问题。不过,如果没有 Spring,它也会发生。基本上你点击this problem。

因此,如果您将建议修改为@Advice.OnMethodExit(onThrowable = Throwable.class),应该没问题。您还应该将.disableClassFormatChanges() 添加到您的代理,顺便说一句。它有助于避免重新转换以前加载的类的问题。 JVM 要求它们不要在结构上进行更改。

我怎么知道发生了什么?我在 ByteBuddy 中激活了控制台登录。以前我使用过你的监听器,让它记录每一个动作,所以我可以看到 BB 实际上被触发了。但是由于您错误地使用了@Advice.Thrown(在建议注释中没有onThrowable = Throwable.class),BB 日志记录确实显示了异常。

public void install() {
  ByteBuddyAgent.install();
  new AgentBuilder.Default()
    .disableClassFormatChanges()
    .with(RETRANSFORMATION)
    .with(AgentBuilder.RedefinitionStrategy.Listener.StreamWriting.toSystemError())
    .with(AgentBuilder.Listener.StreamWriting.toSystemError().withTransformationsOnly())
    .with(AgentBuilder.InstallationListener.StreamWriting.toSystemError())
    .type(ElementMatchers.nameStartsWith("klordy.learning"))
    .transform((builder, typeDescription, classLoader, module) ->
      builder.visit(
        Advice
          .to(TimeAdvice.class)
          .on(isAnnotatedWith(named("klordy.learning.annotation.CostTime")))
      )
    )
    .installOnByteBuddyAgent();
  logger.info("byte buddy modification done.");
}

【讨论】:

    【解决方案2】:

    您的相关类可能已经加载。

    在您的代理配置中设置 .with(RetransformationStrategy.RETRANSFORM) 或在您的 main 方法中加载 Spring 之前安装您的代理。

    【讨论】:

    • 感谢回复。我尝试在代理配置中设置.with(AgentBuilder.RedefinitionStrategy.RETRANSFORMATION),但还是不行。
    猜你喜欢
    • 2014-11-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-10-25
    • 1970-01-01
    • 1970-01-01
    • 2018-09-16
    • 1970-01-01
    相关资源
    最近更新 更多