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("{}")); 
    } 

} 
+4

你不需要保证你所有的控制器都有这个'index'方法吗?通过让你的控制器扩展一些需要该索引方法的接口来解决这个问题。 – ifly6

+0

索引函数将是他们的,但也会有一些自定义函数,这就是为什么我要使它动态化的原因 –

+2

是的,只要该文件实现了接口,您的自定义函数就可以在文件中声明。但是,JVM并不知道您可以保证您的文件具有该方法。您可以保证通过让所有控制器实现一个通用接口。 – ifly6

最简洁的解决方案是使用定义常用功能的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(); }}; 

但这并不改变的事实,你将只很晚检测错误...

+0

谢谢,我试过这个,但它抛出了这个错误: 'java.lang.AbstractMethodError:io.vertx.lib.AppVertical $$ Lambda $ 28/647430455.handle(Ljava/lang/Object;)V' –

+1

如上所述,你必须调整方法名称:'“接受”'→'“句柄”'。 – Holger

+0

谢谢,这工作:) 界面看起来干净和容易,但如果我有在控制器中的动态方法更清洁的方式去更进一步。 –

第一件事,我所看到的,是你需要创建类的实例。

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(); 

希望有所帮助。

+0

我试过这个'route.get(annotation.path())。handler(((Handler )m.invoke(controller.newInstance(),null)));''抛出一个错误为'java.lang .IllegalArgumentException:错误的参数数量' –

+0

显然,既然你索引方法avec一个参数,你需要提供它。 –

+0

但invoke将实际执行该函数,我想将它作为回调传递。 Handler将提供参数回调函数 –