比较Web框架:Seam

发布者:    |       Seam

最近,Simon Brown整理了一套需求,用于比较Java Web框架的简单博客应用。我对他所列出的实际需求有所保留(特别是没有表单提交!),但既然其他框架 作者 已经参与其中,我也将示例移植到了Seam。我想对这篇文章提出一个重大的警告:Seam绝对不是为博客或Web论坛等应用设计的;使用像PHP或Ruby on Rails这样的工具解决这类问题非常简单,真的没有理由使用Java来解决这类问题(除非Java是你唯一知道的语言)。这里的需求没有任何/对话/和/业务流程/,所以Seam所有复杂的状态管理机制都是多余的。尽管如此,框架需要让简单的事情变得简单,你将看到使用Seam解决这个简单问题需要编写多少Java代码。

首先,我从Seamweb.xml, faces-config.xml, application.xmlbuild.xml文件中复制了标准Seamfaces-config.xml预订演示,在几个地方更改了名称,并从中删除了所有JSF导航规则。这些内容都不重要,在Seam应用中总是几乎相同。我还复制了Simon的

screen.css样式表。Simon从一个领域模型开始,其中包含BlogBlogEntry类,在真实的应用程序中,这些类将通过Hibernate或EJB3映射到数据库,还有一个

@Name("blog")
@Startup
@Scope(APPLICATION)
public class BlogService 
{

   private Blog blog;

