使用通用控制器时,如何返回特定控制器固有的视图?
作为这个答案的结果: https : //stackoverflow.com/a/10708026/694597 ,我想知道如何在使用通用控制器时返回特定控制器固有的视图。
在控制器操作中呈现视图时,只需调用由模板引擎生成的普通函数:
public Application extends Controller { public static Result index() { return ok(views.html.index.render(42)); } }
这里, render
是一个对象index
的方法,它的类型为Template1
。
现在的问题是:如何编写能够调用特定于另一个控制器的视图的通用控制器? 或者简单地说: 如何抽象视图 ?
我看到两种解决方案: 控制和reflection的 反转 。
让我们看看如何在一个简单的用例上实现它们。 假设您有以下通用Shower
类,它能够计算包含任何T
类型值的HTML表示的HTTP响应:
public class Shower { public Result show(T value) { // TODO return an HTML representation of `value` } }
控制倒置
要使用控制反转来实现Shower
,我们只需要注入用于执行渲染的Template1
值:
public class Shower { public final Template1 template; public Shower(Template1 template) { this.template = template; } public Result show(T value) { return ok(template.render(value)); } }
要在控制器中使用它,请创建一个Shower
的静态实例并将其注入要使用的模板:
public class Application extends Controller { public static Shower foo = new Shower (views.html.Foo.show.ref()); }
reflection
您可能会发现它太过于需要明确注入模板以用于Shower
每个实例,因此您可能会想要通过reflection来检索它,基于命名约定,例如显示Foo
类型的值,只需在包views.html.Foo
查找名为show
的对象:
public class Shower { private final Class clazz; public Shower(Class clazz) { this.clazz = clazz; } public Result show(T value) throws Exception { Class> object = Play.application().classLoader().loadClass("views.html." + clazz.getSimpleName() + ".show$"); Template1 template = (Template1)object.getField("MODULE$").get(null); return ok(template.render(value)); } }
(这是使用reflection访问Scala对象的方法)
您可以在控制器中按如下方式使用它:
public class Application extends Controller { public static Shower foo = new Shower (Foo.class); }
优点和缺点
基于reflection的解决方案在调用站点上需要较少的样板 ,但事实上它依赖于命名约定使其更加脆弱 。 此外,此解决方案仅在运行时失败时才会失败,而第一个解决方案将在编译时向您显示缺少的模板。 最后但并非最不重要的是,基于reflection的解决方案可能由于reflection而增加一些性能开销 。