目录
请求接口:
思路:
1、首先接收前端传过来的用户名和密码,判断值是否为空,为空则直接返回错误状态码
2、将密码用md5进行盐值加密,加密后生成新密码
3、用前端传过来的用户名和加密后的新密码去用户表查询用户,查不到则直接返回错误状态码
4、使用jwt工具类以用户的id为参数生成token,返回给前端页面
5、以用户生成的token为key,从用户表中查出的用户信息为value,存入redis中,设置一天的过期时间
说明:因为你登录成功后,仅仅是返回给前端的一个token,用户的头像信息个人信息前端都不知道,因此登录成功后接下来前端需要再发请求获取用户信息。
请求接口:
思路:
1、获取用户信息的请求头中有token,我们先接收拿到token
2、用jwt工具类中的方法检验token格式是否合法,不合法返回错误状态码
3、以token为key去redis中获取token对应的用户信息,获取不到则返回错误状态码
4、将从token中获取到的用户信息封装好返回给前端页面
好处:
将用户信息和token绑定存入redis中,减少了再一次查询数据库,提高了查询效率
请求接口:
思路:
1、退出请求的请求头中携带token,我们首先获取到这个token
2、然后将redis中这个token绑定的数据给删除掉即可完成退出
作用:博客上的有些资源,我们需要用户登录后才能访问,如果资源过多需要为每一个请求都加一个判断登录的方法有些代码重复,因此我们采用拦截器统一处理这些请求是否登录。
思路:
1、写一个继承HandlerInterceptor的拦截器类
2、在拦截器类中重写prehandler方法,如果不是controller方法返回true放行,如果是token为空、token格式不正确、token在redis中失效则返回false拦截,否则返回true放行
3、在WebMvcConfigurer中重写addIntercepters方法注册自定义的拦截器
作用:需要登录才能访问的资源请求,在通过登录拦截后,如果我们要使用登录的用户信息这个时候我们再从请求中去获取就显得有点麻烦,不如在通过登录拦截后,将用户的信息保存进ThreadLocal中,之后用到用户信息了再从ThreadLocal中获取用户信息。
思路:
1、定义ThreadLocal的工具类,实例化一个ThreadLocal,并创建它的的put、get、remove方法
2、在通过拦截器的拦截后将登录用户的信息存入ThreadLocal中
3、在所有方法执行完,线程结束前,将ThreadLocal中用完的信息手动删除
图解:
结构==》每一个Thread维护一个ThreadLocalMap,一个ThreadLocalMap中可以有多个Entry实体,而Entry实体的key是ThreadLocal实例,value是当前线程的存储信息。
泄漏==》因为实体Entry的key指向ThreadLocal实例是弱引用,在ThreadLocal实例没有引用的时候就会被垃圾回收掉,因此key的值就变成了null,实体Entry就只剩一个value,按理来说没人用的value应该也被垃圾回收掉,但是value所在的实体Entry在的ThreadLocalMap受当前线程维护,所以实体的Entry的生命周期跟当前线程一样长,如果不手动回收则此Entry实体会一直存在内存中,如果使用了线程池复用线程则会内存占用量越来越大,最终导致内存泄漏。
请求接口:
思路:
1、请求中携带本篇文章的id,我们可以通过id在文章表中查询出文章的相关信息,返回前端页面
2、查询完成后,以查询出的文章内容为已知条件,使用线程池将文章表中此文章的阅读次数+1
线程池更新阅读次数详情:
@EnableAsync+@Async实现共享线程池
- /**
- * @description: 配置线程池相关参数
- * @author: 李传城
- * @date: 2022-11-03 01:20
- */
-
- @Configuration
- @EnableAsync
- public class ThreadPoolConfig {
-
- @Bean("taskExecutor")
- public Executor asyncServiceExecutor() {
- ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
- // 设置核心线程数
- executor.setCorePoolSize(5);
- // 设置最大线程数
- executor.setMaxPoolSize(20);
- //配置队列大小
- executor.setQueueCapacity(Integer.MAX_VALUE);
- // 设置线程活跃时间(秒)
- executor.setKeepAliveSeconds(60);
- // 设置默认线程名称
- executor.setThreadNamePrefix("码神之路博客项目");
- // 等待所有任务结束后再关闭线程池
- executor.setWaitForTasksToCompleteOnShutdown(true);
- //执行初始化
- executor.initialize();
- return executor;
- }
- }
- /**
- * @description: 线程池更新阅读次数
- * @author: 李传城
- * @date: 2022-11-03 01:21
- */
-
- @Component
- public class ThreadService {
-
-
- //把服务配置的线程池名称写上,代表开启这个配置名称的线程池处理当前方法
- @Async("taskExecutor")
- public void updateViewCount(ArticleMapper articleMapper,Article article){
-
- Article articleUpdate = new Article();
- articleUpdate.setViewCounts(article.getViewCounts() + 1);
- LambdaQueryWrapper
queryWrapper = new LambdaQueryWrapper<>(); - queryWrapper.eq(Article::getId,article.getId());
- queryWrapper.eq(Article::getViewCounts,article.getViewCounts());
- articleMapper.update(articleUpdate,queryWrapper);
- try {
- //睡眠5秒 证明不会影响主线程的使用
- Thread.sleep(10);
- System.out.println("文章:"+article.getTitle()+"--阅读次数+1");
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- }