新版 MVC 1.0 (Ozark RI) 介绍

来源:开源中国社区 作者:oschina
  

MVC 1.0 是一个在 JSR 371 下的新  Java EE 8 规范。它被设计成在 JAX-RS API (直到某个时刻,Servlet API  还在讨论中)顶层的一个面向行为的框架,并希望这是一个选择,是面向组件的 JSF, 而不是一个替代品。总而言之,MVC 1.0 是一个不同标准的 Java EE 方法,它被用来构建 Java EE平台(相当接近 Spring MVC )的 Web 应用程序。如果你想要控制你的逻辑,你就会想提供一个 URI 空间来进行完全控制,而 MVC 1.0 可能就是个对的选择。

MVC 1.0 中的M、V和C

假设你现在已经知道,一般而言,模型指的是应用程序的数据,视图则是应用程序的数据展现,还有控制器是用来管理输入、应用逻辑调用、模型的更新以及决定哪个视图应该被渲染的系统响应。在 MVC 1.0 中, 它们之间的关联如图1所示。

Ozark1
图 1: MVC 1.0

模型 (M)

MVC 1.0 增强的模型基本上就是一个定义在javax.mvc.Models中的HashMap, 如下所示。最常见的情况是,你会通过控制器 (JAX-RS 类)来操作这个map,以提供一个名字和对象之间的映射:

1
2
3
4
// MVC 1.0 - javax.mvc.Models 的源代码
public interface Models extends Map<String,
   Object>, Iterable<String> {
}

这个 HashMap 将会被控制器逻辑操作,并且会对视图进行更新。

注意: 如果你决定编写一个 MVC 1.0 实现, 那你就必须提供一个针对Models接口的实现。

尽管 Models 模型必须被所有的视图引擎支持, 不过MVC还带来了另外一种基于CDI (@Named)的模型。CDI 模型相比Models而言更受推荐, 但它也是可选的! 如你稍后将会看到的, MVC 1.0的官方实现也支持 CDI 模型。其它的实现并不会被强制要有 CDI 模型!

视图(V)

存储于模型中的数据应该通过视图(模板)显示出来。为此,视图可以使用特定于视图类型的占位符。每一个占位符都将会被来自于模型的对应数据替换。例如,如果你提供了其中有一个作者属性的Book对象给下面的这些视图,那你就会得到相同的结果 (最常见的就是你将使用EL来指代来自于模型的对应数据):

    JSP 视图: ${book.author}

    Facelets 视图: #{book.author}

    Handlebars 视图: {{book.author}}

    Thymeleaf 视图: #{book.author}

如你在图1中所看到的,视图是有如下所示的 ViewEngine 来呈现的:

1
2
3
4
5
6
7
8
9
10
// MVC 1.0 - source code of javax.mvc.engine.ViewEngine
public interface ViewEngine {
   String VIEW_FOLDER =
      "javax.mvc.engine.ViewEngine.viewFolder";
   String DEFAULT_VIEW_FOLDER = "/WEB-INF/views/";
 
   boolean supports(String view);
   void processView(ViewEngineContext context)
      throws ViewEngineException;
}

ViewEngine 的任务就是混合模型和视图,而如你将会看到的, 官方的 MVC 1.0 实现带来了几个视图引擎 (首要的视图引擎是针对 JSP 和 Facelets 的)。换言之,视图引擎能够从不同的模型那里获取到数据,然后将需要的视图渲染(产生HTML标记)出来。开发者也可以编写他们自己的引擎,而且如你在本文中将会看到的,这并不是一项非常困难的任务。

控制器 (C)

控制器是应用程序的“大脑”; 它负责结合数据模型和视图,以此来服务于用户的请求(显示应用程序的页面)。在 MVC 1.0 中, 控制器是以 JAX-RS 风格来实现的。对于初学者和有经验的Java EE开发者而言,这都大大降低了其学习曲线。一旦你了解了如何编写JAX-RS资源, 那也就了解了如何编写 MVC 1.0 的控制器,反之亦然。而这两者之前有几个重要的不同和类似之处。

MVC 1.0 和 JAX-RS 主要的不同之处

