課程計划
- 1、商品詳情頁面展示,動態展示(jsp + redis)
- 2、使用freemarker實現網頁靜態化(解決高並發)
- 3、使用ActiveMq同步生成靜態網頁
1、商品詳情頁面展示,動態展示(jsp + redis)

從架構中可以看出商品詳情頁面是一個表現層工程。
創建一個商品詳情頁面展示的Maven工程。
1.1、工程搭建
表現層工程taotao-item-web。打包方式war。可以參考表現層工程taotao-portal-web。
不使用骨架創建該Maven工程,繼承父工程,不在贅圖!
1.1.1、pom文件
配置對taotao-manager-interface的依賴和修改tomcat端口號。
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.taotao</groupId>
<artifactId>taotao-parent</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<artifactId>taotao-item-web</artifactId>
<packaging>war</packaging>
<dependencies>
<!-- 配置對taotao-common的依賴 -->
<dependency>
<groupId>com.taotao</groupId>
<artifactId>taotao-common</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
<!-- 配置對taotao-manager-interface的依賴:表現層調用服務要通過該接口 -->
<dependency>
<groupId>com.taotao</groupId>
<artifactId>taotao-manager-interface</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
<!-- Spring -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jms</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
</dependency>
<!-- JSP相關 -->
<dependency>
<groupId>jstl</groupId>
<artifactId>jstl</artifactId>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jsp-api</artifactId>
<scope>provided</scope>
</dependency>
<!-- 配置對dubbo的依賴 -->
<!-- dubbo相關 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>dubbo</artifactId>
<!-- 排除對低版本jar包的依賴 -->
<exclusions>
<exclusion>
<artifactId>spring</artifactId>
<groupId>org.springframework</groupId>
</exclusion>
<exclusion>
<artifactId>netty</artifactId>
<groupId>org.jboss.netty</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
</dependency>
<dependency>
<groupId>com.github.sgroschupf</groupId>
<artifactId>zkclient</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<!-- 配置Tomcat插件 -->
<plugin>
<groupId>org.apache.tomcat.maven</groupId>
<artifactId>tomcat7-maven-plugin</artifactId>
<configuration>
<port>8086</port>
<path>/</path>
</configuration>
</plugin>
</plugins>
</build>
</project>
1.1.2、框架整合
整合后的框架結構如下圖所示(並導入靜態頁面):

1.1.3、springmvc.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.2.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-4.2.xsd
http://code.alibabatech.com/schema/dubbo
http://code.alibabatech.com/schema/dubbo/dubbo.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.2.xsd">
<!-- 配置加載屬性文件 -->
<context:property-placeholder location="classpath:resource/resource.properties"/>
<!-- 配置包掃描器,掃描所有需要帶@Controller注解的類 -->
<context:component-scan base-package="com.taotao.item.controller" />
<!-- 配置注解驅動 -->
<mvc:annotation-driven />
<!-- 配置視圖解析器 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsp/" />
<property name="suffix" value=".jsp" />
</bean>
<!-- 引用dubbo服務 :需要先引入dubbo的約束-->
<dubbo:application name="taotao-item-web"/>
<dubbo:registry protocol="zookeeper" address="192.168.25.128:2181"/>
<!-- <dubbo:reference interface="com.taotao.content.service.ContentService" id="contentService" /> -->
</beans>
1.1.4、web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
id="WebApp_ID" version="2.5">
<display-name>taotao-item-web</display-name>
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
<!-- 配置解決post亂碼的過濾器 -->
<filter>
<filter-name>characterEncodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>utf-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>characterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- 配置springmvc的前端控制器 -->
<servlet>
<servlet-name>taotao-item-web</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!-- contextConfigLocation不是必須的, 如果不配置contextConfigLocation,
springmvc的配置文件默認在:WEB-INF/servlet的name+"-servlet.xml" -->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring/springmvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>taotao-item-web</servlet-name>
<!-- 攔截(*.html)結尾的請求,實現了網頁的偽靜態化,SEO:搜索引擎優化-->
<url-pattern>*.html</url-pattern>
</servlet-mapping>
</web-app>
1.2、功能分析
在搜索結果頁面點擊商品圖片或者商品標題,展示商品詳情頁面。
在商品搜索系統中的搜索結果頁面search.jsp中,修改如下:

請求的url:/item/{itemId}
參數:商品id
返回值:String 邏輯視圖
業務邏輯:
1、從url中取參數,商品id
2、根據商品id查詢商品信息(tb_item)得到一個TbItem對象,但是呢,缺少images屬性,我們可以創建一個pojo繼承TbItem,添加一個getImages()方法,放在在taotao-item-web工程中。由於沒有涉及到網絡傳輸,所以該pojo不需要實現序列化接口。
代碼如下:
/**
* 增加新屬性images的TbItem
* @author chenmingjun
* @date 2018年11月27日下午1:23:26
* @version 1.0
*/
public class Item extends TbItem {
public Item() {
}
public Item(TbItem tbItem) {
// 由於我們根據商品id查詢到的是TbItem,但是我們需要的是Item
// 方式一:初始化屬性,將TbItem中的屬性的值設置到Item中的屬性中來
this.setId(tbItem.getId());
this.setTitle(tbItem.getTitle());
this.setSellPoint(tbItem.getSellPoint());
this.setPrice(tbItem.getPrice());
this.setNum(tbItem.getNum());
this.setBarcode(tbItem.getBarcode());
this.setImage(tbItem.getImage());
this.setCid(tbItem.getCid());
this.setStatus(tbItem.getStatus());
this.setCreated(tbItem.getCreated());
this.setUpdated(tbItem.getUpdated());
// 方式二:使用工具類,將“原來數據TbItem”中的屬性的值拷貝到“現在數據Item”的屬性中來
// BeanUtils.copyProperties(tbItem, this);
}
public String[] getImages() {
String image2 = this.getImage();
if (image2 != null && !"".equals(image2)) {
String[] strings = image2.split(",");
return strings;
}
return null;
}
}
3、根據商品id查詢商品描述。
4、展示到頁面。
1.3、Dao層
查詢tb_item、tb_item_desc兩個表,都是單表查詢。可以使用逆向工程。
1.4、Service層
1.4.1、分析
在taotao-manager-interface和taotao-manager-service工程中添加接口的方法和實現。
1、根據商品id查詢商品信息
參數:商品id
返回值:TbItem
2、根據商品id查詢商品描述
參數:商品id
返回值:TbItemDesc
1.4.2、接口定義
taotao-manager-interface工程中定義ItemService.java
/**
* 測試:根據商品id查詢商品信息
* @param itemId
* @return
*/
TbItem getItemById(Long itemId);
/**
* 根據商品id查詢商品描述
* @param itemId
* @return
*/
TbItemDesc getItemDescById(Long itemId);
1.4.3、接口實現
taotao-manager-service工程中ItemServiceImpl.java
@Override
public TbItem getItemById(Long itemId) {
TbItem tbItem = itemMapper.selectByPrimaryKey(itemId);
return tbItem;
}
@Override
public TbItemDesc getItemDescById(Long itemId) {
TbItemDesc tbItemDesc = itemDescMapper.selectByPrimaryKey(itemId);
return tbItemDesc;
}
1.4.4、發布服務
在taotao-manager-service工廠中applicationContext-service.xml中發布服務:

