立山 发表于 2024-6-29 13:27:49

高并发项目-分布式Session解决方案

分布式Session解决方案

1.生存Session,进入商品列表页面

1.生存Session

1.编写工具类

1.MD5Util.java

package com.sxs.seckill.utils;

import org.apache.commons.codec.digest.DigestUtils;

/**
* Description: MD5加密工具类
*
* @Author sun
* @Create 2024/5/5 14:23
* @Version 1.0
*/
public class MD5Util {
    /**
   * 将一个字符串转换为MD5
   * @param src
   * @return
   */
    public static String md5(String src) {
      return DigestUtils.md5Hex(src);
    }

    // 固定的salt
    public static final String SALT = "4tIY5VcX";
    // 第一次加密加盐
    public static String inputPassToMidPass(String inputPass) {
      // 加盐
      String str = SALT.charAt(0) + inputPass + SALT.charAt(6);
      return md5(str);
    }

    /**
   * 第二次加密加盐
   * @param midPass
   * @param salt
   * @return
   */
    public static String midPassToDBPass(String midPass, String salt) {
      String str = salt.charAt(0) + midPass + salt.charAt(5);
      return md5(str);
    }

    /**
   * 两次加密
   * @param input
   * @param saltDB
   * @return
   */
    public static String inputPassToDBPass(String input, String saltDB) {
      String midPass = inputPassToMidPass(input);
      String dbPass = midPassToDBPass(midPass, saltDB);
      return dbPass;
    }
}

2.CookieUtil.java

package com.sxs.seckill.utils;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.net.URLEncoder;

/**
* Description: Cookie工具类
*
* @Author sun
* @Create 2024/5/6 16:04
* @Version 1.0
*/


public class CookieUtil {
    /**
   * 得到 Cookie 的值, 不编码
   *
   * @param request
   * @param cookieName
   * @return
   */
    public static String getCookieValue(HttpServletRequest request, String
            cookieName) {
      return getCookieValue(request, cookieName, false);
    }

