一,FreeMarker百度百科定义
FreeMarker是一款模板引擎: 即一种基于模板和要改变的数据, 并用来生成输出文本(HTML网页、电子邮件、配置文件、源代码等)的通用工具。 它不是面向最终用户的,而是一个Java类库,是一款程序员可以嵌入他们所开发产品的组件。
假设在一个应用系统中需要一个HTML页面如下:
1
2
3
4
5
6
7
8
9
10
|
< html > < head > < title >Welcome!</ title > </ head > < body > < h1 >Welcome Big Joe!</ h1 > < p >Our latest product: < a href = "products/greenmouse.html" >green mouse</ a >! </ body > </ html > |
页面中的用户名(即上面的“Big Joe”)是登录这个网页的访问者的名字, 并且最新产品的数据应该来自于数据库才能随时更新。所以,不能直接在HTML页面中输入“Big Joe”、“greenmouse”及链接, 不能使用静态HTML代码。可以使用要求输出的模板来解决,模板和静态页面是相同的,只是它会包含一些FreeMarker将它们变成动态内容的指令:
1
2
3
4
5
6
7
8
9
10
|
< html > < head > < title >Welcome!</ title > </ head > < body > < h1 >Welcome ${user}!</ h1 > < p >Our latest product: < a href = "${latestProduct.url}" >${latestProduct.name}</ a >! </ body > </ html > |
模板文件存放在Web服务器上,当有人来访问这个页面,FreeMarker就会介入执行,然后动态转换模板,用最新的数据内容替换模板中${...}的部分,之后将结果发送到访问者的Web浏览器中。访问者的Web浏览器就会接收到例如第一个HTML示例那样的内容(也就是没有FreeMarker指令的HTML代码),访问者也不会察觉到服务器端使用的FreeMarker。(存储在Web服务器端的模板文件是不会被修改的;替换也仅仅出现在Web服务器的响应中。)
为模板准备的数据整体被称作为数据模型。数据模型是树形结构(就像硬盘上的文件夹和文件),在视觉效果上, 数据模型可以是(这只是一个形象化显示,数据模型不是文本格式,它来自于Java对象):
1
2
3
4
5
6
7
8
9
|
(root) | +- user = "Big Joe" | +- latestProduct | +- url = "products/greenmouse.html" | +- name = "green mouse" |
早期版本中,可以从数据模型中选取这些值,使用user和latestProduct.name表达式即可。类比于硬盘的树形结构,数据模型就像一个文件系统,“(root)”和latestProduct就对应着目录(文件夹),而user、url和name就是这些目录中的文件。
总体上,模板和数据模型是FreeMarker来生成输出所必须的组成部分:模板 + 数据模型 = 输出 。
二,项目的时序图
三,获取模板和数据
(一)dataurl数据获得
1.通过nginx,数据源操作的表先搞清楚根据id获得model
实体
@Data @ToString @Document(collection = "cms_config") public class CmsConfig { @Id private String id; private String name; private List<CmsConfigModel> model; }
其中的
CmsConfigModel
@Data @ToString public class CmsConfigModel { private String key; private String name; private String url; private Map mapValue; private String value; }
api
1 @Api("Cms配置接口管理") 2 public interface CmsConfigControllerApi { 3 @ApiOperation("根据id查询数据") 4 CmsConfig getModel(String id); 5 6 }
controller
@RestController @RequestMapping("/cms/config") public class CmsConfigController implements CmsConfigControllerApi { @Autowired CmsConfigService cmsConfigService; @Override @GetMapping("/getmodel/{id}") public CmsConfig getModel(@PathVariable("id") String id) { CmsConfig model = cmsConfigService.getModel(id); return model; } }
repository
1 public interface CmsConfigRepository extends MongoRepository<CmsConfig,String> { 2 }
2,远程调用resttemplate
用okhttpclient
2.1添加okhttpclient的pom.xml依赖
2.2启动类上加上bean声明,
@Bean public RestTemplate restTemplate(){ return new RestTemplate(new OkHttp3ClientHttpRequestFactory()); }
2.3测试
restTemplate.getForEntity获得
@SpringBootTest @RunWith(SpringRunner.class) public class ResttemplateTest { @Autowired RestTemplate restTemplate; @Test public void resttemplateTest(){ ResponseEntity<Map> forEntity = restTemplate.getForEntity("http://localhost:31001/cms/config/getmodel/5a", Map.class); Object body = forEntity.getBody(); System.out.println(body); } }
(二)模板从属于一个站点,
@Controller public class TestController { @Autowired RestTemplate restTemplate; @RequestMapping("/banner") public String banner(Map map){ //获取数据 ResponseEntity<Map> responseEntity = restTemplate.getForEntity("http://localhost:31001/cms/config/getmodel/5a", Map.class); Map body = responseEntity.getBody(); map.putAll(body); return "index_banner"; }
index_banner.ftl模板
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <div class="banner-roll"> <div class="amodel as item> <div class="item" style="background-image: url(${item.value});"></div> </#list> </#if> <#-- <div class="item" style="background-image: url(../img/widget-bannerA.jpg);"></div> <div class="item" style="background-image: url(../img/widget-banner3.png);"></div> <div class="item" style="background-image: url(../img/widget-bannerB.jpg);"></div> <div class="item" style="background-image: url(../img/widget-bannerA.jpg);"></div> <div class="item" style="background-image: url(../img/widget-banner3.png);"></div>--> </div> <div class="indicators"></div> </div> <script type="text/javascript" src="http://www.x.com/plugins/jquery/dist/jquery.js"></script> <script type="text/javascript" src="http://www.x.com/plugins/bootstrap/dist/js/bootstrap.js"></script> <script type="text/javascript"> var tg = $('.banner-item .item'); var num = 0; for (i = 0; i < tg.length; i++) { $('.indicators').append('<span></span>'); $('.indicators').find('span').eq(num).addClass('active'); } function roll() { tg.eq(num).animate({ 'opacity': '1', 'z-index': num }, 1000).siblings().animate({ 'opacity': '0', 'z-index': 0 }, 1000); $('.indicators').find('span').eq(num).addClass('active').siblings().removeClass('active'); if (num >= tg.length - 1) { num = 0; } else { num++; } } $('.indicators').find('span').click(function() { num = $(this).index(); roll(); }); var timer = setInterval(roll, 3000); $('.banner-item').mouseover(function() { clearInterval(timer) }); $('.banner-item').mouseout(function() { timer = setInterval(roll, 3000) }); </script> </body> </html>
(三)数据+模板=页面
四,页面文件存在GridFS中,并从GridFS中获取页面文件
(一)GridFS简介
1.GridFS是MongoDB规范用于存储和检索大文件,如图片,音频文件,视频文件等。这是一种文件系统用来存储文件,但数据存储于MongoDB集合中。GridFS存储文件比其文档大小16MB限制的更大能力。
2.GridFS的划分一个文件分成块存储数据每个块在一个单独的文件,每个最大尺寸255K。
GridFS默认使用两个集合 fs.files (文件简要信息)和 fs.chunks (具体内容)存储该文件的元数据和块。
(二)存取测试
1.存模板
1 @SpringBootTest 2 @RunWith(SpringRunner.class) 3 public class GridsTest { 4 @Autowired 5 private GridFsTemplate gridFsTemplate; 6 @Autowired 7 private GridFSBucket gridFSBucket; 8 @Test 9 public void saveFile() throws FileNotFoundException{ 10 11 FileInputStream inputStream = new FileInputStream(new File("E:\xuecheng\ce.ftl")); 12 ObjectId store = gridFsTemplate.store(inputStream, "ourse", ""); 13 System.out.println(store); 14 15 }
2.取模板
配置文件
import com.mongodb.MongoClient; import com.mongodb.client.MongoDatabase; import com.mongodb.client.gridfs.GridFSBucket; import com.mongodb.client.gridfs.GridFSBuckets; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class MongoConfig { @Value("${spring.data.mongodb.database}") String db; @Bean public GridFSBucket getGridFSBucket(MongoClient mongoClient){ MongoDatabase database = mongoClient.getDatabase(db); GridFSBucket bucket = GridFSBuckets.create(database); return bucket; } }
测试
@SpringBootTest @RunWith(SpringRunner.class) public class GridsTest { @Autowired private GridFsTemplate gridFsTemplate; @Autowired private GridFSBucket gridFSBucket; @Test public void queryFile() throws IOException{ GridFSFile gridFSFile = gridFsTemplate.findOne(Query.query(Criteria.where("_id").is("5cff9c915321159f"))); GridFSDownloadStream gridFSDownloadStream = gridFSBucket.openDownloadStream(gridFSFile.getObjectId()); GridFsResource gridFsResource = new GridFsResource(gridFSFile, gridFSDownloadStream);; InputStream inputStream = gridFsResource.getInputStream(); String template = IOUtils.toString(inputStream, "utf-8"); System.out.println(template); }
gridFsTemplate.findOne(Query.query(Criteria.where("_id").is("5cff9c915321159f")));
HQL语句
文件id(fileId)是唯一标识
四,应用
两种页面静态化选择,我们用第二种
//基于模版文件生成静态化文件 @Test public void generateHtml() throws IOException, TemplateException { //声明配置类 Configuration configuration = new Configuration(Configuration.getVersion()); //设置默认编码 configuration.setDefaultEncoding("utf-8"); //获取模版 String classpath = this.getClass().getResource("/").getPath(); configuration.setDirectoryForTemplateLoading(new File(classpath+"/templates/")); Template template = configuration.getTemplate("test1.ftl"); //获取数据 Map map = new HashMap(); map.put("name","黑马程序员"); //生成静态化页面 String htmlContentString = FreeMarkerTemplateUtils.processTemplateIntoString(template, map); InputStream inputStream = IOUtils.toInputStream(htmlContentString, "utf-8"); OutputStream outputStream = new FileOutputStream(new File("E:\xuecheng\test1.html")); IOUtils.copy(inputStream,outputStream); } //基于模版字符串生成静态化页面 @Test public void generateHtmlByString() throws IOException, TemplateException { //声明配置类 Configuration configuration = new Configuration(Configuration.getVersion()); //设置默认编码 configuration.setDefaultEncoding("utf-8"); //模版 StringTemplateLoader stringTemplateLoader = new StringTemplateLoader(); String content="<!DOCTYPE html> " + "<html lang="en"> " + "<head> " + " <meta charset="UTF-8"> " + " <title>Title</title> " + "</head> " + "<body> " + " ${name} " + " " + "</body> " + "</html>"; stringTemplateLoader.putTemplate("myTemplate",content); configuration.setTemplateLoader(stringTemplateLoader); Template template = configuration.getTemplate("myTemplate"); //获取数据 Map map = new HashMap(); map.put("name","传智播客"); //生成静态化页面 String htmlContentString = FreeMarkerTemplateUtils.processTemplateIntoString(template, map); InputStream inputStream = IOUtils.toInputStream(htmlContentString, "utf-8"); OutputStream outputStream = new FileOutputStream(new File("E:\xuecheng\test2.html")); IOUtils.copy(inputStream,outputStream); }
2.基于模版字符串生成静态化页面
servcie
/model和数据结合,生成静态化页面,根据id生成静态化页面 public String getHtml(String pageId) { //获取数据 Map model = this.getModelByPageId(pageId); if(model == null){ ExceptionCast.cast(CmsCode.CMS_GENERATEHTML_DATAISNULL); } //获取模版 String templateById = this.getTemplateById(pageId); if(StringUtils.isEmpty(templateById)){ ExceptionCast.cast(CmsCode.CMS_GENERATEHTML_TEMPLATEISNULL); } //生成静态页面 String pageHtml = this.generateHtml(model, templateById); if (pageHtml.isEmpty()){ ExceptionCast.cast(CmsCode.CMS_GENERATEHTML_HTMLISNULL); } return pageHtml; } //生成静态页面方法 private String generateHtml(Map model,String templateString){ Configuration configuration = new Configuration(Configuration.getVersion()); configuration.setDefaultEncoding("utf-8"); StringTemplateLoader stringTemplateLoader = new StringTemplateLoader(); stringTemplateLoader.putTemplate("myTemplate",templateString); configuration.setTemplateLoader(stringTemplateLoader); try{ Template template = configuration.getTemplate("myTemplate"); String pageHtml = FreeMarkerTemplateUtils.processTemplateIntoString(template, model); return pageHtml; } catch (Exception e){ e.printStackTrace(); } return null; } //获取模版方法 private String getTemplateById(String pageId) { CmsPage cmsPage = this.findOne(pageId); if(cmsPage == null ){ ExceptionCast.cast(CmsCode.CMS_PAGE_NOTEXISTS); } String templateId = cmsPage.getTemplateId(); Optional<CmsTemplate> repositoryById = cmsTemplateRepository.findById(templateId); if(!repositoryById.isPresent()){ ExceptionCast.cast(CmsCode.CMS_GENERATEHTML_TEMPLATEISNULL); } CmsTemplate cmsTemplate = repositoryById.get(); String templateFileId = cmsTemplate.getTemplateFileId(); GridFSFile gridFsTemplateOne = gridFsTemplate.findOne(Query.query(Criteria.where("_id").is(templateFileId))); GridFSDownloadStream gridFSDownloadStream = gridFSBucket.openDownloadStream(gridFsTemplateOne.getObjectId()); GridFsResource gridFsResource=new GridFsResource(gridFsTemplateOne,gridFSDownloadStream); try { InputStream inputStream = gridFsResource.getInputStream(); String templateContent = IOUtils.toString(inputStream); return templateContent; } catch (IOException e) { e.printStackTrace(); } return null; } //获取数据 private Map getModelByPageId(String pageId){ CmsPage one = this.findOne(pageId); if (one == null) { ExceptionCast.cast(CmsCode.CMS_PAGE_NOTEXISTS); } String dataUrl = one.getDataUrl(); if(StringUtils.isEmpty(dataUrl)){ ExceptionCast.cast(CmsCode.CMS_GENERATEHTML_DATAISNULL); } ResponseEntity<Map> responseEntity = restTemplate.getForEntity(dataUrl, Map.class); return responseEntity.getBody(); }