1.5、表現層
1.5.1、分析
表現層調用服務層的方法,表現層應當是商品詳情工程taotao-item-web。
1.5.2、引用服務
先在taotao-item-web工程中的pom.xml中配置對taotao-manager-interface的依賴。
再在taotao-item-web工程中的springmvc.xml中引入服務:

1.5.3、Controller
請求的url:/item/{itemId}
參數:商品id
返回值:String 邏輯視圖
/**
* 商品詳情的Controller
* @author chenmingjun
* @date 2018年11月27日下午2:48:52
* @version 1.0
*/
@Controller
public class ItemController {
@Autowired
private ItemService itemService;
@RequestMapping("item/{itemId}")
public String showItemInfo(@PathVariable Long itemId, Model model) {
// 跟據商品id查詢商品信息
TbItem tbItem = itemService.getItemById(itemId);
// 把TbItem對象轉換成Item對象
Item item = new Item(tbItem);
// 根據商品id查詢商品描述
TbItemDesc tbItemDesc = itemService.getItemDescById(itemId);
// 把查詢到的數據傳遞給頁面
model.addAttribute("item", item);
model.addAttribute("itemDesc", tbItemDesc);
// 返回邏輯視圖item.jsp
return "item";
}
}
以上是通過數據庫查詢得到商品的數據,進行展示,但是一般商品的詳情頁面的訪問的並發量是比較高的
,所以為了減輕數據庫的壓力
,需要做優化
。
優化方案就是:添加緩存
。
1.6、向業務邏輯中添加緩存
1.6.1、緩存添加分析
使用redis做緩存。
業務邏輯:
1、根據商品id到緩存中命中。
2、查到緩存,直接返回。
3、查不到緩存,查詢數據庫。
4、查到數據,把數據放到緩存中。
5、返回數據。
緩存中緩存熱點數據
,為了提高緩存的使用率
,需要設置緩存的有效期
,一般是一天的時間
,可以根據實際情況調整
。
需要使用String類型
來保存商品數據,為什么呢?
答:因為我們要設置每一個key的過期時間,String類型的key可以設置,而Hash里面的key是不支持設置過期時間的,此時不能使用Hash類型(便於內容歸類)。
使用String類型
來保存商品數據,該如何歸類呢?
答:可以通過加前綴
方法對redis中的key進行歸類
。
例如:
ITEM_INFO:123456:BASE
ITEM_INFO:123456:DESC
[root@itheima bin]# pwd
/usr/local/redis/bin
[root@itheima bin]# ./redis-cli -h 192.168.25.153 -p 6379
192.168.25.153:6379> set ITEM_INFO:123456:BASE 123
OK
192.168.25.153:6379> set ITEM_INFO:123456:DESC 456
OK
192.168.25.153:6379> get ITEM_INFO:123456:BASE
"123"
192.168.25.153:6379> get ITEM_INFO:123456:DESC
"456"
192.168.25.153:6379>
如下圖所示:

擴展知識:
如果把數據庫中的
二維表
保存到redis中,該如何存儲呢?
答:
1、表名就是第一層
2、主鍵就是第二層
3、字段名是第三層
三層使用“:”分隔作為key,value就是字段中的內容。
示例如下:
存一條數據:
tb_user:7:id 7
tb_user:7:username zhangsan
tb_user:7:password 123456
存另一條數據:
tb_user:8:id 8
tb_user:8:username lisi
tb_user:8:password 456789
存其余數據同理,不再贅述!
......
1.6.2、添加redis客戶端到服務層工程
其實緩存加到表現層和服務層都可以,加到表現層只能這個表現層調用,加到服務層可以多個表現層調用,所以推薦將redis客戶端加到服務層工程。可以參考taotao-content-service工程。
在taotao-manager-service工程中的pom.xml中添加如下:

要添加緩存,可以使用之前開發過的JedisClient。

