
这是一个服务端 (支持 JSP 和 FreeMaker) 页面布局工具,特点是简单,无 XML,仅用 400 行源码实现了与 Apache Tiles 类似的页面布局功能。
目前一些服务端 JSP 页面布局工具的缺点:
- Apache Tiles: 功能强大但过于臃肿,源码复杂,第三方库引用多,XML 配置不方便,动态配置功能差。
- Sitemesh: 采用装饰器模式,功能不如 Apache Tiles 灵活。
- JSP Layout 或 Stripes 等 JSP 布局工具:功能不够强,在布局的继承或参数传递上有问题。
JWebBox 特点:
- 简单, 整个项目仅 400 行源码,易于学习和维护。
- 与 jBeanBox 和 jSqlBox 项目类似,用纯 JAVA 类代替 XML 配置(实际上前两个项目是受此项目启发),支持动态配置,配置可以在运行期动态生成和修改。
- 无侵入性,支持 JSP 和 FreeMaker 两种模板混用。可用于整个网站的服务端布局,也可用于编写页面局部零件。
- 支持静态方法、实例方法、URL 引用三种数据准备方式。
jWebBox2.1 版本更新:
添加 FreeMaker 模板支持; 增加一个 JSP 标签; 添加了表格、分页、表单处理的演示; 更正 WebLogic 不能运行的 bug.
使用方法:
在项目的 pom.xml 中添加如下内容:
<dependency>
<groupId>com.github.drinkjava2</groupId>
<artifactId>jwebbox</artifactId>
<version>2.1</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.0.1</version> <!-- 或其它版本 -->
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>javax.servlet.jsp-api</artifactId>
<version>2.3.1</version> <!-- 或其它版本 -->
<scope>provided</scope>
</dependency>
jWebBox 运行于 Java6 或以上,依赖于 javax.servlet-api 和 javax.servlet.jsp-api 这两个运行期库 (通常由 Servlet 容器提供)。
详细介绍
以下通过对示例的解释来详细说明 jWebBox 的使用,示例项目源码位于项目的 jwebbox-demo 目录下,在项目的根目录,也有一个打包好的 jwebbox-demo.war 文件,可直接扔到 Tomcat 或 WebLogic 里运行。
示例 1 – 一个带菜单和底脚的左右布局
服务端代码如下:
public static class demo1 extends WebBox {
{ this.setPage("/WEB-INF/pages/homepage.jsp");
this.setAttribute("menu",
new WebBox("/WEB-INF/pages/menu.jsp").setAttribute("msg", "Demo1 - A basic layout"));
this.setAttribute("body", new LeftRightLayout());
this.setAttribute("footer", "/WEB-INF/pages/footer.jsp");
}
}
public static class LeftRightLayout extends WebBox {
{ this.setPage("/WEB-INF/pages/left_right_layout.jsp");
ArrayList<Object> boxlist = new ArrayList<Object>();
boxlist.add("/WEB-INF/pages/page1.jsp");
boxlist.add("/WEB-INF/pages/page2.jsp");
this.setAttribute("boxlist", boxlist);
}
}
其中 homepage.jsp 是主模板文件,主要内容如下:
<#assign box=JspTaglibs["http://ift.tt/2DNnDUY"] />
<html>
<body>
<div id="temp_content">
<div id="temp_menu">
<@box.show attribute="menu" />
</div>
<@box.show attribute="body" />
<div id="temp_footer">
<@box.show attribute="footer" />
</div>
</div>
</body>
</html>
left_right_layout.jsp 是一个布局模板,内容如下 (其它的 JSP 文件类似,此处略,详见示例):
<%@ taglib prefix="box" uri="http://ift.tt/2DNnDUY"%>
<div id="temp_left" style="margin: 10px; width: 430px; float: left; background-color:#CCFFCC;">
<box:show target="${jwebbox.attributeMap.boxlist[0]}" />
</div>
<div id="temp_right" style="margin: 10px; float: right; width: 430px;background-color:#FFFFCC;">
<box:show target="${jwebbox.attributeMap.boxlist[1]}" />
</div>
解释:
- setPage 方法用于设定当前 WebBox 实例的目标页面 (可选),WebBox 构造器允许带一个页面参数。
- setAttribute 方法在 WebBox 的一个内部 HashMap 中暂存一个键值,值可以为任意 Java 对象类型,相应地取值用 getAttribute 方法,在 JSP 中可以用 EL 表达式${jwebbox.attributeMap.keyname} 获取。
- 在 JSP 页面中调用<box:show attribute=”body” /> 标签来显示对应键值的页面,值只能是 String、WebBox 实例或它们的 List。
- show 标签的另一个用法是<box:show target=”xxx”/>, target 只能是 String、WebBox 或 List。如下 5 种写法在 JSP 中是等同的:
<box:show attribute="menu" />
<box:show target="${jwebbox.attributeMap.menu}" />
<% WebBox.showAttribute(pageContext,"menu");%>
<% WebBox.showTarget(pageContext, WebBox.getAttribute(pageContext,"menu"));%>
<% ((WebBox)WebBox.getAttribute(pageContext,"menu")).show(pageContext);%> //仅当 menu 属性为 WebBox 对象时
后三种写法不推荐,但有助于理解 WebBox 的运作机制。每个被 WebBox 调用的页面,都在 request 中存在一个 WebBox 实例,可以用 request.getAttribute(“jwebbox”) 或 EL 表达式${jwebbox} 获取。
- show 标签使用时必须在 JSP 页面加入 TagLib 库的引用:<%@ taglib prefix=”box” uri=”http://github.com/drinkjava2/jwebbox”%>
- 每个 WebBox 实例,可以设定一个可选的 name 属性,每个页面用只能获取属于自已的一个 WebBox 实例,但是可以用 getFatherWebBox 方法获取当前 WebBox 实例的调用者所在页面的 WebBox 实例 (有点绕口)。
- 在 JSP 和 Servlet 中,jWebBox 支持在页面中动态生成 WebBox 实例并调用 show 方法显示,例如:<% new WebBox(“/somepage.jsp”).setPrepareStaticMethod(“xxx”).show(pageContext); %>
- 本示例项目中运用了一个小技巧,利用一个 Servlet 将所有”.htm” 后缀的访问转化对 WebBox 的创建和显示,在 web.xml 中配置如下
<servlet>
<servlet-name>htm2box</servlet-name>
<jsp-file>/htm2box.jsp</jsp-file>
</servlet>
<servlet-mapping>
<servlet-name>htm2box</servlet-name>
<url-pattern>*.htm</url-pattern>
</servlet-mapping>
其中 htm2box.jsp 当作 Servlet 来使用,作用类似于 Spring MVC 中的 DispatcherServlet:
<%@page import="org.apache.commons.lang.StringUtils"%><%@page import="com.github.drinkjava2.jwebbox.WebBox"%><%
String uri=StringUtils.substringBefore(request.getRequestURI(),".");
uri = StringUtils.substringAfterLast(uri, "/");
if (uri == null || uri.length() == 0)
uri = "demo1";
WebBox box = (WebBox) Class.forName("com.github.drinkjava2.jwebboxdemo.DemoBoxConfig$" + uri).newInstance();
box.show(pageContext);
%>
示例 1 的输出:
示例 2 – 布局的继承
服务端代码:
public static class demo2 extends demo1 {
{ ((WebBox) this.getAttribute("menu")).setAttribute("msg", "Demo2 - Change body layout");
this.setAttribute("body", new TopDownLayout());
}
}
public static class TopDownLayout extends LeftRightLayout {
{ this.setPage("/WEB-INF/pages/top_down_layout.jsp");
}
}
demo2 继承于 demo1 类,将”body” 属性改成了一个上下布局 top_down_layout.jsp 模板 (源码见示例)。
示例 2 的输出:
示例 3 – 数据准备
服务端代码:
public static class demo3 extends demo1 {
{ setPrepareStaticMethod(DemoBoxConfig.class.getName() + ".changeMenu");
setAttribute("body", new WebBox().setText("<div style=\"width:900px\"> This is body text </div>")
.setPrepareURL("/WEB-INF/pages/prepare.jsp").setPrepareBean(new Printer()));
setAttribute("footer", new WebBox("/WEB-INF/pages/footer.jsp").setPrepareBean(new Printer())
.setPrepareBeanMethod("print"));
}
}
public static void changeMenu(PageContext pageContext, WebBox callerBox) throws IOException {
((WebBox) callerBox.getAttribute("menu")).setAttribute("msg",
"Demo3 - Prepare methods <br/>This is modified by \"changeMenu\" static method");
}
public static class Printer {
public void prepare(PageContext pageContext, WebBox callerBox) throws IOException {
pageContext.getOut().write("This is printed by Printer's default \"prepare\" method <br/>");
}
public void print(PageContext pageContext, WebBox callerBox) throws IOException {
pageContext.getOut().write("This is printed by Printer's \"print\" method <br/>");
pageContext.getOut().write((String) pageContext.getRequest().getAttribute("urlPrepare"));
}
}
相比与普通的 Include 指令,Apache Tiles 和 jWebBox 这类布局工具的优势之一在于可以在各个子页面加载之前进行数据准备工作,从而达到模块式开发的目的。jWebBox 有三种数据准备方式:
- setPrepareStaticMethod 方法指定一个静态方法用于数据准备。
- setPrepareBean 方法指定一个对象实例用于数据准备,用 setPrepareBeanMethod 来指定对象的方法名,如果不指定方法名,将缺省使用”prepare” 作为方法名。
- setPrepareURL 方法将调用一个 URL 来作为数据谁备,这是一个服务端的 URL 引用,可以访问/WEB-INF 目录下的内容。
- setText 方法可以额外设置一小段文本,将直接作为 HTML 代码片段插入到子页面前面。
各个准备方法及页面输出的顺序如下:
prepareStaticMethod -> prepareBeanMethod -> PrepareURL -> text output -> page
示例 3 输出:
示例 4 – 列表
服务端代码:
public static class demo4 extends demo1 {
{
((WebBox) this.getAttribute("menu")).setAttribute("msg", "Demo4 - List");
ArrayList<Object> child = new ArrayList<Object>();
for (int i = 1; i <= 3; i++)
child.add(new WebBox("/WEB-INF/pages/page" + i + ".jsp").setText(" "));
ArrayList<Object> mainList = new ArrayList<Object>();
for (int i = 1; i <= 3; i++) {
mainList.add("/WEB-INF/pages/page" + i + ".jsp");
if (i == 2)
mainList.add(child);
}
this.setAttribute("body", mainList);
}
}
如果属性是一个列表,当 JSP 页面中调用<box:show attribute=”xxx” /> 方法时,如果值是一个 List, 将假定 List 中属性为页面或 WebBox 实例并依次显示。
示例 4 输出:
示例 5 – FreeMaker 模板支持
从 2.1 版起,jWebBox 开始支持 FreeMaker, 且可以与 JSP 混用,例如如下配置:
public static class demo5 extends WebBox {
{ this.setPage("/WEB-INF/pages/homepage.ftl");
this.setAttribute("menu",
new WebBox("/WEB-INF/pages/menu.jsp").setAttribute("msg", "Demo5 - Freemaker demo"));
this.setAttribute("body", new FreemakerLeftRightLayout());
this.setAttribute("footer", new WebBox("/WEB-INF/pages/footer.jsp"));
}
}
FreeMaker 不支持直接在页面嵌入 Java 代码,语法也与 JSP 不同,引入标签要写成<#assign box=JspTaglibs[“http://ift.tt/2CDveWC;] />, show 标签要写成<@box.show attribute=”menu” />
使用 FreeMaker, 需要在 web.xml 中添加如下配置:
<servlet>
<servlet-name>freemarker</servlet-name>
<servlet-class>freemarker.ext.servlet.FreemarkerServlet</servlet-class>
<init-param>
<param-name>TemplatePath</param-name>
<param-value>/</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>freemarker</servlet-name>
<url-pattern>*.ftl</url-pattern>
</servlet-mapping>
并在 pom.xml 中添加对 FreeMaker 库的依赖:
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>2.3.23</version> <!--或更新版-->
</dependency>
示例 5 输出:
示例 6 – 表格和分页演示
这个例子展示了利用 WebBox 配置的继承功能来创建表格和分页条组件,输出两个表格和分页条,并处理表单提交数据。因篇幅较长,此处只摘录布局部分代码:
public static class demo6 extends demo1 {
{
setAttribute("menu",
((WebBox) this.getAttribute("menu")).setAttribute("msg", "Demo6 - Table & Pagination"));
List<WebBox> bodyList = new ArrayList<WebBox>();
bodyList.add(new TableBox());
bodyList.add(new TablePaginBarBox());
bodyList.add(new WebBox().setText(
"<br/>-----------------------------------------------------------------------------------"));
bodyList.add(new CommentBox());
bodyList.add(new CommentPaginBarBox());
bodyList.add(new WebBox("/WEB-INF/pages/commentform.jsp"));
this.setPrepareStaticMethod(DemoBoxConfig.class.getName() + ".receiveCommentPost");
this.setAttribute("body", bodyList);
}
class TableBox extends WebBox {
{
this.setPrepareBean(new PrepareForDemo6()).setPrepareBeanMethod("prepareTable");
setPage("/WEB-INF/pages/page_table.jsp");
setAttribute("pageId", "table");
setAttribute("targetList", tableDummyData);
setAttribute("row", 3).setAttribute("col", 4);
setAttribute("render", new WebBox("/WEB-INF/pages/render_table.jsp"));
}
}
class TablePaginBarBox extends TableBox {
{
this.setPrepareBean(new PrepareForDemo6()).setPrepareBeanMethod("preparePaginBar");
setPage("/WEB-INF/pages/pagin_bar.jsp");
}
}
class CommentBox extends TableBox {
{
setAttribute("pageId", "comment");
setAttribute("targetList", commentDummyData);
setAttribute("row", 3).setAttribute("col", 1);
setAttribute("render", new WebBox("/WEB-INF/pages/render_comment.jsp"));
}
}
class CommentPaginBarBox extends CommentBox {
{
this.setPrepareBean(new PrepareForDemo6()).setPrepareBeanMethod("preparePaginBar");
setPage("/WEB-INF/pages/pagin_bar.jsp");
}
}
}
示例 6 截图:
以上即为 jWebBox 的全部说明文档,如有不清楚处,可以查看项目源码或示例项目的源码。
转自 http://ift.tt/2DQDT7l
The post jWebBox v2.1 发布,JSP/FreeMaker 服务端布局工具 appeared first on Linuxeden开源社区.
http://ift.tt/2CFfMtg
没有评论:
发表评论