MVC 1.0 和 JAX-RS 主要的不同如下:

  • MVC 控制器就是一个在类型/方法上带有注解(javax.mvc.annotation.Controller)的JAX-RS 资源。

  • @Controller 可以在类型或者方法级别使用。如果你有一个 JAX-RS 资源,它里面带有一个实际上是MVC控制器的方法子集,这就非常有用。这是一个混合型的类,既可以扮演JAX-RS的角色,也可以作为一个 MVC 控制器。

  • MVC 类必须只能是被CDI管理的bean(并非 JAX-RS 本地类, EJB, 被管理的BEAN, 等等这些)。这一限制对于混合型的类也是如此。这样的类必须是被CDI管理的bean。

  •  由MVC控制器返回的字符串会被理解成一个视图路径而非文本内容(例如,指向一个JSP页面)。因此要对这个方面留点心,因为JAX-RS资源可能会返回内容文本,而 MVC 控制器不会。

  • 对于一个响应而言,默认的媒体类型被假定成 text/html, 不过另外还是可以使用 @Produces 来声明其它类型,就像 JAX-RS 中一样。

  • 返回为空(void)的MVC控制器必须用 @View 注解在类型/方法上进行装饰,以指定要显示的视图。

  • 我们可以通过 javax.mvc.Viewable 类来封装一个视图路径,以及与其处理过程相关的额外的信息。

  • 方法 toString() 会在其它(译者注:被返回的)Java类型上被调用,而其执行结果会被理解成一个视图路径。

  • 返回非空类型的 MVC 控制器可能也会用 @View 来装饰。在这种情况下, @View 指向的是这个控制器的默认视图。默认视图只会在返回非空类型的控制器返回为空(null)时被使用到。

MVC 1.0 和 JAX-RS 主要的类似之处
 

     MVC 1.0 和 JAX-RS 之间主要的类似之处如下:

  • 所有在JAX-RS资源中可以被注入的参数类型在MVC控制器中也是可以使用的。

  • 资源类实例默认的声明周期,在JAVA-RS和MVC中都是每个请求期间内(借助CDI,实现可能会支持其他的生命周期)。      

  • 在其它生命周期中适应于JAX-RS类的注意事项,同样适用于MVC类。

MVC 注解简介

    MVC 1.0 带来了如下这些注解:

  • @Controller (javax.mvc.annotation.Controller):  应用于类级别, 它定义了一个MVC控制器。应用于方法级别,它定义了一个混合型的类(控制器和JAX-RS资源)。

  • @View (javax.mvc.annotation.View): 应用于类级别, 它指向所用返回为空(void)的控制器方法的视图。应用于方法级别,它会指向返回为空(void)的控制器方法的视图, 或者指向返回为非空类型的控制器方法返回为null时的视图 (也就是默认视图)。

  • @CsrfValid (javax.mvc.annotation.CsrfValid): 只可以被应用在方法上,并且在调用控制器之前需要有一个CSRF令牌必须被验证通过。验证失败的话就会借助 ForbiddenException发出信号。

  • @RedirectScoped (javax.mvc.annotation.RedirectScoped): 可以被应用于类型,方法或者域级别; 它所指向的那个特定的bean在重定向作用域中。

基于动作 vs 基于组件

当我们说到 基于动作 同 基于组件的比较时,指的是 MVC  1.0 同 JSF 的比较。显而易见,JSF是一项成熟的技术,发不过几次重要的版本,而MVC1.0只是一个在Java EE 8上才刚刚新登场的规范而已。他们都是基于MVC的,风格迥异。好吧,我们不会在 MVC 1.0 和 JSF的比较上太过坚持, 不过你最好把如下图标中所示的一些事实牢记于心。首先,将模型、视图和控制在MVC1.0和JSF之间做一个区分很重要。图2不言自明。

Ozark2

图 2: JSF MVC

基于动作的MVC 基于组件的MVC
Web页面的设计需要开发者自己掌握(他们可以从大返回的技术面上做选择,诸如 HTML5, JS, UI 库,等等)。 组件通过框架在页面中庸HTML/JS代码来渲染。页面作者将使用框架提供的UI组件的集合,而且可以选择原生的HTML。
手动处理请求参数 自动处理请求参数
跟踪验证/会话的任务的重担要开发者自己承担。 框架会按照开发者的配置来完成对会话和验证的管理。
视图不保持,请求无状态。 状态在多个请求间默认会(在客户端或者服务端上)被维持, 但JSF也支持无状态。
以请求为中心 以页面为中心
对可重复使用的行为支持有线 组件实现了行为的可重复使用。

