淘淘商城系列——网页静态化——业务逻辑及测试

来源:互联网 发布:运行windows窗口管理器 编辑:程序博客网 时间:2024/06/04 19:19

首先,我们把ActiveMQ的客户端jar包的依赖添加到工程中,即需要在taotao-item-web工程中添加对ActiveMQ的依赖,如下图所示。
这里写图片描述
下面我们需要把taotao-search-service工程下的applicationContext-activemq.xml文件拷贝到taotao-item-web工程下的spring目录下并且更名为springmvc-activemq.xml,除此之外,我们还得对springmvc-activemq.xml文件的内容做一些修改,修改后的内容如下:

<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.129: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-add-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></beans>

上面配置文件中配置了一个消息监听器,那么我们需要创建这么一个消息监听器,当监听到有添加商品的消息时便去生成静态页面,消息监听器类如下图所示。
这里写图片描述
为方便大家复制,现将HtmlGenListener消息监听器的代码给出。

/** * 监听商品添加时事件,然后生成商品静态页面 * <p>Title: HtmlGenListener</p> * <p>Description: </p> * <p>Company: www.itcast.cn</p>  * @version 1.0 */public class HtmlGenListener implements MessageListener {    @Autowired    private ItemService itemService;    @Autowired    private FreeMarkerConfigurer freeMarkerConfigurer;    @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();            Long itemId =  new Long(strItemId);            // 3、根据商品id查询商品基本消息、商品描述。(即数据集已准备完毕)            TbItem tbItem = itemService.getItemById(itemId);            Item item = new Item(tbItem);            TbItemDesc tbItemDesc = itemService.getItemDesc(itemId);            // 创建数据集            Map data = new HashMap();            data.put("item", item);            data.put("itemDesc", tbItemDesc);            // 4、创建商品详情页面的模板            // 5、指定静态文件输出目录            Configuration configuration = freeMarkerConfigurer.getConfiguration();            Template template = configuration.getTemplate("item.htm");            FileWriter out = new FileWriter(new File(HTML_OUT_PATH + itemId + ".html"));            // 6、生成静态文件            template.process(data, out);            // 关闭流            out.close();        } catch (Exception e) {            e.printStackTrace();        }    }}

代码中输出的文件路径配置在配置文件中,如下图所示。
这里写图片描述
以上代码写完之后,紧接着我们就要来测试一下了。我们先启动各个服务,zookeeper服务、image服务、Solr服务(我这里用的是集群版)、Redis服务(我这里用的是单机版)、ActiveMQ服务。然后依次启动taotao-manager、taotao-content、taotao-search、taotao-manager-web、taotao-portal-web、 taotao-search-web、taotao-item-web这7个工程。都成功启动后,我们在淘淘商城后台页面添加一个商品,如下图所示。
这里写图片描述
添加完商品后,我们到静态文件目录下查看生成的静态文件,发现啥都没有,如下图所示,不是说好了能生成静态页面吗?这尼玛是要闹哪一出!!!
这里写图片描述
除此之外,Eclipse控制台还打印java.lang.NullPointerException异常,如下图所示。
这里写图片描述
然而当我们再次点击提交按钮还是提交该商品时,到静态文件目录下查看生成的静态文件,发现他妈的就有了,如下图所示。
这里写图片描述
这到底是一个什么样的问题啊!这究竟是为什么啊?我们可以看看taotao-manager-service工程下的ItemServiceImpl实现类中的添加商品的方法,即addItem方法,如下图所示。
这里写图片描述
大家仔细阅读一下addItem方法,然后我谈谈这个怪问题出现的原因。我们第一次刚开机,电脑比较慢,我们添加商品时,一旦添加完商品之后就直接发送消息了,这时事务提交了吗?由于addItem这个方法还没执行完,消息就已经发送出去了。我们必须明确一点就是,只有这个方法结束之后才会提交事务,现在在这个方法内部还没有提交事务之前,消息就已经发送出去了,消息发送出去之后,这个消息马上就被订阅方接收了,接收了之后,他会马上根据商品id去查询这个商品,由于他去查询商品的时候,我们这个事务还没提交,那么这就是在不同的事务里面,我们那边事务没提交,这边他在查,能查到吗?很显然查不到,查不到之后,所以就报了一个空指针异常。那么第二次为什么好使了呢?第二次我们由于已经处理过这个数据库了,已经添加过一次商品了,数据库中的有些服务已经被调到内存里面了,处理速度就比较快了,那么下一次我们再提交的时候,一旦发完消息之后,这个方法执行完了,事务马上提交,提交之后,订阅方再查询数据库的时候,他就能查询到这个商品信息了。
问题出现了,那到底该怎么解决呢?这里我给出两种解决方案,任君选择。第一种方案,别把发送消息的代码写在Service层中,我们可以写在表现层里面,即写在taotao-manager-web工程下的ItemController类中的addItem方法里面。在这里面发送消息,百分之百就会没问题。因为我们在这儿发消息,说明Service层中添加商品的方法它已然执行完了,服务已经执行完了,商品就已经有了,这时候我们再发个消息,订阅方百分之百的能查到商品信息。第二种方案,正如我所说的那样,在添加商品的时候是涉及到事务的,事务提交之后才能在数据库中查询到商品信息,假如网络不济,造成事务还没提交,接收消息的一端想去查询商品信息,这时显然是查询不到的,为了等待事务提交,采用三次尝试的机制,所以我们应该把HtmlGenListener消息监听器的代码修改一下,修改后如下图所示。
这里写图片描述
为方便大家复制,现将修改后的HtmlGenListener消息监听器的代码给出。

/** * 监听商品添加时事件,然后生成商品静态页面 * <p>Title: HtmlGenListener</p> * <p>Description: </p> * <p>Company: www.itcast.cn</p>  * @version 1.0 */public class HtmlGenListener implements MessageListener {    @Autowired    private ItemService itemService;    @Autowired    private FreeMarkerConfigurer freeMarkerConfigurer;    @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();            Long itemId =  new Long(strItemId);            // 3、根据商品id查询商品基本消息、商品描述。(即数据集已准备完毕)            /*             * 等待事务的提交,采用三次尝试的机会             *              * 根据商品id查询商品基本信息,这里需要注意的是消息发送方法             * 有可能还没有提交事务,因此这里是有可能取不到商品信息的,             * 为了避免这种情况出现,我们最好等待事务提交,这里我采用3次             * 尝试的方法,每尝试一次休眠一秒                */            TbItem tbItem = null;            for (int i = 0; i < 3; i++) {                Thread.sleep(1000); // 休眠一秒                tbItem = itemService.getItemById(itemId);                // 如果获取到了商品基本信息,那就退出循环                if (tbItem != null) {                    break;                }            }            Item item = new Item(tbItem);            TbItemDesc tbItemDesc = itemService.getItemDesc(itemId);            // 创建数据集            Map data = new HashMap();            data.put("item", item);            data.put("itemDesc", tbItemDesc);            // 4、创建商品详情页面的模板            // 5、指定静态文件输出目录            Configuration configuration = freeMarkerConfigurer.getConfiguration();            Template template = configuration.getTemplate("item.htm");            FileWriter out = new FileWriter(new File(HTML_OUT_PATH + itemId + ".html"));            // 6、生成静态文件            template.process(data, out);            // 关闭流            out.close();        } catch (Exception e) {            e.printStackTrace();        }    }}

这里,我选择第二种解决方案。
接下来我们重新启动taotao-item-web工程,再次添加一个商品,然后我们到静态文件目录下查看生成的静态文件,如下图所示,我们双击生成的静态页面。
这里写图片描述
可以看到生成的静态页面是没有样式的,这是因为我们没有把样式文件放到相应的目录下。
这里写图片描述
为了让样式好看,我们把taotao-item-web工程的webapp目录下的css、images、js文件拷贝到”F:\temp\freemarker”目录下也就是要与”item”目录在同一级别,如下图所示。
这里写图片描述
要想看到页面正常效果,我们可以先使用windows版的nginx,大家可以到nginx官网下载一个windows版本的安装包,我下载的是1.8.0版本,解压后进入它的conf目录下,打开nginx.conf文件,修改location下面的root目录为”F:/temp/freemarker”,如下图所示。
这里写图片描述
修改完后,回到上一级目录,双击nginx.exe运行nginx,会看到一闪而过,这时nginx便启动了。
这里写图片描述
下面我们便使用nginx来访问我们的静态网页,在浏览器地址栏中输入http://localhost/item/149724166800604.html即可看到如下图所示界面,页面正常展示出了商品详情页面。
这里写图片描述

阅读全文
0 0