• 扩展AbstractAnnotationConfigDispatcherServletInitializer的任意类都会自动配置DispatcherServlet和Spring应用上下文,Spring应用上下文会位于应用程序的servlet上下文中。
  • AbstractAnnotationConfigDispatcherServletInitializer是一个抽象类,且继承了AbstractDispatcherServletInitializer类。还有一个无参的构造方法。
  • 方法可以返回WebApplicationContext的对象。实现这个类,反过来会查找实现WebApplicationContext的类并将配置的任务交给他们来完成。
package com.ssm.chapter22.config;import java.util.Properties;import javax.sql.DataSource;import org.apache.commons.dbcp.BasicDataSourceFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.mapper.MapperScannerConfigurer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.ComponentScan.Filter;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.JdkSerializationRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.stereotype.Repository;
import org.springframework.stereotype.Service;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.transaction.annotation.TransactionManagementConfigurer;import redis.clients.jedis.JedisPoolConfig;@Configuration
//定义Spring 扫描的包
@ComponentScan(value= "com.*", includeFilters= {@Filter(type = FilterType.ANNOTATION, value ={Service.class})})
//使用事务驱动管理器
@EnableTransactionManagement
//实现接口TransactionManagementConfigurer,这样可以配置注解驱动事务
public class RootConfig implements TransactionManagementConfigurer {private DataSource dataSource = null;/*** 配置数据库.* @return 数据连接池*/@Bean(name = "dataSource")public DataSource initDataSource() {if (dataSource != null) {return dataSource;}Properties props = new Properties();props.setProperty("driverClassName", "com.mysql.jdbc.Driver");props.setProperty("url", "jdbc:mysql://localhost:3306/chapter22");props.setProperty("username", "root");props.setProperty("password", "123456");props.setProperty("maxActive", "200");props.setProperty("maxIdle", "20");props.setProperty("maxWait", "30000");try {dataSource = BasicDataSourceFactory.createDataSource(props);} catch (Exception e) {e.printStackTrace();}return dataSource;}/**** 配置SqlSessionFactoryBean* @return SqlSessionFactoryBean*/@Bean(name="sqlSessionFactory")public SqlSessionFactoryBean initSqlSessionFactory() {SqlSessionFactoryBean sqlSessionFactory = new SqlSessionFactoryBean();sqlSessionFactory.setDataSource(initDataSource());//配置MyBatis配置文件Resource resource = new ClassPathResource("mybatis/mybatis-config.xml");sqlSessionFactory.setConfigLocation(resource);return sqlSessionFactory;}/**** 通过自动扫描,发现MyBatis Mapper接口* @return Mapper扫描器*/@Bean public MapperScannerConfigurer initMapperScannerConfigurer() {MapperScannerConfigurer msc = new MapperScannerConfigurer();msc.setBasePackage("com.*");msc.setSqlSessionFactoryBeanName("sqlSessionFactory");msc.setAnnotationClass(Repository.class);return msc;}/*** 实现接口方法,注册注解事务,当@Transactional 使用的时候产生数据库事务 */@Override@Bean(name="annotationDrivenTransactionManager")public PlatformTransactionManager annotationDrivenTransactionManager() {DataSourceTransactionManager transactionManager = new DataSourceTransactionManager();transactionManager.setDataSource(initDataSource());return transactionManager;}@Bean(name = "redisTemplate")public RedisTemplate initRedisTemplate() {JedisPoolConfig poolConfig = new JedisPoolConfig();//最大空闲数poolConfig.setMaxIdle(50);//最大连接数poolConfig.setMaxTotal(100);//最大等待毫秒数poolConfig.setMaxWaitMillis(20000);//创建Jedis链接工厂JedisConnectionFactory connectionFactory = new JedisConnectionFactory(poolConfig);connectionFactory.setHostName("localhost");connectionFactory.setPort(6379);//调用后初始化方法,没有它将抛出异常connectionFactory.afterPropertiesSet();//自定Redis序列化器RedisSerializer jdkSerializationRedisSerializer = new JdkSerializationRedisSerializer();RedisSerializer stringRedisSerializer = new StringRedisSerializer();//定义RedisTemplate,并设置连接工程[修改为:工厂]RedisTemplate redisTemplate = new RedisTemplate();redisTemplate.setConnectionFactory(connectionFactory);//设置序列化器redisTemplate.setDefaultSerializer(stringRedisSerializer);redisTemplate.setKeySerializer(stringRedisSerializer);redisTemplate.setValueSerializer(stringRedisSerializer);redisTemplate.setHashKeySerializer(stringRedisSerializer);redisTemplate.setHashValueSerializer(stringRedisSerializer);return redisTemplate;}}
package com.ssm.chapter22.config;import javax.servlet.MultipartConfigElement;
import javax.servlet.ServletRegistration.Dynamic;import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;public class WebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {// Spring IoC环境配置@Overrideprotected Class<?>[] getRootConfigClasses() {// 配置Spring IoC资源return new Class<?>[] { RootConfig.class };}// DispatcherServlet环境配置@Overrideprotected Class<?>[] getServletConfigClasses() {// 加载Java配置类return new Class<?>[] { WebConfig.class };}// DispatchServlet拦截请求配置@Overrideprotected String[] getServletMappings() {return new String[] { "*.do" };}/*** @param dynamic*            Servlet上传文件配置.*/@Overrideprotected void customizeRegistration(Dynamic dynamic) {// 配置上传文件路径String filepath = "e:/mvc/uploads";// 5MBLong singleMax = (long) (5 * Math.pow(2, 20));// 10MBLong totalMax = (long) (10 * Math.pow(2, 20));// 设置上传文件配置dynamic.setMultipartConfig(new MultipartConfigElement(filepath, singleMax, totalMax, 0));}}
package com.ssm.chapter22.config;import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Executor;import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.ComponentScan.Filter;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;
import org.springframework.http.MediaType;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.scheduling.annotation.AsyncConfigurerSupport;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Controller;
import org.springframework.web.servlet.HandlerAdapter;
import org.springframework.web.servlet.ViewResolver;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter;
import org.springframework.web.servlet.view.InternalResourceViewResolver;@Configuration
//定义Spring MVC扫描的包
@ComponentScan(value="com.*", includeFilters= {@Filter(type = FilterType.ANNOTATION, value = Controller.class)})
//启动Spring MVC配置
@EnableWebMvc
public class WebConfig extends AsyncConfigurerSupport { /**** 通过注解 @Bean 初始化视图解析器* @return ViewResolver 视图解析器*/@Bean(name="internalResourceViewResolver")public ViewResolver initViewResolver() {InternalResourceViewResolver viewResolver =new InternalResourceViewResolver();viewResolver.setPrefix("/WEB-INF/jsp/");viewResolver.setSuffix(".jsp");return viewResolver;}/*** 初始化RequestMappingHandlerAdapter,并加载Http的Json转换器* @return  RequestMappingHandlerAdapter 对象*/@Bean(name="requestMappingHandlerAdapter") public HandlerAdapter initRequestMappingHandlerAdapter() {//创建RequestMappingHandlerAdapter适配器RequestMappingHandlerAdapter rmhd = new RequestMappingHandlerAdapter();//HTTP JSON转换器MappingJackson2HttpMessageConverter  jsonConverter = new MappingJackson2HttpMessageConverter();//MappingJackson2HttpMessageConverter接收JSON类型消息的转换MediaType mediaType = MediaType.APPLICATION_JSON_UTF8;List<MediaType> mediaTypes = new ArrayList<MediaType>();mediaTypes.add(mediaType);//加入转换器的支持类型jsonConverter.setSupportedMediaTypes(mediaTypes);//往适配器加入json转换器rmhd.getMessageConverters().add(jsonConverter);return rmhd;}@Overridepublic Executor getAsyncExecutor() {ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();taskExecutor.setCorePoolSize(5);taskExecutor.setMaxPoolSize(10);taskExecutor.setQueueCapacity(200);taskExecutor.initialize();return taskExecutor;}
}
package com.ssm.chapter22.controller;import java.util.HashMap;
import java.util.Map;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.ResponseBody;import com.ssm.chapter22.service.UserRedPacketService;@Controller
@RequestMapping("/userRedPacket")
public class UserRedPacketController {@Autowiredprivate UserRedPacketService userRedPacketService = null;@RequestMapping(value = "/grapRedPacket")@ResponseBodypublic Map<String, Object> grapRedPacket(Long redPacketId, Long userId) {// 抢红包int result = userRedPacketService.grapRedPacket(redPacketId, userId);Map<String, Object> retMap = new HashMap<String, Object>();boolean flag = result > 0;retMap.put("success", flag);retMap.put("message", flag ? "抢红包成功" : "抢红包失败");return retMap;}//乐观锁请求,使用时修改javascript的请求路径@RequestMapping(value = "/grapRedPacketForVersion")@ResponseBodypublic Map<String, Object> grapRedPacketForVersion(Long redPacketId, Long userId) {// 抢红包int result = userRedPacketService.grapRedPacketForVersion(redPacketId, userId);Map<String, Object> retMap = new HashMap<String, Object>();boolean flag = result > 0;retMap.put("success", flag);retMap.put("message", flag ? "抢红包成功" : "抢红包失败");return retMap;}@RequestMapping(value = "/grapRedPacketByRedis")@ResponseBodypublic Map<String, Object> grapRedPacketByRedis(Long redPacketId, Long userId) {Map<String, Object> resultMap = new HashMap<String, Object>();Long result = userRedPacketService.grapRedPacketByRedis(redPacketId, userId);boolean flag = result > 0;resultMap.put("result", flag);resultMap.put("message", flag ? "抢红包成功": "抢红包失败");return resultMap;}
}
package com.ssm.chapter22.dao;import org.apache.ibatis.annotations.Param;
import org.springframework.stereotype.Repository;import com.ssm.chapter22.pojo.RedPacket;
@Repository
public interface RedPacketDao {/*** 获取红包信息.* @param id --红包id* @return 红包具体信息*/public RedPacket getRedPacket(Long id);/*** 扣减抢红包数.* @param id -- 红包id* @return 更新记录条数*/public int decreaseRedPacket(Long id);/**** 使用for update语句加锁.* @param id ——红包id* @return 红包信息*/public RedPacket getRedPacketForUpdate(Long id);public int decreaseRedPacketForVersion(@Param("id") Long id, @Param("version") Integer version);}
package com.ssm.chapter22.dao;import org.springframework.stereotype.Repository;
import com.ssm.chapter22.pojo.UserRedPacket;@Repository
public interface UserRedPacketDao {/*** 插入抢红包信息.* @param userRedPacket ——抢红包信息* @return 影响记录数.*/public int grapRedPacket(UserRedPacket  userRedPacket);
}
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ssm.chapter22.dao.RedPacketDao"><!-- 查询红包具体信息 --><select id="getRedPacket" parameterType="long"resultType="com.ssm.chapter22.pojo.RedPacket">select id, user_id as userId, amount, send_date assendDate, total,unit_amount as unitAmount, stock, version, note fromT_RED_PACKETwhere id = #{id}</select><!-- 扣减抢红包库存 --><update id="decreaseRedPacket">update T_RED_PACKET set stock = stock - 1 where id =#{id}</update><!-- 查询红包具体信息 --><select id="getRedPacketForUpdate" parameterType="long"resultType="com.ssm.chapter22.pojo.RedPacket">select id, user_id as userId, amount, send_date assendDate, total,unit_amount as unitAmount, stock, version, notefromT_RED_PACKET where id = #{id} for update</select><!-- 通过版本号扣减抢红包 每更新一次,版本增1, 其次增加对版本号的判断 --><update id="decreaseRedPacketForVersion">update T_RED_PACKETset stock = stock - 1,version = version + 1where id = #{id}and version = #{version}</update>
</mapper>
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ssm.chapter22.dao.UserRedPacketDao"><!-- 插入抢红包信息 --><insert id="grapRedPacket" useGeneratedKeys="true" keyProperty="id" parameterType="com.ssm.chapter22.pojo.UserRedPacket">insert into T_USER_RED_PACKET( red_packet_id, user_id, amount, grab_time, note)values (#{redPacketId}, #{userId}, #{amount}, now(), #{note}) </insert>
</mapper>
package com.ssm.chapter22.pojo;import java.io.Serializable;
import java.sql.Timestamp;public class RedPacket implements Serializable {private Long id;private Long userId;private Double amount;private Timestamp sendDate;private Integer total;private Double unitAmount;private Integer stock;private Integer version;private String note;public Long getId() {return id;}public void setId(Long id) {this.id = id;}public Long getUserId() {return userId;}public void setUserId(Long userId) {this.userId = userId;}public Double getAmount() {return amount;}public void setAmount(Double amount) {this.amount = amount;}public Timestamp getSendDate() {return sendDate;}public void setSendDate(Timestamp sendDate) {this.sendDate = sendDate;}public Integer getTotal() {return total;}public void setTotal(Integer total) {this.total = total;}public Double getUnitAmount() {return unitAmount;}public void setUnitAmount(Double unitAmount) {this.unitAmount = unitAmount;}public Integer getStock() {return stock;}public void setStock(Integer stock) {this.stock = stock;}public Integer getVersion() {return version;}public void setVersion(Integer version) {this.version = version;}public String getNote() {return note;}public void setNote(String note) {this.note = note;}private static final long serialVersionUID = 1049397724701962381L;
}
package com.ssm.chapter22.pojo;import java.io.Serializable;
import java.sql.Timestamp;public class UserRedPacket implements Serializable {private Long id;private Long redPacketId;private Long userId;private Double amount;private Timestamp grabTime;private String note;private static final long serialVersionUID = -5617482065991830143L;public Long getId() {return id;}public void setId(Long id) {this.id = id;}public Long getRedPacketId() {return redPacketId;}public void setRedPacketId(Long redPacketId) {this.redPacketId = redPacketId;}public Long getUserId() {return userId;}public void setUserId(Long userId) {this.userId = userId;}public Double getAmount() {return amount;}public void setAmount(Double amount) {this.amount = amount;}public Timestamp getGrabTime() {return grabTime;}public void setGrabTime(Timestamp grabTime) {this.grabTime = grabTime;}public String getNote() {return note;}public void setNote(String note) {this.note = note;}}
package com.ssm.chapter22.service.impl;import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
import java.sql.Timestamp;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.List;import javax.sql.DataSource;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.BoundListOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;import com.ssm.chapter22.pojo.UserRedPacket;
import com.ssm.chapter22.service.RedisRedPacketService;@Service
public class RedisRedPacketServiceImpl implements RedisRedPacketService {private static final String PREFIX = "red_packet_list_";// 每次取出1000条,避免一次取出消耗太多内存private static final int TIME_SIZE = 1000;@Autowiredprivate RedisTemplate redisTemplate = null; // RedisTemplate@Autowiredprivate DataSource dataSource = null; // 数据源@Override// 开启新线程运行@Asyncpublic void saveUserRedPacketByRedis(Long redPacketId, Double unitAmount) {System.err.println("开始保存数据");Long start = System.currentTimeMillis();// 获取列表操作对象BoundListOperations ops = redisTemplate.boundListOps(PREFIX + redPacketId);Long size = ops.size();Long times = size % TIME_SIZE == 0 ? size / TIME_SIZE : size / TIME_SIZE + 1;int count = 0;List<UserRedPacket> userRedPacketList = new ArrayList<UserRedPacket>(TIME_SIZE);for (int i = 0; i < times; i++) {// 获取至多TIME_SIZE个抢红包信息List userIdList = null;if (i == 0) {userIdList = ops.range(i * TIME_SIZE, (i + 1) * TIME_SIZE);} else {userIdList = ops.range(i * TIME_SIZE + 1, (i + 1) * TIME_SIZE);}userRedPacketList.clear();// 保存红包信息for (int j = 0; j < userIdList.size(); j++) {String args = userIdList.get(j).toString();String[] arr = args.split("-");String userIdStr = arr[0];String timeStr = arr[1];Long userId = Long.parseLong(userIdStr);Long time = Long.parseLong(timeStr);// 生成抢红包信息UserRedPacket userRedPacket = new UserRedPacket();userRedPacket.setRedPacketId(redPacketId);userRedPacket.setUserId(userId);userRedPacket.setAmount(unitAmount);userRedPacket.setGrabTime(new Timestamp(time));userRedPacket.setNote("抢红包 " + redPacketId);userRedPacketList.add(userRedPacket);}// 插入抢红包信息count += executeBatch(userRedPacketList);}// 删除Redis列表redisTemplate.delete(PREFIX + redPacketId);Long end = System.currentTimeMillis();System.err.println("保存数据结束,耗时" + (end - start) + "毫秒,共" + count + "条记录被保存。");}/*** 使用JDBC批量处理Redis缓存数据.* * @param userRedPacketList*            -- 抢红包列表* @return 抢红包插入数量.*/private int executeBatch(List<UserRedPacket> userRedPacketList) {Connection conn = null;Statement stmt = null;int[] count = null;try {conn = dataSource.getConnection();conn.setAutoCommit(false);stmt = conn.createStatement();for (UserRedPacket userRedPacket : userRedPacketList) {String sql1 = "update T_RED_PACKET set stock = stock-1 where id=" + userRedPacket.getRedPacketId();DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");String sql2 = "insert into T_USER_RED_PACKET(red_packet_id, user_id, " + "amount, grab_time, note)"+ " values (" + userRedPacket.getRedPacketId() + ", " + userRedPacket.getUserId() + ", "+ userRedPacket.getAmount() + "," + "'" + df.format(userRedPacket.getGrabTime()) + "'," + "'"+ userRedPacket.getNote() + "')";stmt.addBatch(sql1);stmt.addBatch(sql2);}// 执行批量count = stmt.executeBatch();// 提交事务conn.commit();} catch (SQLException e) {/********* 错误处理逻辑 ********/throw new RuntimeException("抢红包批量执行程序错误");} finally {try {if (conn != null && !conn.isClosed()) {conn.close();}} catch (SQLException e) {e.printStackTrace();}}// 返回插入抢红包数据记录return count.length / 2;}
}
package com.ssm.chapter22.service.impl;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;import com.ssm.chapter22.dao.RedPacketDao;
import com.ssm.chapter22.pojo.RedPacket;
import com.ssm.chapter22.service.RedPacketService;@Service
public class RedPacketServiceImpl implements RedPacketService {@Autowiredprivate RedPacketDao  redPacketDao = null;@Override@Transactional(isolation=Isolation.READ_COMMITTED, propagation = Propagation.REQUIRED)public RedPacket getRedPacket(Long id) {return redPacketDao.getRedPacket(id);}@Override@Transactional(isolation=Isolation.READ_COMMITTED, propagation = Propagation.REQUIRED)public int decreaseRedPacket(Long id) {return redPacketDao.decreaseRedPacket(id);}}
package com.ssm.chapter22.service.impl;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;import com.ssm.chapter22.dao.RedPacketDao;
import com.ssm.chapter22.dao.UserRedPacketDao;
import com.ssm.chapter22.pojo.RedPacket;
import com.ssm.chapter22.pojo.UserRedPacket;
import com.ssm.chapter22.service.RedisRedPacketService;
import com.ssm.chapter22.service.UserRedPacketService;import redis.clients.jedis.Jedis;@Service
public class UserRedPacketServiceImpl implements UserRedPacketService {@Autowiredprivate UserRedPacketDao userRedPacketDao = null;@Autowiredprivate RedPacketDao redPacketDao = null;// 失败private static final int FAILED = 0;@Override@Transactional(isolation = Isolation.READ_COMMITTED, propagation = Propagation.REQUIRED)public int grapRedPacket(Long redPacketId, Long userId) {// 获取红包信息// RedPacket redPacket = redPacketDao.getRedPacket(redPacketId);// 悲观锁RedPacket redPacket = redPacketDao.getRedPacketForUpdate(redPacketId);// 当前小红包库存大于0if (redPacket.getStock() > 0) {redPacketDao.decreaseRedPacket(redPacketId);// 生成抢红包信息UserRedPacket userRedPacket = new UserRedPacket();userRedPacket.setRedPacketId(redPacketId);userRedPacket.setUserId(userId);userRedPacket.setAmount(redPacket.getUnitAmount());userRedPacket.setNote("抢红包 " + redPacketId);// 插入抢红包信息int result = userRedPacketDao.grapRedPacket(userRedPacket);return result;}// 失败返回return FAILED;}// 乐观锁,无重入@Override@Transactional(isolation = Isolation.READ_COMMITTED, propagation = Propagation.REQUIRED)public int grapRedPacketForVersion(Long redPacketId, Long userId) {// 获取红包信息,注意version值RedPacket redPacket = redPacketDao.getRedPacket(redPacketId);// 当前小红包库存大于0if (redPacket.getStock() > 0) {// 再次传入线程保存的version旧值给SQL判断,是否有其他线程修改过数据int update = redPacketDao.decreaseRedPacketForVersion(redPacketId, redPacket.getVersion());// 如果没有数据更新,则说明其他线程已经修改过数据,本次抢红包失败if (update == 0) {return FAILED;}// 生成抢红包信息UserRedPacket userRedPacket = new UserRedPacket();userRedPacket.setRedPacketId(redPacketId);userRedPacket.setUserId(userId);userRedPacket.setAmount(redPacket.getUnitAmount());userRedPacket.setNote("抢红包 " + redPacketId);// 插入抢红包信息int result = userRedPacketDao.grapRedPacket(userRedPacket);return result;}// 失败返回return FAILED;}// 乐观锁,按时间戳重入// @Override// @Transactional(isolation = Isolation.READ_COMMITTED, propagation =// Propagation.REQUIRED)// public int grapRedPacketForVersion(Long redPacketId, Long userId) {// // 记录开始时间// long start = System.currentTimeMillis();// // 无限循环,等待成功或者时间满100毫秒退出// while (true) {// // 获取循环当前时间// long end = System.currentTimeMillis();// // 当前时间已经超过100毫秒,返回失败// if (end - start > 100) {// return FAILED;// }// // 获取红包信息,注意version值// RedPacket redPacket = redPacketDao.getRedPacket(redPacketId);// // 当前小红包库存大于0// if (redPacket.getStock() > 0) {// // 再次传入线程保存的version旧值给SQL判断,是否有其他线程修改过数据// int update = redPacketDao.decreaseRedPacketForVersion(redPacketId,// redPacket.getVersion());// // 如果没有数据更新,则说明其他线程已经修改过数据,则重新抢夺// if (update == 0) {// continue;// }// // 生成抢红包信息// UserRedPacket userRedPacket = new UserRedPacket();// userRedPacket.setRedPacketId(redPacketId);// userRedPacket.setUserId(userId);// userRedPacket.setAmount(redPacket.getUnitAmount());// userRedPacket.setNote("抢红包 " + redPacketId);// // 插入抢红包信息// int result = userRedPacketDao.grapRedPacket(userRedPacket);// return result;// } else {// // 一旦没有库存,则马上返回// return FAILED;// }// }// }// 乐观锁,按次数重入// @Override// @Transactional(isolation = Isolation.READ_COMMITTED, propagation =// Propagation.REQUIRED)// public int grapRedPacketForVersion(Long redPacketId, Long userId) {// for (int i = 0; i < 3; i++) {// // 获取红包信息,注意version值// RedPacket redPacket = redPacketDao.getRedPacket(redPacketId);// // 当前小红包库存大于0// if (redPacket.getStock() > 0) {// // 再次传入线程保存的version旧值给SQL判断,是否有其他线程修改过数据// int update = redPacketDao.decreaseRedPacketForVersion(redPacketId,// redPacket.getVersion());// // 如果没有数据更新,则说明其他线程已经修改过数据,则重新抢夺// if (update == 0) {// continue;// }// // 生成抢红包信息// UserRedPacket userRedPacket = new UserRedPacket();// userRedPacket.setRedPacketId(redPacketId);// userRedPacket.setUserId(userId);// userRedPacket.setAmount(redPacket.getUnitAmount());// userRedPacket.setNote("抢红包 " + redPacketId);// // 插入抢红包信息// int result = userRedPacketDao.grapRedPacket(userRedPacket);// return result;// } else {// // 一旦没有库存,则马上返回// return FAILED;// }// }// return FAILED;// }@Autowiredprivate RedisTemplate redisTemplate = null;@Autowiredprivate RedisRedPacketService redisRedPacketService = null;// Lua脚本String script = "local listKey = 'red_packet_list_'..KEYS[1] \n" + "local redPacket = 'red_packet_'..KEYS[1] \n"+ "local stock = tonumber(redis.call('hget', redPacket, 'stock')) \n" + "if stock <= 0 then return 0 end \n"+ "stock = stock -1 \n" + "redis.call('hset', redPacket, 'stock', tostring(stock)) \n"+ "redis.call('rpush', listKey, ARGV[1]) \n" + "if stock == 0 then return 2 end \n" + "return 1 \n";// 在缓存LUA脚本后,使用该变量保存Redis返回的32位的SHA1编码,使用它去执行缓存的LUA脚本[加入这句话]String sha1 = null;@Overridepublic Long grapRedPacketByRedis(Long redPacketId, Long userId) {// 当前抢红包用户和日期信息String args = userId + "-" + System.currentTimeMillis();Long result = null;// 获取底层Redis操作对象Jedis jedis = (Jedis) redisTemplate.getConnectionFactory().getConnection().getNativeConnection();try {// 如果脚本没有加载过,那么进行加载,这样就会返回一个sha1编码if (sha1 == null) {sha1 = jedis.scriptLoad(script);}// 执行脚本,返回结果Object res = jedis.evalsha(sha1, 1, redPacketId + "", args);result = (Long) res;// 返回2时为最后一个红包,此时将抢红包信息通过异步保存到数据库中if (result == 2) {// 获取单个小红包金额String unitAmountStr = jedis.hget("red_packet_" + redPacketId, "unit_amount");// 触发保存数据库操作Double unitAmount = Double.parseDouble(unitAmountStr);System.err.println("thread_name = " + Thread.currentThread().getName());redisRedPacketService.saveUserRedPacketByRedis(redPacketId, unitAmount);}} finally {// 确保jedis顺利关闭if (jedis != null && jedis.isConnected()) {jedis.close();}}return result;}
}
package com.ssm.chapter22.service;public interface RedisRedPacketService {/*** 保存redis抢红包列表* @param redPacketId --抢红包编号* @param unitAmount -- 红包金额*/public void saveUserRedPacketByRedis(Long redPacketId, Double unitAmount);
}
package com.ssm.chapter22.service;import com.ssm.chapter22.pojo.RedPacket;public interface RedPacketService {/*** 获取红包* @param id ——编号* @return 红包信息*/public RedPacket getRedPacket(Long id);/*** 扣减红包* @param id——编号* @return 影响条数.*/public int decreaseRedPacket(Long id);}
package com.ssm.chapter22.service;public interface UserRedPacketService {/*** 保存抢红包信息.* @param redPacketId 红包编号* @param userId 抢红包用户编号* @return 影响记录数.*/public int grapRedPacket(Long redPacketId, Long userId);public int grapRedPacketForVersion(Long redPacketId, Long userId);/*** 通过Redis实现抢红包* @param redPacketId --红包编号* @param userId -- 用户编号* @return  * 0-没有库存,失败 * 1--成功,且不是最后一个红包* 2--成功,且是最后一个红包*/public Long grapRedPacketByRedis(Long redPacketId, Long userId);}
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configurationPUBLIC "-//mybatis.org//DTD Config 3.0//EN""http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration><mappers><mapper resource="com/ssm/chapter22/mapper/UserRedPacket.xml"/><mapper resource="com/ssm/chapter22/mapper/RedPacket.xml"/></mappers>
</configuration>
log4j.rootLogger=INFO , stdout
log4j.logger.org.mybatis=INFO
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%5p %d %C: %m%n
create database chapter22;use chapter22;
/*==============================================================*/
/* Table: 红包表                                        */
/*==============================================================*/
create table T_RED_PACKET
(id                   int(12)                        not null auto_increment,user_id              int(12)                        not null,amount               decimal(16,2)                  not null,send_date            timestamp                      not null,total                int(12)                        not null,unit_amount          decimal(12)                    not null,stock                int(12)                        not null,version              int(12) default 0              not null,note                 varchar(256)                    null,primary key clustered (id)
);/*==============================================================*/
/* Table: 用户抢红包表                                                */
/*==============================================================*/
create table T_USER_RED_PACKET
(id                   int(12)                        not null auto_increment,red_packet_id        int(12)                        not null,user_id              int(12)                        not null,amount               decimal(16,2)                  not null,grab_time            timestamp                      not null,note                 varchar(256)                   null,primary key clustered (id)
);/**
* 插入一个20万元金额,2万个小红包,每个10元的红包数据
*/
insert into T_RED_PACKET(user_id, amount, send_date, total, unit_amount, stock, note)values(1, 200000.00, now(), 20000, 10.00, 20000,'20万元金额,2万个小红包,每个10元');
<%@ page language="java" contentType="text/html; charset=UTF-8"pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><title>参数</title><!-- 加载Query文件--><script type="text/javascript" src="https://code.jquery.com/jquery-3.2.0.js"></script><script type="text/javascript">$(document).ready(function () {//模拟30000个异步请求,进行并发var max = 30000;for (var i = 1; i <= max; i++) {//jQuery的post请求,请注意这是异步请求$.post({//请求抢id为1的红包//根据自己请求修改对应的url和大红包编号url: "./userRedPacket/grapRedPacketByRedis.do?redPacketId=8&userId=" + i,//成功后的方法success: function (result) {}});}});</script></head><body></body>
</html>

SSM系类代码:org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer相关推荐

  1. SSM系类代码:java.io.UnsupportedEncodingException

    使用 UnsupportedEncodingException 的软件包 java.io 通过数据流.序列化和文件系统提供系统输入和输出. java.lang 提供利用 Java 编程语言进行程序设计 ...

  2. org.springframework.web.servlet.handler.AbstractHandlerExceptionResolver.logException Resolved

    场景: spring项目中无法访问到对应controller,查看日志,没有报错,只有warnring: org.springframework.web.servlet.handler.Abstrac ...

  3. 单元测试Error creating bean with name org.springframework.web.servlet.resource.Resource

    单元测试代码 @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations = { "classpath*: ...

  4. org.springframework.web.servlet.view.ContentNegotiatingViewResolver

    restful服务中一个重要的特性就是一种资源可以有多种表现形式,在springmvc中可以使用ContentNegotiatingViewResolver这个视图解析器来实现这种方式. 描述资源的三 ...

  5. WARN : org.springframework.web.servlet.PageNotFound - No mapping found for HTTP request with URI [/J

    解决:WARN : org.springframework.web.servlet.PageNotFound - No mapping found for HTTP request with URI ...

  6. Error creating bean with name ‘org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping#0‘

    今天跑一个SpringMVC的web项目,刚运行tomcat就报出500,根据网上查的诸多资料进行修改都没能解决. 运行日志输出报的错误: javax.servlet.ServletException ...

  7. org.springframework.web.servlet.PageNotFound - No mapping found for HTTP request with URI

    org.springframework.web.servlet.PageNotFound - No mapping found for HTTP request with URI 在重启项目中会遇到[ ...

  8. spring mvc学习(50):java.lang.ClassNotFoundException: org.springframework.web.servlet. DispatcherSe

    今天朋友发了个maven项目给我看,问我为什么启动不了.说实话,一直用Jfinal都快不会用spring了- 还是决定看看. 接收了文件,是maven构建的,打开eclipse,导入maven项目,然 ...

  9. 令新手头痛的java.lang.ClassNotFoundException: org.springframework.web.servlet.DispatcherServlet错误...

    最近在学习springMVC,做helloworld实例的时候运行服务器一直报:java.lang.ClassNotFoundException: org.springframework.web.se ...

  10. java.lang.ClassNotFoundException: org.springframework.web.servlet.HandlerMapping解决方法

    [现象] Caused by: java.lang.ClassNotFoundException: org.springframework.web.servlet.HandlerMapping at ...

最新文章

  1. MySQL数据库性能优化--SQL优化
  2. Boost:boost::asio模块的allocation service分配服务测试程序
  3. 《Java程序性能优化》之设计优化
  4. QT+Halcon综合示例:clip回形针2D位姿检测
  5. projecteuler_problem11
  6. Java 三个线程依次输出ABC
  7. (三)JAVA使用POI操作excel
  8. caffemodel中的参数及特征的抽取
  9. WinEdt中xelatex.exe等exe did not succeed问题
  10. 迅雷离线下载 linux,linux 下使用迅雷离线下载
  11. 《此生未完成》:她说,名利权情,没有一样是不辛苦的
  12. Linux第7章Gdk及Cairo基础,Linux第7章Gdk及Cairo基础概要1.ppt
  13. OGL纹理之纹理过滤器和多级渐进纹理mipmap
  14. 【经典论文解读】YOLO 目标检测
  15. python 爬取壁纸网站并保存图片到本地
  16. java 文件拷贝保留原来的属性_Java常用属性拷贝工具类使用总结
  17. python dataframe 写入到doc文件_将Python Pandas DataFrame写入Word文档
  18. android qq底部图片选择器,Android 高仿QQ图片选择器
  19. 知网复制太麻烦了?试试这个方法
  20. neuoj1472 yuki的氪金之旅(倒置树状数组

热门文章

  1. Linux下视频流媒体服务器搭建详解
  2. c语言程序设计实训的心得,C语言程序设计实习心得体会
  3. 批处理系统、分时操作系统、实时操作系统
  4. ZoomIt 演示辅助软件 屏幕放大镜
  5. 申请与认证IB课程全流程
  6. 电子词典系统vc++_《VC++ 编程词典(珍藏版)》
  7. 如何使用Javascript复制到剪贴板
  8. 编程实现类Logo语言(海龟语言)
  9. ecshop shopex_json.php,Shopex到ECShop的转换教程
  10. html5网页制作代码 大学生网页制作作业代码 (旅游网站官网滚动模板)