表1:基于动作 vs 基于组件

如图3中所示, MVC 1.0 设计近观:

Ozark3
图 3: MVC 1.0 风格

用户的每一个请求都将会被MVC基于指定路径来分配给合适的控制器(控制器类的一个实例会被初始化并且每次请求都会初始化)。控制器是用开发者来编写的,并且在同一个应用程序中可以有多个。依赖于进入的请求路径,这些可以是纯粹的 MVC 控制器或者同时是MVC 控制器和 JAX-RS 资源的混合型类。此外,路径也许指向的是应该对该请求进行处理的动作方法。一般,这个动作方法将对模型进行操作,并且指向应该向用户显示的视图。视图会反应当前的模型数据 (大多数一般是通过 EL)。JSF的设计就像图 4 中所描述的这样:

Ozark4
图 4: JSF-MVC 风格

在JSF中,控制器是一个叫做 FacesServlet 的 servlet,而用户不能对其进行修改/扩展。这个servlet负责处理所有应该经历JSF生命周期或者指向JSF资源的用户请求。FacesServlet 在请求类型之间做了区分,并据此来进行任务的委托。JSF的生命周期是一个基于两个阶段的复杂的"机械化的“声明周期:执行然后渲染。执行阶段有如下五个过程: 视图重置,应用请求值,处理验证,更新模型值,然后调用应用程序。渲染阶段包含一个过程,叫做渲染响应。这个过程负责渲染出显示给用户看到视图。每个请求都会经历从执行阶段到渲染阶段的所有或者部分阶段。

注意: 在 JSF 和 MVC 做出选择所要依赖的因素有许多,不过你可以牢记于心的是,它们都提供了额外的模板引擎并且可以无状态的进行使用。综合起来比较一个MVC框架,JSF实际的表现要更好。论及代码,有大量的类似之处。不过,还是有大量的基础性的差异,也不不能让你立刻见得那么明显。还有就是最后但不是最重要的,你的考虑到自己关于JSF和MVC的专业知识掌握程序。

Ozark RI 概述
 

MVC 1.0 的参考实现被命名为 Ozark。你可以从图5中了解到Ozark主要的功能特性:

Ozark5
图 5: MVC 1.0 参考实现 (Ozark)

Ozark 有两个模型: 模型的首要实现以及可选的CDI模型。此外, Ozark 还有3中视图引擎 : ServletViewEngine, JspViewEngine, 以及 FaceletsViewEngine。当然,你还可以通过实现 ViewEngine 或者扩展现有的视图引擎来实现更多的视图。

HelloWorld 示例
 

这一节我们从一个非常简单的Hello World示例开始。基本上,我们将会有一个HTML开始页面/视图(index.html),它里面会包含一个指向Ozark控制器的动作的链接(路径)。这个动作只会简单的返回另外一个页面/视图 (hello.html)。

首先,我们需要配置 Ozark 应用程序(就像是一个 JAX-RS 应用程序), 意思就是对应用程序响应请求的基础URI进行一下设置。稍后我们会对此进行仔细的研究,而现在我们先用最简单的方式来完成这个任务:

1
2
3
@ApplicationPath("resources")
public class HelloApplication extends Application {
}

此外我们可以编写HTML页面,非常地简单。  index.html 页面的相关代码如下:

1
<a href="resources/hello">Hello World!</a>

href 属性的值呈现的是一个指向我们的控制器/动作的路径。这个路径包含了基础的URI(resources)以及一个相对URI(hello) ,它们之间用一个斜线 "/" 分隔开来:

1
2
3
4
5
6
7
8
9
10
11
12
13
import javax.mvc.annotation.Controller;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
 
@Controller
@Path("hello")
public class HelloController {
 
    @GET
    public String helloAction() {
       return "/hello.html";
    }
   }

第5行: 在这一行,你能看到有一个@Controller注解。这个注解是 MVC 1.0 特有的,而且是标识出这是一个 MVC 1.0 控制器的地标。你也能将它用在方法上,只对 helloAction() 方法加上这个注解,不过那样做对于混合型类 (Ozark 控制器/JAX-RS 资源) 而言更常见。

第6行: 此外,我们使用@Path注解来指向相对于控制器的URI。因为我们只有一个方法(动作), 所以我们可以说类级别上的@Path最后会被解析到 helloAction() 方法。当控制器中有多个方法(动作)时,就需要在方法(动作)级别上也使用@Path了。每个方法(动作)都会拥有它自己的路径。

