zoukankan      html  css  js  c++  java
  • day76_淘淘商城项目_09_商品详情页动态展示实现(jsp+redis) + FreeMarker模板引擎入门 + 商品详情页静态化实现(Win版本的nginx作http服务器)_匠心笔记

    课程计划

    • 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<ObjectObject> 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<ObjectObject> 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缓存
        二、网页静态化
  • 相关阅读:
    新代(Syntec)机床的IP设置
    使用任务计划程序实现用户未登录情况下的程序开机自启动
    sql server 数据库访问端口配置
    Http请求
    EF
    SQL Server常用处理
    利用ZXing生成条码二维码例子
    SQL JOIN常见情况
    C#ORM框架收集
    sql server连接oracle并实现增删改查
  • 原文地址:https://www.cnblogs.com/chenmingjun/p/10046664.html
Copyright © 2011-2022 走看看