Java在回调函数中传递字符串变量
如何在回调函数中传递方法类变量?Java在回调函数中传递字符串变量
我有下面的代码:
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("{}"));
}
}
最简洁的解决方案是使用定义常用功能的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(); }};
但这并不改变的事实,你将只很晚检测错误...
谢谢,我试过这个,但它抛出了这个错误: 'java.lang.AbstractMethodError:io.vertx.lib.AppVertical $$ Lambda $ 28/647430455.handle(Ljava/lang/Object;)V' –
如上所述,你必须调整方法名称:'“接受”'→'“句柄”'。 – Holger
谢谢,这工作:) 界面看起来干净和容易,但如果我有在控制器中的动态方法更清洁的方式去更进一步。 –
第一件事,我所看到的,是你需要创建类的实例。
Class controller = Class.forName("some.package"+controllers.getString(i));
Object myInstance = controller.newInstance();
然后你从它那里得到你的方法,并称之为:
Method myMethod = ...
myMethod.invoke(myInstance, null); // or with some params
如果你只需要调用“指数”,你也可以让你的控制器实现共同的接口,然后:
MyInterface myInterface = (MyInterface) myInstance;
myInterface.index();
希望有所帮助。
我试过这个'route.get(annotation.path())。handler(((Handler
显然,既然你索引方法avec一个参数,你需要提供它。 –
但invoke将实际执行该函数,我想将它作为回调传递。 Handler将提供参数回调函数 –
你不需要保证你所有的控制器都有这个'index'方法吗?通过让你的控制器扩展一些需要该索引方法的接口来解决这个问题。 – ifly6
索引函数将是他们的,但也会有一些自定义函数,这就是为什么我要使它动态化的原因 –
是的,只要该文件实现了接口,您的自定义函数就可以在文件中声明。但是,JVM并不知道您可以保证您的文件具有该方法。您可以保证通过让所有控制器实现一个通用接口。 – ifly6