    /**
   * 得到 Cookie 的值, *
   *
   * @param request
   * @param cookieName
   * @return
   */
    public static String getCookieValue(HttpServletRequest request, String
            cookieName, boolean isDecoder) {
      Cookie[] cookieList = request.getCookies();
      if (cookieList == null || cookieName == null) {
            return null;
      }
      String retValue = null;
      try {
            for (int i = 0; i < cookieList.length; i++) {
                if (cookieList.getName().equals(cookieName)) {
                  if (isDecoder) {
                        retValue = URLDecoder.decode(cookieList.getValue(), "UTF-8");
                  } else {
                        retValue = cookieList.getValue();
                  }
                  break;
                }
            }
      } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
      }
      return retValue;
    }

    /**
   * 得到 Cookie 的值, *
   *
   * @param request
   * @param cookieName
   * @param encodeString
   * @return
   */
    public static String getCookieValue(HttpServletRequest request, String
            cookieName, String encodeString) {
      Cookie[] cookieList = request.getCookies();
      if (cookieList == null || cookieName == null) {
            return null;
      }
      String retValue = null;
      try {
            for (int i = 0; i < cookieList.length; i++) {
                if (cookieList.getName().equals(cookieName)) {
                  retValue = URLDecoder.decode(cookieList.getValue(), encodeString);
                  break;
                }
            }
      } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
      }
      return retValue;
    }

    /**
   * 设置 Cookie 的值 不设置生效时间默认浏览器关闭即失效,也不编码
   */
    public static void setCookie(HttpServletRequest request, HttpServletResponse
            response, String cookieName, String cookieValue) {
      setCookie(request, response, cookieName, cookieValue, -1);
    }

    /**
   * 设置 Cookie 的值 在指定时间内生效,但不编码
   */
    public static void setCookie(HttpServletRequest request, HttpServletResponse
            response, String cookieName, String cookieValue, int cookieMaxage) {
      setCookie(request, response, cookieName, cookieValue, cookieMaxage,
                false);
    }

    /**
   * 设置 Cookie 的值 不设置生效时间,但编码
   */
    public static void setCookie(HttpServletRequest request, HttpServletResponse
            response, String cookieName, String cookieValue, boolean isEncode) {
      setCookie(request, response, cookieName, cookieValue, -1, isEncode);
    }

    /**
   * 设置 Cookie 的值 在指定时间内生效, 编码参数
   */
    public static void setCookie(HttpServletRequest request, HttpServletResponse
            response, String cookieName, String cookieValue, int cookieMaxage, boolean
                                       isEncode) {
      doSetCookie(request, response, cookieName, cookieValue, cookieMaxage,
                isEncode);
    }

    /**
   * 设置 Cookie 的值 在指定时间内生效, 编码参数(指定编码)
   */
    public static void setCookie(HttpServletRequest request, HttpServletResponse
            response, String cookieName, String cookieValue, int cookieMaxage, String
                                       encodeString) {
      doSetCookie(request, response, cookieName, cookieValue, cookieMaxage, encodeString);
    }

    /**
   * 删除 Cookie 带 cookie 域名
   */
    public static void deleteCookie(HttpServletRequest request, HttpServletResponse response, String cookieName) {
      doSetCookie(request, response, cookieName, "", -1, false);
    }

    /**
   * 设置 Cookie 的值,并使其在指定时间内生效
   *
   * @param cookieMaxage cookie 生效的最大秒数
   */
    private static final void doSetCookie(HttpServletRequest request, HttpServletResponse response, String cookieName, String cookieValue,
                                          int cookieMaxage, boolean isEncode) {
      try {
            if (cookieValue == null) {
                cookieValue = "";
            } else if (isEncode) {
                cookieValue = URLEncoder.encode(cookieValue, "utf-8");
            }
            Cookie cookie = new Cookie(cookieName, cookieValue);
            if (cookieMaxage > 0) {
                cookie.setMaxAge(cookieMaxage);
            }
            // if (null != request) {// 设置域名的 cookie
            // String domainName = getDomainName(request);
            // if (!"localhost".equals(domainName)) {
            // cookie.setDomain(domainName);
            // }
            // }
            cookie.setPath("/");
            response.addCookie(cookie);
      } catch (Exception e) {
            e.printStackTrace();
      }
    }

    /**
   * 设置 Cookie 的值,并使其在指定时间内生效
   *
   * @param cookieMaxage cookie 生效的最大秒数
   */
    private static final void doSetCookie(HttpServletRequest request,
                                          HttpServletResponse response, String cookieName, String cookieValue,
                                          int cookieMaxage, String encodeString) {
      try {
            if (cookieValue == null) {
                cookieValue = "";
            } else {
                cookieValue = URLEncoder.encode(cookieValue, encodeString);
            }
            Cookie cookie = new Cookie(cookieName, cookieValue);
            if (cookieMaxage > 0) {
                cookie.setMaxAge(cookieMaxage);
            }
            if (null != request) {// 设置域名的 cookie
                String domainName = getDomainName(request);
                System.out.println(domainName);
                if (!"localhost".equals(domainName)) {
                  cookie.setDomain(domainName);
                }
            }
            cookie.setPath("/");
            response.addCookie(cookie);
      } catch (Exception e) {
            e.printStackTrace();
      }
    }

    /**
   * 得到 cookie 的域名
   */
    private static final String getDomainName(HttpServletRequest request) {
      String domainName = null;
      // 通过 request 对象获取访问的 url 地址
      String serverName = request.getRequestURL().toString();
      if ("".equals(serverName)) {
            domainName = "";
      } else {
            // 将 url 地下转换为小写
            serverName = serverName.toLowerCase();
            // 如果 url 地址是以 http://开头 将 http://截取
            if (serverName.startsWith("http://")) {
                serverName = serverName.substring(7);
            }
            int end = serverName.length();
            // 判断 url 地址是否包含"/"
            if (serverName.contains("/")) {
                // 得到第一个"/"出现的位置
                end = serverName.indexOf("/");
            }
            // 截取
            serverName = serverName.substring(0, end);
            // 根据"."进行分割
            final String[] domains = serverName.split("\\.");
            int len = domains.length;
            if (len > 3) {
                // www.abc.com.cn
                domainName = domains + "." + domains + "." +
                        domains;
            } else if (len > 1) {
                // abc.com or abc.cn
                domainName = domains + "." + domains;
            } else {
                domainName = serverName;
            }
      }
      if (domainName.indexOf(":") > 0) {
            String[] ary = domainName.split("\\:");
            domainName = ary;
      }
      return domainName;
    }
}

2.关于session和cookie关系的回顾



[*]当欣赏器请求到服务端时cookie会携带sessionid
[*]然后在服务端getSession时会得到当前用户的session
[*]cookie-sessionid 连接到session
3.修改UserServiceImpl.java的doLogin方法,增长生存信息到session的逻辑

https://img-blog.csdnimg.cn/img_convert/fd1d0595f995b3f0fd055508b24f528d.png
4.测试,用户票据成功生存到cookie中

