后台技术: Spring+SpringMVC
前台技术:html+CSS+JS
商品数据的获取途径:从后台系统的接口获取数据。
<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.enjoyshop.parent</groupId>
<artifactId>enjoyshop-parent</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<groupId>com.enjoyshop.web</groupId>
<artifactId>enjoyshop-web</artifactId>
<version>1.0.0-SNAPSHOT</version>
<packaging>war</packaging>
<dependencies>
<dependency>
<groupId>com.enjoyshop.common</groupId>
<artifactId>enjoyshop-common</artifactId>
<version>1.0.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.enjoyshop.manage</groupId>
<artifactId>enjoyshop-manage-pojo</artifactId>
<version>1.0.0-SNAPSHOT</version>
</dependency>
<!-- 单元测试 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
</dependency>
<!-- Jackson Json处理工具包 -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</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>
<dependency>
<groupId>taglibs</groupId>
<artifactId>standard</artifactId>
<version>1.1.2</version>
</dependency>
<!-- Apache工具组件 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-io</artifactId>
</dependency>
<!-- httpclient -->
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
</dependency>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.6.0</version>
</dependency>
<!-- 时间操作组件 -->
<dependency>
<groupId>joda-time</groupId>
<artifactId>joda-time</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.amqp</groupId>
<artifactId>spring-rabbit</artifactId>
<version>1.4.0.RELEASE</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.tomcat.maven</groupId>
<artifactId>tomcat7-maven-plugin</artifactId>
<configuration>
<port>8082</port>
<path>/</path>
</configuration>
</plugin>
</plugins>
</build>
</project>
<?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>enjoyshop-web</display-name>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring/applicationContext*.xml</param-value>
</context-param>
<!--Spring的ApplicationContext 载入 -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!-- 编码过滤器,以UTF8编码 -->
<filter>
<filter-name>encodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>encodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- 配置SpringMVC框架入口 -->
<servlet>
<servlet-name>enjoyshop-web</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring/enjoyshop-web-servlet.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>enjoyshop-web</servlet-name>
<!-- 伪静态,好处:有利于SEO(搜索引擎优化) -->
<url-pattern>*.html</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>enjoyshop-web</servlet-name>
<url-pattern>/service/*</url-pattern>
</servlet-mapping>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
</welcome-file-list>
</web-app>
这部分配置和后台系统配置类似,不做具体描述。
package com.enjoyshop.web.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.ModelAndView;
import com.enjoyshop.web.service.IndexService;
@Controller
public class IndexController {
@Autowired
private IndexService indexService;
@RequestMapping(value = "index", method = { RequestMethod.GET, RequestMethod.POST })
public ModelAndView index() {
ModelAndView mv = new ModelAndView("index");
// 获取大广告位数据
String indexAD1 = this.indexService.queryIndexAD1();
//将数据放入request请求中
mv.addObject("indexAD1", indexAD1);
// 右上角广告位数据
String indexAD2 = this.indexService.queryIndexAD2();
mv.addObject("indexAD2", indexAD2);
return mv;
}
}
@Service
public class ApiService implements BeanFactoryAware {
private BeanFactory beanFactory;
@Autowired(required=false)
private RequestConfig requestConfig;
/** * 执行GET请求,响应200返回内容,404返回null * * @param url * @return * @throws ClientProtocolException * @throws IOException */
public String doGet(String url) throws ClientProtocolException, IOException {
// 创建http GET请求
HttpGet httpGet = new HttpGet(url);
httpGet.setConfig(requestConfig);
CloseableHttpResponse response = null;
try {
// 执行请求
response = getHttpClient().execute(httpGet);
// 判断返回状态是否为200
if (response.getStatusLine().getStatusCode() == 200) {
return EntityUtils.toString(response.getEntity(), "UTF-8");
}
} finally {
if (response != null) {
response.close();
}
}
return null;
}
/** * 带有参数的GET请求,响应200返回内容,404返回null * * @param url * @param params * @return * @throws ClientProtocolException * @throws IOException * @throws URISyntaxException */
public String doGet(String url, Map<String, String> params)
throws ClientProtocolException, IOException, URISyntaxException {
URIBuilder builder = new URIBuilder(url);
for (Map.Entry<String, String> entry : params.entrySet()) {
builder.setParameter(entry.getKey(), entry.getValue());
}
return doGet(builder.build().toString());
}
/** * 执行post请求 * * @param url * @param params * @return * @throws IOException */
public HttpResult doPost(String url, Map<String, String> params) throws IOException {
// 创建http POST请求
HttpPost httpPost = new HttpPost(url);
httpPost.setConfig(requestConfig);
if (null != params) {
List<NameValuePair> parameters = new ArrayList<NameValuePair>(0);
for (Map.Entry<String, String> entry : params.entrySet()) {
parameters.add(new BasicNameValuePair(entry.getKey(), entry.getValue()));
}
// 构造一个form表单式的实体
UrlEncodedFormEntity formEntity = new UrlEncodedFormEntity(parameters, "UTF-8");
// 将请求实体设置到httpPost对象中
httpPost.setEntity(formEntity);
}
CloseableHttpResponse response = null;
try {
// 执行请求
response = getHttpClient().execute(httpPost);
return new HttpResult(response.getStatusLine().getStatusCode(),
EntityUtils.toString(response.getEntity(), "UTF-8"));
} finally {
if (response != null) {
response.close();
}
}
}
public HttpResult doPostJson(String url, String json) throws IOException {
// 创建http POST请求
HttpPost httpPost = new HttpPost(url);
httpPost.setConfig(requestConfig);
if (null != json) {
// 构造一个字符串式的实体
StringEntity stringEntity = new StringEntity(json, ContentType.APPLICATION_JSON);
// 将请求实体设置到httpPost对象中
httpPost.setEntity(stringEntity);
}
CloseableHttpResponse response = null;
try {
// 执行请求
response = getHttpClient().execute(httpPost);
return new HttpResult(response.getStatusLine().getStatusCode(),
EntityUtils.toString(response.getEntity(), "UTF-8"));
} finally {
if (response != null) {
response.close();
}
}
}
//每次都从工厂中获取一个bean,实现httpclient的多例
private CloseableHttpClient getHttpClient() {
return this.beanFactory.getBean(CloseableHttpClient.class);
}
//用于spring初始化时设置BeanFactory,否则spring无法初始化这个service
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.beanFactory = beanFactory;
}
}
通过访问后台系统接口获取数据
@Autowired
private ApiService apiService;
private static final ObjectMapper MAPPER = new ObjectMapper();
@Value("${ENJOYSHOP_MANAGE_URL}")
private String ENJOYSHOP_MANAGE_URL;
@Value("${INDEX_AD1_URL}")
private String INDEX_AD1_URL;
@Value("${INDEX_AD2_URL}")
private String INDEX_AD2_URL;
public String queryIndexAD1() {
try {
String url = ENJOYSHOP_MANAGE_URL + INDEX_AD1_URL;
String jsonData = this.apiService.doGet(url);
if (StringUtils.isEmpty(jsonData)) {
return null;
}
// 解析json,生成相应的json数据
JsonNode jsonNode = MAPPER.readTree(jsonData);
ArrayNode rows = (ArrayNode) jsonNode.get("rows");
List<Map<String, Object>> result = new ArrayList<Map<String, Object>>();
for (JsonNode row : rows) {
Map<String, Object> map = new LinkedHashMap<String, Object>();
map.put("srcB", row.get("pic").asText());
map.put("height", 240);
map.put("alt", row.get("title").asText());
map.put("width", 670);
map.put("src", row.get("pic").asText());
map.put("widthB", 550);
map.put("href", row.get("url").asText());
map.put("heightB", 240);
result.add(map);
}
return MAPPER.writeValueAsString(result);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
public String queryIndexAD2() {
try {
String url = ENJOYSHOP_MANAGE_URL + INDEX_AD2_URL;
String jsonData = this.apiService.doGet(url);
if (StringUtils.isEmpty(jsonData)) {
return null;
}
// 解析json,生成前端所需要的json数据
JsonNode jsonNode = MAPPER.readTree(jsonData);
ArrayNode rows = (ArrayNode) jsonNode.get("rows");
List<Map<String, Object>> result = new ArrayList<Map<String, Object>>();
for (JsonNode row : rows) {
Map<String, Object> map = new LinkedHashMap<String, Object>();
map.put("width", 310);
map.put("height", 70);
map.put("src", row.get("pic").asText());
map.put("href", row.get("url").asText());
map.put("alt", row.get("title").asText());
map.put("widthB", 210);
map.put("heightB", 70);
map.put("srcB", row.get("pic").asText());
result.add(map);
}
return MAPPER.writeValueAsString(result);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
因为商品类目的数据接口在后台系统中,所以前台系统可以直接通过接口服务获取数据。但是后台系统的URL为manage.enjoyshop.com,而前台系统的URL为www.enjoyshop.com。这里涉及到了跨域请求的问题。浏览器对ajax请求的限制,不允许跨域请求资源。
http://www.a.com请求http://www.b.com下的资源是跨域。
http://www.a.com请求http://www.a.com:8080下的资源是跨域。
http://a.a.com请求http://b.a.com下的资源是跨域。
http://www.a.com http://www.a.com/api下的资源不是跨域。
不同的域名或不同的端口都是跨域请求。
可以使用jsonp技术来解决这个问题。具体关于jsonp的介绍请参考这里。点我
var category = {OBJ: $("#_JD_ALLSORT"),
URL_Serv: "http://manage.enjoyshop.com/rest/api/item/cat?callback=category.getDataService",
·······
}
public class StringHttpMessageConverter extends AbstractHttpMessageConverter<String> {
public static final Charset DEFAULT_CHARSET = Charset.forName("ISO-8859-1");
private final Charset defaultCharset;
······
}
这里来自定义一个消息转化器来处理json
package com.enjoyshop.common.spring.exetend.converter.json;
import java.io.IOException;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.http.HttpOutputMessage;
import org.springframework.http.converter.HttpMessageNotWritableException;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import com.fasterxml.jackson.core.JsonEncoding;
import com.fasterxml.jackson.core.JsonProcessingException;
//继承自MappingJackson2HttpMessageConverter,自定义消息转化器
public class CallbackMappingJackson2HttpMessageConverter extends MappingJackson2HttpMessageConverter {
// 做jsonp的支持的标识,在请求参数中加该参数
private String callbackName;
@Override
protected void writeInternal(Object object, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException {
// 从threadLocal中获取当前的Request对象
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();
String callbackParam = request.getParameter(callbackName);
if(StringUtils.isEmpty(callbackParam)){
// 没有找到callback参数,直接返回json数据
super.writeInternal(object, outputMessage);
}else{
JsonEncoding encoding = getJsonEncoding(outputMessage.getHeaders().getContentType());
try {
String result =callbackParam+"("+super.getObjectMapper().writeValueAsString(object)+");";
IOUtils.write(result, outputMessage.getBody(),encoding.getJavaName());
}
catch (JsonProcessingException ex) {
throw new HttpMessageNotWritableException("Could not write JSON: " + ex.getMessage(), ex);
}
}
}
public String getCallbackName() {
return callbackName;
}
public void setCallbackName(String callbackName) {
this.callbackName = callbackName;
}
}
需要在SpringMVC的配置文件中来加载该消息转化器。
<!-- 定义注解驱动 -->
<mvc:annotation-driven>
<mvc:message-converters register-defaults="true">
<!-- 配置一个消息转化器解决中文乱码(将对象序列化为json时默认使用iso-8859-1) <bean class="org.springframework.http.converter.StringHttpMessageConverter"> <constructor-arg index="0" value="UTF-8"></constructor-arg> </bean> -->
<!-- 设置自定义的消息转化器处理json -->
<bean class="com.enjoyshop.common.spring.exetend.converter.json.CallbackMappingJackson2HttpMessageConverter">
<property name="callbackName" value="callback"></property>
</bean>
</mvc:message-converters>
</mvc:annotation-driven>
商品详情页面的URL形式为http://www.enjoyshop.com/item/{itemId}.html。
@RequestMapping("item")
@Controller
public class ItemController {
@Autowired
private ItemService itemService;
@RequestMapping(value="{itemId}",method=RequestMethod.GET)
public ModelAndView showDetail(@PathVariable("itemId") Long itemId){
ModelAndView mv=new ModelAndView("item");
//添加商品基本数据
Item item=this.itemService.queryItemById(itemId);
mv.addObject("item",item);
//添加商品描述数据
ItemDesc itemDesc=this.itemService.queryItemDescByItemId(itemId);
mv.addObject("itemDesc", itemDesc);
//添加商品规格参数
String itemParam=this.itemService.queryItemParamByItemId(itemId);
mv.addObject("itemParam", itemParam);
return mv;
}
}
@Service
public class ItemService {
@Autowired
private ApiService apiService;
@Value("${ENJOYSHOP_MANAGE_URL}")
private String ENJOYSHOP_MANAGE_URL;
private static final ObjectMapper MAPPER = new ObjectMapper();
public static final String REDIS_KEY="ENJOYSHOP_WEB_ITEM_DETAIL_";
private static final Integer REDIS_TIME=60*60*24;
@Autowired
private RedisService redisService;
//获取商品基本信息
public Item queryItemById(Long itemId) {
String key=REDIS_KEY+itemId;
try {
//先从缓存中命中
String cacheData=this.redisService.get(key);
if(StringUtils.isNotEmpty(cacheData)){
return MAPPER.readValue(cacheData, Item.class);
}
} catch (Exception e1) {
e1.printStackTrace();
}
try {
String url = ENJOYSHOP_MANAGE_URL + "/rest/item/" + itemId;
String jsonData = this.apiService.doGet(url);
if (StringUtils.isEmpty(jsonData)) {
return null;
}
try {//需要加try catch防止影响原逻辑
//写入缓存
this.redisService.set(key, jsonData, REDIS_TIME);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return MAPPER.readValue(jsonData, Item.class);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
//获取商品描述信息
public ItemDesc queryItemDescByItemId(Long itemId) {
try {
String url = ENJOYSHOP_MANAGE_URL + "/rest/item/desc/" + itemId;
String jsonData = this.apiService.doGet(url);
if (StringUtils.isEmpty(jsonData)) {
return null;
}
return MAPPER.readValue(jsonData, ItemDesc.class);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
//获取商品规格参数信息
public String queryItemParamByItemId(Long itemId) {
try {
String url = ENJOYSHOP_MANAGE_URL + "/rest/item/param/item/" + itemId;
String jsonData = this.apiService.doGet(url);
// 解析JSON
JsonNode jsonNode = MAPPER.readTree(jsonData);
ArrayNode paramData = (ArrayNode) MAPPER.readTree(jsonNode.get("paramData").asText());
StringBuilder sb = new StringBuilder();
sb.append(
"<table cellpadding=\"0\" cellspacing=\"1\" width=\"100%\" border=\"0\" class=\"Ptable\"><tbody>");
for (JsonNode param : paramData) {
sb.append("<tr><th class=\"tdTitle\" colspan=\"2\">" + param.get("group").asText() + "</th></tr>");
ArrayNode params = (ArrayNode) param.get("params");
for (JsonNode p : params) {
sb.append("<tr><td class=\"tdTitle\">" + p.get("k").asText() + "</td><td>" + p.get("v").asText()
+ "</td></tr>");
}
}
sb.append("</tbody></table>");
return sb.toString();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
@RequestMapping(value = "{itemId}", method = RequestMethod.GET)
public ResponseEntity<Item> queryById(@PathVariable("itemId") Long itemId) {
try {
Item item = this.itemService.queryById(itemId);
if (null == item) {
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(null);
}
return ResponseEntity.ok(item);
} catch (Exception e) {
e.printStackTrace();
}
// 出错 500
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(null);
}
查询商品描述信息
@RequestMapping(value = "{itemId}", method = RequestMethod.GET)
public ResponseEntity<ItemDesc> queryByItemId(@PathVariable("itemId") Long itemId) {
try {
ItemDesc itemDesc = this.itemDescService.queryById(itemId);
if (null == itemDesc) {
// 资源不存在,404
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(null);
}
// 200
return ResponseEntity.ok(itemDesc);
} catch (Exception e) {
e.printStackTrace();
}
// 错误,500
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(null);
}
查询商品规格参数信息
@RequestMapping(method = RequestMethod.GET)
public ResponseEntity<EasyUIResult> queryItemParamList(@RequestParam(value = "page", defaultValue = "1") Integer page,
@RequestParam(value = "rows", defaultValue = "30") Integer rows) {
try {
PageInfo<ItemParam> pageInfo = this.itemParamService.queryPageList(page, rows);
EasyUIResult easyUIResult = new EasyUIResult(pageInfo.getTotal(), pageInfo.getList());
return ResponseEntity.ok(easyUIResult);
} catch (Exception e) {
e.printStackTrace();
}
// 出错 500
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(null);
}
后台系统中如果将商品修改,前台系统没有进行数据的同步,仍然从Redis的缓存中获取数据,导致前端系统不能够实时显示最新的数据。
基本思路:如果在后台系统中商品修改后向其他系统发送通知,其他系统做出对应的处理(清理Redis中的缓存),就可以实现同步。
首先在前台系统中开发一个接口,用于后台系统调用该接口来删除缓存。
@RequestMapping("item/cache")
@Controller
public class ItemCacheController {
@Autowired
private RedisService redisService;
//删除缓存数据
@RequestMapping(value="{itemId}",method=RequestMethod.POST)
public ResponseEntity<Void> deleteCache(@PathVariable("itemId") Long itemId){
try {
String key=ItemService.REDIS_KEY+itemId;
this.redisService.del(key);
return ResponseEntity.status(HttpStatus.NO_CONTENT).build();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
}
}
在后台系统更新商品数据的逻辑中,加入调用前台接口的方法。
public void updateItem(Item item, String desc, String itemParams) {
// 强制设置不能修改的字段为null
item.setStatus(null);
item.setCreated(null);
super.updateSelective(item);
// 修改商品描述数据
ItemDesc itemDesc = new ItemDesc();
itemDesc.setItemId(item.getId());
itemDesc.setItemDesc(desc);
this.itemDescService.updateSelective(itemDesc);
// 修改商品规格参数
this.itemParamItemService.updateItemParamItem(item.getId(), itemParams);
try {
// 需要通知其他系统进行数据更新
String url = ENJOYSHOP_WEB_URL + "/item/cache/" + item.getId() +".html";
this.apiService.doPost(url, null);
} catch (Exception e) {
e.printStackTrace();
}
}
RabbitMQ是一个开源的AMQP实现,服务器端用Erlang语言编写,支持多种客户端,如:Python、Ruby、.NET、Java、JMS、C、PHP、ActionScript、XMPP、STOMP等,支持AJAX。用于在分布式系统中存储转发消息,在易用性、扩展性、高可用性等方面表现不俗。
关于RabbitMQ的更多介绍请参考这里。点我
这里采用通配符模式,这种模式更具有扩展性。
有两种方法:
1、 在配置文件中将队列和交换机完成绑定。
2、 在RabbitMQ的管理界面中手动完成绑定。
这里采用手动绑定的方式,优点如下:
1、 若使用配置文件绑定的方式,如果绑定关系发生变化,需要修改配置文件,并且服务需要重启。
2、 采用手动绑定,管理更加灵活,更容易对绑定关系进行权限管理,流程管理。
1、 将Item对象做json序列化发送
a) 数据大
b) 有些数据其他人是可能用不到的
2、 发送商品的id、操作类型。
这里采用第二种方式。
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:rabbit="http://www.springframework.org/schema/rabbit" xsi:schemaLocation="http://www.springframework.org/schema/rabbit http://www.springframework.org/schema/rabbit/spring-rabbit-1.4.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd">
<!-- 定义RabbitMQ的连接工厂 -->
<rabbit:connection-factory id="connectionFactory" host="${rabbitmq.host}" port="${rabbitmq.port}" username="${rabbitmq.username}" password="${rabbitmq.password}" virtual-host="${rabbitmq.vhost}" />
<!-- 定义管理 -->
<rabbit:admin connection-factory="connectionFactory"/>
<!-- 定义交换机 -->
<rabbit:topic-exchange name="enjoyshop-item-exchange" durable="true" auto-declare="true"/>
<!-- 定义模板 -->
<rabbit:template id="template" connection-factory="connectionFactory" exchange="enjoyshop-item-exchange"/>
</beans>
@Autowired
private RabbitTemplate rabbitTemplate;
private static final ObjectMapper MAPPER = new ObjectMapper();
public void saveItem(Item item, String desc, String itemParams) {
// 默认为上架状态
item.setStatus(1);
// 防止前端恶意添加id值
item.setId(null);
//保存商品信息
super.save(item);
//构建ItemDesc数据并保存
ItemDesc itemDesc = new ItemDesc();
itemDesc.setItemId(item.getId());
itemDesc.setItemDesc(desc);
this.itemDescService.save(itemDesc);
//构建ItemParamItem数据并保存
ItemParamItem itemParamItem = new ItemParamItem();
itemParamItem.setItemId(item.getId());
itemParamItem.setParamData(itemParams);
this.itemParamItemService.save(itemParamItem);
//发送mq消息
sendMsg(item.getId(), "insert");
}
public void updateItem(Item item, String desc, String itemParams) {
// 强制设置不能修改的字段为null
item.setStatus(null);
item.setCreated(null);
super.updateSelective(item);
// 修改商品描述数据
ItemDesc itemDesc = new ItemDesc();
itemDesc.setItemId(item.getId());
itemDesc.setItemDesc(desc);
this.itemDescService.updateSelective(itemDesc);
// 修改商品规格参数
this.itemParamItemService.updateItemParamItem(item.getId(), itemParams);
// try {
// // 需要通知其他系统进行数据更新
// String url = ENJOYSHOP_WEB_URL + "/item/cache/" + item.getId() +
// ".html";
// this.apiService.doPost(url, null);
// } catch (Exception e) {
// e.printStackTrace();
// }
//发送mq消息
sendMsg(item.getId(), "update");
}
private void sendMsg(long itemId,String type){
try {
//发送mq详细通知其他系统进行数据更新
Map<String, Object> msg = new HashMap<String, Object>();
msg.put("itemId", itemId);
msg.put("type", type);
msg.put("date", System.currentTimeMillis());
this.rabbitTemplate.convertAndSend("item."+type, MAPPER.writeValueAsString(msg));
} catch (Exception e) {
e.printStackTrace();
}
}
Spring和RabbitMQ整合文件
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:rabbit="http://www.springframework.org/schema/rabbit" xsi:schemaLocation="http://www.springframework.org/schema/rabbit http://www.springframework.org/schema/rabbit/spring-rabbit-1.4.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd">
<!-- 定义RabbitMQ的连接工厂 -->
<rabbit:connection-factory id="connectionFactory" host="${rabbitmq.host}" port="${rabbitmq.port}" username="${rabbitmq.username}" password="${rabbitmq.password}" virtual-host="${rabbitmq.vhost}" />
<!-- 定义管理 -->
<rabbit:admin connection-factory="connectionFactory"/>
<!-- 定义队列 -->
<rabbit:queue name="enjoyshop-web-item-queue" durable="true" auto-declare="true"/>
<!-- 定义消费者 -->
<bean id="itemMQHandler" class="com.enjoyshop.web.mq.handler.ItemMQHandler"/>
<!-- 消费者监听队列 -->
<rabbit:listener-container connection-factory="connectionFactory">
<rabbit:listener ref="itemMQHandler" method="execute" queue-names="enjoyshop-web-item-queue"/>
</rabbit:listener-container>
</beans>
接收消息,清理缓存
package com.enjoyshop.web.mq.handler;
import org.springframework.beans.factory.annotation.Autowired;
import com.enjoyshop.common.service.RedisService;
import com.enjoyshop.web.service.ItemService;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
public class ItemMQHandler {
private static final ObjectMapper MAPPER = new ObjectMapper();
@Autowired
private RedisService redisService;
//拿到商品id,删除redis缓存
public void execute(String msg) {
try {
JsonNode jsonNode = MAPPER.readTree(msg);
Long itemId = jsonNode.get("itemId").asLong();
String key = ItemService.REDIS_KEY + itemId;
this.redisService.del(key);
} catch (Exception e) {
e.printStackTrace();
}
}
}