最近从事的工作是web方面的,主要j2ee,spring jsp这些内容,由于刚入门,很多的技术都不了解。所谓初生牛犊不怕虎,刚入门,各种不顺手,比如写jsp,总是重复很多的代码,各种不爽,然后就去看jsp的模板技术,
看来看去也是各种不爽,于是就有了自己写一个工具的想法。
ok,废话一堆。先说说jsp模板,最简单的include,成熟的解决有tiles,还有什么setmesh,当日还有更强大的什么fremaker,volocity(是这样写的吗,记不清楚)等等。
include不说,实在太简陋,tiles其实是很不错的,不是很重,学习成本还算好,用的也比较广吧,当然要说广,肯定有后面那几个了。可是这些对我来说,都太重。我要的是什么样的?我记得rails里面的模板,大概就是layout里面写一句yield就ok了。
没错,这就是我想要的。写好layout,把什么header、menu什么的搞定,内容里面一个div套着一个yield,你就可以专注于你的子视图了,框架自动把layout应用到你的视图上去。
就这个简单的功能,为什么要那么复杂的框架、插件呢?
不过jsp,看看好像还真没有。那就自己实现吧,反正我是初入门,什么都不知道,不知者无畏嘛。
屁话一堆,总算到该说些正题了。
那么,怎么实现呢?我的思路是,layout里面写好布局后,正文部分就用${yield}代替,
当请求子视图的时候,自动的将子视图插入到layout中,替换${yield},然后生成一个新的合成视图,存入缓存文件中,由新的视图相应前端的请求。
我们知道spring里面有个配置viewresolver的类,可以在有请求的时候,查找视图,构建视图,并返回给前端。我们就在这里做我们的事情。
我们继承一个InternalResourceViewResolver,该resolver会根据视图查找相应的InternalResourceView来处理jsp视图。InternalResourceViewResolver有一个buildView的调用,该调用根据视图名称构建view。
我们就重载这个方法,根据前面的思路,首先找到模板文件和子视图,然后替换掉模板文件中的${yield},写入到新的jsp文件,然后重定向到该文件,就ok了。具体看代码:
1 package lin.layoutsample.framework; 2 3 import lin.layoutsample.utils.FileUtils; 4 import lin.layoutsample.utils.StringUtils; 5 6 import org.springframework.web.servlet.view.AbstractUrlBasedView; 7 import org.springframework.web.servlet.view.InternalResourceViewResolver; 8 9 public class LayoutViewResolver extends InternalResourceViewResolver { 10 11 private static String DEFUALT_YIELD_CMD = "${yield}"; 12 private static String DEFUALT_LAYOUT_TEMP_DIR = "/WEB-INF/layout/layout_temp"; 13 private static String DEFUALT_LAYOUT = "/WEB-INF/layout/layout.jsp"; 14 15 /** 16 * layout文件。默认为/WEB-INF/layout/layout.jsp。 17 * */ 18 private String layout = DEFUALT_LAYOUT; 19 20 /** 21 * 经过替换后的视图缓存目录,默认为/WEB-INF/layout/layout_temp。 22 * */ 23 private String temp_path = DEFUALT_LAYOUT_TEMP_DIR; 24 25 /** 26 * 默认为${yield}。模板中该命令会被子视图替换。类似rails的yield。 27 * */ 28 private String yield_cmd = DEFUALT_YIELD_CMD; 29 30 /** 31 * 32 * 函数首先根据指定的view在缓存区生成对应的文件,然后将模板文件(默认为layout.jsp)中的${yield}替换成视图的内容。 33 * 再调用超类的buildview函数生成新的被视图并返回。这样就完成了模板的替换和重定向到新生成的视图。 34 * */ 35 @Override 36 protected AbstractUrlBasedView buildView(String viewName) throws Exception { 37 String url = getPrefix() + viewName + getSuffix(); 38 String target_view_real_path = super.getServletContext().getRealPath( 39 getTemp_path()) 40 + url; 41 FileUtils.createFile(target_view_real_path); 42 String layout_real_path = super.getServletContext().getRealPath( 43 getLayout()); 44 String layout_content = FileUtils.read(layout_real_path); 45 String view_content = FileUtils.read(super.getServletContext() 46 .getRealPath(url)); 47 String result_view_content = StringUtils.replace(layout_content, "\\Q" 48 + getYield_cmd() + "\\E", view_content); 49 FileUtils.write(target_view_real_path, result_view_content); 50 51 AbstractUrlBasedView view = super.buildView(getTemp_path() + getPrefix()+ viewName); 52 return view; 53 } 54 55 public String getLayout() { 56 return layout; 57 } 58 59 public void setLayout(String layout) { 60 this.layout = layout; 61 } 62 63 public String getTemp_path() { 64 return temp_path; 65 } 66 67 public void setTemp_path(String temp_path) { 68 this.temp_path = temp_path; 69 } 70 71 public String getYield_cmd() { 72 return yield_cmd; 73 } 74 75 public void setYield_cmd(String yield_cmd) { 76 this.yield_cmd = yield_cmd; 77 } 78 }
是不是很简单?只需要一个方法,然后就是在web的配置里面将默认的InternalResourceViewResolver 替换成我们的就可以了。
1 <?xml version="1.0" encoding="UTF-8"?> 2 <beans xmlns="http://www.springframework.org/schema/beans" 3 xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 4 xmlns:p="http://www.springframework.org/schema/p" xmlns:context="http://www.springframework.org/schema/context" 5 xmlns:dwr="http://www.directwebremoting.org/schema/spring-dwr" 6 xmlns:security="http://www.springframework.org/schema/security" 7 xsi:schemaLocation="http://www.springframework.org/schema/beans 8 http://www.springframework.org/schema/beans/spring-beans.xsd 9 http://www.springframework.org/schema/context 10 http://www.springframework.org/schema/context/spring-context.xsd 11 http://www.springframework.org/schema/mvc 12 http://www.springframework.org/schema/mvc/spring-mvc.xsd 13 http://www.directwebremoting.org/schema/spring-dwr 14 http://www.directwebremoting.org/schema/spring-dwr-2.0.xsd 15 http://www.springframework.org/schema/security 16 http://www.springframework.org/schema/security/spring-security-3.2.xsd 17 "> 18 19 <!-- 自动扫描controller包下的所有类,使其认为spring mvc的控制器 --> 20 <context:component-scan base-package="lin.layoutsample.controller" /> 21 22 23 <!-- 通过注解,把URL映射到Controller上,该标签默认注册DefaultAnnotationHandlerMapping和AnnotationMethodHandlerAdapter --> 24 <mvc:annotation-driven /> 25 26 27 <!-- 对模型视图名称的解析,即在模型视图名称添加前后缀 --> 28 <bean class="lin.layoutsample.framework.LayoutViewResolver"> 29 <property name="prefix" value="/" /> 30 <property name="suffix" value=".jsp" /> 31 <property name="layout" value="/WEB-INF/layout/layout.jsp" /> 32 </bean> 33 </beans>
这里我们可以配置我们的模板文件(一般叫layout.jsp)的路径,当然你也可以不用${yield}来占位,你可以自己配置成属性就行了。
完整的layout.jsp文件如下,路径我一般放在/WEB-INF/layout/下面。
1 <%@ page language="java" contentType="text/html; charset=UTF-8" 2 pageEncoding="UTF-8"%> 3 4 <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%> 5 6 <!DOCTYPE html> 7 <html> 8 <% 9 String path = request.getContextPath(); 10 String basePath = request.getScheme() + "://" 11 + request.getServerName() + ":" + request.getServerPort() 12 + path + "/"; 13 %> 14 <base href="<%=basePath%>"> 15 <head> 16 <%@include file="/WEB-INF/layout/_include.jsp"%> 17 <title>花花世界</title> 18 </head> 19 <body> 20 <%@include file="/WEB-INF/layout/_header.jsp"%> 21 22 <div class="container-fluid"> 23 <div class="row"> 24 <%@ include file="/WEB-INF/layout/_menu.jsp"%> 25 <div class="page-content"> 26 <div class="container-fluid" data-role="main"> 27 <!-- Main component for a primary marketing message or call to action --> 28 29 ${yield} 30 31 </div> 32 <!-- container-fluid --> 33 </div> 34 <!-- page-content --> 35 </div> 36 37 38 </div> 39 <!-- /container --> 40 41 <%@include file="/WEB-INF/layout/_import.jsp"%> 42 </body> 43 </html>
子视图就很简单了只需要一行示例代码就行了,文件say-hi.jsp如下。
1 <h2>hi world!!</h2>
我们的控制器完全不需要考虑模板的事情,新建一个子视图返回就行了。
1 package lin.layoutsample.controller; 2 3 import org.slf4j.Logger; 4 import org.slf4j.LoggerFactory; 5 import org.springframework.stereotype.Controller; 6 import org.springframework.web.bind.annotation.RequestMapping; 7 import org.springframework.web.servlet.ModelAndView; 8 9 10 @Controller 11 @RequestMapping("/hello") 12 public class HelloController { 13 private Logger logger = LoggerFactory.getLogger(HelloController.class); 14 15 @RequestMapping("/say-hi") 16 public ModelAndView sayHi(){ 17 logger.debug("HelloController:sayHi"); 18 ModelAndView mv = new ModelAndView("WEB-INF/views/say-hi"); 19 return mv; 20 } 21 }
效果如下图:
是不是很简单,对开发者来说,只需要配置一下,别的都不需要关心,就可以完成模板布局的应用了。
不过我是初学,入门,哪些商业的模板工具自然有他得用武之地,我这个超轻量级的模板解决方案,写写小网页还是足够了。
有兴趣的tx可以去github上下载我的代码https://github.com/linbirg/wish。
欢迎大神批评指正。