https://img-blog.csdnimg.cn/img_convert/470c89860699e35a1d8ec0c7dced230f.png
2.访问到商品列表页面

1.编写GoodsController.java 验证用户登录后进入商品列表页

package com.sxs.seckill.controller;

import com.sxs.seckill.pojo.User;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.CookieValue;
import org.springframework.web.bind.annotation.RequestMapping;

import javax.servlet.http.HttpSession;

/**
* Description:
*
* @Author sun
* @Create 2024/5/6 18:16
* @Version 1.0
*/
@Controller
@Slf4j
@RequestMapping("/goods")
public class GoodsController {
    // 进入到商品首页
    @RequestMapping("/toList")
    public String toList(HttpSession session, Model model, @CookieValue("userTicket") String ticket) {
      // 首先判断是否有票据
      if (null == ticket) {
            return "login";
      }
      // 根据票据来获取用户信息
      User user = (User) session.getAttribute(ticket);
      if (null == user) {
            return "login";
      }
      // 将用户信息存入model中,返回到前端
      model.addAttribute("user", user);
      return "goodsList";
    }
}

2.商品列表页goodsList.html

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>商品列表</title>
</head>
<body>
<h1>商品列表</h1>
<p th:text="'hi: ' + ${user.nickname}"></p>
</body>
</html>
3.测试登录成功后进入商品列表页

https://img-blog.csdnimg.cn/img_convert/d62ec67686611ec0f956963e0c6ddb71.png
https://img-blog.csdnimg.cn/img_convert/ac7891ca95183e9dcf33965f3d4a0283.png
2.分布式session解决方案

1.session绑定/粘滞(不常用)

https://img-blog.csdnimg.cn/img_convert/ec9465430397e61c5d63ec553bd60515.png
https://img-blog.csdnimg.cn/img_convert/a90c939d8f058a762826161da0be677e.png
2.session复制

https://img-blog.csdnimg.cn/img_convert/1e2ba68f7427b58521057cad6183cc6e.png
https://img-blog.csdnimg.cn/img_convert/05a0b7cce8d11fdf2be650a11487e140.png
3.前端存储

https://img-blog.csdnimg.cn/img_convert/15cfcdf0e07ff6e23d87fbf6c3d1df57.png
4.后端会合存储

https://img-blog.csdnimg.cn/img_convert/82c57c172aaf9ceb432971b6d0087fab.png
3.方案一:SpringSession实现分布式Session

1.安装使用redis-desktop-manager

1.不停下一步,安装到D盘

https://img-blog.csdnimg.cn/img_convert/82ca8051b48fd3d7b6e98b233c14fff5.png
2.起首要确保redis集群的端口是开放的并使其支持远程访问(之前配置过)

3.使用telnet指令测试某个服务是否能够连接成功

telnet 140.143.164.206 7489
https://img-blog.csdnimg.cn/img_convert/8ad69dec30c085d7539f65e58e4ed999.png
4.连接Redis,先测试连接然后确定

https://img-blog.csdnimg.cn/img_convert/78eb0eb3dfec2eacafc2b98c555963c8.png
5.在redis下令行设置两个键

https://img-blog.csdnimg.cn/img_convert/77fd34792be018d16309b564a1aa7d74.png
6.在可视化工具检察

https://img-blog.csdnimg.cn/img_convert/acc9ad6be6d0526ad6f6baf24361d7da.png
2.项目整合Redis并配置分布式session

1.pom.xml引入依赖

      <!--spring data redis 依赖, 即 spring 整合 redis-->
      <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
            <version>2.4.5</version>
      </dependency>
      <!--pool2 对象池依赖-->
      <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-pool2</artifactId>
            <version>2.9.0</version>
      </dependency>
      <!--实现分布式 session, 即将 Session 保存到指定的 Redis-->
      <dependency>
            <groupId>org.springframework.session</groupId>
            <artifactId>spring-session-data-redis</artifactId>
      </dependency>
2.application.yml配置Redis

spring:
redis:
    password:# Redis服务器密码
    database: 0 # 默认数据库为0号
    timeout: 10000ms # 连接超时时间是10000毫秒
    lettuce:
      pool:
      max-active: 8 # 最大活跃连接数,使用负值表示没有限制,最佳配置为核数*2
      max-wait: 10000ms # 最大等待时间,单位为毫秒,使用负值表示没有限制,这里设置为10秒
      max-idle: 200 # 最大空闲连接数
      min-idle: 5 # 最小空闲连接数
    cluster:
      nodes:
      -
      -
