一、实现搜索功能
1、实现跳转到搜索结果页面
(1)、查看点击搜索按钮发送的url
(2)、创建SearchController.java
package com.taotao.search.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
/**
* 搜索功能
*
* @author Administrator
*
*/
@Controller
@RequestMapping("search")
public class SearchController {
@RequestMapping
public String search(String q) {
try {
q = new String(q.getBytes("iso-8859-1"), "UTF-8");
} catch (Exception e) {
e.printStackTrace();
}
return "search";
}
}
2、分析页面的搜索功能代码
taotao-search-web的search.jsp页面分析页面需要展示(需要放到Model中的)数据有:
${query}:搜索关键字,直接回显即可
${itemList} :商品列表
${page}:当前页码数,由页面直接提交到后台,直接回显
${totalPages}:总页数,查询结果的总页数,需要返回查询数据总条数进行计算
total%rows==0?total/rows:(total/rows)+1
(total+rows-1)/rows(了解)
这样查询一次solr索引库,需要返回两个数据,一个是商品结果集list,另一个是数据总条数有当前页码数page,但是没有页面显示数据条数rows
基于安全考虑,这里不能让用户指定rows。否则用户可以指定一次查询很多商品数据。这样返回的数据量会变得很大,如果恶意访问会造成很大的访问压力,很不安全。所以由我们自己指定。
我们指定16,一页显示16条
分析可知图片需要放入集合中,因此,需要在item的pojo中添加getImages方法,返回值为String数组
3、实现搜索功能的后台代码开发
(1)、在taotao-sso-interface项目中添加taotao-common的依赖
(2)、在taotao-sso-web中添加资源文件内容
(3)、SearchController.java
package com.taotao.search.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import com.taotao.common.pojo.TaoResult;
import com.taotao.manager.pojo.Item;
import com.taotao.search.service.SearchService;
/**
* 搜索功能
*
* @author Administrator
*
*/
@Controller
@RequestMapping("search")
public class SearchController {
@Value("${TAOTAO_SEARCH_ROWS}")
private Integer rows;
@Autowired
private SearchService searchService;
@RequestMapping
public String search(Model model, String q, @RequestParam(value = "page", defaultValue = "0") Integer page) {
try {
q = new String(q.getBytes("iso-8859-1"), "UTF-8");
} catch (Exception e) {
e.printStackTrace();
}
// 调用搜索服务,实现商品搜索
TaoResult<Item> taoResult = this.searchService.search(q, page, this.rows);
// 进行页面数据回显
// 搜索关键词
model.addAttribute("query", q);
// 商品搜索的结果集
model.addAttribute("itemList", taoResult.getRows());
// 当前页码数
model.addAttribute("page", page);
// 总页数
long total = taoResult.getTotal();
model.addAttribute("totalPages", total % this.rows == 0 ? total / this.rows : (total / rows) + 1);
return "search";
}
}
4、SearchService的接口和实现类
(1)、配置taotao-search-service中solr应用的资源文件
(2)、加入solr的spring配置文件applicationContext-solr.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:mvc="http://www.springframework.org/schema/mvc" xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<!-- 配置单机版连接对象 -->
<bean class="org.apache.solr.client.solrj.impl.HttpSolrServer">
<!-- 配置单机版接口的地址 -->
<constructor-arg name="baseURL" value="${solr.baseURL}" />
</bean>
<!-- 配置集群版连接对象 -->
<bean class="org.apache.solr.client.solrj.impl.CloudSolrServer">
<!-- 配置zookeeper集群的地址 -->
<constructor-arg name="zkHost" value="${cloud.zkHost}" />
<!-- 配置索引名字 -->
<property name="defaultCollection" value="${cloud.collection}" />
</bean>
</beans>
(3)、编写SearchService接口和实现类
package com.taotao.search.service;
import com.taotao.common.pojo.TaoResult;
import com.taotao.manager.pojo.Item;
/**
* 搜索功能的业务层接口
*
* @author Administrator
*
*/
public interface SearchService {
/**
* 根据关键词搜索商品列表
*
* @param q
* @param page
* @param rows
* @return
*/
public TaoResult<Item> search(String q, Integer page, Integer rows);
}
package com.taotao.search.service.impl;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import org.apache.commons.lang3.StringUtils;
import org.apache.solr.client.solrj.SolrQuery;
import org.apache.solr.client.solrj.impl.CloudSolrServer;
import org.apache.solr.client.solrj.response.QueryResponse;
import org.apache.solr.common.SolrDocument;
import org.apache.solr.common.SolrDocumentList;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.taotao.common.pojo.TaoResult;
import com.taotao.manager.pojo.Item;
import com.taotao.search.service.SearchService;
/**
* 搜索功能的业务层实现类
*
* @author Administrator
*
*/
@Service
public class SearchServiceImpl implements SearchService {
@Autowired
private CloudSolrServer cloudSolrServer;
/**
* 根据关键词搜索商品列表
*
* @param q
* @param page
* @param rows
* @return
*/
public TaoResult<Item> search(String q, Integer page, Integer rows) {
// 创建搜索对象
SolrQuery solrQuery = new SolrQuery();
// 设置搜索语句
if (StringUtils.isNotBlank(q)) {
solrQuery.setQuery("item_title:" + q + " AND item_status:1");
} else {
// 如果没有关键字,就查询所有状态为1
solrQuery.setQuery("item_status:1");
}
// 设置分页
solrQuery.setStart((page - 1) * rows);
solrQuery.setRows(rows);
// 设置高亮
solrQuery.setHighlight(true);
solrQuery.addHighlightField("item_title");
solrQuery.setHighlightSimplePre("<font color='red'>");
solrQuery.setHighlightSimplePost("</font>");
try {
// 发起请求获取response
QueryResponse response = this.cloudSolrServer.query(solrQuery);
// 获取高亮数据
Map<String, Map<String, List<String>>> map = response.getHighlighting();
// 获取结果集
SolrDocumentList results = response.getResults();
// 声明存放商品的容器list
List<Item> list = new ArrayList<>();
// 遍历结果集,把Document数据封装到List<Item>中
for (SolrDocument solrDocument : results) {
// 解析高亮数据
List<String> hlist = map.get(solrDocument.get("id").toString()).get("item_title");
Item item = new Item();
// 商品id
item.setId(Long.parseLong(solrDocument.get("id").toString()));
// 商品title
if (hlist != null && hlist.size() > 0) {
item.setTitle(hlist.get(0));
}
// 商品price
item.setPrice(Long.parseLong(solrDocument.get("item_price").toString()));
// 商品image
item.setImage(solrDocument.get("item_image").toString());
// 商品cid
item.setCid(Long.parseLong(solrDocument.get("item_cid").toString()));
// 商品status,status设置的是不存储,所以这里获取不到,取出的结果是null
// 封装结果数据到list容器中
list.add(item);
}
// 封装返回对象
TaoResult<Item> taoResult = new TaoResult<>(results.getNumFound(), list);
return taoResult;
} catch (Exception e) {
e.printStackTrace();
}
// 如果出现异常,返回空对象
return new TaoResult<>();
}
}
4、测试运行
二、ActiveMQ介绍及安装
1、ActiveMQ简介
MQ : Message Queue 就是消息队列
ActiveMQ 是Apache出品,最流行的,能力强劲的开源消息总线。ActiveMQ 是一个完全支持JMS1.1和J2EE 1.4规范的 JMS Provider实现,尽管JMS规范出台已经是很久的事情了,但是JMS在当今的J2EE应用中间仍然扮演着特殊的地位。
ActiveMQ消息的传递有两种类型:
一种是点对点的,即一个生产者和一个消费者一一对应;
另一种是发布/订阅模式,即一个生产者产生消息并进行发送后,可以由多个消费者进行接收。
主要特点:
(1). 多种语言和协议编写客户端。语言: Java, C, C++, C#, Ruby, Perl, Python, PHP。应用协议: OpenWire,Stomp REST,WS Notification,XMPP,AMQP
(2). 完全支持JMS1.1和J2EE 1.4规范 (持久化,XA消息,事务)
(3). 对Spring的支持,ActiveMQ可以很容易内嵌到使用Spring的系统里面去
(4). 通过了常见J2EE服务器(如 Geronimo,JBoss 4, GlassFish,WebLogic)的测试,其中通过JCA 1.5 resource adaptors的配置,可以让ActiveMQ可以自动的部署到任何兼容J2EE1.4 商业服务器上
(5). 支持多种传送协议:in-VM,TCP,SSL,NIO,UDP,JGroups,JXTA
(6). 支持通过JDBC和journal提供高速的消息持久化
(7). 从设计上保证了高性能的集群,客户端-服务器,点对点
(8). 支持Ajax
(9). 支持与Axis的整合
(10). 可以很容易得调用内嵌JMS provider,进行测试
2、JMS简介
JMS即Java消息服务(Java Message Service)应用程序接口,是一个Java平台中关于面向消息中间件(MOM)的API,用于在两个应用程序之间,或分布式系统中发送消息,进行异步通信。
JMS是一个与具体平台无关的API,具有跨平台性。
它主要用于在生产者和消费者之间进行消息传递,生产者负责产生消息,而消费者负责接收消息。把它应用到实际的业务需求中的话我们可以在特定的时候利用生产者生成一消息,并进行发送,对应的消费者在接收到对应的消息后去完成对应的业务逻辑。
JMS定义了五种不同的消息正文格式:
• StreamMessage -- Java原始值的数据流
• MapMessage--一套名称-值对
• TextMessage--一个字符串对象
• ObjectMessage--一个序列化的 Java对象
• BytesMessage--一个字节的数据流
3、activeMQ安装
http://blog.csdn.net/wingzhezhe/article/details/73462380
三、ActiveMQ使用
四、项目整合ActiveMQ
1、整合分析
使用ActiveMQ的方式解决数据同步的问题
需要在taotao-manager发送消息
在taotao-search接收并消费消息
2、改造消息生产者后台代码
(1)、在taotao-manager-service中添加消息生产者的配置文件
<?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:jms="http://www.springframework.org/schema/jms" 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.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd
http://www.springframework.org/schema/jms http://www.springframework.org/schema/jms/spring-jms-4.0.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.0.xsd">
<!-- 真正可以产生Connection的ConnectionFactory,由对应的 JMS服务厂商提供 -->
<bean id="targetConnectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory">
<property name="brokerURL" value="tcp://192.168.37.161:61616" />
</bean>
<!-- Spring用于管理真正的ConnectionFactory的ConnectionFactory -->
<bean id="connectionFactory"
class="org.springframework.jms.connection.SingleConnectionFactory">
<!-- 目标ConnectionFactory对应真实的可以产生JMS Connection的ConnectionFactory -->
<property name="targetConnectionFactory" ref="targetConnectionFactory" />
</bean>
<!-- Spring提供的JMS工具类,它可以进行消息发送 -->
<bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate">
<!-- 这个connectionFactory对应的是我们定义的Spring提供的那个ConnectionFactory对象 -->
<property name="connectionFactory" ref="connectionFactory" />
</bean>
<!--这个是主题目的地,一对多的 -->
<bean id="topicDestination" class="org.apache.activemq.command.ActiveMQTopic">
<constructor-arg value="topic" />
</bean>
</beans>
(2)、改造taotao-manager-service中的ItemServiceImpl.java
//JmsTemplate发送消息
@Autowired
private JmsTemplate jmsTemplate;
//Destination消息目的地
@Autowired
private Destination destination;
//json工具类
private static final ObjectMapper MAPPER = new ObjectMapper();
/**
* 发送消息的方法
* @param id
* @param string
*/
private void sendMQ(final Long itemId, final String type) {
//发送消息
this.jmsTemplate.send(this.destination, new MessageCreator() {
@Override
public Message createMessage(Session session) throws JMSException {
//创建TextMessage消息体
TextMessage textMessage = new ActiveMQTextMessage();
//封装消息数据,使用json格式的数据传递{typ:save,itemId:1}
//声明Map转为json
Map<String, Object> map = new HashMap<String, Object>();
map.put("itemId", itemId);
map.put("type", type);
try {
//把Map转换为json
String json = MAPPER.writeValueAsString(map);
//设置json到TextMessage消息体中
textMessage.setText(json);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//返回消息体
return textMessage;
}
});
}
3、改造消息消费者的后台代码
(1)、在taotao-search-service系统中加入消息消费者的spring配置文件
<?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:jms="http://www.springframework.org/schema/jms" 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.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd
http://www.springframework.org/schema/jms http://www.springframework.org/schema/jms/spring-jms-4.0.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.0.xsd">
<!-- 真正可以产生Connection的ConnectionFactory,由对应的 JMS服务厂商提供 -->
<bean id="targetConnectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory">
<property name="brokerURL" value="tcp://192.168.37.161: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="topic" />
</bean>
<!-- 配置消息监听器 -->
<bean id="itemMessageListenere" class="com.taotao.search.message.ItemMessageListener"></bean>
<!-- 配置一个jms监听容器 -->
<bean id="jmsContainer" class="org.springframework.jms.listener.DefaultMessageListenerContainer">
<property name="connectionFactory" ref="connectionFactory"></property>
<property name="destination" ref="topicDestination"></property>
<property name="messageListener" ref="itemMessageListenere"></property>
</bean>
</beans>
(2)、编写消息监听类
package com.taotao.search.message;
import javax.jms.Message;
import javax.jms.MessageListener;
import javax.jms.TextMessage;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.taotao.search.service.SearchService;
/**
* 商品消息的监听器
* @author Administrator
*
*/
public class ItemMessageListener implements MessageListener {
@Autowired
private static final ObjectMapper MAPPER = new ObjectMapper();
@Autowired
private SearchService searchService;
@Override
public void onMessage(Message message) {
//判断消息类型是不是TextMessage
if(message instanceof TextMessage){
//强转
TextMessage textMessage = (TextMessage) message;
try {
//获取消息内容
String json = textMessage.getText();
//判断消息是否为空
if(StringUtils.isNotBlank(json)){
//解析消息
JsonNode jsonNode = MAPPER.readTree(json);
String type = jsonNode.get("type").asText();
long itemId = jsonNode.get("itemId").asLong();
//根据消息内容,添加商品到索引库中
if("save".equals(type)){
//新增操作
this.searchService.saveItemToSolr(itemId);
}
}
//
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
(3)、添加service层的接口和实现类
/**
* 添加商品到索引库
*
* @param itemId
*/
public void saveItemToSolr(long itemId);
@Autowired
private ItemMapper itemMapper;
/**
* 保存商品信息到索引库中
*/
public void saveItemToSolr(long itemId) {
// 根据商品id查询商品数据
Item item = this.itemMapper.selectByPrimaryKey(itemId);
// 把商品数据封装到SolrInputDocument中
SolrInputDocument doc = new SolrInputDocument();
// 商品id
doc.addField("id", item.getId());
// 商品item_title
doc.addField("item_title", item.getTitle());
// 商品item_price
doc.addField("item_price", item.getPrice());
// 商品item_image
doc.addField("item_image", item.getImage());
// 商品item_cid
doc.addField("item_cid", item.getCid());
// 商品item_status
doc.addField("item_status", item.getStatus());
try {
// 保存商品到索引库中
this.cloudSolrServer.add(doc);
// 提交
this.cloudSolrServer.commit();
} catch (Exception e) {
e.printStackTrace();
}
}