第9行: 因为我们倾向于通过HTML的<a/>标识来抵达helloAction(), 我们会声明这个动作能够处理的只能是GET请求。HTML的 <a/> 会触发GET请求。

第11行: 在这一行有一个有趣的细节。这里,我们声明了在这个动作的影响完成之后视图应该被渲染显示给用户。在我们这里,视图被命名为 hello.html。因为视图被存储在和index.html相同的目录 (在webapp目录中), 我们需要为其路径带上一个斜线 "/"作为前缀。没有这个斜线的话,hello.html就不会被定位/发现。这是稍后会讨论的另外一个主题了。因此,hello.html简单得令人有点尴尬:

1
<h1>Hello World! (HTML page)</h1>

整个应用程序被叫做 HelloWorld。

在这个示例中,我们使用了 HTML 页面,不过如我们早前所说, Ozark 也已经为 Servlet, JSP, 以及 Facelets 都声明了视图引擎。这就意味着 HelloWorld 应用程序可以使用 JSP, Facelets, 或者 Servlet 重写,方法非常容易。

在 JSP 的方式中,我们只是简单地吧 index.html 和 hello.html 重命名为 index.jsp 和 hello.jsp。这样就行了!只是为了好玩,你可以先下面这样编写index.jsp  (整个应用程序被叫做 HelloWorldJSP):

1
2
3
4
<body>
   <% String str = "resources/hello";%>
   <a href="<%=str%>">Hello World!</a>
</body>

在 Servlet 的方式中, 需要展示给用户的内容是由 HelloServlet 返回的.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@WebServlet("/HelloServlet")
public class HelloServlet extends HttpServlet {
   ...
   protected void processRequest(HttpServletRequest
         request, HttpServletResponse response)
      throws ServletException, IOException {
         response.setContentType("text/html;charset=UTF-8");
      try (PrintWriter out = response.getWriter()) {
         ...
         out.println("<h1>Hello World! (Servlet)</h1>");
         ...
      }
   }
   ...
}

控制器只是简单地指向这个 Servlet:

1
2
3
4
@GET
public String helloAction() {
   return "/HelloServlet";
}

整个应用程序被叫做 HelloWorldServlet。

现在,最后一种情况留给了 Facelets。你的意识到 Facelets 的支持默认是不启用的。使用Facelets的 MVC应用程序需要打包一个web.xml部署描述符,带有如下代码所示的 *.xhtml 扩展名映射 (你也可以使用前缀映射 (例如. /faces/*):

1
2
3
4
5
6
7
8
9
10
11
<servlet>
   <servlet-name>Faces Servlet</servlet-name>
   <servlet-class>
      javax.faces.webapp.FacesServlet
   </servlet-class>
   <load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
   <servlet-name>Faces Servlet</servlet-name>
   <url-pattern>*.xhtml*</url-pattern>
</servlet-mapping>

如果你的应用程序是以一个Facelet开始的, 那么你就需要也对欢迎页面进行一下配置。 例如:

1
2
3
<welcome-file-list&gt
   <welcome-file>index.xhtml</welcome-file>
</welcome-file-list>

如果你的应用程序并不是以一个Facelets页面开始的(例如,是从一个JSP/HTML页面开始的), 那么你就不需要这个部分了,并且仍然将Facelets用于剩下的页面,除了开始页面之外。

注意: 遵照 MVC 1.0 规范,值得注意的是如果你选择使用Facelets作为MVC应用程序的视图技术,一般的 POST回传就不会被MVC运行时处理了。

完整的应用程序被叫做HelloWorldFacelets

 
本文转自:开源中国社区 [http://www.oschina.net]
本文标题:新版 MVC 1.0 (Ozark RI) 介绍
本文地址:http://www.oschina.net/translate/introduction-to-the-new-mvc-1.0-ozark-ri
参与翻译:leoxu, 无若

英文原文:Introduction to the New MVC 1.0 (Ozark RI)


时间:2016-06-10 21:55 来源:开源中国社区 作者:oschina 原文链接

好文,顶一下
(0)
0%
文章真差,踩一下
(0)
0%
------分隔线----------------------------


把开源带在你的身边-精美linux小纪念品
无觅相关文章插件,快速提升流量