1.6.3、編寫redis配置文件applicationContext-redis.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.2.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.2.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.2.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-4.2.xsd
http://code.alibabatech.com/schema/dubbo
http://code.alibabatech.com/schema/dubbo/dubbo.xsd
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util-4.2.xsd">
<!-- 配置對redis單機版的連接 -->
<bean id="jedisPool" class="redis.clients.jedis.JedisPool">
<constructor-arg name="host" value="192.168.25.153"></constructor-arg>
<constructor-arg name="port" value="6379"></constructor-arg>
</bean>
<!-- 手動配置的jedis單機版客戶端實現類bean:會在spring容器中加載 -->
<bean id="jedisClientPool" class="com.taotao.jedis.JedisClientPool"/>
<!-- 單機版和集群版不能共存,使用單機版時注釋集群版的配置。使用集群版,把單機版注釋。-->
<!--
配置對redis集群版的連接
<bean id="jedisCluster" class="redis.clients.jedis.JedisCluster">
<constructor-arg>
<set>
<bean class="redis.clients.jedis.HostAndPort">
<constructor-arg name="host" value="192.168.25.153"></constructor-arg>
<constructor-arg name="port" value="7001"></constructor-arg>
</bean>
<bean class="redis.clients.jedis.HostAndPort">
<constructor-arg name="host" value="192.168.25.153"></constructor-arg>
<constructor-arg name="port" value="7002"></constructor-arg>
</bean>
<bean class="redis.clients.jedis.HostAndPort">
<constructor-arg name="host" value="192.168.25.153"></constructor-arg>
<constructor-arg name="port" value="7003"></constructor-arg>
</bean>
<bean class="redis.clients.jedis.HostAndPort">
<constructor-arg name="host" value="192.168.25.153"></constructor-arg>
<constructor-arg name="port" value="7004"></constructor-arg>
</bean>
<bean class="redis.clients.jedis.HostAndPort">
<constructor-arg name="host" value="192.168.25.153"></constructor-arg>
<constructor-arg name="port" value="7005"></constructor-arg>
</bean>
<bean class="redis.clients.jedis.HostAndPort">
<constructor-arg name="host" value="192.168.25.153"></constructor-arg>
<constructor-arg name="port" value="7006"></constructor-arg>
</bean>
</set>
</constructor-arg>
</bean>
手動配置的jedis集群版客戶端實現類bean:會在spring容器中加載
<bean id="jedisClientCluster" class="com.taotao.jedis.JedisClientCluster"/>
-->
</beans>
1.6.4、添加緩存
在taotao-manager-service工程中添加如下緩存。
實現類ItemServiceImpl.java需要的屬性:

實現類ItemServiceImpl的方法如下:
取商品信息后添加至緩存:
@Override
public TbItem getItemById(Long itemId) {
// 添加緩存的原則是:不能夠影響現有的業務邏輯
// 查詢數據庫之前先查詢緩存
try {
if (itemId != null) {
// 注入JedisClient對象,根據key獲取緩存
String jsonstring = jedisClient.get(ITEM_INFO_KEY + ":" + itemId + ":BASE");
if (StringUtils.isNotBlank(jsonstring)) { // 緩存中有,則轉換后返回
// 重新設置(更新)緩存過期時間
jedisClient.expire(ITEM_INFO_KEY + ":" + itemId + ":BASE", ITEM_INFO_KEY_EXPIRE);
// 把jsonstring轉成java對象
TbItem tbItem = JsonUtils.jsonToPojo(jsonstring, TbItem.class);
return tbItem;
}
}
} catch (Exception e) {
e.printStackTrace();
}
// 第一次查詢沒有,緩存中沒有命中,則去查詢數據庫
TbItem tbItem = itemMapper.selectByPrimaryKey(itemId);
// 把從數據庫中查詢到的結果添加到緩存
try {
if (tbItem != null) {
// 注入JedisClient對象,添加緩存
jedisClient.set(ITEM_INFO_KEY + ":" + itemId + ":BASE", JsonUtils.objectToJson(tbItem)); // redis中不能存java對象,存的都是字符串(json串)
// 設置緩存過期時間
jedisClient.expire(ITEM_INFO_KEY + ":" + itemId + ":BASE", ITEM_INFO_KEY_EXPIRE);
}
} catch (Exception e) {
e.printStackTrace();
}
return tbItem;
}
同理,取商品描述信息后添加至緩存:
@Override
public TbItemDesc getItemDescById(Long itemId) {
// 添加緩存的原則是:不能夠影響現有的業務邏輯
// 查詢數據庫之前先查詢緩存
try {
if (itemId != null) {
// 注入JedisClient對象,根據key獲取緩存
String jsonstring = jedisClient.get(ITEM_INFO_KEY + ":" + itemId + ":DESC");
if (StringUtils.isNotBlank(jsonstring)) { // 緩存中有,則轉換后返回
// 重新設置(更新)緩存過期時間
jedisClient.expire(ITEM_INFO_KEY + ":" + itemId + ":DESC", ITEM_INFO_KEY_EXPIRE);
// 把jsonstring轉成java對象
TbItemDesc tbItemDesc = JsonUtils.jsonToPojo(jsonstring, TbItemDesc.class);
return tbItemDesc;
}
}
} catch (Exception e) {
e.printStackTrace();
}
// 第一次查詢沒有,緩存中沒有命中,則去查詢數據庫
TbItemDesc tbItemDesc = itemDescMapper.selectByPrimaryKey(itemId);
// 把從數據庫中查詢到的結果添加到緩存
try {
if (tbItemDesc != null) {
// 注入JedisClient對象,添加緩存
jedisClient.set(ITEM_INFO_KEY + ":" + itemId + ":DESC", JsonUtils.objectToJson(tbItemDesc)); // redis中不能存java對象,存的都是字符串(json串)
// 設置緩存過期時間
jedisClient.expire(ITEM_INFO_KEY + ":" + itemId + ":DESC", ITEM_INFO_KEY_EXPIRE);
}
} catch (Exception e) {
e.printStackTrace();
}
return tbItemDesc;
}
1.6.5、添加屬性文件並加載屬性文件
第一步:添加屬性文件
如下圖:

第二步:在applicationContext-dao.xml中加載屬性文件:使用:
<context:property-placeholder location="classpath:properties/*.properties" />
2、使用FreeMarker實現網頁靜態化(解決高並發)
什么是靜態化?
通過一些技術手段(FreeMarker)將動態的頁面(JSP、asp.net、php) 轉換成靜態的頁面,通過瀏覽器直接訪問靜態頁面。
為什么要靜態化?
1、通過瀏覽器直接訪問靜態的頁面,不需要經過程序處理,它的訪問速度高。
2、穩定性好。
3、更有效的防止安全漏洞問題,比如:不易遭受黑客攻擊。
4、靜態的頁面更容易被搜索引擎收錄。
怎么樣實現靜態化?
可以使用FreeMarker模板引擎
實現網頁靜態化或者Velocity模板引擎
實現網頁靜態化。案例我們使用FreeMarker模板引擎
。
2.1、什么是FreeMarker
FreeMarker
是一個用Java語言
編寫的模板引擎
,它基於模板輸出文本
。FreeMarker與Web容器無關
,即在Web運行時,它並不知道Servlet或HTTP。它不僅可以用作表現層的實現技術,而且還可以用於生成XML,JSP或Java等。
目前企業中:主要用FreeMarker
做靜態頁面或是頁面展示。
也可以使用Velocity
模板引擎快速生成代碼,有空自學。
2.2、FreeMarker的使用方法
由於我們需要訪問靜態頁面,靜態頁面也算一個表現層,所以我們在taotao-item-web中測試
使用FreeMarker。
我們先把FreeMarker的jar包添加到taotao-item-web工程中。
<!-- 配置對FreeMarker模板引擎的依賴 --!>
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>2.3.23</version>
</dependency>
在Maven工程中是添加依賴。

