Freemarker

存在一份模板文件,需要替换其中指定的数据,现有的代码是通过正则匹配替换的。其中模板配置文件大致如下,需要将模板中的内容动态替换

1
2
3
jobmanager.rpc.address: {{app-name}}-jobmanager-headless
taskmanager.numberOfTaskSlots: {{numberOfTaskSlots}}
kubernetes.namespace: {{namespace}}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
String flinkConfig = FileUtils.getFileContent("flink/flink-conf.yaml");		 //指定resource路径下文件
Map<String, String> configData = new HashMap<String, String>(); //获取文件内容
configData.put("numberOfTaskSlots",flinkEntity.getSlot().toString()); //传入数据
configData.put("app-name",flinkEntity.getName());
configData.put("namespace",flinkEntity.getNamespace());
String ConfigContent = FileUtils.parsePattern(flinkConfig,configData); //根据正则表达式将文本中的变量使用实际的数据替换成无变量的文本

/**
* 获取文件内容
* @param filePath
* @return
*/
public static String getFileContent(String filePath){
try {
Resource resource = new ClassPathResource(filePath);
InputStream is = resource.getInputStream();
InputStreamReader isr = new InputStreamReader(is,"utf-8");
BufferedReader br = new BufferedReader(isr);
String data = null;
StringBuffer sb = new StringBuffer();
while ((data = br.readLine()) != null) {
sb.append(data + System.getProperty("line.separator"));
}
br.close();
isr.close();
is.close();
return sb.toString();
} catch (IOException e) {
e.printStackTrace();
return null;
}
}


/**
* 根据正则表达式将文本中的变量使用实际的数据替换成无变量的文本
* @param content
* @param data
* @return
*/
public static String parsePattern(String content, Map<String, String> data) {
String pattern = "\\{\\{(.+?)\\}\\}";
Pattern p = Pattern.compile(pattern);
Matcher m = p.matcher(content);
StringBuffer sb = new StringBuffer();
while (m.find()) {
String key = m.group(1);
String value = data.get(key);
m.appendReplacement(sb, value == null ? "" : value);
}
m.appendTail(sb);
return sb.toString();
}

但是新的模板内有计算需求,要不就在代码中计算好传进来,要不在模板中计算,使用freemarker

FreeMarker 是一款 模板引擎: 即一种基于模板和要改变的数据, 并用来生成输出文本(HTML网页,电子邮件,配置文件,源代码等)的通用工具。 它不是面向最终用户的,而是一个Java类库,是一款程序员可以嵌入他们所开发产品的组件

Figure

这种方式通常被称为 MVC (模型 视图 控制器) 模式,对于动态网页来说,是一种特别流行的模式。 它帮助从开发人员(Java 程序员)中分离出网页设计师(HTML设计师)。设计师无需面对模板中的复杂逻辑, 在没有程序员来修改或重新编译代码时,也可以修改页面的样式。

而FreeMarker最初的设计,是被用来在MVC模式的Web开发框架中生成HTML页面的,它没有被绑定到 Servlet或HTML或任意Web相关的东西上。它也可以用于非Web应用环境中。

这里有个官方模板:http://freemarker.foofun.cn/pgui_quickstart_all.html

下面是代码配置:需要在启动主类加上注解配置@SpringBootApplication(exclude = { FreeMarkerAutoConfiguration.class })

1
2
3
4
5
6
<!--Pom.xml-->
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>2.3.23</version>
</dependency>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
//FreeMarkerConfig.java
import freemarker.template.Configuration;
import freemarker.template.TemplateExceptionHandler;
import org.springframework.stereotype.Component;
import java.io.File;

/**
* http://freemarker.foofun.cn/pgui_quickstart_createconfiguration.html
* 不需要重复创建 Configuration 实例; 它的代价很高,尤其是会丢失缓存。Configuration 实例就是应用级别的单例。
* 当使用多线程应用程序(比如Web网站),Configuration 实例中的设置就不能被修改。它们可以被视作为 "有效的不可改变的" 对象,
* 也可以继续使用 安全发布 技术 (参考 JSR 133 和相关的文献)来保证实例对其它线程也可用。
* 比如, 通过final或volatile字段来声明实例,或者通过线程安全的IoC容器,但不能作为普通字段。
* (Configuration 中不处理修改设置的方法是线程安全的。)
*/
@Component
public class FreemarkerConfig {
public Configuration getFreemarker() {
try{
Configuration cfg = new Configuration(Configuration.getVersion());
cfg.setClassForTemplateLoading(this.getClass(), "/template"); //指定模板目录:resources/template
cfg.setDefaultEncoding("UTF-8");
cfg.setTemplateExceptionHandler(TemplateExceptionHandler.RETHROW_HANDLER);
return cfg;
}catch (Exception e){
e.printStackTrace();
}
return null;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
import com.alibaba.fastjson.JSONObject;
import com.api.config.FreemarkerConfig;
import com.api.model.entity.RosEntity;
import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateExceptionHandler;
import org.springframework.beans.factory.annotation.Autowired;

import java.io.File;
import java.io.StringWriter;
import java.io.Writer;
import java.util.HashMap;
import java.util.Map;

public class TemplateUtils {

/**
*
* @param data 需要转换的数据
* @param fileName 需要转换的模板
* @param cfg FreeMarker
* @return
*/
public static String resolveTemplate (Map data,String fileName,FreemarkerConfig cfg){
try{
//将会创建一个 {{fileName}} 的 Template 实例,通过读取 /resources/template/{{fileName}} 文件,之后解析(编译)它。Template 实例以解析后的形式存储模板, 而不是以源文件的文本形式。
Template temp =cfg.getFreemarker().getTemplate(fileName);
Writer out = new StringWriter();
//它用数据模型data和 Writer 对象作为参数,然后向 Writer 对象写入产生的内容。
temp.process(data, out);
String result = out.toString();
out.close();
return result;
}catch (Exception e){
e.printStackTrace();
}
return null;
}
}

下面是模板文件:config.template

1
2
3
4
5
6
7
8
9
10
11
12
max_connections = ${ (100 * flavor.ram/512)?c }
max_user_connections = ${ (100 * flavor.ram/512)?c }
back_log = ${ (50 + 20 * flavor.ram/512)?c }
open_files_limit = ${ (512 * flavor.ram/512)?c }
table_open_cache = ${ (256 * flavor.ram/512)?c }
table_definition_cache = ${ (256 * flavor.ram/512)?c }
query_cache_size = ${ (8 * flavor.ram/512)?c }M
tmp_table_size = ${ (8 * flavor.ram/512)?c }M
max_heap_table_size = ${ (8 * flavor.ram/512)?c }M
thread_cache_size = ${ (4 * flavor.ram/512)?c }
innodb_buffer_pool_size = ${ (250 * flavor.ram/512)?c }M
innodb_open_files = ${ (512 * flavor.ram/512)?c }
1
2
3
4
5
6
7
8
9
10
11
import com.api.config.FreemarkerConfig;

public class DealImpl {
@Autowired
private FreemarkerConfig cfg;

Map<String, Object> data = new HashMap<>();
data.put("flavor", server.getFlavor()); //传入的Falvor对象,其中包含ram,cpu等属性

String db = TemplateUtils.resolveTemplate(data,"mysql/config.template",cfg);
}

这里有个坑是freemarker里的数值计算后,会没三位数字有个逗号,可以用?c将其强制转换为字符串