需求產生原因
- 要求在同一個接口中,根據不同的參數,返回不同的視圖結果
- 所有的視圖中的數據基本一致
- 要求頁面能靜態化,優化SEO
例如:A接口返回客戶的信息
- 客戶A在調用接口時,返回其個性化定制的頁面A
- 客戶B在調用這個接口時,返回其個性化主頁B
實現方式 freemaker 的 TemplateLoader
freemaker的配置類freemarker.template.Configuration中提供了一個配置模版加載器的方法
setTemplateLoader
,需求是要求同時能加載本地和遠程的模版,但是只提供了一個模版加載器的set方法,查詢文檔后官方給出了建議
//遠程模版加載 RemoteTemplateLoader remoteTemplateLoader = new RemoteTemplateLoader(remotePath); //本地模版加載 ClassTemplateLoader classTemplateLoader = new ClassTemplateLoader(getClass(), "/WEB-INF/pages/"); MultiTemplateLoader templateLoader = new MultiTemplateLoader(new TemplateLoader[] {classTemplateLoader,remoteTemplateLoader});
- SpringBoot配置
import freemarker.cache.ClassTemplateLoader;
import freemarker.cache.MultiTemplateLoader;
import freemarker.cache.TemplateLoader;
import freemarker.template.TemplateDirectiveModel;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer;
import javax.annotation.PostConstruct;
import java.util.Map;
import java.util.Set;
/**
* Freemarker配置
* @author wpy
* @create 2017/11/8 14:51
* @project_name jcstore
*/
@Configuration
public class FreemarkerConfig {
@Autowired
private FreeMarkerConfigurer freeMarkerConfigurer;
@Autowired
private WebApplicationContext applicationContext;
// @Value("")
private String remotePath = "http://localhost:8080/static";
@PostConstruct
public void freeMarkerConfigurer() {
freemarker.template.Configuration configuration = freeMarkerConfigurer.getConfiguration();
//注冊所有自定義標簽
Map<String, TemplateDirectiveModel> tagsMap = applicationContext.getBeansOfType(TemplateDirectiveModel.class);
Set<Map.Entry<String, TemplateDirectiveModel>> entries = tagsMap.entrySet();
entries.forEach(entry ->
configuration.setSharedVariable(entry.getKey(), entry.getValue())
);
//遠程模版加載
RemoteTemplateLoader remoteTemplateLoader = new RemoteTemplateLoader(remotePath);
//本地模版加載
ClassTemplateLoader classTemplateLoader = new ClassTemplateLoader(getClass(), "/WEB-INF/pages/");
MultiTemplateLoader templateLoader = new MultiTemplateLoader(new TemplateLoader[] {classTemplateLoader,remoteTemplateLoader});
configuration.setTemplateLoader(templateLoader);
}
}
- RemoteTemplateLoader 實現
import freemarker.cache.URLTemplateLoader;
import java.net.URL;
import java.net.URLConnection;
/**
* 自定義遠程模板加載器,用來加載文件服務
*
* @author Administrator
*/
public class RemoteTemplateLoader extends URLTemplateLoader {
// 遠程模板文件的存儲路徑(目錄)
private String remotePath;
public RemoteTemplateLoader(String remotePath) {
if (remotePath == null) {
throw new IllegalArgumentException("remotePath is null");
}
this.remotePath = canonicalizePrefix(remotePath);
if (this.remotePath.indexOf('/') == 0) {
this.remotePath = this.remotePath.substring(this.remotePath
.indexOf('/') + 1);
}
}
@Override
protected URL getURL(String name) {
String fullPath = this.remotePath + name;
if ((this.remotePath.equals("/")) && (!isSchemeless(fullPath))) {
return null;
}
if (name.contains("WEB-INF/template/")) {
fullPath = fullPath.replace("WEB-INF/template/", "");
}
URL url = null;
try {
url = new URL(fullPath);
URLConnection con = url.openConnection();
long lastModified = con.getLastModified();
if (lastModified == 0) {
url = null;
}
} catch (Exception e) {
e.printStackTrace();
url = null;
}
return url;
}
private static boolean isSchemeless(String fullPath) {
int i = 0;
int ln = fullPath.length();
if ((i < ln) && (fullPath.charAt(i) == '/'))
i++;
while (i < ln) {
char c = fullPath.charAt(i);
if (c == '/')
return true;
if (c == ':')
return false;
i++;
}
return true;
}
}
- 自定義標簽實現
/**
* 自定義標簽示例
*
* <table style="width: 633px; height: 217px;" border="0">
<tbody>
<tr>
<td style="text-align: center;"><span style="font-size: 13px;"><strong>類型</strong></span></td>
<td style="text-align: center;"><span style="font-size: 13px;"><strong>FreeMarker接口</strong></span></td>
<td style="text-align: center;"><span style="font-size: 13px;"><strong>FreeMarker實現</strong></span></td>
</tr>
<tr>
<td style="text-align: center;"><span style="font-size: 13px;">字符串</span></td>
<td style="text-align: left;"><span style="font-size: 13px;">TemplateScalarModel</span></td>
<td style="text-align: left;"><span style="font-size: 13px;">SimpleScalar</span></td>
</tr>
<tr>
<td style="text-align: center;"><span style="font-size: 13px;">數值</span></td>
<td style="text-align: left;"><span style="font-size: 13px;">TemplateNumberModel</span></td>
<td style="text-align: left;"><span style="font-size: 13px;">SimpleNumber</span></td>
</tr>
<tr>
<td style="text-align: center;"><span style="font-size: 13px;">日期</span></td>
<td style="text-align: left;"><span style="font-size: 13px;">TemplateDateModel</span></td>
<td style="text-align: left;"><span style="font-size: 13px;">SimpleDate</span></td>
</tr>
<tr>
<td style="text-align: center;"><span style="font-size: 13px;">布爾</span></td>
<td style="text-align: left;"><span style="font-size: 13px;">TemplateBooleanModel</span></td>
<td style="text-align: left;"><span style="font-size: 13px;">TemplateBooleanModel.TRUE</span></td>
</tr>
<tr>
<td style="text-align: center;"><span style="font-size: 13px;">哈希</span></td>
<td style="text-align: left;"><span style="font-size: 13px;">TemplateHashModel</span></td>
<td style="text-align: left;"><span style="font-size: 13px;">SimpleHash</span></td>
</tr>
<tr>
<td style="text-align: center;"><span style="font-size: 13px;">序列</span></td>
<td style="text-align: left;"><span style="font-size: 13px;">TemplateSequenceModel</span></td>
<td style="text-align: left;"><span style="font-size: 13px;">SimpleSequence</span></td>
</tr>
<tr>
<td style="text-align: center;"><span style="font-size: 13px;">集合</span></td>
<td style="text-align: left;"><span style="font-size: 13px;">TemplateCollectionModel</span></td>
<td style="text-align: left;"><span style="font-size: 13px;">SimpleCollection</span></td>
</tr>
<tr>
<td style="text-align: center;"><span style="font-size: 13px;">節點</span></td>
<td><span style="font-size: 13px;">TemplateNodeModel</span></td>
<td><span style="font-size: 13px;">NodeModel</span></td>
</tr>
</tbody>
</table>
* @author wpy
* @create 2017/11/8 14:34
* @project_name jcstore
*/
@Component
public class ExampleTag implements TemplateDirectiveModel {
/**
* 標簽中的參數 name
*/
private static final String NAME = "name";
/**
*
*/
private static final String AGE = "age";
/**
*
*/
private static final String SEX = "sex";
private DefaultObjectWrapper objectWrapper;
{
Version version = new Version("2.3.21");
DefaultObjectWrapperBuilder defaultObjectWrapperBuilder = new DefaultObjectWrapperBuilder(version);
objectWrapper = defaultObjectWrapperBuilder.build();
}
@Override
public void execute(Environment env, Map params, TemplateModel[] loopVars, TemplateDirectiveBody body) throws TemplateException, IOException {
TemplateScalarModel name = (TemplateScalarModel) params.get("name");
TemplateNumberModel age = (TemplateNumberModel) params.get("age");
TemplateScalarModel sex = (TemplateScalarModel) params.get("sex");
JSONObject jsonObject = new JSONObject();
jsonObject.put("name",name.getAsString() + 1);
jsonObject.put("age",age.getAsNumber().intValue() + 1);
jsonObject.put("sex",sex.getAsString().equals("男") ? "女":"男");
TemplateModel wrap = objectWrapper.wrap(jsonObject);
if(loopVars.length > 0){
loopVars[0] = wrap;
}
body.render(env.getOut());
}
}
- 模版 exampleTag.ftl
<html>
<head></head>
<body>
<h1> 自定義標簽測試(我是遠程模版)</h1>
<p>
<@exampleTag name = "張三" age = 18 sex = "男"; loopv>
<h1>${loopv.name}</h1>
<h1>${loopv.age}</h1>
<h1>${loopv.sex}</h1>
</@exampleTag>
</p>
</body>
</html>
- CustomTagController
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
/**
* @author wpy
* @create 2017/11/8 15:02
* @project_name jcstore
*/
@Controller
public class CustomTagController {
@RequestMapping("/testTags")
public String testTags(){
return "/common/exampleTag";
}
@RequestMapping("/testTagsRemote")
public String testTagsRemote(){
return "/exampleTag";
}
}