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

如何使用ByteBuddy代理现有对象

支阳波
2023-03-14

例如,假设有一个接口(StoredOnDatabase)带有一些有用的方法,可以从数据库中读取和写入bean。假设有一些类没有实现这个接口,而是用注释@bean进行了注释。当这个注释出现时,我想:

  1. 创建实现StoredonDatabase接口的bean代理;
  2. 为setter添加拦截器,当bean的属性被修改时,我可以使用这些拦截器来“跟踪”;
  3. 使用对所有这些bean都有效的泛型equals()和hashCode()方法。

我不想改变POJO的类别。一个简单的解决方案可以是在实例化bean之前使用ByteBuddy完成所有这些。这可能是一个解决方案,但我想知道是否可以将bean实例化为一个干净的POJO并添加代理的其他功能。

我正在尝试使用ByteBuddy,我认为我有一个可行的解决方案,但它似乎比我预想的要复杂得多。

如上所述,我需要代理类的实例,以便向它们添加新的接口,拦截对现有方法的调用,并替换现有方法(主要是equals()、hashCode()和toString())。

似乎与我所需要的比较接近的示例是以下内容(复制自ByteBuddy教程):

class Source {
  public String hello(String name) { return null; }
}

class Target {
  public static String hello(String name) {
    return "Hello " + name + "!";
  }
}

String helloWorld = new ByteBuddy()
  .subclass(Source.class)
  .method(named("hello")).intercept(MethodDelegation.to(Target.class))
  .make()
  .load(getClass().getClassLoader())
  .getLoaded()
  .newInstance()
  .hello("World");

我是不是漏掉了什么?

共有1个答案

丁鹏鹍
2023-03-14

这里有一个AspectJ解决方案。我认为这比ByteBuddy版本简单得多,可读性更强。让我们从与前面相同的resource类开始:

package de.scrum_master.app;

public class Resource {
  private String id;

  public String getId() {
    return id;
  }

  public void setId(String id) {
    this.id = id;
  }
}

现在,让我们通过AspectJ的ITD(inter-type definition)将以下内容添加resource类中。导言:

  • 直接初始化id成员的构造函数
  • ToString()方法
  • 等于(*)方法
package de.scrum_master.aspect;

import de.scrum_master.app.Resource;

public aspect MethodIntroductionAspect {
  public Resource.new(String id) {
    this();
    setId(id);
  }

  public boolean Resource.equals(Object obj) {
    if (!(obj instanceof Resource))
      return false;
    return getId().equals(((Resource) obj).getId());
  }

  public String Resource.toString() {
    return "Resource[id=" + getId() + "]";
  }
}
package de.scrum_master.app;

import static org.junit.Assert.*;

import org.junit.Test;

public class ResourceTest {
  @Test
  public void useConstructorWithArgument() {
    assertNotEquals(null, new Resource("dummy"));
  }

  @Test
  public void testToString() {
    assertEquals("Resource[id=dummy]", new Resource("dummy").toString());
  }

  @Test
  public void testEquals() {
    assertEquals(new Resource("A"), new Resource("A"));
    assertNotEquals(new Resource("A"), new Resource("B"));
  }
}

Marco,也许我不能让你相信这比你自己的解决方案更好,但是如果我可以并且你需要一个Maven POM,请告诉我。

更新:

我刚刚为您创建了简单的Maven POM(单模块项目):

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>de.scrum-master.stackoverflow</groupId>
  <artifactId>aspectj-itd-example-57525767</artifactId>
  <version>1.0-SNAPSHOT</version>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <java.source-target.version>8</java.source-target.version>
    <aspectj.version>1.9.4</aspectj.version>
  </properties>

  <build>

    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>3.3</version>
        <configuration>
          <source>${java.source-target.version}</source>
          <target>${java.source-target.version}</target>
          <!-- IMPORTANT -->
          <useIncrementalCompilation>false</useIncrementalCompilation>
        </configuration>
      </plugin>
      <plugin>
        <groupId>org.codehaus.mojo</groupId>
        <artifactId>aspectj-maven-plugin</artifactId>
        <version>1.11</version>
        <configuration>
          <!--<showWeaveInfo>true</showWeaveInfo>-->
          <source>${java.source-target.version}</source>
          <target>${java.source-target.version}</target>
          <Xlint>ignore</Xlint>
          <complianceLevel>${java.source-target.version}</complianceLevel>
          <encoding>${project.build.sourceEncoding}</encoding>
          <!--<verbose>true</verbose>-->
          <!--<warn>constructorName,packageDefaultMethod,deprecation,maskedCatchBlocks,unusedLocals,unusedArguments,unusedImport</warn>-->
        </configuration>
        <executions>
          <execution>
            <!-- IMPORTANT -->
            <phase>process-sources</phase>
            <goals>
              <goal>compile</goal>
              <goal>test-compile</goal>
            </goals>
          </execution>
        </executions>
        <dependencies>
          <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjtools</artifactId>
            <version>${aspectj.version}</version>
          </dependency>
          <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>${aspectj.version}</version>
          </dependency>
        </dependencies>
      </plugin>
    </plugins>

  </build>

  <dependencies>
    <dependency>
      <groupId>org.aspectj</groupId>
      <artifactId>aspectjrt</artifactId>
      <version>${aspectj.version}</version>
      <scope>runtime</scope>
    </dependency>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.12</version>
      <scope>test</scope>
    </dependency>
  </dependencies>

