Java 在回调中将字符串变量作为函数传递
Java pass a string variable as a function in a callback
如何在回调中传递方法 class 变量?
我有以下代码:
PostsController postController = new PostsController();
router.get("/").handler(postController::index);
但是我的controller会是dynamic的,我怎么传dynamicclass和它的函数。
我尝试了以下代码
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?
帖子控制器
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("{}"));
}
}
我能看到的第一件事是,您需要创建 class 的实例。
Class controller = Class.forName("some.package"+controllers.getString(i));
Object myInstance = controller.newInstance();
然后你从中得到你的方法,并调用它:
Method myMethod = ...
myMethod.invoke(myInstance, null); // or with some params
如果你只需要调用"index",你也可以让你的controller实现一个通用的接口,然后:
MyInterface myInterface = (MyInterface) myInstance;
myInterface.index();
希望对您有所帮助。
最干净的解决方案是使用 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(); }};
但这并不能改变您将很晚才检测到错误的事实……
如何在回调中传递方法 class 变量?
我有以下代码:
PostsController postController = new PostsController();
router.get("/").handler(postController::index);
但是我的controller会是dynamic的,我怎么传dynamicclass和它的函数。 我尝试了以下代码
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?
帖子控制器
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("{}"));
}
}
我能看到的第一件事是,您需要创建 class 的实例。
Class controller = Class.forName("some.package"+controllers.getString(i));
Object myInstance = controller.newInstance();
然后你从中得到你的方法,并调用它:
Method myMethod = ...
myMethod.invoke(myInstance, null); // or with some params
如果你只需要调用"index",你也可以让你的controller实现一个通用的接口,然后:
MyInterface myInterface = (MyInterface) myInstance;
myInterface.index();
希望对您有所帮助。
最干净的解决方案是使用 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(); }};
但这并不能改变您将很晚才检测到错误的事实……