抽象类的代理而不改变用法

时间:2018-01-17 21:52:20

标签: java oop aop aspectj

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

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

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()而不对整个代码库中的所有方法进行更改 - 这将是获取代理实例而不是实际实例,并对其执行操作。

有没有推荐的方法来实现这个目标?

谢谢!

1 个答案:

答案 0 :(得分:1)

昨晚我醒来觉得无聊,所以尽管你缺乏反馈我创建了一点Carbonado showcase project并在GitHub上分享了它。我做了三次提交:

  1. Initial commit Maven项目已经为AspectJ做了准备,还有一个JUnit测试让我知道Carbonado实际上是如何工作的,因为我之前从未使用它。

  2. Add failing unit test了解预期由方面提供的tryLoad()行为。

  3. Add aspect to make unit test pass。 Aspect挂钩到tryLoad()并自动创建不存在的记录。我不知道我是否猜对了你真正想要实现的目标,但如果它是另一回事,那就改变方面实施。

  4. 示例代码

    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);
      }
    
    }
    

    享受!