目录


文章目录

  • 目录
  • 购物车
  • ObjectMapper对象和JSON转换工具类
  • 判断用户是否登录
  • 拦截器(AOP)
  • 在web服务器config配置拦截器拦截策略
  • UserInterceptor自定义拦截器
  • spring整合redis集群
  • 添加redis.properties配置文件
  • 在common工具jar的config配置redis
  • 购物车DubboCartServiceImpl
  • redis.properties添加card

  • redis配置
  • 数据结构选择
  • 用hash类型
  • 单点登陆
  • sso服务器添加DubboUserServiceImpl


购物车

ObjectMapper对象和JSON转换工具类

public class JSONUtils {
    private static final ObjectMapper MAPPER = new ObjectMapper();
    //将对象转化为JSON
    public static String toJSON(Object target){
        try {
           return MAPPER.writeValueAsString(target);
        } catch (JsonProcessingException e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }
    }
    //将JSON转化为对象
    public static <T> T toObject(String json,Class<T> targetClass){
        try {
            return MAPPER.readValue(json, targetClass);
        } catch (JsonProcessingException e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }
    }
}

判断用户是否登录

拦截器(AOP)

在web服务器config配置拦截器拦截策略
@Configuration //标识一个配置类
public class MvcConfigurer implements WebMvcConfigurer{
    //注入拦截器
    @Autowired
    private UserInterceptor userInterceptor;
    //配置拦截器拦截策略
    public void addInterceptors(InterceptorRegistry registry){
        registry.addInterceptor(userInterceptor)
            //拦截cart、order的所有请求
            .addPathPatterns("/cart/**","/order/**");
    }
}
UserInterceptor自定义拦截器

自定义类实现HandlerInterceptor接口,重写放法:

preHandle

  • 调用时间:Controller方法处理之前

postHandle

  • 调用前提:preHandle返回true
  • Controller方法处理完之后,DispatcherServlet进行视图的渲染之前,也就是说在这个方法中你可以对ModelAndView进行操作

afterCompletion

  • 调用前提:preHandle返回true
  • DispatcherServlet进行视图的渲染之后,多用于清理资源
@Component //表示将此类标记为Spring容器中的一个Bean
public class UserInterceptor implements HandlerInterceptor{
    @autowired
    private JedisCluster jedis;//注入redis集群对象
    
    //preHandle 处理之前调用
    @Override
    public boolean preHandle(HttpServletRequest request,HttpServletResponse response,Object handler) throws Exception{
        String ticket = null; //cookie存放的令牌
        //判断cookie中是否有记录
        Cookie[] cookies = request.getCookies();
        if(cookies!=null&&cookies.length>0){
            for(Cookie cookie:cookies){
                if("JH_TICKET".equals(cookie.getName())){
                    //有令牌直接取出value(存放的是uuid,单点登陆放到redis的key(value是userJSON))
                    ticket = cookie.getValue();
                    break;
                }
            }
        }
        //判断ticket是否有效
        if(!StringUtils.isEmpty(ticket)){
            if(jedis.exists(ticket)){
                //去除redis中以ticket为key的value
                String userJSON = jedis.get(ticket);
                User user = ObjectMapperUtil.toObject(userJSON,User.class);
                //存到ThreadLocal对象(同线程变量)
                UserThreadLocal.set(user);
                return true; //去执行controller
            }
        }
        //未登陆,重定向到登陆页面
        response.sendRedirect("/userLogin.thml");
        return false;	//不执行controller
    }
    
    @Override
    public void afterCompletion(HttpServletRequest request,HttpServletResponse response,Object handler,Exception ex) throws Exception{
        //为了防止内存泄露,将多余的数据删除
        //request.removeAttribute("JH_USER");
        UserThreadLocal.remove();
    }
}

spring整合redis集群

添加redis.properties配置文件
redis.nodes=192.168.126.129:7000,192.168.126.129:7001,192.168.126.129:7002...
在common工具jar的config配置redis
@configuration //标识一个配置类
//加载redis配置文件
@PropertySource("classpath:/properties/redis.properties")
public class RedisConfig{
    //获取redis配置文件
    @Value("${redis.nodes}")
    private String nodes;
    
