fix template bug

This commit is contained in:
Looly
2020-03-10 00:51:16 +08:00
parent 30e73d4b5a
commit 0781be4e1c
14 changed files with 308 additions and 132 deletions

View File

@@ -2,45 +2,58 @@ package cn.hutool.extra.template;
import java.io.Serializable;
import java.nio.charset.Charset;
import java.util.Objects;
import cn.hutool.core.util.CharsetUtil;
/**
* 模板配置
*
*
* @author looly
* @since 4.1.0
*/
public class TemplateConfig implements Serializable {
private static final long serialVersionUID = 2933113779920339523L;
/** 编码 */
public static final TemplateConfig DEFAULT = new TemplateConfig();
/**
* 编码
*/
private Charset charset;
/** 模板路径如果ClassPath或者WebRoot模式则表示相对路径 */
/**
* 模板路径如果ClassPath或者WebRoot模式则表示相对路径
*/
private String path;
/** 模板资源加载方式 */
/**
* 模板资源加载方式
*/
private ResourceMode resourceMode;
/**
* 自定义引擎当多个jar包引入时可以自定使用的默认引擎
*/
private Class<? extends TemplateEngine> customEngine;
/**
* 默认构造使用UTF8编码默认从ClassPath获取模板
*/
public TemplateConfig() {
this((String)null);
this(null);
}
/**
* 构造默认UTF-8编码
*
*
* @param path 模板路径如果ClassPath或者WebRoot模式则表示相对路径
*/
public TemplateConfig(String path) {
this(path, ResourceMode.STRING);
}
/**
* 构造默认UTF-8编码
*
* @param path 模板路径如果ClassPath或者WebRoot模式则表示相对路径
*
* @param path 模板路径如果ClassPath或者WebRoot模式则表示相对路径
* @param resourceMode 模板资源加载方式
*/
public TemplateConfig(String path, ResourceMode resourceMode) {
@@ -49,9 +62,9 @@ public class TemplateConfig implements Serializable {
/**
* 构造
*
* @param charset 编码
* @param path 模板路径如果ClassPath或者WebRoot模式则表示相对路径
*
* @param charset 编码
* @param path 模板路径如果ClassPath或者WebRoot模式则表示相对路径
* @param resourceMode 模板资源加载方式
*/
public TemplateConfig(Charset charset, String path, ResourceMode resourceMode) {
@@ -62,21 +75,21 @@ public class TemplateConfig implements Serializable {
/**
* 获取编码
*
*
* @return 编码
*/
public Charset getCharset() {
return charset;
}
/**
* 获取编码
*
*
* @return 编码
* @since 4.1.11
*/
public String getCharsetStr() {
if(null == this.charset) {
if (null == this.charset) {
return null;
}
return this.charset.toString();
@@ -84,7 +97,7 @@ public class TemplateConfig implements Serializable {
/**
* 设置编码
*
*
* @param charset 编码
*/
public void setCharset(Charset charset) {
@@ -93,7 +106,7 @@ public class TemplateConfig implements Serializable {
/**
* 获取模板路径如果ClassPath或者WebRoot模式则表示相对路径
*
*
* @return 模板路径
*/
public String getPath() {
@@ -102,7 +115,7 @@ public class TemplateConfig implements Serializable {
/**
* 设置模板路径如果ClassPath或者WebRoot模式则表示相对路径
*
*
* @param path 模板路径
*/
public void setPath(String path) {
@@ -111,7 +124,7 @@ public class TemplateConfig implements Serializable {
/**
* 获取模板资源加载方式
*
*
* @return 模板资源加载方式
*/
public ResourceMode getResourceMode() {
@@ -120,67 +133,81 @@ public class TemplateConfig implements Serializable {
/**
* 设置模板资源加载方式
*
*
* @param resourceMode 模板资源加载方式
*/
public void setResourceMode(ResourceMode resourceMode) {
this.resourceMode = resourceMode;
}
/**
* 获取自定义引擎null表示系统自动判断
*
* @return 自定义引擎null表示系统自动判断
* @since 5.2.1
*/
public Class<? extends TemplateEngine> getCustomEngine() {
return customEngine;
}
/**
* 设置自定义引擎null表示系统自动判断
*
* @param customEngine 自定义引擎null表示系统自动判断
* @return this
* @since 5.2.1
*/
public TemplateConfig setCustomEngine(Class<? extends TemplateEngine> customEngine) {
this.customEngine = customEngine;
return this;
}
/**
* 资源加载方式枚举
*
*
* @author looly
*/
public enum ResourceMode {
/** 从ClassPath加载模板 */
/**
* 从ClassPath加载模板
*/
CLASSPATH,
/** 从File目录加载模板 */
/**
* 从File目录加载模板
*/
FILE,
/** 从WebRoot目录加载模板 */
/**
* 从WebRoot目录加载模板
*/
WEB_ROOT,
/** 从模板文本加载模板 */
/**
* 从模板文本加载模板
*/
STRING,
/** 复合加载模板分别从File、ClassPath、Web-root、String方式尝试查找模板 */
/**
* 复合加载模板分别从File、ClassPath、Web-root、String方式尝试查找模板
*/
COMPOSITE
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((charset == null) ? 0 : charset.hashCode());
result = prime * result + ((path == null) ? 0 : path.hashCode());
result = prime * result + ((resourceMode == null) ? 0 : resourceMode.hashCode());
return result;
public boolean equals(Object o) {
if (this == o){
return true;
}
if (o == null || getClass() != o.getClass()){
return false;
}
TemplateConfig that = (TemplateConfig) o;
return Objects.equals(charset, that.charset) &&
Objects.equals(path, that.path) &&
resourceMode == that.resourceMode &&
Objects.equals(customEngine, that.customEngine);
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
TemplateConfig other = (TemplateConfig) obj;
if (charset == null) {
if (other.charset != null) {
return false;
}
} else if (!charset.equals(other.charset)) {
return false;
}
if (path == null) {
if (other.path != null) {
return false;
}
} else if (!path.equals(other.path)) {
return false;
}
return resourceMode == other.resourceMode;
public int hashCode() {
return Objects.hash(charset, path, resourceMode, customEngine);
}
}

View File

@@ -2,17 +2,25 @@ package cn.hutool.extra.template;
/**
* 引擎接口,通过实现此接口从而使用对应的模板引擎
*
*
* @author looly
*/
public interface TemplateEngine {
/**
* 使用指定配置文件初始化模板引擎
*
* @param config 配置文件
* @return this
*/
TemplateEngine init(TemplateConfig config);
/**
* 获取模板
*
*
* @param resource 资源,根据实现不同,此资源可以是模板本身,也可以是模板的相对路径
* @return 模板实现
*/
Template getTemplate(String resource);
}

View File

@@ -1,5 +1,6 @@
package cn.hutool.extra.template.engine;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.ServiceLoaderUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.template.TemplateConfig;
@@ -35,9 +36,15 @@ public class TemplateFactory {
* @return {@link Engine}
*/
private static TemplateEngine doCreate(TemplateConfig config) {
final TemplateEngine engine = ServiceLoaderUtil.loadFirstAvailable(TemplateEngine.class);
final Class<? extends TemplateEngine> customEngineClass = config.getCustomEngine();
final TemplateEngine engine;
if(null != customEngineClass){
engine = ReflectUtil.newInstance(customEngineClass);
}else{
engine = ServiceLoaderUtil.loadFirstAvailable(TemplateEngine.class);
}
if(null != engine){
return engine;
return engine.init(config);
}
throw new TemplateException("No template found ! Please add one of template jar to your project !");

View File

@@ -23,15 +23,13 @@ import cn.hutool.extra.template.TemplateEngine;
*/
public class BeetlEngine implements TemplateEngine {
private final GroupTemplate engine;
private GroupTemplate engine;
// --------------------------------------------------------------------------------- Constructor start
/**
* 默认构造
*/
public BeetlEngine() {
this(new TemplateConfig());
}
public BeetlEngine() {}
/**
* 构造
@@ -39,7 +37,7 @@ public class BeetlEngine implements TemplateEngine {
* @param config 模板配置
*/
public BeetlEngine(TemplateConfig config) {
this(createEngine(config));
init(config);
}
/**
@@ -48,12 +46,30 @@ public class BeetlEngine implements TemplateEngine {
* @param engine {@link GroupTemplate}
*/
public BeetlEngine(GroupTemplate engine) {
this.engine = engine;
init(engine);
}
// --------------------------------------------------------------------------------- Constructor end
@Override
public TemplateEngine init(TemplateConfig config) {
init(createEngine(config));
return this;
}
/**
* 初始化引擎
* @param engine 引擎
*/
private void init(GroupTemplate engine){
this.engine = engine;
}
@Override
public Template getTemplate(String resource) {
if(null == this.engine){
init(TemplateConfig.DEFAULT);
}
return BeetlTemplate.wrap(engine.getTemplate(resource));
}
@@ -65,7 +81,7 @@ public class BeetlEngine implements TemplateEngine {
*/
private static GroupTemplate createEngine(TemplateConfig config) {
if (null == config) {
config = new TemplateConfig();
config = TemplateConfig.DEFAULT;
}
switch (config.getResourceMode()) {

View File

@@ -1,17 +1,14 @@
package cn.hutool.extra.template.engine.enjoy;
import org.beetl.core.GroupTemplate;
import com.jfinal.template.source.FileSourceFactory;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.extra.template.Template;
import cn.hutool.extra.template.TemplateConfig;
import cn.hutool.extra.template.TemplateConfig.ResourceMode;
import cn.hutool.extra.template.TemplateEngine;
import com.jfinal.template.source.FileSourceFactory;
import org.beetl.core.GroupTemplate;
/**
* Enjoy库的引擎包装
@@ -29,9 +26,7 @@ public class EnjoyEngine implements TemplateEngine {
/**
* 默认构造
*/
public EnjoyEngine() {
this(new TemplateConfig());
}
public EnjoyEngine() {}
/**
* 构造
@@ -39,8 +34,7 @@ public class EnjoyEngine implements TemplateEngine {
* @param config 模板配置
*/
public EnjoyEngine(TemplateConfig config) {
this(createEngine(config));
this.resourceMode = config.getResourceMode();
init(config);
}
/**
@@ -49,12 +43,33 @@ public class EnjoyEngine implements TemplateEngine {
* @param engine {@link com.jfinal.template.Engine}
*/
public EnjoyEngine(com.jfinal.template.Engine engine) {
this.engine = engine;
init(engine);
}
// --------------------------------------------------------------------------------- Constructor end
@Override
public TemplateEngine init(TemplateConfig config) {
if(null == config){
config = TemplateConfig.DEFAULT;
}
this.resourceMode = config.getResourceMode();
init(createEngine(config));
return this;
}
/**
* 初始化引擎
* @param engine 引擎
*/
private void init(com.jfinal.template.Engine engine){
this.engine = engine;
}
@Override
public Template getTemplate(String resource) {
if(null == this.engine){
init(TemplateConfig.DEFAULT);
}
if (ObjectUtil.equal(ResourceMode.STRING, this.resourceMode)) {
return EnjoyTemplate.wrap(this.engine.getTemplateByString(resource));
}
@@ -68,7 +83,6 @@ public class EnjoyEngine implements TemplateEngine {
* @return {@link GroupTemplate}
*/
private static com.jfinal.template.Engine createEngine(TemplateConfig config) {
Assert.notNull(config, "Template config is null !");
final com.jfinal.template.Engine engine = com.jfinal.template.Engine.create("Hutool-Enjoy-Engine-" + IdUtil.fastSimpleUUID());
engine.setEncoding(config.getCharsetStr());

View File

@@ -26,9 +26,7 @@ public class FreemarkerEngine implements TemplateEngine {
/**
* 默认构造
*/
public FreemarkerEngine() {
this(new TemplateConfig());
}
public FreemarkerEngine() {}
/**
* 构造
@@ -36,7 +34,7 @@ public class FreemarkerEngine implements TemplateEngine {
* @param config 模板配置
*/
public FreemarkerEngine(TemplateConfig config) {
this(createCfg(config));
init(config);
}
/**
@@ -45,12 +43,32 @@ public class FreemarkerEngine implements TemplateEngine {
* @param freemarkerCfg {@link Configuration}
*/
public FreemarkerEngine(Configuration freemarkerCfg) {
this.cfg = freemarkerCfg;
init(freemarkerCfg);
}
// --------------------------------------------------------------------------------- Constructor end
@Override
public TemplateEngine init(TemplateConfig config) {
if(null == config){
config = TemplateConfig.DEFAULT;
}
init(createCfg(config));
return this;
}
/**
* 初始化引擎
* @param freemarkerCfg Configuration
*/
private void init(Configuration freemarkerCfg){
this.cfg = freemarkerCfg;
}
@Override
public Template getTemplate(String resource) {
if(null == this.cfg){
init(TemplateConfig.DEFAULT);
}
try {
return FreemarkerTemplate.wrap(this.cfg.getTemplate(resource));
} catch(IOException e) {

View File

@@ -21,9 +21,7 @@ public class RythmEngine implements TemplateEngine {
/**
* 默认构造
*/
public RythmEngine() {
this(new TemplateConfig());
}
public RythmEngine() {}
/**
* 构造
@@ -31,7 +29,7 @@ public class RythmEngine implements TemplateEngine {
* @param config 模板配置
*/
public RythmEngine(TemplateConfig config) {
this(createEngine(config));
init(config);
}
/**
@@ -40,13 +38,33 @@ public class RythmEngine implements TemplateEngine {
* @param engine {@link org.rythmengine.RythmEngine}
*/
public RythmEngine(org.rythmengine.RythmEngine engine) {
this.engine = engine;
init(engine);
}
// --------------------------------------------------------------------------------- Constructor end
@Override
public TemplateEngine init(TemplateConfig config) {
if(null == config){
config = TemplateConfig.DEFAULT;
}
init(createEngine(config));
return this;
}
/**
* 初始化引擎
* @param engine 引擎
*/
private void init(org.rythmengine.RythmEngine engine){
this.engine = engine;
}
@Override
public Template getTemplate(String resource) {
return RythmTemplate.wrap(engine.getTemplate(resource));
if(null == this.engine){
init(TemplateConfig.DEFAULT);
}
return RythmTemplate.wrap(this.engine.getTemplate(resource));
}
/**

View File

@@ -28,9 +28,7 @@ public class ThymeleafEngine implements TemplateEngine {
/**
* 默认构造
*/
public ThymeleafEngine() {
this(new TemplateConfig());
}
public ThymeleafEngine() {}
/**
* 构造
@@ -38,8 +36,7 @@ public class ThymeleafEngine implements TemplateEngine {
* @param config 模板配置
*/
public ThymeleafEngine(TemplateConfig config) {
this(createEngine(config));
this.config = config;
init(config);
}
/**
@@ -48,12 +45,33 @@ public class ThymeleafEngine implements TemplateEngine {
* @param engine {@link org.thymeleaf.TemplateEngine}
*/
public ThymeleafEngine(org.thymeleaf.TemplateEngine engine) {
this.engine = engine;
init(engine);
}
// --------------------------------------------------------------------------------- Constructor end
@Override
public TemplateEngine init(TemplateConfig config) {
if(null == config){
config = TemplateConfig.DEFAULT;
}
this.config = config;
init(createEngine(config));
return this;
}
/**
* 初始化引擎
* @param engine 引擎
*/
private void init(org.thymeleaf.TemplateEngine engine){
this.engine = engine;
}
@Override
public Template getTemplate(String resource) {
if(null == this.engine){
init(TemplateConfig.DEFAULT);
}
return ThymeleafTemplate.wrap(this.engine, resource, (null == this.config) ? null : this.config.getCharset());
}

View File

@@ -21,9 +21,7 @@ public class VelocityEngine implements TemplateEngine {
/**
* 默认构造
*/
public VelocityEngine() {
this(new TemplateConfig());
}
public VelocityEngine() {}
/**
* 构造
@@ -31,7 +29,7 @@ public class VelocityEngine implements TemplateEngine {
* @param config 模板配置
*/
public VelocityEngine(TemplateConfig config) {
this(createEngine(config));
init(config);
}
/**
@@ -40,10 +38,27 @@ public class VelocityEngine implements TemplateEngine {
* @param engine {@link org.apache.velocity.app.VelocityEngine}
*/
public VelocityEngine(org.apache.velocity.app.VelocityEngine engine) {
this.engine = engine;
init(engine);
}
// --------------------------------------------------------------------------------- Constructor end
@Override
public TemplateEngine init(TemplateConfig config) {
if(null == config){
config = TemplateConfig.DEFAULT;
}
init(createEngine(config));
return this;
}
/**
* 初始化引擎
* @param engine 引擎
*/
private void init(org.apache.velocity.app.VelocityEngine engine){
this.engine = engine;
}
/**
* 获取原始的引擎对象
*
@@ -56,6 +71,9 @@ public class VelocityEngine implements TemplateEngine {
@Override
public Template getTemplate(String resource) {
if(null == this.engine){
init(TemplateConfig.DEFAULT);
}
return VelocityTemplate.wrap(engine.getTemplate(resource));
}

View File

@@ -27,11 +27,17 @@ public class TemplateUtilTest {
@Test
public void createEngineTest() {
// 默认模板引擎此处为Beetl
// 字符串模板, 默认模板引擎此处为Beetl
TemplateEngine engine = TemplateUtil.createEngine(new TemplateConfig());
Template template = engine.getTemplate("hello,${name}");
String result = template.render(Dict.create().set("name", "hutool"));
Assert.assertEquals("hello,hutool", result);
// classpath中获取模板
engine = TemplateUtil.createEngine(new TemplateConfig("templates", ResourceMode.CLASSPATH));
Template template2 = engine.getTemplate("beetl_test.btl");
String result2 = template2.render(Dict.create().set("name", "hutool"));
Assert.assertEquals("hello,hutool", result2);
}
@Test
@@ -52,7 +58,8 @@ public class TemplateUtilTest {
@Test
public void rythmEngineTest() {
// 字符串模板
TemplateEngine engine = new RythmEngine(new TemplateConfig("templates"));
TemplateEngine engine = TemplateUtil.createEngine(
new TemplateConfig("templates").setCustomEngine(RythmEngine.class));
Template template = engine.getTemplate("hello,@name");
String result = template.render(Dict.create().set("name", "hutool"));
Assert.assertEquals("hello,hutool", result);
@@ -66,13 +73,15 @@ public class TemplateUtilTest {
@Test
public void freemarkerEngineTest() {
// 字符串模板
TemplateEngine engine = new FreemarkerEngine(new TemplateConfig("templates", ResourceMode.STRING));
TemplateEngine engine = TemplateUtil.createEngine(
new TemplateConfig("templates", ResourceMode.STRING).setCustomEngine(FreemarkerEngine.class));
Template template = engine.getTemplate("hello,${name}");
String result = template.render(Dict.create().set("name", "hutool"));
Assert.assertEquals("hello,hutool", result);
//ClassPath模板
engine = new FreemarkerEngine(new TemplateConfig("templates", ResourceMode.CLASSPATH));
engine = TemplateUtil.createEngine(
new TemplateConfig("templates", ResourceMode.CLASSPATH).setCustomEngine(FreemarkerEngine.class));
template = engine.getTemplate("freemarker_test.ftl");
result = template.render(Dict.create().set("name", "hutool"));
Assert.assertEquals("hello,hutool", result);
@@ -81,13 +90,15 @@ public class TemplateUtilTest {
@Test
public void velocityEngineTest() {
// 字符串模板
TemplateEngine engine = new VelocityEngine(new TemplateConfig("templates", ResourceMode.STRING));
TemplateEngine engine = TemplateUtil.createEngine(
new TemplateConfig("templates", ResourceMode.STRING).setCustomEngine(VelocityEngine.class));
Template template = engine.getTemplate("你好,$name");
String result = template.render(Dict.create().set("name", "hutool"));
Assert.assertEquals("你好,hutool", result);
//ClassPath模板
engine = new VelocityEngine(new TemplateConfig("templates", ResourceMode.CLASSPATH));
engine = TemplateUtil.createEngine(
new TemplateConfig("templates", ResourceMode.CLASSPATH).setCustomEngine(VelocityEngine.class));
template = engine.getTemplate("templates/velocity_test.vtl");
result = template.render(Dict.create().set("name", "hutool"));
Assert.assertEquals("你好,hutool", result);
@@ -97,13 +108,15 @@ public class TemplateUtilTest {
@Test
public void enjoyEngineTest() {
// 字符串模板
TemplateEngine engine = new EnjoyEngine(new TemplateConfig("templates"));
TemplateEngine engine = TemplateUtil.createEngine(
new TemplateConfig("templates").setCustomEngine(EnjoyEngine.class));
Template template = engine.getTemplate("#(x + 123)");
String result = template.render(Dict.create().set("x", 1));
Assert.assertEquals("124", result);
//ClassPath模板
engine = new EnjoyEngine(new TemplateConfig("templates", ResourceMode.CLASSPATH));
engine = new EnjoyEngine(
new TemplateConfig("templates", ResourceMode.CLASSPATH).setCustomEngine(EnjoyEngine.class));
template = engine.getTemplate("enjoy_test.etl");
result = template.render(Dict.create().set("x", 1));
Assert.assertEquals("124", result);
@@ -112,13 +125,15 @@ public class TemplateUtilTest {
@Test
public void thymeleafEngineTest() {
// 字符串模板
TemplateEngine engine = new ThymeleafEngine(new TemplateConfig("templates"));
TemplateEngine engine = TemplateUtil.createEngine(
new TemplateConfig("templates").setCustomEngine(ThymeleafEngine.class));
Template template = engine.getTemplate("<h3 th:text=\"${message}\"></h3>");
String result = template.render(Dict.create().set("message", "Hutool"));
Assert.assertEquals("<h3>Hutool</h3>", result);
//ClassPath模板
engine = new ThymeleafEngine(new TemplateConfig("templates", ResourceMode.CLASSPATH));
engine = TemplateUtil.createEngine(
new TemplateConfig("templates", ResourceMode.CLASSPATH).setCustomEngine(ThymeleafEngine.class));
template = engine.getTemplate("thymeleaf_test.ttl");
result = template.render(Dict.create().set("message", "Hutool"));
Assert.assertEquals("<h3>Hutool</h3>", result);