原理:

使用步驟:
第一步:創建一個Configuration對象,直接new一個對象。構造方法的參數就是FreeMarker對應的版本號。
第二步:設置模板文件所在的路徑。
第三步:設置模板文件使用的字符集。一般就是utf-8。
第四步:使用Configuration對象加載一個模板對象,即創建一個模板對象。
第五步:創建一個模板使用的數據集,可以是POJO也可以是map。一般是Map。
第六步:創建一個Writer對象,一般創建一個FileWriter對象,指定生成文件路徑和文件名。
第七步:調用模板對象的process方法輸出文件。
第八步:關閉流。
模板:
${hello}
在taotao-item-web工程中新建一個測試類,測試使用FreeMarker。如下圖所示:

測試代碼如下:
@Test
public void freeMarkerTest() throws Exception {
// 第一步:創建一個Configuration對象,直接new一個對象。構造方法的參數就是FreeMarker對應的版本號。
Configuration configuration = new Configuration(Configuration.getVersion());
// 第二步:設置模板文件所在的路徑。
configuration.setDirectoryForTemplateLoading(new File("D:/learn/Eclipse/eclipse-jee-mars-2-win32-x86_64/workspace/taotao/taotao-item-web/src/main/webapp/WEB-INF/ftl/test/"));
// 第三步:設置模板文件使用的字符集。一般就是utf-8
configuration.setDefaultEncoding("utf-8");
// 第四步:使用Configuration對象加載一個模板對象,即創建一個模板對象。
Template template = configuration.getTemplate("hello.ftl");
// 第五步:創建一個模板使用的數據集,可以是POJO也可以是Map。推薦使用是Map。因為Map比較靈活。
Map<Object, Object> dataModel = new HashMap<>();
// 向數據集中添加數據
// 1、取Map中key對應的值
dataModel.put("hello", "this is my first freemarker test.");
// 第六步:創建一個Writer對象,一般創建一個FileWriter對象,指定生成的文件路徑和文件名。
Writer out = new FileWriter(new File("D:/temp/javaee28/test/hello.txt"));
// 第七步:調用模板對象的process方法輸出文件。
template.process(dataModel, out);
// 第八步:關閉流。
out.close();
}
2.3、Eclipse中可以使用FreeMarker的插件
將以下文件放置到eclipse的安裝目錄的plugins中,重啟eclipse就可以用了。xxx.ftl文件就會出現高亮及顏色相關的提示。

