基于Springboot实现体质测试数据分析源码+论文展示
Posted 静香是个程序媛
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了基于Springboot实现体质测试数据分析源码+论文展示相关的知识,希望对你有一定的参考价值。
基于Springboot实现体质测试数据分析【源码+论文】
开发语言:Java
框架:springboot
JDK版本:JDK1.8
服务器:tomcat7
数据库:mysql 5.7
数据库工具:Navicat11
开发软件:eclipse/myeclipse/idea
Maven包:Maven3.3.9
摘要
随着科学技术的飞速发展,社会的方方面面、各行各业都在努力与现代的先进技术接轨,通过科技手段来提高自身的优势,体质测试数据分析及可视化设计当然也不能排除在外。体质测试数据分析及可视化设计是以实际运用为开发背景,运用软件工程原理和开发方法,采用springboot框架构建的一个管理系统。整个开发过程首先对软件系统进行需求分析,得出系统的主要功能。接着对系统进行总体设计和详细设计。总体设计主要包括系统功能设计、系统总体结构设计、系统数据结构设计和系统安全设计等;详细设计主要包括系统数据库访问的实现,主要功能模块的具体实现,模块实现关键代码等。最后对系统进行功能测试,并对测试结果进行分析总结,得出系统中存在的不足及需要改进的地方,为以后的系统维护提供了方便,同时也为今后开发类似系统提供了借鉴和帮助。这种个性化的网上体质测试数据分析及可视化设计特别注重交互协调与管理的相互配合,激发了管理人员的创造性与主动性,对体质测试数据分析及可视化设计而言非常有利。
本体质测试数据分析及可视化设计采用的数据库是Mysql,使用springboot框架开发。在设计过程中,充分保证了系统代码的良好可读性、实用性、易扩展性、通用性、便于后期维护、操作方便以及页面简洁等特点。
关键词:体质测试数据分析及可视化设计,springboot框架 Mysql数据库 Java技术
研究的内容
目前许多人仍将传统的纸质工具作为信息管理的主要工具,而网络技术的应用只是起到辅助作用。在对网络工具的认知程度上,较为传统的office软件等仍是人们使用的主要工具,而相对全面且专业的体质测试数据分析及可视化设计的信息管理软件仍没有得到大多数人的了解或认可。本选题则旨在通过标签分类管理等方式,实现管理员:管理员:首页、个人中心、学生管理、教师管理、日常运动管理、运动分析管理、成绩信息管理、论坛管理、系统管理,学生;首页、个人中心、日常运动管理、运动分析管理、成绩信息管理、论坛管理,教师;首页、个人中心、日常运动管理、运动分析管理、成绩信息管理、系统管理,前台首页;首页、论坛信息、公告信息、个人中心、后台管理、客服功能。体质测试数据分析及可视化设计信息管理功能,从而达到对体质测试数据分析及可视化设计信息的高效管理。
需求分析
体质测试数据分析及可视化设计主要是为了提高工作人员的工作效率和更方便快捷的满足学生,更好存储所有数据信息及快速方便的检索功能,对系统的各个模块是通过许多今天的发达系统做出合理的分析来确定考虑学生的可操作性,遵循开发的系统优化的原则,经过全面的调查和研究。
系统所要实现的功能分析,对于现在网络方便的管理,系统要实现学生可以直接在平台上进行查看所有数据信息,根据需求可以进行在线添加,删除或修改体质测试数据分析及可视化设计信息,这样既能节省时间,不用再像传统的方式耽误时间,真的很难去满足学生的各种需求。所以体质测试数据分析及可视化设计的开发不仅仅是能满足学生的需求,还能提高管理员的工作效率,减少原有不必要的工作量。
系统页面展示
如需要可扫取文章下方二维码联系得源码
Springboot基于Redisson实现Redis分布式可重入锁案例到源码分析
一、前言
我们在实现使用Redis实现分布式锁,最开始一般使用SET resource-name anystring NX EX max-lock-time
进行加锁,使用Lua脚本保证原子性进行实现释放锁。这样手动实现比较麻烦,对此Redis官网也明确说Java版使用Redisson
来实现。小编也是看了官网慢慢的摸索清楚,特写此记录一下。==从官网到整合Springboot到源码解读==,以==单节点为例==,==小编的理解都在注释里==,希望可以帮助到大家!!
二、为什么使用Redisson
1. 我们打开官网
2. 我们可以看到官方让我们去使用其他
3. 打开官方推荐
4. 找到文档
Redisson地址
5. Redisson结构
三、Springboot整合Redisson
1. 导入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
</dependency>
<!--redis分布式锁-->
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson</artifactId>
<version>3.12.0</version>
</dependency>
2. 以官网为例查看如何配置
3. 编写配置类
import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @author wangzhenjun
* @date 2022/2/9 9:57
*/
@Configuration
public class MyRedissonConfig
/**
* 所有对redisson的使用都是通过RedissonClient来操作的
* @return
*/
@Bean(destroyMethod="shutdown")
public RedissonClient redisson()
// 1. 创建配置
Config config = new Config();
// 一定要加redis://
config.useSingleServer().setAddress("redis://192.168.17.130:6379");
// 2. 根据config创建出redissonClient实例
RedissonClient redissonClient = Redisson.create(config);
return redissonClient;
4. 官网测试加锁例子
5. 根据官网简单Controller接口编写
@ResponseBody
@GetMapping("/hello")
public String hello()
// 1.获取一把锁,只要锁名字一样,就是同一把锁
RLock lock = redisson.getLock("my-lock");
// 2. 加锁
lock.lock();// 阻塞试等待 默认加的都是30s
// 带参数情况
// lock.lock(10, TimeUnit.SECONDS);// 10s自动解锁,自动解锁时间一定要大于业务的执行时间。
try
System.out.println("加锁成功" + Thread.currentThread().getId());
Thread.sleep(30000);
catch (InterruptedException e)
e.printStackTrace();
finally
// 3. 解锁
System.out.println("解锁成功:" + Thread.currentThread().getId());
lock.unlock();
return "hello";
6. 测试
四、lock.lock()源码分析
1. 打开RedissonLock
实现类
2. 找到实现方法
@Override
public void lock()
try
// 我们发现不穿过期时间源码默认过期时间为-1
lock(-1, null, false);
catch (InterruptedException e)
throw new IllegalStateException();
3. 按住Ctrl进去lock方法
private void lock(long leaseTime, TimeUnit unit, boolean interruptibly) throws InterruptedException
// 获取线程的id,占有锁的时候field的值为UUID:线程号id
long threadId = Thread.currentThread().getId();
// 尝试获得锁
Long ttl = tryAcquire(leaseTime, unit, threadId);
// lock acquired 获得锁,返回
if (ttl == null)
return;
// 这里说明获取锁失败,就通过线程id订阅这个锁
RFuture<RedissonLockEntry> future = subscribe(threadId);
if (interruptibly)
commandExecutor.syncSubscriptionInterrupted(future);
else
commandExecutor.syncSubscription(future);
try
// 这里进行自旋,不断尝试获取锁
while (true)
// 继续尝试获取锁
ttl = tryAcquire(leaseTime, unit, threadId);
// lock acquired 获取成功
if (ttl == null)
// 直接返回,挑出自旋
break;
// waiting for message 继续等待获得锁
if (ttl >= 0)
try
future.getNow().getLatch().tryAcquire(ttl, TimeUnit.MILLISECONDS);
catch (InterruptedException e)
if (interruptibly)
throw e;
future.getNow().getLatch().tryAcquire(ttl, TimeUnit.MILLISECONDS);
else
if (interruptibly)
future.getNow().getLatch().acquire();
else
future.getNow().getLatch().acquireUninterruptibly();
finally
// 取消订阅
unsubscribe(future, threadId);
// get(lockAsync(leaseTime, unit));
4. 进去尝试获取锁方法
private Long tryAcquire(long leaseTime, TimeUnit unit, long threadId)
// 直接进入异步方法
return get(tryAcquireAsync(leaseTime, unit, threadId));
private <T> RFuture<Long> tryAcquireAsync(long leaseTime, TimeUnit unit, long threadId)
// 这里进行判断如果没有设置参数leaseTime = -1
if (leaseTime != -1)
return tryLockInnerAsync(leaseTime, unit, threadId, RedisCommands.EVAL_LONG);
// 此方法进行获得锁,过期时间为看门狗的默认时间
// private long lockWatchdogTimeout = 30 * 1000;看门狗默认过期时间为30s
// 加锁和过期时间要保证原子性,这个方法后面肯定调用执行了Lua脚本,我们下面在看
RFuture<Long> ttlRemainingFuture = tryLockInnerAsync(commandExecutor.getConnectionManager().getCfg().getLockWatchdogTimeout(), TimeUnit.MILLISECONDS, threadId, RedisCommands.EVAL_LONG);
// 开启一个定时任务进行不断刷新过期时间
ttlRemainingFuture.onComplete((ttlRemaining, e) ->
if (e != null)
return;
// lock acquired 获得锁
if (ttlRemaining == null)
// 刷新过期时间方法,我们下一步详细说一下
scheduleExpirationRenewal(threadId);
);
return ttlRemainingFuture;
5. 查看tryLockInnerAsync()
方法
<T> RFuture<T> tryLockInnerAsync(long leaseTime, TimeUnit unit, long threadId, RedisStrictCommand<T> command)
internalLockLeaseTime = unit.toMillis(leaseTime);
return commandExecutor.evalWriteAsync(getName(), LongCodec.INSTANCE, command,
// 首先判断锁是否存在
"if (redis.call(exists, KEYS[1]) == 0) then " +
// 存在则获取锁
"redis.call(hset, KEYS[1], ARGV[2], 1); " +
// 然后设置过期时间
"redis.call(pexpire, KEYS[1], ARGV[1]); " +
"return nil; " +
"end; " +
// hexists查看哈希表的指定字段是否存在,存在锁并且是当前线程持有锁
"if (redis.call(hexists, KEYS[1], ARGV[2]) == 1) then " +
// hincrby自增一
"redis.call(hincrby, KEYS[1], ARGV[2], 1); " +
// 锁的值大于1,说明是可重入锁,重置过期时间
"redis.call(pexpire, KEYS[1], ARGV[1]); " +
"return nil; " +
"end; " +
// 锁已存在,且不是本线程,则返回过期时间ttl
"return redis.call(pttl, KEYS[1]);",
Collections.<Object>singletonList(getName()), internalLockLeaseTime, getLockName(threadId));
6. 进入4留下的定时任务scheduleExpirationRenewal()
方法
一步步往下找源码:==scheduleExpirationRenewal --->renewExpiration==
根据下面源码,定时任务刷新时间为:internalLockLeaseTime / 3,是看门狗的1/3,即为10s刷新一次
private void renewExpiration()
ExpirationEntry ee = EXPIRATION_RENEWAL_MAP.get(getEntryName());
if (ee == null)
return;
Timeout task = commandExecutor.getConnectionManager().newTimeout(new TimerTask()
@Override
public void run(Timeout timeout) throws Exception
ExpirationEntry ent = EXPIRATION_RENEWAL_MAP.get(getEntryName());
if (ent == null)
return;
Long threadId = ent.getFirstThreadId();
if (threadId == null)
return;
RFuture<Boolean> future = renewExpirationAsync(threadId);
future.onComplete((res, e) ->
if (e != null)
log.error("Cant update lock " + getName() + " expiration", e);
return;
if (res)
// reschedule itself
renewExpiration();
);
, internalLockLeaseTime / 3, TimeUnit.MILLISECONDS);
ee.setTimeout(task);
五、lock.lock(10, TimeUnit.SECONDS)源码分析
1. 打开实现类
@Override
public void lock(long leaseTime, TimeUnit unit)
try
// 这里的过期时间为我们输入的10
lock(leaseTime, unit, false);
catch (InterruptedException e)
throw new IllegalStateException();
2. 方法lock()
实现展示,同三.3源码
3. 直接来到尝试获得锁tryAcquireAsync()
方法
private <T> RFuture<Long> tryAcquireAsync(long leaseTime, TimeUnit unit, long threadId)
// 这里进行判断如果没有设置参数leaseTime = -1,此时我们为10
if (leaseTime != -1)
// 来到此方法
return tryLockInnerAsync(leaseTime, unit, threadId, RedisCommands.EVAL_LONG);
// 此处省略后面内容,前面以详细说明。。。。
4. 打开tryLockInnerAsync()
方法
我们不难发现和没有传过期时间的方法一样,只不过leaseTime的值变了。
<T> RFuture<T> tryLockInnerAsync(long leaseTime, TimeUnit unit, long threadId, RedisStrictCommand<T> command)
internalLockLeaseTime = unit.toMillis(leaseTime);
return commandExecutor.evalWriteAsync(getName(), LongCodec.INSTANCE, command,
// 首先判断锁是否存在
"if (redis.call(exists, KEYS[1]) == 0) then " +
// 存在则获取锁
"redis.call(hset, KEYS[1], ARGV[2], 1); " +
// 然后设置过期时间
"redis.call(pexpire, KEYS[1], ARGV[1]); " +
"return nil; " +
"end; " +
// hexists查看哈希表的指定字段是否存在,存在锁并且是当前线程持有锁
"if (redis.call(hexists, KEYS[1], ARGV[2]) == 1) then " +
// hincrby自增一
"redis.call(hincrby, KEYS[1], ARGV[2], 1); " +
// 锁的值大于1,说明是可重入锁,重置过期时间
"redis.call(pexpire, KEYS[1], ARGV[1]); " +
"return nil; " +
"end; " +
// 锁已存在,且不是本线程,则返回过期时间ttl
"return redis.call(pttl, KEYS[1]);",
Collections.<Object>singletonList(getName()), internalLockLeaseTime, getLockName(threadId));
六、lock.unlock()源码分析
1. 打开方法实现
@Override
public void unlock()
try
// 点击进入释放锁方法
get(unlockAsync(Thread.currentThread().getId()));
catch (RedisException e)
if (e.getCause() instanceof IllegalMonitorStateException)
throw (IllegalMonitorStateException) e.getCause();
else
throw e;
// Future<Void> future = unlockAsync();
// future.awaitUninterruptibly();
// if (future.isSuccess())
// return;
//
// if (future.cause() instanceof IllegalMonitorStateException)
// throw (IllegalMonitorStateException)future.cause();
//
// throw commandExecutor.convertException(future);
2. 打开unlockAsync()
方法
@Override
public RFuture<Void> unlockAsync(long threadId)
RPromise<Void> result = new RedissonPromise<Void>();
// 解锁方法,后面展开说
RFuture<Boolean> future = unlockInnerAsync(threadId);
// 完成
future.onComplete((opStatus, e) ->
if (e != null)
// 取消到期续订
cancelExpirationRenewal(threadId);
// 将这个未来标记为失败并通知所有人
result.tryFailure(e);
return;
// 状态为空,说明解锁的线程和当前锁不是同一个线程
if (opStatus == null)
IllegalMonitorStateException cause = new IllegalMonitorStateException("attempt to unlock lock, not locked by current thread by node id: "
+ id + " thread-id: " + threadId);
result.tryFailure(cause);
return;
cancelExpirationRenewal(threadId);
result.trySuccess(null);
);
return result;
3. 打开unlockInnerAsync()
方法
protected RFuture<Boolean> unlockInnerAsync(long threadId)
return commandExecutor.evalWriteAsync(getName(), LongCodec.INSTANCE, RedisCommands.EVAL_BOOLEAN,
// 判断释放锁的线程和已存在锁的线程是不是同一个线程,不是返回空
"if (redis.call(hexists, KEYS[1], ARGV[3]) == 0) then " +
"return nil;" +
"end; " +
// 释放锁后,加锁次数减一
"local counter = redis.call(hincrby, KEYS[1], ARGV[3], -1); " +
// 判断剩余数量是否大于0
"if (counter > 0) then " +
// 大于0 ,则刷新过期时间
"redis.call(pexpire, KEYS[1], ARGV[2]); " +
"return 0; " +
"else " +
// 释放锁,删除key并发布锁释放的消息
"redis.call(del, KEYS[1]); " +
"redis.call(publish, KEYS[2], ARGV[1]); " +
"return 1; "+
"end; " +
"return nil;",
Arrays.<Object>asList(getName(), getChannelName()), LockPubSub.UNLOCK_MESSAGE, internalLockLeaseTime, getLockName(threadId));
七、总结
这样大家就跟着小编走完了一遍底层源码,是不是感觉自己又行了,哈哈哈。小编走下来一遍觉得收货还是蛮大的,以前不敢点进去源码,进去就懵逼了,所以人要大胆的向前迈出第一步。一起加油吧,看到这里不一键三连,有点对不起小编了哦!!
顺便推广一下自己的网站!!!
以上是关于基于Springboot实现体质测试数据分析源码+论文展示的主要内容,如果未能解决你的问题,请参考以下文章
基于springboot在线学习平台设计与实现.zip(项目源码)
基于springboot在线购物书城商城设计与实现源码.rar(含源码及数据库文件)
Springboot基于Redisson实现Redis分布式可重入锁案例到源码分析