https://img-blog.csdnimg.cn/img_convert/7b58bf455883b4aad2f6bbec254fc729.png
3.启动测试

1.登录

https://img-blog.csdnimg.cn/img_convert/d23d95b9eec97ffdb985d37a3cb48aba.png
2.Redis可视化工具发现session成功存到redis

https://img-blog.csdnimg.cn/img_convert/f851cc8b2c191bb8751653ee2fc877e9.png
https://img-blog.csdnimg.cn/img_convert/58beb2d97acb66b45b201f8d4ebfc0d3.png
4.方案二:统一存放用户信息到Redis

1.修改pom.xml,去掉分布式springsession的依赖

https://img-blog.csdnimg.cn/img_convert/96bf7cc3e37e72908428d8c9b207f467.png
2.将用户信息放到Redis

1.添加Redis配置类 com/sxs/seckill/config/RedisConfig.java

package com.sxs.seckill.config;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

import java.time.Duration;

/**
* Description:
*
* @Author sun
* @Create 2024/4/29 21:29
* @Version 1.0
*/
@EnableCaching
@Configuration
public class RedisConfig extends CachingConfigurerSupport {
    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
      RedisTemplate<String, Object> template =
                new RedisTemplate<>();
      System.out.println("template=>" + template);
      RedisSerializer<String> redisSerializer =
                new StringRedisSerializer();
      Jackson2JsonRedisSerializer jackson2JsonRedisSerializer =
                new Jackson2JsonRedisSerializer(Object.class);
      ObjectMapper om = new ObjectMapper();
      om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
      om.activateDefaultTyping(
                LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.WRAPPER_ARRAY);
      jackson2JsonRedisSerializer.setObjectMapper(om);
      template.setConnectionFactory(factory);
      // key 序列化方式
      template.setKeySerializer(redisSerializer);
      // value 序列化
      template.setValueSerializer(jackson2JsonRedisSerializer);
      // value hashmap 序列化
      template.setHashValueSerializer(jackson2JsonRedisSerializer);
      return template;
    }

    @Bean
    public CacheManager cacheManager(RedisConnectionFactory factory) {
      RedisSerializer<String> redisSerializer =
                new StringRedisSerializer();
      Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new
                Jackson2JsonRedisSerializer(Object.class);
      // 解决查询缓存转换异常的问题
      ObjectMapper om = new ObjectMapper();
      om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
      om.activateDefaultTyping(
                LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.WRAPPER_ARRAY);
      jackson2JsonRedisSerializer.setObjectMapper(om);
      // 配置序列化(解决乱码的问题),过期时间 600 秒
      RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
                .entryTtl(Duration.ofSeconds(600))
                .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(redisSerializer))
                .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer))
                .disableCachingNullValues();
      RedisCacheManager cacheManager = RedisCacheManager.builder(factory)
                .cacheDefaults(config)
                .build();
      return cacheManager;
    }
}
2.修改 com/sxs/seckill/service/impl/UserServiceImpl.java

1.注入RedisTemplate

https://img-blog.csdnimg.cn/img_convert/6f523e9616ea456be5a3de4c0066851e.png
2.修改doLogin方法,将用户信息放到Redis中

https://img-blog.csdnimg.cn/img_convert/fa34a7c18510b2fb818b052bc0930898.png
3.启动测试

1.登录

https://img-blog.csdnimg.cn/img_convert/d9b27ebff61da7a1b537011334cf5b09.png
2.可视化工具检察用户信息

https://img-blog.csdnimg.cn/img_convert/6f43bae5c490ca9e9b0fff3a200f2840.png
3.实现使用Redis + Cookie实现登录,可以访问商品列表页面

1.刚才已经实现了Redis记录信息的功能,但是校验还没实现,修改GoodsController.java完成校验

1.注入RedisTemplate

https://img-blog.csdnimg.cn/img_convert/5109e098e94d214ef49d7b58c5bde148.png
2.从Redis中获取校验信息,举行校验

https://img-blog.csdnimg.cn/img_convert/39a99eaf3f881d2602bbbc7c2de0cb0e.png
2.测试

1.登录成功后访问商品列表页面

https://img-blog.csdnimg.cn/img_convert/30246cd68a0c01e5aff627b5b92a222e.png
5.扩展:自定义参数剖析器,直接获取User

1.修改 com/sxs/seckill/controller/GoodsController.java 使参数直接为User

    // 进入到商品首页
    @RequestMapping("/toList")
    public String toList(Model model, User user) {
      // 判断是否有用户信息
      if (null == user) {
            return "login";
      }
      // 将用户信息存入model中,返回到前端
      model.addAttribute("user", user);
      return "goodsList";
    }