2.4、FreeMarker模板的語法使用
注意:我們模板使用的數據集是Map集合,所以以下例子中均與Map集合有關
。
2.4.1、取Map中key的值
上面的2.2、FreeMarker的使用方法
中演示的代碼就是該例子。
編輯模型數據的java代碼邏輯如下:
// 1、取Map中key的值
dataModel.put("hello", "this is my first freemarker test.");
模板文件hello.ftl代碼如下:
${hello}
2.4.2、取Map中pojo的屬性的值
Student對象。屬性有:學號、姓名、年齡、家庭住址。
/**
* 測試FreeMarker使用的POJO
* @author chenmingjun
* @date 2018年11月29日
* @version V1.0
*/
public class Student {
private int id;
private String name;
private int age;
private String address;
public Student() {
}
public Student(int id, String name, int age, String address) {
super();
this.id = id;
this.name = name;
this.age = age;
this.address = address;
}
// getter方法和setter方法
模板文件student.ftl的取值方式:${key.property},如下圖所示:

編輯模型數據的java代碼邏輯如下:
@Test
public void freeMarkerTest() throws Exception {
// 第一步:創建一個Configuration對象,直接new一個對象。構造方法的參數就是FreeMarker對應的版本號。
Configuration configuration = new Configuration(Configuration.getVersion());
// 第二步:設置模板文件所在的路徑。
configuration.setDirectoryForTemplateLoading(new File("D:/learn/Eclipse/eclipse-jee-mars-2-win32-x86_64/workspace/taotao/taotao-item-web/src/main/webapp/WEB-INF/ftl/test/"));
// 第三步:設置模板文件使用的字符集。一般就是utf-8
configuration.setDefaultEncoding("utf-8");
// 第四步:使用Configuration對象加載一個模板對象,即創建一個模板對象。
Template template = configuration.getTemplate("student.ftl");
// 第五步:創建一個模板使用的數據集,可以是POJO也可以是Map。推薦使用是Map。因為Map比較靈活。
Map<Object, Object> dataModel = new HashMap<>();
// 向數據集中添加數據
// 1、取Map中key的值
// dataModel.put("hello", "this is my first freemarker test.");
// 2、取Map中pojo屬性的值
Student student = new Student(1, "小明", 18, "北京青年路");
dataModel.put("student", student);
// 第六步:創建一個Writer對象,一般創建一個FileWriter對象,指定生成的文件路徑和文件名。
Writer out = new FileWriter(new File("D:/temp/javaee28/test/student.html"));
// 第七步:調用模板對象的process方法輸出文件。
template.process(dataModel, out);
// 第八步:關閉流。
out.close();
}
輸出的文件如下:

注意:訪問map中pojo中的pojo的屬性,使用
屬性導航
的方式。
如果模型數據中設置的值是1000以上,會出現千分位(
1,000
),在取值的時候可以使用
?c
去除(
${student.id?c}
)。
2.4.3、取Map中List集合中的數據
模板文件student2.ftl代碼如下:
<#list studentList as student>
${student.id}/${studnet.name}
</#list>
循環使用格式:
<#list 要循環的數據 as 循環后的數據>
</#list>
編輯模型數據的java代碼邏輯如下:
@Test
public void freeMarkerTest() throws Exception {
// 第一步:創建一個Configuration對象,直接new一個對象。構造方法的參數就是FreeMarker對應的版本號。
Configuration configuration = new Configuration(Configuration.getVersion());
// 第二步:設置模板文件所在的路徑。
configuration.setDirectoryForTemplateLoading(new File("D:/learn/Eclipse/eclipse-jee-mars-2-win32-x86_64/workspace/taotao/taotao-item-web/src/main/webapp/WEB-INF/ftl/test/"));
// 第三步:設置模板文件使用的字符集。一般就是utf-8
configuration.setDefaultEncoding("utf-8");
// 第四步:使用Configuration對象加載一個模板對象,即創建一個模板對象。
Template template = configuration.getTemplate("student2.ftl");
// 第五步:創建一個模板使用的數據集,可以是POJO也可以是Map。推薦使用是Map。因為Map比較靈活。
Map<Object, Object> dataModel = new HashMap<>();
// 向數據集中添加數據
// 1、取Map中key的值
// dataModel.put("hello", "this is my first freemarker test.");
// 2、取Map中pojo的屬性的值
// Student student = new Student(1, "小明", 18, "北京青年路");
// dataModel.put("student", student);
// 3、取Map中List集合中的數據
List<Student> studentList = new ArrayList<>();
studentList.add(new Student(1, "小明1", 18, "北京青年路1"));
studentList.add(new Student(2, "小明2", 19, "北京青年路2"));
studentList.add(new Student(3, "小明3", 20, "北京青年路3"));
studentList.add(new Student(4, "小明4", 21, "北京青年路4"));
studentList.add(new Student(5, "小明5", 22, "北京青年路5"));
dataModel.put("studentList", studentList);
// 第六步:創建一個Writer對象,一般創建一個FileWriter對象,指定生成的文件路徑和文件名。
Writer out = new FileWriter(new File("D:/temp/javaee28/test/student2.html"));
// 第七步:調用模板對象的process方法輸出文件。
template.process(dataModel, out);
// 第八步:關閉流。
out.close();
}
修改模板:
student2.ftl
<html>
<head>
<title>FreeMarker測試頁面</title>
</head>
<body>
<table border="1">
<tr>
<th>序號</th>
<th>學號</th>
<th>姓名</th>
<th>年齡</th>
<th>家庭住址</th>
</tr>
<#list studentList as student>
<#if student_index % 2 == 0>
<tr bgcolor="red">
<#else>
<tr bgcolor="blue">
</#if>
<td>${student_index}</td>
<td>${student.id}</td>
<td>${student.name}</td>
<td>${student.age}</td>
<td>${student.address}</td>
</tr>
</#list>
</table>
</body>
</html>
其中:studentList as student
中的
studentList:為模型設置中的key
student:為集合中的元素的名稱(可以任意)
瀏覽器效果如下:

2.4.4、取Map中Map集合中的數據
編輯模型數據的java代碼邏輯:
@Test
public void freeMarkerTest() throws Exception {
// 第一步:創建一個Configuration對象,直接new一個對象。構造方法的參數就是FreeMarker對應的版本號。
Configuration configuration = new Configuration(Configuration.getVersion());
// 第二步:設置模板文件所在的路徑。
configuration.setDirectoryForTemplateLoading(new File("D:/learn/Eclipse/eclipse-jee-mars-2-win32-x86_64/workspace/taotao/taotao-item-web/src/main/webapp/WEB-INF/ftl/test/"));
// 第三步:設置模板文件使用的字符集。一般就是utf-8
configuration.setDefaultEncoding("utf-8");
// 第四步:使用Configuration對象加載一個模板對象,即創建一個模板對象。
Template template = configuration.getTemplate("student3.ftl");
// 第五步:創建一個模板使用的數據集,可以是POJO也可以是Map。推薦使用是Map。因為Map比較靈活。
Map<Object, Object> dataModel = new HashMap<>();
// 向數據集中添加數據
// 1、取Map中key的值
// dataModel.put("hello", "this is my first freemarker test.");
// 2、取Map中pojo的屬性的值
// Student student = new Student(1, "小明", 18, "北京青年路");
// dataModel.put("student", student);
// 3、取Map中List集合中的數據
// List<Student> studentList = new ArrayList<>();
// studentList.add(new Student(1, "小明1", 18, "北京青年路1"));
// studentList.add(new Student(2, "小明2", 19, "北京青年路2"));
// studentList.add(new Student(3, "小明3", 20, "北京青年路3"));
// studentList.add(new Student(4, "小明4", 21, "北京青年路4"));
// studentList.add(new Student(5, "小明5", 22, "北京青年路5"));
// dataModel.put("studentList", studentList);
// 4、取Map中Map集合中的數據
Map<Object, Object> studentMap = new HashMap<>();
studentMap.put("stu1",new Student(1, "小藝1", 18, "北京物資學院1"));
studentMap.put("stu2",new Student(1, "小藝2", 19, "北京物資學院2"));
studentMap.put("stu3",new Student(1, "小藝3", 20, "北京物資學院3"));
studentMap.put("stu4",new Student(1, "小藝4", 21, "北京物資學院4"));
studentMap.put("stu5",new Student(1, "小藝5", 22, "北京物資學院5"));
dataModel.put("studentMap", studentMap);
// 第六步:創建一個Writer對象,一般創建一個FileWriter對象,指定生成的文件路徑和文件名。
Writer out = new FileWriter(new File("D:/temp/javaee28/test/student3.html"));
// 第七步:調用模板對象的process方法輸出文件。
template.process(dataModel, out);
// 第八步:關閉流。
out.close();
}
修改模板:
student3.ftl
<html>
<head>
<title>FreeMarker測試頁面</title>
</head>
<body>
<table border="1">
<tr>
<th>序號</th>
<th>學號</th>
<th>姓名</th>
<th>年齡</th>
<th>家庭住址</th>
</tr>
<#list studentMap?keys as key>
<#if key_index % 2 == 0>
<tr bgcolor="red">
<#else>
<tr bgcolor="blue">
</#if>
<td>${key_index}</td>
<td>${studentMap[key].id}</td>
<td>${studentMap[key].name}</td>
<td>${studentMap[key].age}</td>
<td>${studentMap[key].address}</td>
</tr>
</#list>
</table>
</body>
</html>
瀏覽器效果如下:

2.4.5、取循環中的下標
模板代碼的格式如下:
<#list studentList as student>
${student_index}
</#list>
下標從0開始,當然也可以支持運算,比如:${student_index+1}
則輸出為1,2,3,…
演示同上2.4.3、取Map中List集合中的數據
所示。
2.4.6、判斷
模板代碼的格式如下:
<#if student_index % 2 == 0>
...
<#else>
...
</#if>
演示同上2.4.3、取Map中List集合中的數據
所示。
2.4.7、取Map中的日期類型
編輯模型數據的java代碼邏輯:
// 5、取Map中的日期類型
dataModel.put("date", new Date());
修改模板:
student4.ftl
模板代碼的格式如下:
${date} (date是屬性名)如果傳來的是一個Date類型數據會報錯,取出來時需要進行格式化。格式化方式如下:
示例如下:
當前日期:${date?date}<br>
當前時間:${date?time}<br>
當前日期和時間:${date?datetime}<br>
自定義日期格式:${date?string("yyyy/MM/dd HH:mm:ss")}<br>
瀏覽器效果如下:
當前日期:2018-11-30
當前時間:11:20:20
當前日期和時間:2018-11-30 11:20:20
自定義日期格式:2018/11/30 11:20:20
2.4.8、對Map中的null值的處理
編輯模型數據的java代碼邏輯如下:

模板文件代碼如下:

這是沒有問題的,如果代碼中注釋掉呢?即在模板代碼中直接取一個不存在的值(值為null)時會報異常。
所以需要針對空值(null)做處理。
模板代碼的格式如下:
方式一:
${test!"說明是null值,作為默認值的我出現了"}
方式二:
${test!""}
方式三:
${test!}
方式四:
${test!}
<br>
// 使用if判斷null值<br>
<#if test??>
取的test不是null值
<#else>
取的test是null值
</#if>
2.4.9、include標簽
模板代碼的格式如下:
<#include “模板名稱”>
示例如下:
先創建一個模板文件:hello.ftl,模板中的內容為:${hello}
再創建的另一個模板文件 student5.ftl 中使用 <#include "hello.ftl"/> 引用上述模板。
編輯模型數據的java代碼邏輯如下:
@Test
public void freeMarkerTest() throws Exception {
// 第一步:創建一個Configuration對象,直接new一個對象。構造方法的參數就是FreeMarker對應的版本號。
Configuration configuration = new Configuration(Configuration.getVersion());
// 第二步:設置模板文件所在的路徑。
configuration.setDirectoryForTemplateLoading(new File("D:/learn/Eclipse/eclipse-jee-mars-2-win32-x86_64/workspace/taotao/taotao-item-web/src/main/webapp/WEB-INF/ftl/test/"));
// 第三步:設置模板文件使用的字符集。一般就是utf-8
configuration.setDefaultEncoding("utf-8");
// 第四步:使用Configuration對象加載一個模板對象,即創建一個模板對象。
Template template = configuration.getTemplate("student5.ftl");
// 第五步:創建一個模板使用的數據集,可以是POJO也可以是Map。推薦使用是Map。因為Map比較靈活。
Map<Object, Object> dataModel = new HashMap<>();
// 向數據集中添加數據
// 1、取Map中key對應的的值
dataModel.put("hello", "this is my first freemarker test.");
// 2、取Map中pojo的屬性的值
// Student student = new Student(1, "小明", 18, "北京青年路");
// dataModel.put("student", student);
// 3、取Map中List集合中的數據
// List<Student> studentList = new ArrayList<>();
// studentList.add(new Student(1, "小明1", 18, "北京青年路1"));
// studentList.add(new Student(2, "小明2", 19, "北京青年路2"));
// studentList.add(new Student(3, "小明3", 20, "北京青年路3"));
// studentList.add(new Student(4, "小明4", 21, "北京青年路4"));
// studentList.add(new Student(5, "小明5", 22, "北京青年路5"));
// dataModel.put("studentList", studentList);
// 4、取Map中Map集合中的數據
// Map<Object, Object> studentMap = new HashMap<>();
// studentMap.put("stu1", new Student(1, "小藝1", 18, "北京物資學院1"));
// studentMap.put("stu2", new Student(2, "小藝2", 19, "北京物資學院2"));
// studentMap.put("stu3", new Student(3, "小藝3", 20, "北京物資學院3"));
// studentMap.put("stu4", new Student(4, "小藝4", 21, "北京物資學院4"));
// studentMap.put("stu5", new Student(5, "小藝5", 22, "北京物資學院5"));
// dataModel.put("studentMap", studentMap);
// 5、取Map中的日期類型
// dataModel.put("date", new Date());
// 6、對Map中的null值的處理
// dataModel.put("test", "雲雀叫了一整天");
// 第六步:創建一個Writer對象,一般創建一個FileWriter對象,指定生成的文件路徑和文件名。
Writer out = new FileWriter(new File("D:/temp/javaee28/test/student5.html"));
// 第七步:調用模板對象的process方法輸出文件。
template.process(dataModel, out);
// 第八步:關閉流。
out.close();
}
2.5、FreeMarker整合spring
為了測試方便,在taotao-item-web工程中的pom.xml引入jar包:
FreeMarker的jar包

注意:還需要spring-context-support的jar包

2.5.1、創建整合spring的配置文件
可以在taotao-item-web工程中的springmvc.xml中配置
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.2.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-4.2.xsd
http://code.alibabatech.com/schema/dubbo
http://code.alibabatech.com/schema/dubbo/dubbo.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.2.xsd">
<!-- 配置加載屬性文件 -->
<context:property-placeholder location="classpath:resource/resource.properties"/>
<!-- 配置包掃描器,掃描所有需要帶@Controller注解的類 -->
<context:component-scan base-package="com.taotao.item.controller" />
<!-- 配置注解驅動 -->
<mvc:annotation-driven />
<!-- 配置視圖解析器 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsp/" />
<property name="suffix" value=".jsp" />
</bean>
<!-- 配置FreeMarker -->
<bean id="freemarkerConfig" class="org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer">
<property name="templateLoaderPath" value="/WEB-INF/ftl/test" /><!-- 用於測試的目錄,實際中需要修改該目錄 -->
<property name="defaultEncoding" value="UTF-8" />
</bean>
<!-- 引用dubbo服務 :需要先引入dubbo的約束-->
<dubbo:application name="taotao-item-web"/>
<dubbo:registry protocol="zookeeper" address="192.168.25.128:2181"/>
<dubbo:reference interface="com.taotao.service.ItemService" id="itemService" />
</beans>
因為測試方法所在的方法是java環境,但是現在需要的是web環境,需要將FreeMarker注入進去,所以需要編寫Controller進行測試,而不能編寫測試方法了。
2.5.2、Controller
請求的url:/genHTML
參數:無
返回值:OK(String,需要使用@ResponseBody)
業務邏輯:
1、從spring容器中獲得FreeMarkerConfigurer對象。
2、從FreeMarkerConfigurer對象中獲得Configuration對象。
3、使用Configuration對象獲得Template對象。
4、創建模型數據集。設置模型數據一般使用的是Map,也可以使用POJO。
5、創建輸出文件的Writer對象。
6、調用模板對象的process方法,生成文件。
7、關閉流。
測試代碼如下:
/**
* FreeMarker測試管理的Controller
* @author chenmingjun
* @date 2018年11月30日 下午1:31:29
* @version V1.0
*/
@Controller
public class GenHTMLTestController {
@Autowired
private FreeMarkerConfigurer freeMarkerConfigurer;
@RequestMapping("/genHTML")
@ResponseBody
public String genHtml() throws Exception {
// 1、從spring容器中獲得FreeMarkerConfigurer對象。
// 2、從FreeMarkerConfigurer對象中獲得Configuration對象。
Configuration configuration = freeMarkerConfigurer.getConfiguration();
// 3、使用Configuration對象獲得Template對象。
Template template = configuration.getTemplate("hello.ftl");
// 4、創建模型數據集。設置模型數據一般使用的是Map,也可以使用POJO。
Map<Object, Object> dataModel = new HashMap<>();
dataModel.put("hello", "freemarker & spring test");
// 5、創建輸出文件的Writer對象。指定輸出目錄及文件名。
Writer out = new FileWriter(new File("D:/temp/javaee28/test/test.html"));
// 6、調用模板對象的process方法,生成文件。
template.process(dataModel, out);
// 7、關閉流。
out.close();
return "OK";
}
}
2.6、商品詳情頁面靜態化
2.6.1、網頁靜態化-實現方案分析(十分重要)
輸出文件的名稱:商品id+“.html”
輸出文件的路徑:工程外部的任意目錄。
網頁訪問:使用nginx(http服務器)訪問靜態網頁。在此方案下tomcat只有一個作用就是生成靜態頁面(因為tomcat的強項是處理jsp,對於處理靜態資源的訪問不擅長)。
工程部署:可以把taotao-item-web部署到多個服務器上。
生成靜態頁面的時機:商品添加后,生成靜態頁面。可以使用Activemq,訂閱topic方式(監聽商品添加事件)。

多台服務器訂閱同一個主題(topic) 多台服務器生成的html都是一樣。
2.6.2、網頁靜態化-FreeMarker模板改造
原來使用的是JSP展示頁面,我們可以參考原來的JSP頁面樣式展示,將JSP中的JSTL標簽
、@page
等語法,換成freemarker的標簽及語法規則。並命名文件名為xxx.ftl,在taotao-item-web工程中的WEB-INF目錄下,如下圖:

注意:在footer.ftl中,需要處理空值的問題,例如:

2.7、商品詳情頁面靜態化方案實現(Windows版本的nginx作http服務器)
2.7.1、實現分析
在taotao-item-service工程中消費(接收)消息。
使用ActiveMQ需要導入ActiveMQ的依賴包,在Maven工程中是添加依賴。
在taotao-item-web工程中的pom.xnl文件中添加依賴:
<!-- 配置對ActiveMQ客戶端的依賴 -->
<dependency>
<groupId>org.apache.activemq</groupId>
<artifactId>activemq-all</artifactId>
</dependency>
接收消息:
先配置消息相關的配置文件(包括目的地,自定義的消息監聽容器)
編寫自定義的消息監聽器實現類(實現MessageListener接口)
獲取消息中的商品ID,查詢出數據集(模板和數據)
生成靜態網頁的邏輯:
要做的事情:准備模板文件,准備數據集,數據集通過消息獲取商品的id查詢數據庫獲取。
1、配置FreeMarker的配置文件(模板的目錄,默認字符集)
2、獲取Configuration
3、設置數據集
4、加載模板
5、設置輸出目錄文件(FileWriter)
6、生成文件,關閉流(輸出文件的名稱:商品id+".html")
7、部署http服務器(推薦使用nginx)
2.7.2、消息監聽器的編寫
/**
* 監聽消息-生成靜態網頁
* @author chenmingjun
* @date 2018年11月30日 下午6:07:43
* @version V1.0
*/
public class ItemAddGenHTMLMessageListener implements MessageListener {
// 注入ItemService
@Autowired
private ItemService itemService;
// 注入freeMarkerConfigurer
@Autowired
private FreeMarkerConfigurer freeMarkerConfigurer;
@Value("${HTML_OUT_PATH}")
private String HTML_OUT_PATH;
@Override
public void onMessage(Message message) {
if (message instanceof TextMessage) {
// 1、從消息中取出商品id
TextMessage textMessage = (TextMessage) message;
try {
String text = textMessage.getText();
if (StringUtils.isNotBlank(text)) {
// 2、通過接收到的消息轉換成商品id,根據商品id查詢商品的信息
Long itemId = Long.valueOf(text);
// 需要等待一下“服務層的消息生產者taotao-manager-service”的事務提交,否則會報空指針異常
Thread.sleep(1000);
// 調用商品服務查詢商品的信息
TbItem tbItem = itemService.getItemById(itemId);
Item item = new Item(tbItem);
TbItemDesc tbItemDesc = itemService.getItemDescById(itemId);
// 3、使用FreeMarker生成靜態頁面
this.genHtml("item.ftl", tbItem, tbItemDesc);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
/**
* 使用FreeMarker生成靜態頁面的方法
* @param templateName
* @param tbItem
* @param tbItemDesc
* @throws Exception
*/
private void genHtml(String templateName, Item item, TbItemDesc tbItemDesc) throws Exception {
// 1、從spring容器中獲得FreeMarkerConfigurer對象。
// 2、從FreeMarkerConfigurer對象中獲得Configuration對象。
Configuration configuration = freeMarkerConfigurer.getConfiguration();
// 3、使用Configuration對象獲得Template對象。
Template template = configuration.getTemplate(templateName);
// 4、創建模型數據集。設置模型數據一般使用的是Map,也可以使用POJO。
Map<Object, Object> dataModel = new HashMap<>();
dataModel.put("item", item);
dataModel.put("itemDesc", tbItemDesc);
// 5、創建輸出文件的Writer對象。指定輸出目錄及文件名。
Writer out = new FileWriter(new File(HTML_OUT_PATH + item.getId() + ".html"));
// 6、調用模板對象的process方法,生成文件。
template.process(dataModel, out);
// 7、關閉流。
out.close();
}
}
編寫所需要的屬性文件resource.properties
#靜態頁面的輸出路徑
HTML_OUT_PATH=D:/temp/javaee28/item/
springmvc.xml中加載所需的屬性文件
<!-- 配置加載屬性文件 -->
<context:property-placeholder location="classpath:resource/resource.properties"/>
2.7.3、配置springmvc-activemq.xml 和 web.xml
在taotao-item-service工程中
需要配置消費者端的配置文件springmvc-activemq.xml:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.2.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.2.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.2.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-4.2.xsd
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util-4.2.xsd">
<!-- 真正可以產生Connection的ConnectionFactory,由對應的 JMS服務廠商提供 -->
<bean id="targetConnectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory">
<property name="brokerURL" value="tcp://192.168.25.168:61616"></property>
</bean>
<!-- Spring用於管理真正的ConnectionFactory的ConnectionFactory -->
<bean id="connectionFactory" class="org.springframework.jms.connection.SingleConnectionFactory">
<!-- 目標ConnectionFactory對應真實的可以產生JMS Connection的ConnectionFactory -->
<property name="targetConnectionFactory" ref="targetConnectionFactory"></property>
</bean>
<!-- 接收和發送消息時使用的類 -->
<!-- 配置消息的消費者 -->
<!-- 先配置自定義的監聽器 -->
<bean id="itemAddGenHTMLMessageListener" class="com.taotao.item.listener.ItemAddGenHTMLMessageListener" />
<!-- 再配置消息監聽容器 -->
<bean class="org.springframework.jms.listener.DefaultMessageListenerContainer">
<property name="connectionFactory" ref="connectionFactory" />
<property name="destination" ref="itemAddTopic" />
<property name="messageListener" ref="itemAddGenHTMLMessageListener" />
</bean>
<!-- 由於新增商品,對應的商品搜索索引庫要同步、要生成訂單頁面、要同步緩存等,即很多地方要監聽商品添加這個事件,所以我們使用Topic -->
<!-- 這個是話題目的地,一對多的 -->
<bean id="itemAddTopic" class="org.apache.activemq.command.ActiveMQTopic">
<constructor-arg name="name" value="item-add-topic"></constructor-arg>
</bean>
</beans>
需要在web.xml中配置加載springmvc-activemq.xml文件:

測試:在后台管理系統中,添加商品,測試能夠生成靜態頁面。
2.7.4、http服務器的安裝及配置
使用nginx作為Http服務器,演示我們暫使用windows版本的nginx。下次我們使用Linux版本的nginx。

解壓到相應的磁盤,(注意:不要將nginx解壓到帶有中文目錄的目錄中,否則啟動不起來)
修改/conf目錄下的nginx.conf配置如下:

2.7.5、添加JS及樣式等靜態資源

2.8.5、啟動nginx並測試
第一種方式:雙擊nginx.exe
第二種方式:使用命令:
cd到nginx所在的目錄:
啟動命令:start nginx.exe
關閉命令:nginx.exe -s stop
刷新配置文件:nginx.exe -s reload
查看任務管理器:如下:

說明啟動成功。
瀏覽器訪問地址:http://localhost/item/149265523408245.html
測試成功!
注意:為了后續的學習的方便,這里只是演示如何生成靜態頁面,因為需要先生成靜態頁面才能訪問,而生成靜態頁面比較麻煩,所以后面的學習依舊使用動態頁面展示商品詳情。
3、兩天學習小結
通過這兩天的學習,現在總結一下:
1、商品詳情頁面模塊的實現:
通過solr全文搜索找到商品,通過商品id去redis中找當前id的緩存,找不到就去數據庫中查找並添加到緩存中。
為了提高redis的高可用,把不常訪問的商品從redis緩存中清除:使用定時。
每次點擊都會把key的時間重置,當key在他的生命中沒有被點擊就會從redis中清除,再次訪問時再次添加。
2、兩方面影響用戶訪問速度:
數據庫查詢
使用緩存
3、服務器生成html頁面
使用freemaker生成靜態頁面
4、Freemaker生成靜態頁面的時機
添加商品后使用activemq廣播消息,freemaker監聽到消息后去數據庫查詢商品並生成靜態頁面。
為什么不去redis中獲取商品信息呢?
答:添加商品時還沒有及時存儲到redis中。因為activemq廣播消息到redis接收消息需要一些時間。
為什么不直接使用商品信息,卻還要到數據庫中查詢?
答:因為不在一個項目中,傳輸數據麻煩,也起不到提高效率的作用,而且修改數據時也要修改靜態頁面。
redis存儲數據庫表信息;
Key = 表名:id:字段
Value = 字段值
提高用戶的訪問速度的兩種方案:
一、使用redis緩存
二、網頁靜態化