Reputation: 11
I currently have an interface。that looks like this
public interface JRepository<E, ID> {
// sqlClient
JSqlClient sql();
// crud methods
E findNullable(ID id);
List<E> findByIds(Iterable<ID> ids);
}
This interface has an implementation class that looks like this
public class JRepositoryImpl<E, ID> implements JRepository<E, ID> {
@Override
public JSqlClient sql() {
return just return sqlClient;
}
@Override
public E findNullable(ID id) {
// Some logic
return sql().findById(id);
}
@Override
public List<E> findByIds(Iterable<ID> ids) {
// Some logic
return sql().findByIds(ids);
}
}
Users can choose to extend the interface to get CRUD methods that looks like this
public interface TestRepository extends JRepository<Book, Long> {
}
Now I want to implement the interface for the user-defined TestRepository through the default implementation class JRepositoryImpl.
I encountered some issues with Quarkus’s Gizmo. It seems that Gizmo is unable to copy the method bodies from the methods.
It seems that when I use methods like ClassCreator and MethodCreator, there are no APIs available to copy the method bodies.
If I want to achieve my goal, do I have to use the lower-level ClassVisitor and MethodVisitor, or is there a better way? Are there any similar codes that I can reference?
I’m currently encountering some issues while generating the code using bytecode.
@ApplicationScoped
@Unremovable
public class TestRepository_edb259d1f267785b946cb0bcfd7d69d6e7653180Impl implements TestRepository {
private final TestInterface defaultImpl;
public TestRepository_edb259d1f267785b946cb0bcfd7d69d6e7653180Impl() {
}
public TestRepository_edb259d1f267785b946cb0bcfd7d69d6e7653180Impl(JSqlClient var1) {
ClassLoader var2 = Thread.currentThread().getContextClassLoader();
Class var3 = Class.forName("com.Book", false, var2);
TestInterfaceImpl var4 = new TestInterfaceImpl(var1, var3);
this.defaultImpl = (TestInterface)var4;
}
public Object findNullable(Object var1) {
return this.defaultImpl.findNullable(var1);
}
public Class entityType() {
return this.defaultImpl.entityType();
}
public JSqlClient sql() {
return this.defaultImpl.sql();
}
}
When I inject it, Quarkus throws the following error: Caused by: java.lang.NullPointerException: Cannot invoke "TestInterface.findNullable(Object)" because "this.defaultImpl" is null.
public class TestInterfaceImpl<E, ID> implements TestInterface<E, ID> {
protected final JSqlClientImplementor sqlClient;
protected final Class<E> entityType;
protected TestInterfaceImpl(JSqlClient sqlClient) {
this(sqlClient, null);
}
public TestInterfaceImpl(JSqlClient sqlClient, Class<E> entityType) {
this.sqlClient = Utils.validateSqlClient(sqlClient);
this.entityType = entityType;
}
@Override
public JSqlClient sql() {
return sqlClient;
}
@Override
public Class<E> entityType() {
return entityType;
}
@Override
public E findNullable(ID id) {
return sqlClient.getEntities().findById(entityType, id);
}
}
Upvotes: 0
Views: 83
Reputation: 6577
If you know that a default implementation exists and can obtain an instance in the constructor (glossing over how exactly you do that), you don't have to copy the method bodies -- you can just forward to them. That should be fairly simple. If you have a Jandex ClassInfo
for the interface (iface
), you can do just:
for (MethodInfo method : iface.methods()) {
MethodCreator m = clazz.getMethodCreator(method);
ResultHandle delegate = m.readInstanceField(m.getThis(), ...);
ResultHandle [] args = new ResultHandle[method.parametersCount()];
for (int i = 0; i < method.parametersCount(); i++) {
args[i] = m.getParam(i);
}
m.returnValue(m.invokeInterface(method, delegate, args));
}
Note that I'm writing this from memory on my phone, so there might be typos, but the structure should be obvious.
Upvotes: 1