1. 课程计划
第九天:
1、商品详情页面展示,动态展示 jsp + redis
2、使用freemarker实现网页静态化
3、ActiveMq同步生成静态网页
2. 商品详情页面展示
创建一个商品详情页面展示的工程。是一个表现层工程。
2.1. 工程搭建
Taotao-item-web。打包方式war。可以参考taotao-portal-web
2.1.1. Pom文件
<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>
<groupId>com.taotao</groupId>
<artifactId>taotao-item-web</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>war</packaging>
<dependencies>
<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相关 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>dubbo</artifactId>
<!-- 排除依赖 -->
<exclusions>
<exclusion>
<groupId>org.springframework</groupId>
<artifactId>spring</artifactId>
</exclusion>
<exclusion>
<groupId>org.jboss.netty</groupId>
<artifactId>netty</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
</dependency>
<dependency>
<groupId>com.github.sgroschupf</groupId>
<artifactId>zkclient</artifactId>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</dependency>
</dependencies>
<!-- 配置tomcat插件 -->
<build>
<plugins>
<plugin>
<groupId>org.apache.tomcat.maven</groupId>
<artifactId>tomcat7-maven-plugin</artifactId>
<configuration>
<port>8086</port>
<path>/</path>
</configuration>
</plugin>
</plugins>
</build>
</project>
2.2. 功能分析
在搜索结果页面点击商品图片或者商品标题,展示商品详情页面。
请求的url:/item/{itemId}
参数:商品id
返回值:String 逻辑视图
业务逻辑:
1、从url中取参数,商品id
2、根据商品id查询商品信息(tb_item)得到一个TbItem对象,缺少images属性,可以创建一个pojo继承TbItem,添加一个getImages方法。在taotao-item-web工程中。
public class Item extends TbItem {
public String[] getImages() {
String image2 = this.getImage();
if (image2 != null && !"".equals(image2)) {
String[] strings = image2.split(",");
return strings;
}
return null;
}
public Item() {
}
public Item(TbItem tbItem) {
this.setBarcode(tbItem.getBarcode());
this.setCid(tbItem.getCid());
this.setCreated(tbItem.getCreated());
this.setId(tbItem.getId());
this.setImage(tbItem.getImage());
this.setNum(tbItem.getNum());
this.setPrice(tbItem.getPrice());
this.setSellPoint(tbItem.getSellPoint());
this.setStatus(tbItem.getStatus());
this.setTitle(tbItem.getTitle());
this.setUpdated(tbItem.getUpdated());
}
}
3、根据商品id查询商品描述。
4、展示到页面。
2.3. Dao层
查询tb_item, tb_item_desc两个表,都是单表查询。可以使用逆向工程。
1、根据商品id查询商品信息
参数:商品id
返回值:TbItem
2、根据商品id查询商品描述
参数:商品id
返回值:TbItemDesc
@Override
public TbItemDesc getItemDescById(long itemId) {
TbItemDesc itemDesc = itemDescMapper.selectByPrimaryKey(itemId);
return itemDesc;
}
2.4.1. 发布服务
2.5. 表现层
2.5.1. 引用服务
2.5.2. Controller
请求的url:/item/{itemId}
参数:商品id
返回值:String 逻辑视图
@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);
return "item";
}
}
2.6. 向业务逻辑中添加缓存
2.6.1. 缓存添加分析
使用redis做缓存。
业务逻辑:
1、根据商品id到缓存中命中
2、查到缓存,直接返回。
3、差不到,查询数据库
4、把数据放到缓存中
5、返回数据
缓存中缓存热点数据,提供缓存的使用率。需要设置缓存的有效期。一般是一天的时间,可以根据实际情况跳转。
需要使用String类型来保存商品数据。
可以加前缀方法对象redis中的key进行归类。
ITEM_INFO:123456:BASE
ITEM_INFO:123456:DESC
如果把二维表保存到redis中:
1、表名就是第一层
2、主键是第二层
3、字段名第三次
三层使用“:”分隔作为key,value就是字段中的内容。
2.6.2. 把redis相关的jar包添加到工程
淘淘工具类
2.6.3. 添加缓存
@Override
public TbItem getItemById(long itemId) {
try {
//查询缓存
String json = jedisClient.get(ITEM_INFO_PRE + ":" + itemId + ":BASE");
if (StringUtils.isNotBlank(json)) {
//把json转换为java对象
TbItem item = JsonUtils.jsonToPojo(json, TbItem.class);
return item;
}
} catch (Exception e) {
e.printStackTrace();
}
//根据商品id查询商品信息
//TbItem tbItem = itemMapper.selectByPrimaryKey(itemId);
TbItemExample example = new TbItemExample();
//设置查询条件
Criteria criteria = example.createCriteria();
criteria.andIdEqualTo(itemId);
List<TbItem> list = itemMapper.selectByExample(example);
if (list != null && list.size() > 0) {
TbItem item = list.get(0);
try {
//把数据保存到缓存
jedisClient.set(ITEM_INFO_PRE + ":" + itemId + ":BASE", JsonUtils.objectToJson(item));
//设置缓存的有效期
jedisClient.expire(ITEM_INFO_PRE + ":" + itemId + ":BASE", ITEM_INFO_EXPIRE);
} catch (Exception e) {
e.printStackTrace();
}
return item;
}
return null;
}
取商品描述添加缓存:
@Override
public TbItemDesc getItemDescById(long itemId) {
try {
String json = jedisClient.get(ITEM_INFO_PRE + ":" + itemId + ":DESC");
//判断缓存是否命中
if (StringUtils.isNotBlank(json) ) {
//转换为java对象
TbItemDesc itemDesc = JsonUtils.jsonToPojo(json, TbItemDesc.class);
return itemDesc;
}
} catch (Exception e) {
e.printStackTrace();
}
TbItemDesc itemDesc = itemDescMapper.selectByPrimaryKey(itemId);
try {
jedisClient.set(ITEM_INFO_PRE + ":" + itemId + ":DESC", JsonUtils.objectToJson(itemDesc));
//设置过期时间
jedisClient.expire(ITEM_INFO_PRE + ":" + itemId + ":DESC", ITEM_INFO_EXPIRE);
} catch (Exception e) {
e.printStackTrace();
}
return itemDesc;
3. 网页静态化
可以使用Freemarker实现网页静态化。
3.1. 什么是freemarker
FreeMarker是一个用Java语言编写的模板引擎,它基于模板来生成文本输出。FreeMarker与Web容器无关,即在Web运行时,它并不知道Servlet或HTTP。它不仅可以用作表现层的实现技术,而且还可以用于生成XML,JSP或Java 等。
目前企业中:主要用Freemarker做静态页面或是页面展示
3.2. Freemarker的使用方法
把freemarker的jar包添加到工程中。
Maven工程添加依赖
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>2.3.23</version>
</dependency>
使用步骤:
第一步:创建一个Configuration对象,直接new一个对象。构造方法的参数就是freemarker对于的版本号。
第二步:设置模板文件所在的路径。
第三步:设置模板文件使用的字符集。一般就是utf-8.
第四步:加载一个模板,创建一个模板对象。
第五步:创建一个模板使用的数据集,可以是pojo也可以是map。一般是Map。
第六步:创建一个Writer对象,一般创建一FileWriter对象,指定生成的文件名。
第七步:调用模板对象的process方法输出文件。
第八步:关闭流。
模板:
${hello}
@Test
public void genFile() throws Exception {
// 第一步:创建一个Configuration对象,直接new一个对象。构造方法的参数就是freemarker对于的版本号。
Configuration configuration = new Configuration(Configuration.getVersion());
// 第二步:设置模板文件所在的路径。
configuration.setDirectoryForTemplateLoading(new File("D:/workspaces-itcast/term197/taotao-item-web/src/main/webapp/WEB-INF/ftl"));
// 第三步:设置模板文件使用的字符集。一般就是utf-8.
configuration.setDefaultEncoding("utf-8");
// 第四步:加载一个模板,创建一个模板对象。
Template template = configuration.getTemplate("hello.ftl");
// 第五步:创建一个模板使用的数据集,可以是pojo也可以是map。一般是Map。
Map dataModel = new HashMap<>();
//向数据集中添加数据
dataModel.put("hello", "this is my first freemarker test.");
// 第六步:创建一个Writer对象,一般创建一FileWriter对象,指定生成的文件名。
Writer out = new FileWriter(new File("D:/temp/term197/out/hello.html"));
// 第七步:调用模板对象的process方法输出文件。
template.process(dataModel, out);
// 第八步:关闭流。
out.close();
}
3.3. 模板的语法
3.3.1. 访问map中的key
${key}
3.3.2. 访问pojo中的属性
Student对象。学号、姓名、年龄
${key.property}
3.3.3. 取集合中的数据
<#list studentList as student>
s
t
u
d
e
n
t
.
i
d
/
{student.id}/
student.id/{studnet.name}
</#list>
3.3.4. 取循环中的下标
<#list studentList as student>
${student_index}
</#list>
3.3.5. 判断
<#if student_index % 2 == 0>
<#else>
</#if>
3.3.6. 日期类型格式化
3.3.7. Null值的处理
3.3.8. Include标签
<#include “模板名称”>
3.4. Freemarker整合spring
引入jar包:
Freemarker的jar包
3.4.1. 创建整合spring的配置文件
<?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.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.xsd">
<bean id="freemarkerConfig"
class="org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer">
<property name="templateLoaderPath" value="/WEB-INF/ftl/" />
<property name="defaultEncoding" value="UTF-8" />
</bean>
</beans>
需要编写一Controller进行测试
3.4.2. Controller
请求的url:/genhtml
参数:无
返回值:ok (String, 需要使用@ResponseBody)
业务逻辑:
1、从spring容器中获得FreeMarkerConfigurer对象。
2、从FreeMarkerConfigurer对象中获得Configuration对象。
3、使用Configuration对象获得Template对象。
4、创建数据集
5、创建输出文件的Writer对象。
6、调用模板对象的process方法,生成文件。
7、关闭流。
加载配置文件:
@Controller
public class HtmlGenController {
@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 dataModel = new HashMap<>();
dataModel.put("hello", "1000");
// 5、创建输出文件的Writer对象。
Writer out = new FileWriter(new File("D:/temp/term197/out/spring-freemarker.html"));
// 6、调用模板对象的process方法,生成文件。
template.process(dataModel, out);
// 7、关闭流。
out.close();
return "OK";
}
}
3.5. 商品详情页面静态化
3.5.1. 网页的静态化方案
输出文件的名称:商品id+“.html”
输出文件的路径:工程外部的任意目录。
网页访问:使用nginx访问网页。在此方案下tomcat只有一个作用就是生成静态页面。
工程部署:可以把taotao-item-web部署到多个服务器上。
生成静态页面的时机:商品添加后,生成静态页面。可以使用Activemq,订阅topic(商品添加)
1商品详情页面生成Listener
1:在taotao-item-web中添加activemq的依赖
2:实现ActiveMQ的监听器MessagerListener
@Autowired
private ItemService itemService;
@Autowired
private FreeMarkerConfigurer freemarkerConfig;
@Value("${HTML_OUT_PATH}")
private String HTML_OUT_PATH;
@Override
public void onMessage(Message message) {
try {
//1.创建一个MessageListener接口的实现类用于监听创建商品的时候发出的消息
//2.从message中获取商品的id
TextMessage textMessage=(TextMessage) message;
String strItemId = textMessage.getText();
//3.查询商品的基本信息和商品的描述
TbItem tbItem = itemService.getItemById(Long.parseLong(strItemId));
TbItemDesc tbItemDesc = itemService.getItemDescById(Long.parseLong(strItemId));
Item item=new Item(tbItem);
//4.创建商品详情页面模板
Map<String,Object> data=new HashMap<String,Object>();
data.put("item", item);
data.put("itemDesc", tbItemDesc);
Configuration configuration = freemarkerConfig.getConfiguration();
// 根据spring的freemarker的配置文件加载/WEB-INF/ftl/下面的模板页面
Template template = configuration.getTemplate("item.ftl");
//5.指出文件输出目录
Writer out = new FileWriter(new File(HTML_OUT_PATH+strItemId+".html"));
//6.生成静态文件
template.process(data, out);
//7.关闭io流
out.close();
} catch (JMSException | IOException | TemplateException e) {
e.printStackTrace();
}
}
3:在taotao-item-web中配置activemq
为了让该配置文件也被spring加载进去,所以需要在web.xml中修改成:
<!-- 真正可以产生Connection的ConnectionFactory,由对应的 JMS服务厂商提供 -->
<bean id="targetConnectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory">
<property name="brokerURL" value="tcp://192.168.25.128:61616" />
</bean>
<!-- Spring用于管理真正的ConnectionFactory的ConnectionFactory -->
<bean id="connectionFactory"
class="org.springframework.jms.connection.SingleConnectionFactory">
<!-- 目标ConnectionFactory对应真实的可以产生JMS Connection的ConnectionFactory -->
<property name="targetConnectionFactory" ref="targetConnectionFactory" />
</bean>
<!--这个是主题目的地,一对多的 -->
<bean id="topicDestination" class="org.apache.activemq.command.ActiveMQTopic">
<constructor-arg value="item-change-topic" />
</bean>
<!-- 接收消息 -->
<!-- 配置监听器 -->
<bean id="htmlGenListener" class="com.taotao.item.listener.HtmlGenListener" />
<!-- 消息监听容器 -->
<bean class="org.springframework.jms.listener.DefaultMessageListenerContainer">
<property name="connectionFactory" ref="connectionFactory" />
<property name="destination" ref="topicDestination" />
<property name="messageListener" ref="htmlGenListener" />
</bean>
4:在resource.properties
14.把jsp改造为freemarker模板.avi
A: 修改已经存在的jsp为ftl模板页面
将jsp文件夹下面的页面拷贝到ftl下面,然后修改后缀名
注意:
1.去掉jsp有关语句<%@…%>
2.将jsp中的循环语句和条件语句转换成freemarker<#list><#if>
3.处理空值