   @Create
   public void initBlog()
   {
      blog = new Blog();
      blog.setName("Webapp framework blog");
      blog.setDescription("Comparison of J2EE web application frameworks");
      blog.setLocale(new Locale("en", "AU"));
      blog.setTimeZone(TimeZone.getTimeZone("PST"));
      blog.addBlogEntry(new BlogEntry(...);
      blog.addBlogEntry(new BlogEntry(...);
      blog.addBlogEntry(new BlogEntry(...);
   }
  
  @Unwrap
  public Blog getBlog()
  {
     return blog;
  }

}

BlogService样式表。Simon从一个领域模型开始,其中包含类,该类实现了一个静态单例,包含一些测试数据。在Seam中实现单例的静态变量和静态初始化器是一种非常丑陋的方式,所以我冒险将这个类重写为一个应用范围的Seam组件。

这就是开始处理Web应用第一页所需的所有内容。

应用的所有页面都有一些常见的头部信息,所以我们将使用一个facelets模板,template.xhtml,来定义这些共同的东西。

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
                      "[=>http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd]">
<html xmlns="[=>http://www.w3.org/1999/xhtml]"
      [=>xmlns:ui=]"=>http://java.sun.com/jsf/facelets"
      [=>xmlns:h=]"=>http://java.sun.com/jsf/html"
      [=>xmlns:f=]"=>http://java.sun.com/jsf/core"
      [=>xml:lang=]"en"  
      lang="en">
      
<f:view>
   <head>
      <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
      <title>#{blog.name}</title>
      <link href="screen.css" rel="stylesheet" type="text/css" />
   </head>
   
   <body>
      <div id="container">
         <h1>#{blog.name}</h1>
         <h2>#{blog.description}</h2>
         <[=>ui:insert] name="content"/>
      </div>
   </body>
</f:view>

</html>

注意,这全都是带有命名空间的纯XML,没有奇怪的标签。

索引页,index.xhtml,在示例应用中显示了三条最新的博客条目。

<!DOCTYPE composition PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
                             "[=>http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd]">
<[=>ui:composition] xmlns="=>http://www.w3.org/1999/xhtml"
    [=>xmlns:ui=]"=>http://java.sun.com/jsf/facelets"
    [=>xmlns:h=]"=>http://java.sun.com/jsf/html"
    [=>xmlns:f=]"=>http://java.sun.com/jsf/core"
    template="template.xhtml">

<[=>ui:define] name="content">
   <h:dataTable value="#{blog.recentBlogEntries}" var="blogEntry" rows="3">
      <h:column>
         <div class="blogEntry">
            <h3>#{blogEntry.title}</h3>
            <div>
               #{blogEntry.excerpt==null ? blogEntry.body : blogEntry.excerpt}
            </div>
            <p>
               <h:outputLink value="entry.seam" rendered="#{blogEntry.excerpt!=null}">
                  <f:param name="blogEntryId" value="#{blogEntry.id}"/>
                  Read more
               </h:outputLink>
            </p>
            <p>
               Posted on 
               <h:outputText value="#{blogEntry.date}">
                  <f:convertDateTime timeZone="#{blog.timeZone}" locale="#{blog.locale}" type="both"/>
               </h:outputText>
            </p>
         </div>
      </h:column>
   </h:dataTable>
</[=>ui:define]>

</[=>ui:composition]>

这当然也是纯XML。

(通常,我无需在模板中显式定义区域设置。JSF和Seam默认使用请求区域设置。但Simon的要求说明我必须使用样式表。.)

现在,如果我们访问 http://localhost:8080/seam-blog/index.seam,这是结果

https://hibernate.com.cn/~gavin/blog_index.png

现在对于博客条目页(通过点击阅读更多来访问)。假设我已经正确理解了它们,Simon的要求说明如果请求一个不存在的条目,我们应该发送404错误并转发到错误页。在JSF或Seam中,这并不是最自然的事情。通常,JSF应用程序在处理GET请求时使用式MVC。通常,我会编写条目页来处理不存在条目的情况(这很容易)。但是Simon是这里的老板,他的要求需要式设计。我们将使用Seam的/page操作/。

@Name("entryAction")
public class EntryAction
{
   @In private Blog blog;
   @In private FacesContext facesContext;
 
   @RequestParameter
   private String blogEntryId;
   
   @Out(scope=EVENT, required=false)
   private BlogEntry blogEntry;

   public void getBlogEntry() throws IOException
   {
      blogEntry = blog.getBlogEntry(blogEntryId);
      if (blogEntry==null)
      {
         HttpServletResponse response = (HttpServletResponse) facesContext.getExternalContext().getResponse();
         response.sendError(HttpServletResponse.SC_NOT_FOUND);
         facesContext.responseComplete();
      }
   }

}

这个操作旨在在条目页渲染之前运行。它从单例实例中检索请求的Simon从一个领域模型开始,其中包含并将其注入到事件上下文中。如果没有样式表。与请求参数匹配,它将发送404错误。Simon从一个领域模型开始,其中包含我们需要在

WEB-INF/pages.xml中列出这个页面操作。.

<pages>
   <page view-id="/entry.xhtml" action="#{entryAction.getBlogEntry}"/>
</pages>

现在我们可以编写条目页,entry.xhtml:

<!DOCTYPE composition PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
                             "[=>http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd]">
<[=>ui:composition] xmlns="=>http://www.w3.org/1999/xhtml"
                [=>xmlns:ui=]"=>http://java.sun.com/jsf/facelets"
                [=>xmlns:h=]"=>http://java.sun.com/jsf/html"
                [=>xmlns:f=]"=>http://java.sun.com/jsf/core"
                template="template.xhtml">

<[=>ui:define] name="content">
   <div class="blogEntry">
      <h3>#{blogEntry.title}</h3>
      <div>#{blogEntry.body}</div>
      <p>
         Posted on 
         <h:outputText value="#{blogEntry.date}">
            <f:convertDateTime timezone="#{blog.timeZone}" locale="#{blog.locale}" type="both"/>
         </h:outputText>
      </p>
   </div>
</[=>ui:define]>

</[=>ui:composition]>

以及404错误页,404.xhtml:

<!DOCTYPE composition PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
                             "[=>http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd]">
<[=>ui:composition] xmlns="=>http://www.w3.org/1999/xhtml"
                [=>xmlns:ui=]"=>http://java.sun.com/jsf/facelets"
                [=>xmlns:h=]"=>http://java.sun.com/jsf/html"
                [=>xmlns:f=]"=>http://java.sun.com/jsf/core"
                template="template.xhtml">

<[=>ui:define] name="content">
   <h3>Page not found</h3>
</[=>ui:define]>

</[=>ui:composition]>

这个页面需要在web.xml:

<error-page>
   <error-code>404</error-code>
   <location>/404.seam</location>
</error-page>

中列出。

现在如果我点击阅读更多链接,我就会访问URL http://localhost:8080/seam-blog/entry.seam?blogEntryId=3 并看到以下内容

https://hibernate.com.cn/~gavin/blog_entry.png

如果我将URL的id更改为6,我将得到一个404错误

https://hibernate.com.cn/~gavin/blog_404.png


社区资源 我们的GitHub组织 提交错误 我们的论坛 报告安全问题