公司项目一真用的是ssi(spring+springmvc+mybatis/ibatis)感觉还是比较重量级,所以找了个国人开发的JFinal框架学习,方便后续自己开发些东西的话能够快速开发。
但是JFinal默认使用的是freemarket模板引擎,可是一直以后用的都是velocity用顺手了模板引擎就不想变了,虽然支持velocity但不支持velocity的layout布局,也不支持toolbox。所以用JFinal之前先扩展这两个功能做为练手。
一惯的喜欢maven来进行管理代码,JFinal倡导的COC原则还是比较认同的,终于可以告别蛋疼的xml配置。
整完模板,以后开始弄注解方式@action @service和权限控制
<dependency> <groupId>org.apache.velocity</groupId> <artifactId>velocity</artifactId> <version>1.7</version> </dependency> <dependency> <groupId>org.apache.velocity</groupId> <artifactId>velocity-tools</artifactId> <version>2.0</version> </dependency>
下面是重写的VelocityRender类,为了区分自带的VelocityRender,命名为VelocityLayoutRender
package net.yunbiz.shop.web.reader; import java.io.PrintWriter; import java.io.StringWriter; import java.util.Enumeration; import java.util.Iterator; import java.util.Map.Entry; import java.util.Properties; import java.util.Set; import org.apache.velocity.Template; import org.apache.velocity.app.Velocity; import org.apache.velocity.app.VelocityEngine; import org.apache.velocity.exception.MethodInvocationException; import org.apache.velocity.exception.ParseErrorException; import org.apache.velocity.exception.ResourceNotFoundException; import org.apache.velocity.tools.Scope; import org.apache.velocity.tools.ToolManager; import org.apache.velocity.tools.view.ViewToolContext; import com.jfinal.core.JFinal; import com.jfinal.log.Logger; import com.jfinal.render.IMainRenderFactory; import com.jfinal.render.Render; import com.jfinal.render.RenderException; /** * @author zhiyong.xiongzy */ public class VelocityLayoutRender extends Render { private static final long serialVersionUID = 1012573049421601960L; private transient static final String encoding = getEncoding(); private transient static final String contentType = "text/html;charset=" + encoding; /** * The velocity.properties key for specifying the servlet's error template. */ public static final String PROPERTY_ERROR_TEMPLATE = "tools.view.servlet.error.template"; /** * The velocity.properties key for specifying the relative directory holding * layout templates. */ public static final String PROPERTY_LAYOUT_DIR = "tools.view.servlet.layout.directory"; /** * The velocity.properties key for specifying the servlet's default layout * template's filename. */ public static final String PROPERTY_DEFAULT_LAYOUT = "tools.view.servlet.layout.default.template"; /** * The default error template's filename. */ public static final String DEFAULT_ERROR_TEMPLATE = "error.html"; /** * The default layout directory */ public static final String DEFAULT_LAYOUT_DIR = "layout/"; /** * The default filename for the servlet's default layout */ public static final String DEFAULT_DEFAULT_LAYOUT = "default.html"; public static final String TOOLBOX_FILE = "WEB-INF/vm-toolbox.xml"; /** * The context key that will hold the content of the screen. * This key ($screen_content) must be present in the layout template for the * current screen to be rendered. */ public static final String KEY_SCREEN_CONTENT = "screen_content"; /** * The context/parameter key used to specify an alternate layout to be used * for a request instead of the default layout. */ public static final String KEY_LAYOUT = "layout"; /** * The context key that holds the {@link Throwable} that broke the rendering * of the requested screen. */ public static final String KEY_ERROR_CAUSE = "error_cause"; /** * The context key that holds the stack trace of the error that broke the * rendering of the requested screen. */ public static final String KEY_ERROR_STACKTRACE = "stack_trace"; /** * The context key that holds the {@link MethodInvocationException} that * broke the rendering of the requested screen. * If this value is placed in the context, then $error_cause will hold the * error that this invocation exception is wrapping. */ public static final String KEY_ERROR_INVOCATION_EXCEPTION = "invocation_exception"; protected static String errorTemplate; protected static String layoutDir; protected static String defaultLayout; private transient static final Properties properties = new Properties(); private transient static boolean notInit = true; public VelocityLayoutRender(String view) { this.view = view; } public static void setProperties(Properties properties) { Set<Entry<Object, Object>> set = properties.entrySet(); for (Iterator<Entry<Object, Object>> it = set.iterator(); it.hasNext();) { Entry<Object, Object> e = it.next(); VelocityLayoutRender.properties.put(e.getKey(), e.getValue()); } } public void render() { init(); PrintWriter writer = null; try { VelocityEngine velocityEngine = new VelocityEngine(); ViewToolContext context = new ViewToolContext(velocityEngine, request, response, JFinal.me().getServletContext()); ToolManager tm = new ToolManager(); tm.setVelocityEngine(velocityEngine); tm.configure(JFinal.me().getServletContext().getRealPath(TOOLBOX_FILE)); if (tm.getToolboxFactory().hasTools(Scope.REQUEST)) { context.addToolbox(tm.getToolboxFactory().createToolbox(Scope.REQUEST)); } if (tm.getToolboxFactory().hasTools(Scope.APPLICATION)) { context.addToolbox(tm.getToolboxFactory().createToolbox(Scope.APPLICATION)); } if (tm.getToolboxFactory().hasTools(Scope.SESSION)) { context.addToolbox(tm.getToolboxFactory().createToolbox(Scope.SESSION)); } for (@SuppressWarnings("unchecked") Enumeration<String> attrs = request.getAttributeNames(); attrs.hasMoreElements();) { String attrName = attrs.nextElement(); context.put(attrName, request.getAttribute(attrName)); } Template template = Velocity.getTemplate(view); StringWriter sw = new StringWriter(); template.merge(context, sw); context.put(KEY_SCREEN_CONTENT, sw.toString()); response.setContentType(contentType); writer = response.getWriter(); // BufferedWriter writer = new // BufferedWriter(new // OutputStreamWriter(System.out)); Object obj = context.get(KEY_LAYOUT); String layout = (obj == null) ? null : obj.toString(); if (layout == null) { // no alternate, use default layout = defaultLayout; } else { // make it a full(er) path layout = layoutDir + layout; } try { // load the layout template template = Velocity.getTemplate(layout); } catch (ResourceNotFoundException e) { Logger.getLogger(VelocityLayoutRender.class).error("Can't load layout \"" + layout + "\"", e); // if it was an alternate layout we couldn't get... if (!layout.equals(defaultLayout)) { // try to get the default layout // if this also fails, let the exception go try { template = Velocity.getTemplate(defaultLayout); } catch (ResourceNotFoundException e2) { Logger.getLogger(VelocityLayoutRender.class).error("Can't load layout \"" + defaultLayout + "\"", e2); } } } template.merge(context, writer); writer.flush(); // flush and cleanup } catch (ResourceNotFoundException e) { throw new RenderException("Error : cannot find template " + view, e); } catch (ParseErrorException e) { throw new RenderException("Syntax error in template " + view + ":" + e, e); } catch (Exception e) { throw new RenderException(e); } finally { if (writer != null) writer.close(); } } private void init() { if (notInit) { String webPath = JFinal.me().getServletContext().getRealPath("/"); properties.setProperty(Velocity.FILE_RESOURCE_LOADER_PATH, webPath); properties.setProperty(Velocity.ENCODING_DEFAULT, encoding); properties.setProperty(Velocity.INPUT_ENCODING, encoding); properties.setProperty(Velocity.OUTPUT_ENCODING, encoding); Velocity.init(properties); // setup layout errorTemplate = VelocityLayoutRender.properties.getProperty(PROPERTY_ERROR_TEMPLATE, DEFAULT_ERROR_TEMPLATE); layoutDir = VelocityLayoutRender.properties.getProperty(PROPERTY_LAYOUT_DIR, DEFAULT_LAYOUT_DIR); defaultLayout = VelocityLayoutRender.properties.getProperty(PROPERTY_DEFAULT_LAYOUT, DEFAULT_DEFAULT_LAYOUT); // preventive error checking! directory must end in / if (!layoutDir.endsWith("/")) { layoutDir += '/'; } // for efficiency's sake, make defaultLayout a full path now defaultLayout = layoutDir + defaultLayout; // log the current settings Logger.getLogger(VelocityLayoutRender.class).info("VelocityRender: Error screen is '" + errorTemplate + "'"); Logger.getLogger(VelocityLayoutRender.class).info("VelocityRender: Layout directory is '" + layoutDir + "'"); Logger.getLogger(VelocityLayoutRender.class).info("VelocityRender: Default layout template is '" + defaultLayout + "'"); notInit = false; } } public static final class VelocityLayoutRenderFactory implements IMainRenderFactory { public Render getRender(String view) { return new VelocityLayoutRender(view); } public String getViewExtension() { return ".html"; } } }
然后把在webapp下面建个目录layout用于放置layout文件,默认的layout文件路径是 layout/default.html
<!doctype html> <html> <title>Jfinal-velocity-layout-toolbox</title> </head> 我是layout页面内容 <div class="wrap"> $!{screen_content} </div> </body> </html>
toolbox配置文件不可少,vm-toolbox.xml,路径参考VelocityLayoutRender.TOOLBOX_FILE
暂时放几个常用的,后续自己可以扩展功能
<?xml version="1.0"?> <toolbox> <xhtml>true</xhtml> <tool> <key>stringUtils</key> <scope>application</scope> <class>org.apache.commons.lang.StringUtils</class> </tool> <tool> <key>esc</key> <scope>application</scope> <class>org.apache.velocity.tools.generic.EscapeTool</class> </tool> <tool> <key>number</key> <scope>application</scope> <class>org.apache.velocity.tools.generic.NumberTool</class> <parameter name="format" value="#0.00" /> </tool> <tool> <key>gson</key> <scope>application</scope> <class>com.google.gson.Gson </class> </tool> </toolbox>
最后一步就简单了,在Config中指定视图处理器
public class ShopConfig extends JFinalConfig { /** * 配置常量 */ public void configConstant(Constants me) { // 加载少量必要配置,随后可用getProperty(...)获取值 loadPropertyFile("a_little_config.txt"); me.setDevMode(getPropertyToBoolean("devMode", false)); RenderFactory.setMainRenderFactory(new VelocityLayoutRenderFactory()); } //后面的代码省略...... }