如何在回调中传递方法类变量?
我有以下代码:
PostsController postController = new PostsController();
router.get("/").handler(postController::index);
但我的控制器将是动态的,我如何传递动态类及其功能。 我尝试了以下代码
Class controller = Class.forName("some.package"+controllerName); //PostsController
Method[] methods = controller.getMethods();
//some loop
Method m = methods[i]; // This is the index method of my controller
router.get(somePath).handler(m); // How can I pass my method m as callback?
PostsController
public class PostsController {
@Route(method="get", path = "/")
public void index(RoutingContext routingContext){
routingContext.response()
.putHeader("content-type", "application/json; charset=utf-8")
.end(Json.encodePrettily("{}"));
}
}
答案 0 :(得分:2)
最干净的解决方案是使用定义常用功能的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"
调整为功能接口的函数方法的实际名称。如果type参数有一个绑定,你必须更改t.changeParameterType(0, Object.class)
行以使用实际绑定(类型擦除后的Handler
'参数类型)。
通常,这需要非常小心,因为您没有立即的错误反馈。在最坏的情况下,只有在实际调用回调时(使用某些参数)才会检测到错误。
原则上,您也可以创建一个封装方法的Reflective调用的lambda表达式,即
Handler<RoutingContext> handler=rc -> { try {
m.invoke(capturedInstance, rc);
} catch(ReflectiveOperationException ex) { ex.printStackTrace(); }};
但这并没有改变你只会很晚才发现错误的事实......
答案 1 :(得分:0)
我能看到的第一件事是你需要创建一个类的实例。
Class controller = Class.forName("some.package"+controllers.getString(i));
Object myInstance = controller.newInstance();
然后你从中得到你的方法,然后调用它:
Method myMethod = ...
myMethod.invoke(myInstance, null); // or with some params
如果你只需要调用“index”,你也可以让你的控制器实现一个通用接口,然后:
MyInterface myInterface = (MyInterface) myInstance;
myInterface.index();
希望有所帮助。