|
| 1 | +## 什么是静态化 |
| 2 | + |
| 3 | +静态化是指把动态生成的HTML页面变为静态内容保存,以后用户的请求到来,直接访问静态页面,不再经过服务的渲染。 |
| 4 | + |
| 5 | +而静态的HTML页面可以部署在nginx中,从而大大提高并发能力,减小tomcat压力。 |
| 6 | + |
| 7 | +## 如何实现静态化 |
| 8 | + |
| 9 | +目前,静态化页面都是通过模板引擎来生成,而后保存到nginx服务器来部署。常用的模板引擎比如: |
| 10 | + |
| 11 | +- Freemarker |
| 12 | +- Velocity |
| 13 | +- Thymeleaf |
| 14 | + |
| 15 | +我们之前就使用的Thymeleaf,来渲染html返回给用户。Thymeleaf除了可以把渲染结果写入Response,也可以写到本地文件,从而实现静态化。 |
| 16 | + |
| 17 | +## Thymeleaf实现静态化 |
| 18 | + |
| 19 | +**概念** |
| 20 | + |
| 21 | +先说下Thymeleaf中的几个概念: |
| 22 | + |
| 23 | +- Context:运行上下文 |
| 24 | +- TemplateResolver:模板解析器 |
| 25 | +- TemplateEngine:模板引擎 |
| 26 | + |
| 27 | +> Context |
| 28 | +
|
| 29 | +上下文: 用来保存模型数据,当模板引擎渲染时,可以从Context上下文中获取数据用于渲染。 |
| 30 | + |
| 31 | +当与SpringBoot结合使用时,我们放入Model的数据就会被处理到Context,作为模板渲染的数据使用。 |
| 32 | + |
| 33 | +> TemplateResolver |
| 34 | +
|
| 35 | +模板解析器:用来读取模板相关的配置,例如:模板存放的位置信息,模板文件名称,模板文件的类型等等。 |
| 36 | + |
| 37 | +当与SpringBoot结合时,TemplateResolver已经由其创建完成,并且各种配置也都有默认值,比如模板存放位置,其默认值就是:templates。比如模板文件类型,其默认值就是html。 |
| 38 | + |
| 39 | +> TemplateEngine |
| 40 | +
|
| 41 | +模板引擎:用来解析模板的引擎,需要使用到上下文、模板解析器。分别从两者中获取模板中需要的数据,模板文件。然后利用内置的语法规则解析,从而输出解析后的文件。来看下模板引起进行处理的函数: |
| 42 | + |
| 43 | +```java |
| 44 | +templateEngine.process("模板名", context, writer); |
| 45 | +``` |
| 46 | + |
| 47 | +三个参数: |
| 48 | + |
| 49 | +- 模板名称 |
| 50 | +- 上下文:里面包含模型数据 |
| 51 | +- writer:输出目的地的流 |
| 52 | + |
| 53 | +在输出时,我们可以指定输出的目的地,如果目的地是Response的流,那就是网络响应。如果目的地是本地文件,那就实现静态化了。 |
| 54 | + |
| 55 | +而在SpringBoot中已经自动配置了模板引擎,因此我们不需要关心这个。现在我们做静态化,就是把输出的目的地改成本地文件即可! |
| 56 | + |
| 57 | +## 具体实现步骤 |
| 58 | +#### 相关依赖 |
| 59 | +```xml |
| 60 | +<dependency> |
| 61 | + <groupId>org.springframework.boot</groupId> |
| 62 | + <artifactId>spring-boot-starter-thymeleaf</artifactId> |
| 63 | +</dependency> |
| 64 | + |
| 65 | +<dependency> |
| 66 | + <groupId>org.projectlombok</groupId> |
| 67 | + <artifactId>lombok</artifactId> |
| 68 | +</dependency> |
| 69 | +``` |
| 70 | +#### 准备静态原型 |
| 71 | +`thymeleaf`文件夹下面新建一个`id.html` |
| 72 | +```html |
| 73 | +<!DOCTYPE html> |
| 74 | +<html lang="en" xmlns:th="http://www.thymeleaf.org"> |
| 75 | +<head> |
| 76 | + <meta charset="UTF-8"> |
| 77 | + <title>Thymeleaf 静态页面</title> |
| 78 | +</head> |
| 79 | +<body> |
| 80 | + <h1 th:text="'名字是:' + ${name}"></h1> |
| 81 | + <h1 th:text="'年龄是:' + ${age}"></h1> |
| 82 | + <h1 th:text="'邮箱是:' + ${email}"></h1> |
| 83 | +</body> |
| 84 | +</html> |
| 85 | +``` |
| 86 | +#### 接口和实现类 |
| 87 | +```java |
| 88 | +public interface ThymeleafService { |
| 89 | + |
| 90 | + void createHtml(Long id); |
| 91 | + |
| 92 | + void deleteHtml(Long id); |
| 93 | +} |
| 94 | +``` |
| 95 | +实现类相关说明 |
| 96 | + |
| 97 | +定义一个存放静态文件的目录,这里你也可以写在`application.yml`文件中,再使用`@Value`注解也行。 |
| 98 | +```java |
| 99 | +public static final String destPath = "D:/temp/static"; // 自己手动创建一个存在的文件路径 |
| 100 | +``` |
| 101 | +注入模板引擎`TemplateEngine` |
| 102 | +```java |
| 103 | +@Autowired |
| 104 | +private TemplateEngine templateEngine; |
| 105 | +``` |
| 106 | +准备加载到页面上的数据,讲道理,这里加载的数据是根据id从数据库查询出来的,我这里就写固定了 |
| 107 | +```java |
| 108 | +public Map<String, Object> loadModel(Long id) { |
| 109 | + Map<String, Object> map = new HashMap<>(); |
| 110 | + map.put("name", "tellsea"); |
| 111 | + map.put("age", 20); |
| 112 | + map.put("email", "3210054449@qq.com"); |
| 113 | + return map; |
| 114 | +} |
| 115 | +``` |
| 116 | +接下来就是重点,**创建静态页面的方法** |
| 117 | +``` |
| 118 | +/** |
| 119 | + * 创建html页面 |
| 120 | + * |
| 121 | + * @param id |
| 122 | + * @throws Exception |
| 123 | + */ |
| 124 | +public void createHtml(Long id) { |
| 125 | + // 上下文 |
| 126 | + Context context = new Context(); |
| 127 | + context.setVariables(loadModel(id)); |
| 128 | + // 输出流 |
| 129 | + File dest = new File(destPath, id + ".html"); |
| 130 | + if (dest.exists()) { |
| 131 | + dest.delete(); |
| 132 | + } |
| 133 | + try (PrintWriter writer = new PrintWriter(dest, "UTF-8")) { |
| 134 | + // 生成html,第一个参数是thymeleaf页面下的原型名称 |
| 135 | + templateEngine.process("id", context, writer); |
| 136 | + } catch (Exception e) { |
| 137 | + log.error("[静态页服务]:生成静态页异常", e); |
| 138 | + } |
| 139 | +} |
| 140 | +``` |
| 141 | +这里提供了一个删除静态页面的方法,做戏当然是做全套,删除的接口便于文章后面讲解实战运行 |
| 142 | +```java |
| 143 | +@Override |
| 144 | +public void deleteHtml(Long id) { |
| 145 | + // 输出流 |
| 146 | + File dest = new File(destPath, id + ".html"); |
| 147 | + if (dest.exists()) { |
| 148 | + dest.delete(); |
| 149 | + } |
| 150 | +} |
| 151 | +``` |
| 152 | +#### 测试效果 |
| 153 | +执行`createHtmlTest`测试类,然后查看`D:/temp/static`下生成的静态页面,Nice,直接双击生成的`Html`文件,浏览器就可以访问,效果为带数据的页面,Nice |
| 154 | +```java |
| 155 | +@RunWith(SpringRunner.class) |
| 156 | +@SpringBootTest |
| 157 | +public class ThymeleafServiceTest { |
| 158 | + |
| 159 | + @Autowired |
| 160 | + private ThymeleafService thymeleafService; |
| 161 | + |
| 162 | + @Test |
| 163 | + public void createHtmlTest() { |
| 164 | + thymeleafService.createHtml(1L); |
| 165 | + } |
| 166 | +} |
| 167 | +``` |
| 168 | + |
| 169 | +## 实战分析 |
| 170 | + |
| 171 | +上面的实现的功能我们这里把它理解为对于文章详情的静态页,方便下面说明 |
| 172 | + |
| 173 | +问题:什么时候调用创建、删除? |
| 174 | + |
| 175 | +#### 新增、更新 |
| 176 | +我们新增文章、更新文章,完成相应的操作数据库的业务逻辑之后,需要更新静态页面的数据,调用创建的接口,重新创建一个新的即可 |
| 177 | +```java |
| 178 | +thymeleafService.createHtml(1L); |
| 179 | +``` |
| 180 | +怎么覆盖?仔细看实现类的代码,**在创建页面之前,里面有写如果存在,则删除** |
| 181 | +```java |
| 182 | +if (dest.exists()) { |
| 183 | + dest.delete(); |
| 184 | +} |
| 185 | +``` |
| 186 | + |
| 187 | +#### 删除 |
| 188 | +删除数据库文章之后,直接删除静态页面即可 |
| 189 | +```java |
| 190 | +pageService.deleteHtml(id); |
| 191 | +``` |
| 192 | + |
| 193 | +## 代理静态页面 |
| 194 | + |
| 195 | +我们修改`nginx`,让它对文章详情的请求进行监听,指向本地静态页面,如果本地没找到,才进行反向代理: |
| 196 | +```shell |
| 197 | +server { |
| 198 | + listen 80; |
| 199 | + server_name www.tellsea.com; |
| 200 | + |
| 201 | + proxy_set_header X-Forwarded-Host $host; |
| 202 | + proxy_set_header X-Forwarded-Server $host; |
| 203 | + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; |
| 204 | + |
| 205 | + location /item { |
| 206 | + # 先找本地 |
| 207 | + root html; # 这里的html是root安装目录的html文件夹,可以自定义 |
| 208 | + if (!-f $request_filename) { #请求的文件不存在,就反向代理 |
| 209 | + proxy_pass http://127.0.0.1:8080; |
| 210 | + break; |
| 211 | + } |
| 212 | + } |
| 213 | + |
| 214 | + location / { |
| 215 | + proxy_pass http://127.0.0.1:9002; |
| 216 | + proxy_connect_timeout 600; |
| 217 | + proxy_read_timeout 600; |
| 218 | + } |
| 219 | +} |
| 220 | +``` |
| 221 | +
|
| 222 | +重启测试: |
| 223 | +
|
| 224 | +发现请求速度得到了极大提升,因为访问的是`HTML`的静态页面了。 |
| 225 | +
|
| 226 | +## 相关链接 |
| 227 | +- [示例源码:springboot-thymeleaf-static](https://github.com/Tellsea/springboot-learn/tree/master/springboot-thymeleaf-static "实例源码") |
| 228 | +- [Thymeleaf官方文档-Documentation](https://www.thymeleaf.org/documentation.html "Thymeleaf官方文档-Documentation") |
0 commit comments