6

クラスの 1 つでいくつかのメソッド呼び出しをインターセプトしたいのですが、それらのクラスにはデフォルトのコンストラクターがありません。

次のクラスが与えられた場合、Byte Buddy をセットアップして、生成されたクラスを作成できる引数なしのパブリック コンストラクターも作成するにはどうすればよいでしょうか?

public class GetLoggedInUsersSaga extends AbstractSpaceSingleEventSaga {
    private final UserSessionRepository userSessionRepository;

    @Inject
    public GetLoggedInUsersSaga(final UserSessionRepository userSessionRepository) {
        this.userSessionRepository = userSessionRepository;
    }

    @StartsSaga
    public void handle(final GetLoggedInUsersRequest request) {
       // this is the method in want to intercept
    }
}

編集:これの具体的な使用例は、単体テストのセットアップを簡素化することです。
現在、常に次のように記述する必要があります。

@Test
public void someTest() {
   // Given

   // When
   GetLoggedInUsersRequest request = new GetLoggedInUsersRequest();
   setMessageForContext(request); // <-- always do this before calling handle
   sut.handle(request);

   // Then
}

コンテキストを自動的に設定する @Before メソッドでプロキシを作成するとよいと思いました。

@Before
public void before()  {
    sut = new GetLoggedInUsersSaga(someDependency);
    sut = intercept(sut);
}

@Test
public void someTest() {
   // Given

   // When
   GetLoggedInUsersRequest request = new GetLoggedInUsersRequest();
   sut.handle(request);

   // Then
}

少し遊んでみましたが、残念ながらうまくいきませんでした..

public <SAGA extends Saga> SAGA intercept(final SAGA sagaUnderTest) throws NoSuchMethodException, IllegalAccessException, InstantiationException {
    return (SAGA) new ByteBuddy()
            .subclass(sagaUnderTest.getClass())
            .defineConstructor(Collections.<Class<?>>emptyList(), Visibility.PUBLIC)
            .intercept(MethodCall.invokeSuper())
            .method(ElementMatchers.isAnnotatedWith(StartsSaga.class))
            .intercept(
                    MethodDelegation.to(
                            new Object() {
                                @RuntimeType
                                public Object intercept(
                                        @SuperCall Callable<?> c,
                                        @Origin Method m,
                                        @AllArguments Object[] a) throws Exception {
                                    setMessageForContext((Message) a[0]);
                                    return c.call();
                                }
                            }))
            .make()
            .load(getClass().getClassLoader(), ClassLoadingStrategy.Default.WRAPPER)
            .getLoaded()
            .newInstance();
}

残念ながら、私は取得しました(おそらくctor呼び出しがまだ正しくセットアップされていないためです)

java.lang.IllegalStateException: Cannot invoke public com.frequentis.ps.account.service.audit.GetLoggedInUsersSaga$ByteBuddy$zSZuwhtR() as a super method

これは正しいアプローチですか?
ここでもバイトバディを使用する必要がありますか、それとももっと簡単な/他の方法がありますか?

4

1 に答える 1

2

バイトコードなしでコンストラクタを定義することはできません。これは、Java では違法な抽象コンストラクターになります。将来のバージョン用に、より正確な説明を javadoc に追加する予定です。これを知らせてくれてありがとう。

コンストラクターに必要なスーパーメソッド呼び出しを定義する必要があります。

DynamicType.Builder builder = ...
builder = builder
  .defineConstructor(Collections.<Class<?>>emptyList(), Visibility.PUBLIC)
  .intercept(MethodCall
               .invoke(superClass.getDeclaredConstructor())
               .onSuper())

ここでByte Buddyを使用する必要があるかどうかについては、私が見た小さなコードからはわかりません. 自問すべき質問: コードの量とコードをたどる複雑さの両方を考慮して、コードはより簡単になりますか? Byte Buddy によってコードの使用 (および実行) が容易になる場合は、それを使用してください。そうでない場合は、使用しないでください。

于 2015-07-07T13:30:50.073 に答える