最簡潔的解決方案是使用定義常用功能的interface
,例如,
public interface Controller {
void index(RoutingContext routingContext);
}
public class PostsController implements Controller {
@Route(method="get", path = "/")
public void index(RoutingContext routingContext){
routingContext.response()
.putHeader("content-type", "application/json; charset=utf-8")
.end(Json.encodePrettily("{}"));
}
}
Class<? extends Controller> controllerType =
Class.forName("some.package"+controllers.getString(i))
.asSubclass(Controller.class);
Controller controller=controllerType.newInstance();
Handler<RoutingContext> h=controller::index;
當然,這並不與基於註解的處理進行交互。動態的解決方案看起來像
Class<?> controllerType = Class.forName("some.package"+controllers.getString(i));
MethodHandles.Lookup lookup=MethodHandles.lookup();
Method[] methods = controllerType.getMethods();
Object instance = null;
for (Method m : methods) {
Route annotation = m.getAnnotation(Route.class);
if (annotation != null) {
if(instance == null) instance = controllerType.newInstance();
Handler<RoutingContext> handler
=createHandler(lookup, instance, m, RoutingContext.class);
router.get(annotation.path()).handler(handler);
}
}
static <T> Handler<T> createHandler(MethodHandles.Lookup lookup,
Object instance, Method m, Class<T> arg) throws Throwable {
MethodHandle mh=lookup.unreflect(m);
MethodType t=mh.type();
Class<?> receiver=t.parameterType(0);
if(t.parameterCount()!=2
|| !receiver.isAssignableFrom(instance.getClass())
|| !t.parameterType(1).isAssignableFrom(arg))
throw new IllegalArgumentException(m+" not suitable for Handler<"+arg.getName()+'>');
t=t.dropParameterTypes(0, 1);
return (Handler)LambdaMetafactory.metafactory(lookup, "accept",
MethodType.methodType(Handler.class, receiver),
t.changeParameterType(0, Object.class), mh, t)
.getTarget().invoke(instance);
}
這工作一定假設條件下你Handler
界面,你沒有指定。如果它被定義爲,例如interface Handler<T> extends Consumer<T> {}
,它將開箱即用。否則,您必須將"accept"
修改爲功能接口的功能方法的實際名稱。如果類型參數有綁定,則必須將t.changeParameterType(0, Object.class)
行更改爲使用實際綁定(類型擦除後的Handler
的參數類型)。
一般來說,這需要非常小心,因爲您沒有即時的錯誤反饋。在最壞的情況下,錯誤只會在調用回調時被檢測到(具有某些參數)。
原則,你也可以只創建一個lambda表達式封裝方法的反思調用,即
Handler<RoutingContext> handler=rc -> { try {
m.invoke(capturedInstance, rc);
} catch(ReflectiveOperationException ex) { ex.printStackTrace(); }};
但這並不改變的事實,你將只很晚檢測錯誤...
你不需要保證你所有的控制器都有這個'index'方法嗎?通過讓你的控制器擴展一些需要該索引方法的接口來解決這個問題。 – ifly6
索引函數將是他們的,但也會有一些自定義函數,這就是爲什麼我要使它動態化的原因 –
是的,只要該文件實現了接口,您的自定義函數就可以在文件中聲明。但是,JVM並不知道您可以保證您的文件具有該方法。您可以保證通過讓所有控制器實現一個通用接口。 – ifly6