写在前面
- 模版化应用广泛,所有的网页都是借助模版化,将丰富多彩的数据展现在用户面前。对于后端开发而言也离不开模版,例如代码生成器,邮件短信提示文字内容,更如对于一些大型的分布式系统的配置文件生成等等。
模版其实和数据离不开的,一个模版只是规定了一部分最终内容的格式,最终内容的展示还需要将数据绑定至模版后实现。正常的流程为 获取模版&获取数据 ->模版数据渲染->生成最终内容
实现方式
- 最简单的模版可以是借助字符串拼接或者
String.format
。但这些仅仅只适用于一些简单模版。
String useName = "helloWorld";
String msg1 = "您好" +useName+ "监测到今天是您的生日,祝您生日快乐!";
String msg2 = String.format("您好%s,检测到今天是您的生日,祝您生日快乐!", useName);
Message.send(msg1);
Message.send(msg2);
- 但是对于一些较为复杂的内容,使用👆的方式显然加大了后期的维护难度,同时不利于系统的扩展性与延展性。这里我们借助apache freemarker,在我们的项目中实现一些复杂的模版渲染。
- 引入依赖(这里使用万金油的maven,当然您也可以选择你喜欢的任何打包工具)
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>2.3.29</version>
</dependency>
- 在我们的Resource目录下创建一个自定义文件夹,这里就叫ftl吧
- 在ftl目录下创建我们的模版
birthdayMsg.ftl
内容如下
您好,${useName},检测到今天是您的生日,祝您生日快乐!
- 创建一个模版渲染的工具类
getTemplate()
此方法为获取模版内容的方法
generate()
此方法为模版渲染数据的方法,为方便展示这里使用的数据格式用map描述,如果你喜欢,可以改为自建的pojo
/ftl
:这个为我们在resoruce目录下创建的目录文件夹,你可以改成自己喜欢的样子
package //package保密改成你自己的;
import freemarker.template.Configuration;
import freemarker.template.Template;
import io.transwarp.malasong.shell.model.BaseDataModel;
import java.io.*;
import java.util.Map;
public class TemplateUtil {
public static Configuration configuration;
static {
try{
configuration = new Configuration(Configuration.VERSION_2_3_0);
configuration.setClassForTemplateLoading(TemplateLoader.class, "/ftl");
}catch(Exception e) {
e.printStackTrace();
}
}
/**
* get template instance
* @param templateName
* @return
*/
public static Template getTemplate(String templateName) {
try {
return configuration.getTemplate(templateName);
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
public static String generate(Map<String, String> params, String templatePath) {
Template template = getTemplate(templatePath);
try(StringWriter stringWriter = new StringWriter()) {
template.process(params, stringWriter);
return stringWriter.toString();
}catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
- 现在我们来重新实现下👆简单实现中的业务逻辑
String useName = "helloWorld";
Map<String, String> data = new HashMap<>();
data.put("userName", useName);
String msg = TemplateUtil.generate(data, "/birthdayMsg.ftl");
Message.send(msg1);
- 当我们的通知消息只有一个useName时,显然简单方式较为方便,但是当我们的通知模版内容哦越来越复杂,在使用复杂模式势必会造成简单的text文本与内容高度耦合.后期维护困难。
进阶
- 上面也看到了每次使用模版都需要去通过模版名称获取模版内容,完成最终绑定,如果模版非常多,那么管理起来将会非常困难。这里我们可以对TemplateUtil 再做进一步的封装,让模版名称传入的更优雅,并创建一些Model来描述我们的模版数据结构。
- 首先加大我们的需求难度,对不同国家的人进行生日提醒,提醒时需要告知多少岁,当然不同国家需要使用不同的语言。
- 创建注解类,用于描述我们的模版名称及模版路径
package //;
import java.lang.*;
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface TemplateResource {
String path() default "sql"; // resource下的路径
String template() default ""; // 具体的模板名称${MODEL_LINE_NAME}.ftl
}
- 创建我们的生日提醒的model
package //;
public abstract class BirthdayMsg{
public String useName;
public int age;
// 通过注解获取模版路径及模版名称
public Tuple2<String,String> getPathAndTemplateName() {
TemplateResource annotation = this.getClass().getAnnotation(TemplateResource.class);
if(annotation != null) {
return new Tuple2<>(annotation.path(), annotation.template());
}
return null;
}
// 模版初始化
public Template initTemplate(Tuple2<String,String> pathNameTemplateName) {
Configuration configuration;
try {
//创建Configuration对象
configuration = new Configuration(Configuration.VERSION_2_3_0);
//设置模板所在目录
configuration.setClassForTemplateLoading(MailUtils.class,pathNameTemplateName.t1());
//获取模板
return configuration.getTemplate(pathNameTemplateName.t2());
}catch (Exception e) {
e.printStackTrace();
}
return null;
}
// 内容导出
public String generate() {
Template template = initTemplate(getPathAndTemplateName);
try(StringWriter stringWriter = new StringWriter()) {
template.process(this, stringWriter);
return stringWriter.toString();
}catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
// getter setter ...
}
- 创建中文用户的生日提醒类,,其他语言的我们这里就不列举了,
package //;
@TemplateResource(path="/ch", template="birthdayMsg")
public class BirthdayMsgCh extends BirthdayMsg{
}
- 实际使用
String language = ""
// 依据不同的language新建不同的model
BirthdayMsg model = new XXXXX
// 给model填入数据
....
String msg = model.genrate();
- 这里只是简单的封装下哦,可能还有更好的处理方式,看看你的系统,有哪些可以用模版处理的,赶快行动起来吧,毕竟优雅的处理字符串还是需要的。