</project>

其次,出于测试目的,我停用了IntelliJ IDEA Ultimate中的AspectJ和Spring AOP插件,出于所有的意图和目的,在这里将我的IDE变成了关于AspectJ的社区版。当然,对于AspectJ、本机语法或方面交叉引用信息,您没有特定的语法高亮显示(在应用程序代码中编织了哪些建议或方面代码在哪里编织?)但对ITD的支持是有限的。例如,在单元测试中,您似乎会看到编译问题,因为IDE不知道ITS的构造函数和方法。

但如果现在打开设置对话并将IDE构建委托给Maven...

...您可以从IntelliJ IDEA构建,通过用户界面等运行单元测试。在右边当然您有Maven视图,也可以运行Maven目标。顺便说一句,如果IDEA询问您是否要启用Maven自动导入,您应该接受。

我还将相同的Maven POM导入到一个新的Eclipse项目(安装了AJDT)中,它也运行良好。IDEA和Eclipse项目在一个项目目录中和平共处。

附注:在IDEA Ultimate中,委托给Maven也是必要的,以避免IDE中的编译错误,因为AspectJ ITD支持在IDEA中是如此糟糕。

 类似资料:
  • 我正在用bytebuddyapi编写一个Java代理。因此,我想了解使用Bytebuddy DSL的重传功能加载的类的方法委派。当我使用参数javaagent启动应用程序时,一切正常,控制台输出也会更改,但当在运行时附加java代理时,会执行agentmain方法,但控制台输出不会更改。也许我错过了一些进一步的ByteBuddy配置。任何帮助都将不胜感激! 这是代理代码: 以下是应用程序代码: 这

  • 我需要从Spring应用程序向MongoDB实例抛出文档,在那里我可以利用其数据包中的MongoTemplate。 但是Spring将那些instances字段作为MongoDB文档id,导致数据库中的id重复,从而防止了重复的实例。 null 通过阅读这个问题,我发现字段对于Spring是必需的,我需要添加一个字段。 这就是我将文档插入集合的方式: 我对ByteBuddy完全陌生(我认为这可能是

  • 我正在尝试在项目中检测一些类。当我将代理类打包到jar中并通过-javaagent使用它时,它工作正常。 当我尝试直接在项目中运行它时,检测有时会失败。(我在测试类的静态块中初始化bytepal)。 例如,当我添加这个测试时,我的代码不再被截获。用try/catch做同样的事情是有效的。 有没有一种安全的方法来仪器类在同一个项目没有-javaagent? 项目在OpenJdk11上。

  • 我需要使用远程代理服务器进行测试,但代理需要登录名和密码进行授权。我尝试使用: 帮助我理解我的错误,我确定这是可能的,但如何。我有一个例外:

  • 线程“main”java.lang.LinkageError:loader(sun/misc/launcher$AppClassLoader的实例):试图重复名称:“TestClass”的类定义 我正在尝试实现代理,如:easly-create-java-agents-with-bytebuddy中描述的示例所示 是否有一种方法可以加载Class对象而不导致此问题,或者使用传递给transform

  • 问题内容: 如果我这样做 它打印 问题是,我在代理后面。JVM在Windows的哪里获取代理信息?我该如何设置?我所有其他应用似乎都对我的代理感到非常满意。 问题答案: 从Java 1.5开始,你还可以将实例传递给该方法: 如果你的代理服务器要求身份验证,它将给你响应407。 在这种情况下,你需要以下代码: