享元模式

定义
运用共享技术来有効地支持大量细粒度对象的复用。它通过共享已经存在的对象来大幅度减少需要创建的对象数量、避免大量相似类的开销,从而提高系统资源的利用率。

享元模式和单例的区别:
单例是对象只能自己创建自己,整个应用中只有1个对象 享元模式根据需要共享,不限制被谁创建(有可能有多个对象实例)

优点:
特定环境下,相同对象只要保存一份,这降低了系统中对象的数量,从而降低了系统中细粒度对象给内存带来的压力。‘

缺点:
为了使对象可以共享,需要将一些不能共享的状态外部化,这将增加程序的复杂性。

享元模式案例

案例:用户下单,回话共享

会话跟踪分析

会话跟踪,如果是传统项目用Session或者是Cookie,全项目通用,但在微服务项目中,不用Session也不用Cookie,所以想要在微服务项目中实现会话跟踪,是有一定难度的。
当前微服务项目中,身份识别的主流方法是前端将用户令牌存储到请求头中,每次请求将请求头中的令牌携带到后台,后台每次从请求头中获取令牌来识别用户身份。
我们在项目操作过程中,很多地方都会用到用户身份信息,比如下订单的时候,要知道当前订单属于哪个用户,记录下单关键日志的时候,需要记录用户操作的信息以及用户信息,关键日志记录我们一般用AOP进行拦截操作,此时没法直接把用户身份信息传给AOP。这个时候我们可以利用享元模式实现用户会话信息共享操作。操作流程如下图:

会话共享案例实现

基于上面的分析,我们采用享元模式实现用户会话共享操作,要解决如下几个问题:

  1. 用户会话共享
  2. 会话多线程安全
  3. 订单数据用户信息获取
  4. AOP日志记录用户信息获取

定义共享组件: LogComponent

LogComponent 里面定义了每个线程中不变的用户身份信息 username 、 role 、 sex ,其他的是可能存在变化的数据,例如用于做日志记录的 methodName 、 message 。

@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
public abstract class LogComponent { /********************************************************** * 同一个线程中,记录日志时,username、sex、role相同 **********************************************************/ //用户名字 private String username; //用户性别 private String sex; //用户角色 private String role; /********************************************************** * 同一个线程中,记录日志时,每次访问的不同方法和参数不一样 **********************************************************/ //操作方法 private String methodName; //信息 private String message; /********************************************************** * 业务操作,补充和完善methodName,args参数 **********************************************************/ abstract void supplementLogContent(String... args); /**** * 对username、sex、role赋值[这些是同一个线程中不变的数据] */ public LogComponent(String username, String sex, String role) { this.username = username; this.sex = sex; this.role = role; }
}

享元组件逻辑操作对象: SupplementSource

pplementSource该对象主要用于给当前线程填充共享数据,以及变更访问方法和访问信息等信息的逻辑操作,代码如下:

public class SupplementSource extends LogComponent{/**** * 填充参数 * @param username */ public SupplementSource(String username, String sex, String role) { super(username, sex, role); } /**** * 业务逻辑,完善不同方法的日志记录 * @param args 长度为2,第1个是方法名字,第2个是方日志信息 */ @Override public void supplementLogContent(String... args) { super.setMethodName(args[0]); super.setMessage(args[1]); }
}

多线程安全控制: ThreadUserLog

每个线程请求的时候,我们需要保障会话安全,比如A线程访问和B线程访问,他们的用户会话身份不能因为并发原因而发生混乱。这里我们可以采用ThreadLocal来实现。我们创建一个 ThreadUserLog 对象,并在该对象中创建ThreadLocal 用户存储每个线程的会话信息,并实现 ThreadLocal 的操作,代码如下:

@Component
public class ThreadUserLog { //存储线程对应的用户名日志信息 private static ThreadLocal<LogComponent> userRecode = new ThreadLocal<LogComponent>(); /**** * 添加用户信息记录 */ public void add(LogComponent logComponent){ userRecode.set(logComponent); } /*** * 记录方法名和参数 * @param args */ public String reload(String... args){ //获取对象LogComponent logComponent = userRecode.get(); //设置数据 logComponent.supplementLogContent(args); return logComponent.toString();}/**** * 获取LogComponent */ public LogComponent get(){ return userRecode.get(); } /**** * 移除 */ public void remove(){ userRecode.remove(); }
}

线程会话初始化:AuthorizationInterceptor

AuthorizationInterceptor拦截器的作用是用于初始化用户访问的时候用户的身份信息,并将身份信息存储到 ThreadUserLog的 ThreadLocal中,在用户访问方法结束,销毁 ThreadUserLog的 ThreadLocal中会话,代码如下:

public class AuthorizationInterceptor implements HandlerInterceptor {@Autowiredprivate ThreadUserLog threadUserLog;/***** 将用户会话存储到ThreadLocal中* @param request* @param response* @param handler* @return* @throws Exception*/@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {try {//获取令牌String authorization = request.getHeader("token");//解析令牌if(!StringUtils.isEmpty(authorization)){Map<String, Object> tokenMap = JwtTokenUtil.parseToken(authorization);//封装用户身份信息,存储到ThreadLocal中,供当前线程共享使用//1.封装需要共享的信息//2.创建一个对象继承封装信息,每次共享该对象 (不需要共享,则可以创建另外一个对象继承它)//3.创建共享管理对象,实现共享信息的增加、获取、移除功能LogComponent session = new LogComponent(tokenMap.get("username").toString(),tokenMap.get("name").toString(),tokenMap.get("sex").toString(),tokenMap.get("role").toString(),Integer.valueOf(tokenMap.get("level").toString()));sessionThreadLocal.add(session);return true;}} catch (Exception e) {e.printStackTrace();}//输出令牌校验失败response.setContentType("application/json;charset=utf-8");response.getWriter().print("身份校验失败!");response.getWriter().close();return false;}/*** 移除会话信息* @throws Exception*/@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {sessionThreadLocal.remove();}

共享信息使用:

①AOP记录日志:创建AOP切面类 LogAspect用于记录日志,代码如下:

@Component
@Aspect
@Slf4j
public class LogAspect {@Autowiredprivate ThreadUserLog sessionThreadLocal;/**** 记录日志*/@SneakyThrows@Before("execution(int com.test.shop.service.impl.*.*(..))")public void logRecode(JoinPoint joinPoint){//获取方法名字和参数String methodName = joinPoint.getTarget().getClass().getName()+"."+joinPoint.getSignature().getName();//记录日志log.info("用户【"+sessionThreadLocal.get().toString()+"】访问:"+methodName);}/***** 参数获取*/public String args(Object[] args){StringBuffer buffer = new StringBuffer();for (int i = 0; i <args.length ; i++) {buffer.append("  args("+i+"):"+args[i].toString());}return buffer.toString();}
}

②添加订单获取用户信息:在添加订单方法 OrderServiceImpl.add(Order order)中,从ThreadUserLog中获取 用户会话,并填充给Order,代码如下:

@Autowiredprivate ThreadUserLog sessionThreadLocal;@Overridepublic int add(Order order) {//通过享元模式共享获取线程中的对象order.setUsername(sessionThreadLocal.get().getUsername());//修改库存int mCount = itemService.modify(order.getNum(), order.getItemId());//添加订单int addCount = orderDao.add(order);return addCount;}

添加订单,日志输出可以看到调用添加订单和修改库存时,都记录了日志,并且获取了用户会话,效果如下:

LogComponent(username=zhaoliu, sex=男, role=ROLE_USER,
methodName=com.itheima.shop.service.impl.OrderServiceImpl.add, message= args(0):Order(itemId=1, id=1, money=9999, status=1, num=1, username=null))LogComponent(username=zhaoliu, sex=男, role=ROLE_USER,
methodName=com.itheima.shop.service.impl.ItemServiceImpl.modify, message=  args(0):1 args(1):1)

深入浅出设计模式---5、享元模式相关推荐

  1. 北风设计模式课程---享元模式

    北风设计模式课程---享元模式 一.总结 一句话总结: 不仅要通过视频学,还要看别的博客里面的介绍,搜讲解,搜作用,搜实例 1.享元模式的本质是什么? 池技术:各种缓存池都是享元模式的体现 说到享元模 ...

  2. 每天一个设计模式之享元模式

    作者按:<每天一个设计模式>旨在初步领会设计模式的精髓,目前采用javascript和python两种语言实现.诚然,每种设计模式都有多种实现方式,但此小册只记录最直截了当的实现方式 :) ...

  3. 北风设计模式课程---享元模式与单例模式区别

    北风设计模式课程---享元模式与单例模式区别 一.总结 一句话总结: 不仅要通过视频学,还要看别的博客里面的介绍,搜讲解,搜作用,搜实例 1.享元模式与单例模式区别? 级别:单例模式是类级别的,一个类 ...

  4. .net设计模式 (享元模式)学习笔记

    运用设计模式只是为了解决一类问题的,当解决掉当前一类问题,通常会在解决这个问题时候 带来其他问题  合理应用扬长避短 结构性设计模式:关注的是类与类之间的关系 .net设计模式 (享元模式)学习笔记 ...

  5. Java设计模式之享元模式(UML类图分析+代码详解)

    大家好,我是一名在算法之路上不断前进的小小程序猿!体会算法之美,领悟算法的智慧~ 希望各位博友走过路过可以给我点个免费的赞,你们的支持是我不断前进的动力!! 加油吧!未来可期!! 本文将介绍java设 ...

  6. 详解设计模式:享元模式

    享元模式(Flyweight Pattern),是对象池的一种体现,也是 GoF 的 23 种设计模式中的一种结构型设计模式. 享元模式 主要用于减少创建对象的数量,以减少内存占用和提高性能.它提供了 ...

  7. 设计模式之享元模式详解

    设计模式之享元模式详解 概述 享元模式定义: ​ 运用共享技术来有效地支持大量细粒度对象的复用.它==通过共享已经存在的对象来大幅度减少需要创建的对象数量==.避免大量相似对象的开销,从而提高系统资源 ...

  8. 【设计模式】享元模式(C#)

    [设计模式]享元模式 1.概述 Flyweight Design Pattern,结构型模式.享元模式中的"享元"指被共享的单元.享元模式通过复用对象,以达到节省内存的目的. 用于 ...

  9. Java设计模式之享元模式

    Java设计模式之享元模式 1. 享元模式概述 1.1 享元定义 1.2 享元模式注意事项 2. 享元模式实现 1. 享元模式概述 1.1 享元定义 1)享元模式(享元模式)也叫蝇量模式:利用共享技术 ...

  10. 设计模式之享元模式(Flyweight)摘录

    23种GOF设计模式一般分为三大类:创建型模式.结构型模式.行为模式. 创建型模式抽象了实例化过程,它们帮助一个系统独立于如何创建.组合和表示它的那些对象.一个类创建型模式使用继承改变被实例化的类,而 ...

最新文章

  1. mongodb java项目 源码_spring项目整合mongodb进行开发
  2. 送你200+篇论文,学习图或图神经网络必读!(附下载)
  3. 蚂蚁金服核心技术:百亿特征实时推荐算法揭秘
  4. python谁的课比较好-【年度系列】2018年学习Python最好的5门课程
  5. “双创指数”引行业拼抢,基民的“机会”又双叒叕来了?
  6. altium 去掉部分铺铜_干货|HFSS器件导入Altium进行PCB制作教程!!!
  7. 科普向--详解JavaScript中的数据类型
  8. requirejs插件-domReady插件
  9. 计算机组装维修diy,ITX装机教程实录:三千元ITX迷你电脑组装电脑全过程-DIY装机...
  10. 【SAP-PS笔记】基于WBS直接录入金额做结果分析的项目计划成本
  11. 如何使用stripe_使用Stripe和Laravel出售下载内容
  12. 基于PHP的招聘网站
  13. 洛谷1268树的重量(树)
  14. 「需求工程」需求工程-介绍(第1部分)
  15. sys.path.append方法
  16. 深度学习之数据集标注
  17. 一些经典的算法题目cpp
  18. HT单片机笔记1-时钟配置(2022/2/20)
  19. 关于AJAX的一些知识
  20. 【MySQL篇】MySQL下载位置

热门文章

  1. JavaScript高级程序设计学习总结一
  2. Win10下配置IIS并调试ASP程序
  3. matlab图像低通滤波,用于RGB图像的Matlab低通滤波器
  4. Python必会知识点详细笔记(B站黑马程序员) 上篇 面向过程
  5. 书单 | 专为程序员而写的数学书
  6. 安装vc6出现couldn't find acme setup的解决办法
  7. 华为s5700交换机使用配置
  8. Hadoop安装与配置详细教程
  9. mysql安装包及安装教程(附网盘地址)
  10. css如何调用函数,从CSS调用JavaScript函数