使用通用控制器时,如何返回特定控制器固有的视图?

Leo*_*unt 6 java playframework-2.0

作为这个答案的结果:https://stackoverflow.com/a/10708026/694597,我想知道如何在使用通用控制器时返回特定控制器固有的视图.

Jul*_*Foy 6

在控制器操作中呈现视图时,只需调用由模板引擎生成的普通函数:

public Application extends Controller {
  public static Result index() {
    return ok(views.html.index.render(42));
  }
}
Run Code Online (Sandbox Code Playgroud)

这里,renderindex具有类型的对象的方法Template1<Integer, Html>.

现在的问题是:如何编写能够调用特定于另一个控制器的视图的通用控制器?或者简单地说:如何抽象视图

我看到两种解决方案:控制反射的反转.

让我们看看如何在一个简单的用例上实现它们.假设您有以下通用Shower<T>类能够计算包含任何类型值的HTML表示的HTTP响应T:

public class Shower<T> {
  public Result show(T value) {
    // TODO return an HTML representation of `value`
  }
}
Run Code Online (Sandbox Code Playgroud)

控制倒置

要实现Shower<T>使用控制反转,我们只需要注入Template1<T, Html>用于执行渲染的值:

public class Shower<T> {

  public final Template1<T, Html> template;

  public Shower(Template1<T, Html> template) {
    this.template = template;
  }

  public Result show(T value) {
    return ok(template.render(value));
  }

}
Run Code Online (Sandbox Code Playgroud)

要在控制器中使用它,请创建一个静态实例Shower<T>并将其注入要使用的模板:

public class Application extends Controller {
  public static Shower<Foo> foo = new Shower<Foo>(views.html.Foo.show.ref());
}
Run Code Online (Sandbox Code Playgroud)

反射

您可能会发现它太过于需要明确注入要用于每个实例的模板Shower<T>,因此您可能会想要通过反射来检索它,基于命名约定,例如显示类型的值Foo,只需查找对象show在包中命名views.html.Foo:

public class Shower<T> {

  private final Class<T> clazz;

  public Shower(Class<T> clazz) {
    this.clazz = clazz;
  }

  public Result show(T value) throws Exception {
    Class<?> object = Play.application().classLoader().loadClass("views.html." + clazz.getSimpleName() + ".show$");
    Template1<T, Html> template = (Template1<T, Html>)object.getField("MODULE$").get(null);
    return ok(template.render(value));
  }
}
Run Code Online (Sandbox Code Playgroud)

(这是使用反射访问Scala对象的方法)

您可以在控制器中按如下方式使用它:

public class Application extends Controller {
  public static Shower<Foo> foo = new Shower<Foo>(Foo.class);
}
Run Code Online (Sandbox Code Playgroud)

利弊

基于反射的解决方案在调用站点上需要较少的样板,但事实上它依赖于命名约定使其更加脆弱.此外,此解决方案仅在运行时失败时才会失败,而第一个解决方案将在编译时向您显示缺少的模板.最后但并非最不重要的是,基于反射的解决方案可能由于反射而增加一些性能开销.