公司项目一真用的是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());
}
//后面的代码省略......
}