2016-07-05 106 views
1

如何在回調函數中傳遞方法類變量?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

回答

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"修改爲功能接口的功能方法的實際名稱。如果類型參數有綁定,則必須將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

謝謝,這工作:) 界面看起來乾淨和容易,但如果我有在控制器中的動態方法更清潔的方式去更進一步。 –

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將提供參數回調函數 –