一、ssm复习
主要是看书。然后做了练习
二、ssm+redis抢红包
实体类层
UserRedPacket.java
package com.redpacket.pojo;
import java.io.Serializable;
import java.sql.Timestamp;
public class UserRedPacket implements Serializable{
/**
* 对应user_red_packet表
*/
private static final long serialVersionUID = 1L;
private Long userRedPacketId;
private Long redPacketId;
private Long userId;
private Double amount;
private Timestamp grabTime;
private String note;
/*getter and setter*/
RedPacket.java
package com.redpacket.pojo;
import java.io.Serializable;
import java.sql.Timestamp;
public class RedPacket implements Serializable {
/**
* 对应red-packet表
* 实现serializer接口,对象序列化
*/
private static final long serialVersionUID = 1L;
private Long redPacketId;
private Long userId;
private Double amount;
private Timestamp sendDate;
private Integer total;
private Double unitAmount;
private Integer stock;
private Integer version;
private String note;
/*getter and setter*/
}
dao层实现
RedPacketMapper.java
package com.redpacket.dao;
import java.util.List;
import org.springframework.stereotype.Repository;
import com.redpacket.pojo.RedPacket;
import com.redpacket.pojo.UserRedPacket;
@Repository
public interface RedPacketMapper {
public RedPacket getRedPacket(Long redpacketId);//无锁
public RedPacket getRedPacketForUpdate(Long redpacketId);//悲观锁
public int decreaseRedPacket(Long redpacketId);
public int decreaseRedPacketForVersion(Long redpacketId,Integer version);//乐观锁
//使用Redi时对RedPacket表进行批量操作
public int decreaseRedPacketByRedis(int size,Long redpacketId);
}
UserRedPacketMapper.java
package com.redpacket.dao;
import java.util.List;
import org.springframework.stereotype.Repository;
import com.redpacket.pojo.UserRedPacket;
@Repository
public interface UserRedPacketMapper {
public int insertUserRedPacket(UserRedPacket userRedPacket);
public int insertUserRedPacketByRedis(List<UserRedPacket> userRedPacketList);//采用redis时对数据进行批处理
}
RedPacketMapper.xml
<?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.redpacket.dao.RedPacketMapper">
<select id="getRedPacket" parameterType="long" resultType="com.redpacket.pojo.RedPacket">
select redpacket_id as redpacketId,user_id as userId,amount,send_date as sendDate,total,
unit_amount as unitAmount,stock,version,note from red_pakcet where redpacketId=#{redpacketId}
</select>
<update id="decreaseRedPacket" >
update red_packet set stock=stock-1 where redpacket_id=#{redpacketId}
</update>
<update id="decreaseRedPacketForVersion">
update red_packet set stock=stock-1 where redpacket_id=#{redpacketId} and version=#{version}
</update>
<update id="decreaseRedPacketByRedis">
update red_packet set stock=stock-#{size} where redpacket_id=#{redpacketId}
</update>
<select id="getRedPacketForUpdate" resultType="com.redpacket.pojo.RedPacket">
select redpacket_id as redPacketId,user_id as userId,amount,send_date as sendDate,total,
unit_amount as unitAmount,stock,version,note from red_pakcet where id=#{id} for update
</select>
</mapper>
UserRedPacketMapper.xml
<?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.redpacket.dao.UserRedPacketMapper">
<insert id="insertUserRedPacket" parameterType="com.redpacket.pojo.UserRedPacket"
useGeneratedKeys="true" keyProperty="user_redpacket_id">
insert into user_red_packet(redpacket_id,user_id,amount,grab_time,note)
values(#{redPacketId},#{userId},#{amount},#{grabTime},#{note})
</insert>
<insert id="insertUserRedPacketByRedis" parameterType=""
useGeneratedKeys="true" keyProperty="user_redpacket_id">
insert into user_red_packet(redpacket_id,user_id,amount,grab_time,note)
values
<foreach collection="userRedPacketList" item="userRedPacket" separator=",">
(#{userRedPacket.redPacketId},#{userRedPacket.userId},#{userRedPacket.amount},#{userRedPacket.grabTime},
#{userRedPacket.note})
</foreach>
</insert>
</mapper>
service层实现
RedisRedPacketServiceImpl.java
package com.redpacket.service;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.List;
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 org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import com.redpacket.dao.RedPacketMapper;
import com.redpacket.dao.UserRedPacketMapper;
import com.redpacket.pojo.UserRedPacket;
import redis.clients.jedis.Jedis;
@Service
public class RedisRedPacketServiceImpl implements RedisRedPacketService {
@Autowired
private RedisTemplate redisTemplate=null;
private static final String PREFIX="red_packet_list_";
private static final int TIME_SIZE=1000;//每次操作1000次
//Lua脚本
String script="local listKey='red_packet_list_'..KEYS[1]\n"
+"local redPacket='red_packet_'..KEYS[1]\n"
+"local stock=tonumber(redis.call('heget',redPacket,'stock'))"
+"if stock<=0 then return 0 end \n"
+"stock=stock-1 \n"
+"redis.call('hset',redPacket,'stock',tostring(stock)) \n"
+"redis.call('rpush',lsitkey,ARGV[1]) \n"
+"if stock==0 then return 2 end \n"
+"return 1 \n";
String sha1=null;//保存Lua脚本执行后返回的SHA1编码,使用它去执行Lua脚本
@Autowired
RedPacketMapper redPacketMapper=null;
@Autowired
UserRedPacketMapper userRedPacketMapper=null;
//spring启动任务池的一条线程去运行对应的方法
@Async
public void saveUserRedPacketByRedis(Long redpacketId, Double unitamount) {
// TODO Auto-generated method stub
System.out.println("开始保存redis缓存数据到MySQL数据库");
BoundListOperations ops=redisTemplate.boundListOps(PREFIX+redpacketId);
long start=System.currentTimeMillis();//起始时间
long size=ops.size();
//求出该循环操作几次
long times=size%TIME_SIZE==0?size/TIME_SIZE:size/TIME_SIZE+1;
long count=0;//记录成功操作次数
List<UserRedPacket>userRedPacketsList=new ArrayList<UserRedPacket>();
for(int i=0;i<times;i++) {
List<UserRedPacket>userIdList=null;
if(i==0) {
userIdList=ops.range(0, (i+1)*TIME_SIZE);
}else {
userIdList=ops.range(i*TIME_SIZE+1, (i+1)*TIME_SIZE);
}
userRedPacketsList.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.setAmount(unitamount);
userRedPacket.setUserId(userId);
userRedPacket.setGrabTime(new Timestamp(time));
userRedPacket.setNote("用户:"+userId+"抢红包:"+redpacketId);
userRedPacketsList.add(userRedPacket);
}
executBatch(userRedPacketsList);//保存数据
}
//删除redis缓存里list
redisTemplate.delete(PREFIX+redpacketId);
long end=System.currentTimeMillis();
System.out.println("保存数据结束,共耗时:"+(end-start));
}
/*
* 使用mybatis批量更新、插入数据
*/
@Transactional(isolation=Isolation.READ_COMMITTED,propagation=Propagation.REQUIRED)
public void executBatch(List< UserRedPacket>userRedPacketsList){
//对红包表进行扣减存量操作
redPacketMapper.decreaseRedPacketByRedis(userRedPacketsList.size(),userRedPacketsList.get(0).getRedPacketId());
//插入新的用户红包表值
userRedPacketMapper.insertUserRedPacketByRedis(userRedPacketsList);
}
public long updateRedPacketByRedis(Long redPacketId,Long userId) {
String args=userId+"-"+System.currentTimeMillis();
Long result=null;
Jedis jedis=(Jedis)redisTemplate.getConnectionFactory().getConnection().getNativeConnection();
try {
if(sha1==null) {
sha1=jedis.scriptLoad(script);
}
Object res=jedis.evalsha(sha1, 1,redPacketId+"",args);
result=(Long)res;
//返回值为2时表示红包已经抢完了,此时将红包数据保存到数据库
if(result==2) {
//获取redis中缓存
String unitAmountstr=jedis.hget("red_packet_"+redPacketId, "unit_amount");
Double unitAmount=Double.parseDouble(unitAmountstr);
System.out.println(Thread.currentThread().getName());//当前线程
saveUserRedPacketByRedis(redPacketId, unitAmount);
}
} catch (Exception e) {
e.printStackTrace();
}finally {
if(jedis!=null&&jedis.isConnected())
jedis.close();
}
return result;
}
}
为了执行,执行redis命令
hset red_packet_5 stock 20000
hset red_packet_5 unit_amount10
UserRedPacketServiceImpl.java
package com.redpacket.service;
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.redpacket.dao.RedPacketMapper;
import com.redpacket.dao.UserRedPacketMapper;
import com.redpacket.pojo.RedPacket;
import com.redpacket.pojo.UserRedPacket;
/*
* 用户抢红包主要调用的service层业务逻辑
* 分为三种方式
* 悲观锁、乐观锁、使用Rdis缓存
*/
@Service
public class UserRedPacketServiceImpl implements UserRedPacketService {
@Autowired
private RedPacketMapper redPacketMapper = null;
private static final int FAILED = 0;
@Autowired
private UserRedPacketMapper userRedPacketMapper = null;
@Autowired
private RedisRedPacketService redisRedPacketService=null;
// 不加锁的方式,该方式在高并发下不可行
@Transactional(isolation = Isolation.READ_COMMITTED, propagation = Propagation.REQUIRED)
public int insertUserRedPacket(Long redpacketId, Long userId) {
RedPacket redPacket = redPacketMapper.getRedPacket(redpacketId);
// 如果红包存量大于0则执行插入用户抢红包信息表
if (redPacket.getStock() > 0) {
// 先对红包存量进行减操作
redPacketMapper.decreaseRedPacket(redpacketId);
UserRedPacket userRedPacket = new UserRedPacket();
userRedPacket.setUserId(userId);
userRedPacket.setRedPacketId(redpacketId);
userRedPacket.setAmount(redPacket.getUnitAmount());
userRedPacket.setNote("抢红包成功,抢到红包ID为" + redpacketId);
int result = userRedPacketMapper.insertUserRedPacket(userRedPacket);
return result;
}
return FAILED;// 红包存量不足,抢红包失败
}
// 采用乐观锁version的方式失败率高,可考虑重入的机制
@Transactional(isolation=Isolation.READ_COMMITTED,propagation=Propagation.REQUIRED)
public int insertUserRedPacketForVersion(Long redpacketId,Long userId) {
RedPacket redPacket=redPacketMapper.getRedPacket(redpacketId);
if(redPacket.getStock()>0) {
//先对红包存量进行减操作
int reslut=redPacketMapper.decreaseRedPacketForVersion(redpacketId, redPacket.getVersion());
if(reslut==0)
return FAILED;
UserRedPacket userRedPacket=new UserRedPacket();
userRedPacket.setUserId(userId);
userRedPacket.setRedPacketId(redpacketId);
userRedPacket.setAmount(redPacket.getUnitAmount());
userRedPacket.setNote("抢红包成功,抢到红包ID为"+redpacketId);
int result=userRedPacketMapper.insertUserRedPacket(userRedPacket);
return result;
}
return FAILED;//红包存量不足,抢红包失败
}
// 采用悲观锁的方式,耗时较大
@Transactional(isolation=Isolation.READ_COMMITTED,propagation=Propagation.REQUIRED)
public int insertUserRedPacketForUpdate(Long redpacketId,Long userId) {
RedPacket redPacket=redPacketMapper.getRedPacketForUpdate(redpacketId);
//如果红包存量大于0则执行插入用户抢红包信息表
if(redPacket.getStock()>0) {
//先对红包存量进行减操作
redPacketMapper.decreaseRedPacket(redpacketId);
UserRedPacket userRedPacket=new UserRedPacket();
userRedPacket.setUserId(userId);
userRedPacket.setRedPacketId(redpacketId);
userRedPacket.setAmount(redPacket.getUnitAmount());
userRedPacket.setNote("抢红包成功,抢到红包ID为"+redpacketId);
int result=userRedPacketMapper.insertUserRedPacket(userRedPacket);
return result;
}
return FAILED;//红包存量不足,抢红包失败
}
//使用Redis缓存的方式
@Transactional(isolation=Isolation.READ_COMMITTED,propagation=Propagation.REQUIRED)
public int insertUserRedPacketByRedis(Long redpacketId,Long userId) {
return redisRedPacketService.updateRedPacketByRedis(redpacketId,userId);
}
}
RedPacketServiceImpl.java
package com.redpacket.service;
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.redpacket.dao.RedPacketMapper;
import com.redpacket.pojo.RedPacket;
@Service
public class RedPacketServiceImpl implements RedPacketService {
@Autowired
private RedPacketMapper redPacketMapper=null;
@Transactional(isolation=Isolation.READ_COMMITTED,propagation=Propagation.REQUIRED)
public RedPacket getRedPackt(Long redPacketId) {
// TODO Auto-generated method stub
return redPacketMapper.getRedPacket(redPacketId);
}
@Transactional(isolation=Isolation.READ_COMMITTED,propagation=Propagation.REQUIRED)
public int decreaseRedPacket(Long redPacketId) {
// TODO Auto-generated method stub
return redPacketMapper.decreaseRedPacket(redPacketId);
}
}
controller层实现
package com.redpacket.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.redpacket.service.UserRedPacketService;
@Controller
@RequestMapping("/userRedPacket")
public class UserRedPacketController {
@Autowired
private UserRedPacketService userRedPacketService = null;
@RequestMapping(value = "/grapRedPacket")
@ResponseBody
public Map<String, Object> grapRedPacket(Long redPacketId, Long userId) {
int result = userRedPacketService.insertUserRedPacket(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 = "/grapRedPacketForVersion")
@ResponseBody
public Map<String, Object> grapRedPacketForVersion(Long redPacketId, Long userId) {
int result = userRedPacketService.insertRedPacketForVersion(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")
@ResponseBody
public Map<String, Object> grapRedPacketByRedis(Long redPacketId, Long userId) {
Map<String, Object> resultMap = new HashMap<String, Object>();
int result=userRedPacketService.insertUserRedPacketByRedis(redPacketId, userId);
boolean flag = result > 0;
resultMap.put("result", flag);
resultMap.put("message", flag ? "抢红包成功" : "抢红包失败");
return resultMap;
}
}
前台
grapRedPacket.jsp
<%@ 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: "./userRedPacket/grapRedPacketByRedis.do?redPacketId=5&&userId=" + i,
//成功后的方法
success: function (result) {
}
});
}
});
</script>
</head>
<body>
</body>
</html>
SSM+Redis配置文件
采用注解的方式配置
WebAppInitializer.java
package spring;
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
public class WebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
@Override//springIOC配置
protected Class<?>[] getRootConfigClasses() {
// TODO Auto-generated method stub
return new Class<?>[] {RootConfig.class};
}
@Override//SpringMVC配置
protected Class<?>[] getServletConfigClasses() {
// TODO Auto-generated method stub
return new Class<?>[] {WebConfig.class};
}
@Override//DispatherServlet 拦截请求配置
protected String[] getServletMappings() {
// TODO Auto-generated method stub
return new String[] {"*.do"};
}
}
WebConfig.java
package spring;
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
@ComponentScan(value="com.*", includeFilters= {@Filter(type = FilterType.ANNOTATION, value = Controller.class)})
@EnableWebMvc
public class WebConfig extends AsyncConfigurerSupport {
@Bean(name="internalResourceViewResolver")
public ViewResolver initViewResolver() {
InternalResourceViewResolver viewResolver =new InternalResourceViewResolver();
viewResolver.setPrefix("/WEB-INF/jsp/");
viewResolver.setSuffix(".jsp");
return viewResolver;
}
@Bean(name="requestMappingHandlerAdapter")
public HandlerAdapter initRequestMappingHandlerAdapter() {
RequestMappingHandlerAdapter rmhd = new RequestMappingHandlerAdapter();
MappingJackson2HttpMessageConverter jsonConverter
= new MappingJackson2HttpMessageConverter();
MediaType mediaType = MediaType.APPLICATION_JSON_UTF8;
List<MediaType> mediaTypes = new ArrayList<MediaType>();
mediaTypes.add(mediaType);
jsonConverter.setSupportedMediaTypes(mediaTypes);
rmhd.getMessageConverters().add(jsonConverter);
return rmhd;
}
@Override
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
taskExecutor.setCorePoolSize(5);
taskExecutor.setMaxPoolSize(10);
taskExecutor.setQueueCapacity(200);
taskExecutor.initialize();
return taskExecutor;
}
}
RootConfig.java
package spring;
import java.util.Properties;
import javax.annotation.Resource;
import javax.sql.DataSource;
import org.apache.ibatis.jdbc.Null;
import org.apache.tomcat.dbcp.dbcp2.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.core.io.ClassPathResource;
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.Component;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.transaction.annotation.TransactionManagementConfigurer;
import com.sun.xml.internal.ws.wsdl.writer.document.Service;
import redis.clients.jedis.JedisPoolConfig;
@Configuration
@ComponentScan(value="com.*",includeFilters= {@Filter(type=FilterType.ANNOTATION,value= {Service.class})})
@EnableTransactionManagement
public class RootConfig implements TransactionManagementConfigurer{
private DataSource dataSource=null;
@Bean(name="datasource")
public DataSource initDatasource() {
if(dataSource!=null)
return dataSource;
Properties properties=new java.util.Properties();
properties.setProperty("driverClassName", "com.mysql.jdbc.Driver");
properties.setProperty("url", "jdbc://mysql://localhost:3306/redpacket");;
properties.setProperty("username", "root");
properties.setProperty("password", "root");
properties.setProperty("maxActive", "200");
properties.setProperty("maxIdle", "20");
properties.setProperty("maxWait", "20000");
try {
dataSource=BasicDataSourceFactory.createDataSource(properties);
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
}
return dataSource;
}
@Bean(name="sqlSessionFactory")
public SqlSessionFactoryBean initSqlSessionFactory() {
SqlSessionFactoryBean sqlSessionFactory=new SqlSessionFactoryBean();
sqlSessionFactory.setDataSource(initDatasource());
Resource resource=new ClassPathResource("mybatis/mybatis-config.xml");
sqlSessionFactory.setConfigLocation(resource);
return sqlSessionFactory;
}
@Bean
public MapperScannerConfigurer initMapperScannerConfigurer() {
MapperScannerConfigurer msc=new MapperScannerConfigurer();
msc.setBasePackage("com.*");
msc.setSqlSessionFactoryBeanName("sqlSessionFactory");
msc.setAnnotationClass(Repository.class);
return msc;
}
@Bean(name="redisTemplate")
public RedisTemplate initRedisTempalte() {
JedisPoolConfig poolConfig = new JedisPoolConfig();
poolConfig.setMaxIdle(50);
poolConfig.setMaxTotal(100);
poolConfig.setMaxWaitMillis(20000);
JedisConnectionFactory connectionFactory = new JedisConnectionFactory(poolConfig);
connectionFactory.setHostName("localhost");
connectionFactory.setPort(6379);
connectionFactory.afterPropertiesSet();
RedisSerializer jdkSerializationRedisSerializer = new JdkSerializationRedisSerializer();
RedisSerializer stringRedisSerializer = new StringRedisSerializer();
RedisTemplate redisTemplate = new RedisTemplate();
redisTemplate.setConnectionFactory(connectionFactory);
redisTemplate.setDefaultSerializer(stringRedisSerializer);
redisTemplate.setKeySerializer(stringRedisSerializer);
redisTemplate.setValueSerializer(stringRedisSerializer);
redisTemplate.setHashKeySerializer(stringRedisSerializer);
redisTemplate.setHashValueSerializer(stringRedisSerializer);
return redisTemplate;
}
@Bean(name="annotationDrivenTransactionManager")
public PlatformTransactionManager annotationDrivenTransationManager() {
DataSourceTransactionManager transactionManager=new DataSourceTransactionManager();
transactionManager.setDataSource(initDatasource());
return transactionManager;
}
}
mybaitis-spring.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<mappers>
<mapper resource="com/redpacket/dao/UserRedPacketMapper.xml"/>
<mapper resource="com/redpacket/dao/RedPacketMapper.xml"/>
</mappers>
</configuration>
三、JavaNIO
Java NIO(New IO)是一个可以替代标准Java IO API的IO API(从Java 1.4开始),Java NIO提供了与标准IO不同的IO工作方式。
1.基本组件
image.pngChannels (通道)
标准的IO基于字节流和字符流进行操作的,而NIO是基于通道(Channel)和缓冲区(Buffer)进行操作,数据总是从通道读取到缓冲区中,或者从缓冲区写入到通道中。在Java NIO中,通道是在实体和字节缓冲区之间有效传输数据的媒介。 它从一个实体读取数据,并将其放在缓冲区块中以供消费。下面是JAVA NIO中的一些主要Channel的实现:
- FileChannel:文件通道用于从文件读取数据。
- DatagramChannel:数据报通道可以通过UDP(用户数据报协议)通过网络读取和写入数据
- SocketChannel:数据报通道可以通过TCP(传输控制协议)通过网络读取和写入数据
- ServerSocketChannel:ServerSocketChannel允许用户监听传入的TCP连接,与Web服务器相同对于每个传入连接,都会为连接创建一个SocketChannel
缓冲区(Buffers)
缓冲区(Buffers)用于与NIO通道进行交互。这是写入数据的内存块,以便在稍后再次进行读取。 内存块用NIO缓冲对象包装,这样可以提供更简单的方法来处理内存块。
使用Buffer读写数据一般遵循以下四个步骤:
- 写入数据到Buffer
- 调用flip()方法
- 从Buffer中读取数据
- 调用clear()方法或者compact()方法
当向buffer写入数据时,buffer会记录下写了多少数据。一旦要读取数据,需要通过flip()方法将Buffer从写模式切换到读模式。在读模式下,可以读取之前写入到buffer的所有数据。一旦读完了所有的数据,就需要清空缓冲区,让它可以再次被写入。有两种方式能清空缓冲区:调用clear()或compact()方法。clear()方法会清空整个缓冲区。compact()方法只会清除已经读过的数据。任何未读的数据都被移到缓冲区的起始处,新写入的数据将放到缓冲区未读数据的后面。
在Java NIO中使用的核心缓冲区如下: - CharBuffer
- DoubleBuffer
- IntBuffer
- LongBuffer
- ByteBuffer
- MappedByteBuffer
- ShortBuffer
- FloatBuffer
image.png
buffer的三个属性
capacity
作为一个内存块,Buffer有一个固定的大小值,也叫“capacity”.你只能往里写capacity个byte、long,char等类型。一旦Buffer满了,需要将其清空(通过读数据或者清除数据)才能继续写数据往里写数据。
position
当你写数据到Buffer中时,position表示当前的位置。初始的position值为0.当一个byte、long等数据写到Buffer后, position会向前移动到下一个可插入数据的Buffer单元。position最大可为capacity – 1.
当读取数据时,也是从某个特定位置读。当将Buffer从写模式切换到读模式,position会被重置为0. 当从Buffer的position处读取数据时,position向前移动到下一个可读的位置。
limit
在写模式下,Buffer的limit表示你最多能往Buffer里写多少数据。 写模式下,limit等于Buffer的capacity。
当切换Buffer到读模式时, limit表示你最多能读到多少数据。因此,当切换Buffer到读模式时,limit会被设置成写模式下的position值。换句话说,你能读到之前写入的所有数据(limit被设置成已写数据的数量,这个值在写模式下就是position)
向Buffer中写数据
写数据到Buffer有两种方式:
从Channel写到Buffer。
通过Buffer的put()方法写到Buffer里。
int bytesRead = inChannel.read(buf);//从Channel写到Buffer的例子
buf.put(127);//Buffer的put()方法写到Buffer里
从Buffer中读取数据
从Buffer中读取数据有两种方式:
从Buffer读取数据到Channel。
使用get()方法从Buffer中读取数据。
从Buffer读取数据到Channel的例子:
int bytesWritten = inChannel.write(buf);
使用get()方法从Buffer中读取数据的例子
byte aByte = buf.get();
rewind()方法
Buffer.rewind()将position设回0,所以你可以重读Buffer中的所有数据
clear()与compact()方法
一旦读完Buffer中的数据,清空buffer,需要让Buffer准备好再次被写入。
mark()与reset()方法
通过调用Buffer.mark()方法,可以标记Buffer中的一个特定position。之后可以通过调用Buffer.reset()方法恢复到这个position
equals()与compareTo()方法
可以使用equals()和compareTo()方法两个Buffer。
Java NIO: Non-blocking IO(非阻塞IO)
Java NIO可以让你非阻塞的使用IO,例如:当线程从通道读取数据到缓冲区时,线程还是可以进行其他事情。当数据被写入到缓冲区时,线程可以继续处理它。从缓冲区写入通道也类似。
Selectors(选择器)
选择器用于监听多个通道的事件。因此,单个的线程可以监听多个数据通道。
一个单独的线程可以管理多个channel,从而管理多个网络连接
Selector的创建
Selector selector = Selector.open();
向Selector注册通道
channel.configureBlocking(false);//设置为非阻塞
SelectionKey key = channel.register(selector,
Selectionkey.OP_READ);
register可以监听四种不同类型的事件:
- Connect(SelectionKey.OP_CONNECT)
- Accept(SelectionKey.OP_ACCEPT)
- Read(SelectionKey.OP_READ)
- Write(SelectionKey.OP_WRITE)
SelectionKey
当向Selector注册Channel时,register()方法会返回一个SelectionKey对象。这个对象包含了一些属性: - interest集合
- ready集合(是通道已经准备就绪的操作的集合)
- Channel
- Selector
通过Selector选择通道
一旦向Selector注册了一或多个通道,就可以调用几个重载的select()方法。这些方法返回你所感兴趣的事件(如连接、接受、读或写)已经准备就绪的那些通道。来检测channel中什么事件或操作已经就绪 - isAcceptable();
- isConnectable();
- isReadable();
- isWritable()
- int select()
- int select(long timeout)
- int selectNow()不会阻塞,不管什么通道就绪都立刻返回
selectedKeys()访问“已选择键集”中的就绪通道
2.缓冲区,通道,java程序,数据源和数据接收器之间的相互作用
image.png3.分散/聚集
分散读取
是指数据从一个channel读取到多个buffer
ByteBuffer header = ByteBuffer.allocate(128);
ByteBuffer body = ByteBuffer.allocate(1024);
ByteBuffer[] bufferArray = { header, body };
channel.read(bufferArray);
Scattering Reads在移动下一个buffer前,必须填满当前的buffer,read()方法按照buffer在数组中的顺序将从channel中读取的数据写入到buffer,当一个buffer被写满后,channel紧接着向另一个buffer中写
聚集写入
“聚集写入”用于将数据从多个缓冲区写入单个通道。
ByteBuffer header = ByteBuffer.allocate(128);
ByteBuffer body = ByteBuffer.allocate(1024);
ByteBuffer[] bufferArray = { header, body };
channel.write(bufferArray);
4.通道之间的数据传输
FileChannel.transferTo()方法
transferTo()方法用来从FileChannel到其他通道的数据传输
FileChannel.transferFrom()方法
transferFrom()方法允许从源通道到FileChannel的数据传输。
5.JavaNIO字符集
- UTF-8:8位UCS转换格式。
- US-ASCII:7位ASCII字符。
- UTF-16LE:16位UCS转换,小字节顺序。
- ISO-8859-1:ISO拉丁字母
- UTF-16:16位UCS转换格式。
- UTF-16BE:大字节顺序的16位UCS变换格式
- Charset.displayName() - 在Java NIO中用于返回规范名称的字符集。
- Charset.encode() - 在Java NIO中用于将UNICODE字符集的charbuffer编码为给定字符集的CharBuffer。
- Charset.decode() - 在Java NIO中用于将给定字符集的字符串解码为Unicode字符集的CharBuffer。
Java NIO中的CharsetEncoder
CharsetEncoder用于将Unicode字符编码为字节序列。它还返回一个提供任何错误信息的CoderResult对象。
Java NIO中的CharsetDecoder
CharsetDecoder用于将数组或字节序列解码为Unicode字符。在从ByteBuffer到CharBuffer的解码过程中,获得CoderResult对象。
6.JavaNIO实例
server端
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.nio.charset.Charset;
import java.util.Iterator;
public class NIOServer1 {
// 本地字符集
private static final String LocalCharSetName = "UTF-8";
// 本地服务器监听的端口
private static final int Listenning_Port = 8888;
// 缓冲区大小
private static final int Buffer_Size = 1024;
// 超时时间,单位毫秒
private static final int TimeOut = 3000;
public static void main(String[] args) throws IOException {
// 创建一个在本地端口进行监听的服务Socket通道.并设置为非阻塞方式
ServerSocketChannel serverChannel = ServerSocketChannel.open();
serverChannel.socket().bind(new InetSocketAddress(Listenning_Port));
serverChannel.configureBlocking(false);
// 创建一个选择器并将serverChannel注册到它上面
Selector selector = Selector.open();
serverChannel.register(selector, SelectionKey.OP_ACCEPT);
while (true) {
// 等待某个同道就绪
if (selector.select(TimeOut) == 0) {
System.out.println(".");
continue;
}
// 获得就绪通道的键迭代器
Iterator<SelectionKey> keyIter = selector.selectedKeys().iterator();
// 使用迭代器进行遍历就绪通道
while (keyIter.hasNext()) {
SelectionKey key = keyIter.next();
// 这种情况是有客户端连接过来,准备一个clientChannel与之通信
if (key.isAcceptable()) {
SocketChannel clientChannel = ((ServerSocketChannel) key.channel()).accept();
clientChannel.configureBlocking(false);
clientChannel.register(selector, SelectionKey.OP_READ,
ByteBuffer.allocate(Buffer_Size));
}
// 客户端有写入时
if (key.isReadable()) {
// 获得与客户端通信的信道
SocketChannel clientChannel = (SocketChannel) key.channel();
// 得到并重置缓冲区的主要索引值
ByteBuffer buffer = (ByteBuffer) key.attachment();
buffer.clear();
// 读取信息获得读取的字节数
long bytesRead = clientChannel.read(buffer);
if (bytesRead == -1) {
// 没有读取到内容的情况
clientChannel.close();
} else {
// 将缓冲区准备为数据传出状态
buffer.flip();
// 将获得字节字符串(使用Charset进行解码)
String receivedString = Charset
.forName(LocalCharSetName).newDecoder().decode(buffer).toString();
System.out.println("接收到信息:" + receivedString);
// 准备发送的文本
String sendString = "你好,客户端. 已经收到你的信息" + receivedString;
// 将要发送的字符串编码(使用Charset进行编码)后再进行包装
buffer = ByteBuffer.wrap(sendString.getBytes(LocalCharSetName));
// 发送回去
clientChannel.write(buffer);
// 设置为下一次读取或是写入做准备
key.interestOps(SelectionKey.OP_READ | SelectionKey.OP_WRITE);
}
}
keyIter.remove();
}
}
}
}
客户端
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.Scanner;
public class TestClient{
public static void main(String[] args) throws Exception{
Socket s=new Socket("127.0.0.1",8888);
InputStream inStram=s.getInputStream();
OutputStream outStream=s.getOutputStream();
// 输出
PrintWriter out=new PrintWriter(outStream,true);
out.print("你好,很高兴见到您!");
out.flush();
s.shutdownOutput();// 输出结束
// 输入
Scanner in=new Scanner(inStram);
StringBuilder sb=new StringBuilder();
while(in.hasNextLine()){
String line=in.nextLine();
sb.append(line);
}
String response=sb.toString();
System.out.println("response="+response);
}
}
六、本周总结
本周没有学太多东西,主要是练习巩固。本周在做游戏设计课和数据挖掘课的课程大作业,感觉学习时间安排上出了点问题,下周要注意合理分配时间
网友评论