代理抽象 class 而不改变用法

Proxy for abstract class without changing the usage

我有一个抽象 class(数据库映射)实现了一个接口,其中默认实现在运行时注入(这是另一个库的一部分,无法更改)。

我想通过代理覆盖默认实现之一(因为这似乎是覆盖它的方法)。

public abstract class Table1 implements Storable<Table1>
{   
    @Sequence("ID_SEQUENCE")
    @Alias("ID")
    public abstract String getID();
    public abstract void setID(String ID);

    @Alias("NAME")
    public abstract String getAvailabilityZone();
    public abstract void setAvailabilityZone(String value);
}

public interface Storable<S extends Storable<S>> {
     //a bunch of method definition.
     boolean tryLoad() throws Exception;
}

假设我想覆盖 tryLoad() 方法来做我自己的事情而不是生成的代码提供的东西。鉴于库的性质,这不是我可以通过简单的 @Override 实现的。 目前使用的简单方法如下:

public void method() {
        Table1 t = Repository.storageFor(Table1.class).prepare();
        t.setName( "temp" );
        if (!t.tryLoad())
           t.tryInsert();
}

我想在不更改整个代码库中的所有方法的情况下代理 tryLoad() - 那将是获取代理实例而不是实际实例并对其执行操作。

有什么推荐的方法可以实现吗?

谢谢!

我昨晚醒来觉得很无聊,所以尽管你没有反馈,我还是创建了一些 Carbonado showcase project 并在 GitHub 上分享了它。我做了三个提交:

  1. Initial commit 已经为 AspectJ 准备了 Maven 项目和 JUnit 测试以了解 Carbonado 的实际工作原理,因为我以前从未使用过它。

  2. Add failing unit test 表示 tryLoad() 的行为预计由 aspect 提供。

  3. Add aspect to make unit test pass。方面挂钩 tryLoad() 并自动创建不存在的记录。我不知道我是否猜对了您实际想要实现的目标,但如果它是不同的东西,只需更改方面实现即可。

示例代码

Carbonado 可存储:

package de.scrum_master.app;

import com.amazon.carbonado.Nullable;
import com.amazon.carbonado.PrimaryKey;
import com.amazon.carbonado.Storable;

@PrimaryKey("ID")
public interface StoredMessage extends Storable<StoredMessage> {
  long getID();
  void setID(long id);
  @Nullable String getMessage();
  void setMessage(String message);
}

看点:

package de.scrum_master.aspect;

import com.amazon.carbonado.Storable;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;

@Aspect
public class CarbonadoAspect {
  @Around("call(boolean tryLoad()) && target(storable)")
  public boolean tryInsertIfNotFound(ProceedingJoinPoint thisJoinPoint, Storable storable) throws Throwable {
    System.out.println(thisJoinPoint);
    if ((boolean) thisJoinPoint.proceed())
      return true;
    System.out.println("Not found: " + storable + " -> inserting");
    return storable.tryInsert();
  }
}

JUnit测试:

package de.scrum_master.app;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import com.amazon.carbonado.FetchException;
import com.amazon.carbonado.PersistException;
import com.amazon.carbonado.Repository;
import com.amazon.carbonado.RepositoryException;
import com.amazon.carbonado.Storage;
import com.amazon.carbonado.SupportException;
import com.amazon.carbonado.repo.map.MapRepositoryBuilder;

import de.scrum_master.app.StoredMessage;

public class CarbonadoTest {
  private Repository repo;
  private Storage<StoredMessage> storage;
  StoredMessage message;

  @Before
  public void setUp() throws Exception {
    repo = MapRepositoryBuilder.newRepository();
    storage = repo.storageFor(StoredMessage.class);
    message = storage.prepare();
  }

  @After
  public void tearDown() throws Exception {
    repo.close();
    repo = null;
    storage = null;
    message = null;
  }

  // (...)

  @Test
  public void aspectCreatesNonExistentRecord() throws SupportException, RepositoryException {
    message.setID(1);
    // Without the aspect this would be false
    assertTrue(message.tryLoad());
    assertEquals(message.getID(), 1);
    assertEquals(message.getMessage(), null);
  }

}

尽情享受吧!