2.service层添加方法,通过票据从Redis中获取User对象

1.UserService.java

    /**
   * 根据cookie获取用户
   * @param userTicket
   * @param request
   * @param response
   * @return
   */
    public User getUserByCookie(String userTicket, HttpServletRequest request, HttpServletResponse response);

2.UserServiceImpl.java



[*]这里需要留意,每次获取完User,需要重新设置Cookie,来革新Cookie的时间
[*]缘故原由是,调用这个的目标是为了校验,而用户访问每个页面都要举行校验,如果每次校验之后都不革新Cookie的时间,一旦Cookie失效了,用户就要重新登岸
    @Override
    public User getUserByCookie(String userTicket, HttpServletRequest request, HttpServletResponse response) {
      // 判断是否有票据
      if (null == userTicket) {
            return null;
      }
      // 根据票据来获取用户信息,从redis中获取
      User user = (User) redisTemplate.opsForValue().get("user:" + userTicket);
      if (null == user) {
            return null;
      }
      // 重新设置cookie的有效时间
      CookieUtil.setCookie(request, response, "userTicket", userTicket);
      return user;
    }

3.编写自定义参数剖析器对User范例参数举行剖析 config/UserArgumentResolver.java

package com.sxs.seckill.config;

import com.sxs.seckill.pojo.User;
import com.sxs.seckill.service.UserService;
import com.sxs.seckill.utils.CookieUtil;
import org.springframework.core.MethodParameter;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
* Description: 自定义参数解析器
*
* @Author sun
* @Create 2024/5/7 14:39
* @Version 1.0
*/
@Component
public class UserArgumentResolver implements HandlerMethodArgumentResolver {
    @Resource
    private UserService userService;
    /*
   * 判断是否支持要转换的参数类型,简单来说,就是在这里设置要解析的参数类型
   */
    @Override
    public boolean supportsParameter(MethodParameter methodParameter) {
      // 如果参数类型是User,则进行解析
      Class<?> parameterType = methodParameter.getParameterType();
      if (parameterType == User.class) {
            return true;
      }
      return false;
    }

    /**
   * 编写参数解析的逻辑
   *
   * @param methodParameter
   * @param modelAndViewContainer
   * @param nativeWebRequest
   * @param webDataBinderFactory
   * @return
   * @throws Exception
   */
    @Override
    public Object resolveArgument(MethodParameter methodParameter, ModelAndViewContainer modelAndViewContainer, NativeWebRequest nativeWebRequest, WebDataBinderFactory webDataBinderFactory) throws Exception {
      // 首先获取request和response
      HttpServletRequest request = nativeWebRequest.getNativeRequest(HttpServletRequest.class);
      HttpServletResponse response = nativeWebRequest.getNativeResponse(HttpServletResponse.class);
      // 从cookie中获取票据
      String userTicket = CookieUtil.getCookieValue(request, "userTicket");
      // 如果票据为空,则返回null
      if (null == userTicket) {
            return null;
      }
      // 如果票据不为空,则根据票据获取用户信息
      User user = this.userService.getUserByCookie(userTicket, request, response);
      return user;
    }
}

4.编写config/WebConfig.java 将自定义参数剖析器放到 resolvers 才气见效

package com.sxs.seckill.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import javax.annotation.Resource;
import java.util.List;

/**
* Description:
*
* @Author sun
* @Create 2024/5/7 14:53
* @Version 1.0
*/
@Configuration
// @EnableWebMvc 使用这个注解会导致SpringBoot的自动配置失效,一切都要自己配置,所以建议不要使用这个注解
public class WebConfig implements WebMvcConfigurer {
    // 注入自定义参数解析器
    @Resource
    private UserArgumentResolver userArgumentResolver;
    /**
   * 静态资源加载,静态资源放在哪里就怎么配置
   * @param registry
   */
    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
      registry.addResourceHandler("/**").addResourceLocations("classpath:/static/");
    }

    /**
   * 添加自定义参数解析器到resolvers中,才能生效
   * @param resolvers
   */
    @Override
    public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
      resolvers.add(userArgumentResolver);
    }
}

5.测试

1.在登录之后,可以正常访问商品列表页面

https://img-blog.csdnimg.cn/img_convert/408bc14ff6166b9b13cdb950d8ead43c.png

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
页: [1]
查看完整版本: 高并发项目-分布式Session解决方案