遇到的问题:
一,让订阅程序随着应用程序的启动而启动。
刚开始想到的几种方法
1,让spring注入这个订阅程序,
但是发现Spring会注入这个类,但是不会运行它的main方法。
2,将这个订阅程序作为一个监听器配置在web.xml中,这样就可以tomcat启动的时候一起启动这个订阅程序了。
但是订阅程序是可以启动了,但是tomcat却启动不起来了。
好像是被僵住了,没找到报错信息。不知道怎么回事,这种方式放弃了。
然后百度到一种。
如同这篇:tomcat启动时将缓存放入redis中。
https://blog.csdn.net/nightliar/article/details/78593068#commentBox
我的
订单订阅程序:
package com.wjh.listener;
import com.wjh.dao.RedisDao;
import com.wjh.dao.impl.RedisDaoImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
import javax.annotation.PostConstruct;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import java.util.List;
/**
* Author: 17976
* Date: 2019/2/23 22:29
* Description: redis订阅程序
*/
@Configuration
public class Subscriber {
@Autowired
private RedisDao redisDao;
@PostConstruct
public void doSubscribe() {
new Thread() {
@Override
public void run() {
while (true) {
try {
Thread.sleep(3*1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Jedis jedis=redisDao.getJedis();
jedis.auth("flower");
// config(jedis);
// 只订阅patten匹配的超时事件
System.out.println("开始订阅..........");
jedis.psubscribe(new KeyExpiredListener(), "__key*@0__:expired");
break;
}
}
}.start();
}
private static void config(Jedis jedis) {
String parameter = "notify-keyspace-events";
List<String> notify = jedis.configGet(parameter);
if (notify.get(1).equals("")) {
jedis.configSet(parameter, "Ex"); //过期事件
}
}
}
@Configuration的作用类似于配置一个spring-bean.xml中的标签的作用,主要用于Bean的注入,放置在类上。
https://blog.csdn.net/entomb/article/details/81151533
被@PostConstruct修饰的方法会在服务器加载Servlet的时候运行,并且只会被服务器执行一次。PostConstruct在构造函数之后执行,init()方法之前执行。
转载:
https://www.jianshu.com/p/98cf7d8b9ec3
监听(发布)程序
package com.wjh.listener;
import com.wjh.bean.TOrder;
import com.wjh.dao.mapper.TOrderMapper;
import com.wjh.util.Constants;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import redis.clients.jedis.JedisPubSub;
/**
* Author: 17976
* Date: 2019/2/23 21:34
* Description: 监听redis key是否过期
*/
@Component
public class KeyExpiredListener extends JedisPubSub {
@Autowired
private TOrderMapper orderMapper;
//订阅的频道
@Override
public void onPSubscribe(String pattern, int subscribedChannels) {
// super.onPSubscribe(pattern, subscribedChannels);
System.out.println("onPSubscribe " + pattern + " " + subscribedChannels);
}
//订阅的信息
@Override
public void onPMessage(String pattern, String channel, String message) {
System.out.println("onPMessage pattern -->" + pattern + "-->channel: " + channel + "message--> " + message);
if (message.startsWith("order")){//订单如果超时,查询数据库,如果state为超时未支付,则交易关闭
TOrder order=orderMapper.selectByPrimaryKey(message);
String orderId=order.getId();
System.out.println("过期订单----"+orderId);
if (order.getState().equals(Constants.ORDER_PAY_TIMEOUT)){
order.setState(Constants.ORDER_CLOSED);//超时未支付,交易关闭
}
}
}
}
其它需要的几个类和在Spring中的配置,参考
Jedis发布订阅:
https://blog.csdn.net/flower_CSDN/article/details/87898888
redis客户端发布订阅详解:
https://blog.csdn.net/flower_CSDN/article/details/87907691
将订单存入mysql之前先存入redis,并设置订单的过期时间,这样到了过期时间之后,这个orderid的key的过期事件就会被KeyExpiredListener捕捉到,通过监听器中的onPMessage输出订阅到的信息。
public void addOrder(TOrder order) {
redisDao.setEx("order"+order.getId(), JSON.toJSONString(order),Constants.ORDER_PAY_TIMEOUT);
orderMapper.insert(order);
}
生成唯一不重复的订单号
/**
* 生成的订单长度为24位
* date+uuidHashCode
* 例如:
* date:20190226155041
* orderid:201902261550412018896709
* @return
*/
public String generateOrderId() {
String orderId="";
Integer uuidHashCode = UUID.randomUUID().toString().hashCode();
if (uuidHashCode < 0) {
uuidHashCode = uuidHashCode * (-1);
}
String date = new SimpleDateFormat("yyyyMMddHHmmss").format(new Date());
orderId=date + uuidHashCode;
return orderId;
}
格式化价格
/**
*
* @param bd 要传入的BigDecimal价格对象
* @param num 要保留的小数位数,例如num=2
* @return
*/
public static BigDecimal formatPrice(BigDecimal bd,int num){
if(bd == null || num < 0){
return null;
}
bd = bd.setScale(num, BigDecimal.ROUND_HALF_UP);//BigDecimal.ROUND_HALF_UP四舍五入
return bd;
}
还有订单和订单项的关系,一对多
在点击结算的时候,传入的订单项应该是一个数组,对应同一个orderId
流程:
package com.wjh.controller;
import com.alibaba.fastjson.JSON;
import com.wjh.bean.TBook;
import com.wjh.bean.TOrder;
import com.wjh.bean.TOrderItem;
import com.wjh.bean.TUser;
import com.wjh.service.IBookService;
import com.wjh.service.IOrderItemService;
import com.wjh.service.IOrderServie;
import com.wjh.util.Constants;
import com.wjh.util.ShopUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.math.BigDecimal;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Author: 17976
* Date: 2019/2/25 18:50
* Description:
*/
public class OrderController {
@Autowired
private IOrderServie orderService;
@Autowired
private IOrderItemService orderItemService;
@Autowired
private IBookService bookService;
/**
* 点立即付款的时候就生成订单信息,存入数据库
* 先生成订单
* 再生成订单项
*
* 这里传入的参数orderItem应该是一个json数组格式的,因为一个订单中有多个订单项,对应的orderId只有一个
* @return
*/
@RequestMapping(value = "toOrder",method =RequestMethod.GET )
public String addOrder(String orderItem, HttpServletRequest request){
List<TOrderItem> orderItemList= JSON.parseArray(orderItem,TOrderItem.class);
TOrder order=new TOrder();
String orderId=orderService.generateOrderId();
order.setId(orderId);
HttpSession session=request.getSession();
TUser user=(TUser)session.getAttribute("user");
order.setUid(user.getId());
order.setState(Constants.ORDER_WAIT_ALIPAY);//待支付,等支付完毕之后设置为1:待发货 超时未支付设置为已关闭,同时将该订单对应的订单项从订单表中删除
/**
* 循环将orderItem添加到数据库中
*/
float sum=0;
for (TOrderItem tOrderItem:orderItemList){
tOrderItem.setOrderid(orderId);
//将float格式的价格转换为两位小数
tOrderItem.setPrice(ShopUtil.formatPrice(new BigDecimal(tOrderItem.getPrice()),2).floatValue());
orderItemService.addOrderItem(tOrderItem);
sum+=tOrderItem.getPrice();
}
/**
* 订单价格为个订单项的总和
* BigDecimal.ROUND_HALF_UP表示采用"四舍五入"的模式处理价格
* 保留两位小数
*/
BigDecimal bd=new BigDecimal(sum);
order.setAmt(bd.setScale(2,BigDecimal.ROUND_HALF_UP));
orderService.addOrder(order);
/**
* 存入数据库之后应该查出当前的订单信息,显示到前台,跳转到订单详情页
* 订单详情页:
* 订单信息:
* 订单中的订单项信息:
* 订单项中的商品信息
*/
//订单信息
TOrder tOrder=orderService.queryOrderByOrderid(orderId);
request.setAttribute("order",tOrder);
//订单项信息
List<TOrderItem> orderItems=orderItemService.OrderItemList(orderId);
Map orderItemMap=new HashMap<TOrderItem,TBook>();
for (TOrderItem tOrderItem:orderItems){
TBook book=bookService.selectBookById(tOrderItem.getBookid());
orderItemMap.put(tOrderItem,book);
}
return "order/orderDetail";
}
}