工程中心第二周学习总结

一、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.png

Channels (通道)
标准的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(选择器)
选择器用于监听多个通道的事件。因此,单个的线程可以监听多个数据通道。

image.png

一个单独的线程可以管理多个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.png

3.分散/聚集

分散读取
是指数据从一个channel读取到多个buffer

image.png

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中写
聚集写入
“聚集写入”用于将数据从多个缓冲区写入单个通道。

image.png

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);
    }
}

六、本周总结

本周没有学太多东西,主要是练习巩固。本周在做游戏设计课和数据挖掘课的课程大作业,感觉学习时间安排上出了点问题,下周要注意合理分配时间

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,723评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,080评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,604评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,440评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,431评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,499评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,893评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,541评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,751评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,547评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,619评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,320评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,890评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,896评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,137评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,796评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,335评论 2 342

推荐阅读更多精彩内容

  • Java NIO(New IO)是从Java 1.4版本开始引入的一个新的IO API,可以替代标准的Java I...
    JackChen1024阅读 7,530评论 1 143
  • Java NIO(New IO)是从Java 1.4版本开始引入的一个新的IO API,可以替代标准的Java I...
    zhisheng_blog阅读 1,111评论 0 7
  • 转自 http://www.ibm.com/developerworks/cn/education/java/j-...
    抓兔子的猫阅读 2,272评论 0 22
  • 原文链接 攻破JAVA NIO技术壁垒 现在使用NIO的场景越来越多,很多网上的技术框架或多或少的使用NIO技...
    阳光的技术小栈阅读 498评论 2 3
  • 春暖花开,收拾冰箱时,发现还有一些蚕卵。女儿执意要养,看着她恳切的眼神,我答应了。 五天后,蝼蚁般的蚕...
    zcmz阅读 534评论 0 1