    //在 getJedisCluster() 方法上添加 @Bean 注解则会往 Spring 容器中添加一个名为 JedisCluster 的 Bean,该 Bean 即为方法的返回值。
    @Bean
    public JedisCluster getJedisCluster(){
        //set集合保存redis集群的每个节点(HostAndPort用于创建集群对象)
        Set<HostAndPort> nodeSet = new HashSet<>();
        //切割redis.nodes得到redis节点array
        String[] nodeArray = nodes.split(",");
        for(String node:nodeArray){
            String host = node.split(":")[0];//
            int port = Integer.parseInt(node.split(":")[1]);
            nodeSet.add(new HostAndPort(host,port));
        }
        //返回一个JedisCluster交给spring容器管理
        return new JedisCluster(nodeSet);
    }
}

购物车DubboCartServiceImpl

redis.properties添加card
redis.nodes=192.168.126.129:7000,192.168.126.129:7001,192.168.126.129:7002...
redis.card=card
//dubbo 的service(调用服务超时时间)
@Service(timeout = 3000)
public class DubboCartServiceImpl implements DubboCartService{
    @Autowired	//注入redis集群
    private JedisCluster jedis;
    @Reference(check = false)	//注入itemService
    private DubboItemService dubboItemService;
    @Value("${redis.card}")
    private String CART_KEY;
    //添加购物车
    @Override	
    public SysResult addCart(Long userId,Long itemId,int num){
        //判断购物车里是否存在此商品
        Boolean hexists = jedis.hexists(CART_KEY":"+userId,itemId+"");
        if(hexists){
            //如果存在,取出redis里的Cart JSON信息
            String json = jedis.hget(CART_KEY":"+userId,itemId+"");
            Cart cart = JSONUtils.toPojo(json,Cart.class);
            cart.setNum(cart.getNum()+num); //数量累加
            //写回redis
            jedis.hset(CART_KEY":"+userId,itemId+"",JSONUtils.toJSON(cart));
            //给用户返回结果
            return SysResult.success();
        }
        //如果不存在,根据商品id获取商品信息
        Item item = null;
        //从redis中获取
        if(jedis.exists(itemId)){
            String itemJSON = jedis.get(itemId);
            item = JSONUtils.toObject(itemJSON,Item.class);
        }
        //从数据库直接获取
        item = dubboItemService.selectItemByItemId(itemId);
        if(item!=null)
        	return SysResult.fail();
        //把商品信息存入redis购物车
        Cart cart = new Cart();
        cart.setItem(item).setNum(num);
        jedis.hset(CART_KEY+":"+userId,itemId,JSONUtils.toJSON(cart));
        return SysResult.success();
    }
    
}

redis配置

使用redis缓存存放购物车信息.

注意:

redis是存在内存中的,默认RDB持久化策略(默认配置60s内有10000个key发生变化刷新一次镜像).为了防止数据丢失应手动开启AOF持久化策略(配置appendonly yes)

数据结构选择

常用5种数据类型:string,hash,list,set,sorted set

用hash类型

命令

作用

hset key_name field_name value

添加(field不存在创建,field存在覆盖value)

hget key_name field_name

获取(存在返回field,不存在返回nil)

hexists key_name field_name

判断key中指定field是否存在(存在1,不存在0)

hlen key_name

获取哈希表中字段的数量

单点登陆

sso服务器添加DubboUserServiceImpl

@Service(timeout = 3000)
public class DubboUserServiceImpl implements DubboUserService{
    @Autowired	//注入userMapper
    private UserMapper userMapper;
    @Autowired	//注入redis集群
    private JedisCluster jedis;
    
    @Override
    public String doLogin(User user){
        //根据userName获取数据库中的用户对象
        User userDB = userMapper.selectByKey(user.getUserName());
        //没有直接返回
        if(userDB==null)
            return null;
        //用户输入的密码md5摘要(用于判断密码是否正确)
        String md5Pass = MD5Utils.md5Digest(user.getPassword());
		//判断密码是否正确
        if(MD5Utils.doSal(userDB.getPassword())!=md5Pass)
            return null;
        
        //登陆成功user JSON 存如redis(key=uuid,7天有效期,value=userJSON)
        String uuid = UUID.randomUUID().toString().replace("-","");
        userDB.setPassword(""); //去除重要信息
        jedis.setex(uuid,7*24*60*60,JSONUtils.toJSON(userDB));
    }
}