设计模式七:责任链模式

文章目录

      • 1、责任链模式
      • 2、spring中的责任链模式
        • Spring Interceptor
        • Servlet Filter
        • Netty

1、责任链模式

责任链模式为请求创建了一个接收者对象的链,在这种模式下,通常每个节点都包含对另一个节点者的引用。每个节点针对请求,处理自己感兴趣的内容,处理完之后可以结束,也可以向下一个节点传递继续处理;

角色

  1. 抽象处理者角色:处理请求的抽象类,定义了处理请求的抽象方法;(抽象类或接口实现);
  2. 具体处理者角色:处理请求的具体实现类;(持有下家对象的引用);

例:请假流程都是先由本部门审核,根据时间长短再进行下一级审批

在这里插入图片描述

//抽象类
public abstract class Handler {
    /**
     * 请假天数
     */
    public int maxday;
    /**
     * 请假人
     */
    public String name;

    public Handler(String name, int maxday) {
        this.maxday = maxday;
        this.name = name;
    }

    private Handler nextHandler;

    public void setNextHandler(Handler nextHandler) {
        this.nextHandler = nextHandler;
    }

    /**
     * 处理请假:判断请假天数,超过本部门限定时间则交由上一级部门
     */

    public final void handlingFakeSlips(int day) {
        if (this.maxday >= day) {
            this.agree(day);
        }
        else {
            if (nextHandler != null) {
                System.out.println(name+":天数已超过我的审批权限,已提交我的上级审批");
                nextHandler.handlingFakeSlips(day);
            }
            else {
                System.out.println("天数时间过长,准备辞职吧!!!");
            }
        }
    }

    /**
     * 审批动作:子类来实现
     * @param day
     */
    abstract void agree(int day);
}

//部门实现类
public class RDDepartment extends Handler{
    public RDDepartment(String name, int maxday) {
        super(name, maxday);
    }
    @Override
    void agree(int maxday) {
        System.out.println(name + ":研发部门请假审批通过,请假天数:" + maxday);
    }
}

//主管实现类
public class Supervisor extends Handler{
    public Supervisor(String name, int maxday) {
        super(name, maxday);
    }
    @Override
    void agree(int maxday) {
        System.out.println(name + ":主管请假审批通过,请假天数:" + maxday);
    }
}

//董事实现类
public class Director extends Handler{
    public Director(String name, int maxday) {
        super(name, maxday);
    }
    @Override
    void agree(int maxday) {
        System.out.println(name + ":请假董事审批通过,请假天数:" + maxday);
    }
}

//组装链
public class HandlerChain {
    private Handler head;
    private Handler tail;
    public HandlerContext(){
        RDDepartment rdDepartment = new RDDepartment("研发部门",5);
        Supervisor supervisor = new Supervisor("项目主管",30);
        Director director = new Director("董事",180);

        rdDepartment.setNextHandler(supervisor);
        supervisor.setNextHandler(director);

        head = rdDepartment;
        tail = director;
    }
    public void doHandler(Integer days){
        head.handlingFakeSlips(days);
    }
}


//请求
public class Request {
    public static void main(String[] args) {
        HandlerChain handlerChain = new HandlerChain();
        handlerChain.doHandler(360);
    }
}

优点(when,why):

​ 1.发送者与接收者之间的耦合度降低(解耦)

​ 2.可以灵活添加新的责任链中的对象

缺点:

​ 1.不能保证请求一定被接收

​ 2.一定程度上影响性能

这种形式很难进行动态新增和调整处理节点,一种比较复杂的控制节点的形式如Netty中的责任链模式应用,见下一节

2、spring中的责任链模式

Spring Interceptor

回顾springmvc处理请求的流程:DispatcherServlet接收到请求后,执行doDispatcher()方法,流程回顾请求处理流程图

其中通过HandlerMapping获得的是HandlerExecutionChain 对象

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
    //......
    HandlerExecutionChain mappedHandler = null;
	//......
    mappedHandler = getHandler(processedRequest);
    //......     
    HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
	//......
}

protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
    if (this.handlerMappings != null) {
        for (HandlerMapping mapping : this.handlerMappings) {
            HandlerExecutionChain handler = mapping.getHandler(request);
            if (handler != null) {
                return handler;
            }
        }
    }
    return null;
}

protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
    if (this.handlerAdapters != null) {
        for (HandlerAdapter adapter : this.handlerAdapters) {
            if (adapter.supports(handler)) {
                return adapter;
            }
        }
    }
    throw new ServletException("No adapter for handler [" + handler +
                               "]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
}

HandlerExecutionChain 中包含一个handler对象(后面匹配能处理handler的适配器对象执行,详情对应适配器模式中讲解),还有一个拦截器列表List<HandlerInterceptor> interceptorList,所有的实现了HandlerInterceptor接口的类都会被加载进这个集合中,在请求处理前后分别以责任链的形式调用拦截器的preHandlepostHandle

boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
    HandlerInterceptor[] interceptors = getInterceptors();
    if (!ObjectUtils.isEmpty(interceptors)) {
        for (int i = 0; i < interceptors.length; i++) {
            HandlerInterceptor interceptor = interceptors[i];
            if (!interceptor.preHandle(request, response, this.handler)) {
                triggerAfterCompletion(request, response, null);
                return false;
            }
            this.interceptorIndex = i;
        }
    }
    return true;
}

void applyPostHandle(HttpServletRequest request, HttpServletResponse response, @Nullable ModelAndView mv)
    throws Exception {

    HandlerInterceptor[] interceptors = getInterceptors();
    if (!ObjectUtils.isEmpty(interceptors)) {
        for (int i = interceptors.length - 1; i >= 0; i--) {
            HandlerInterceptor interceptor = interceptors[i];
            interceptor.postHandle(request, response, this.handler, mv);
        }
    }
}

这里的链是由集合List维护,使用List有序的特性一次调用每个拦截器,通过方法返回的结果判断是否需要传递到下一个拦截器

Servlet Filter

servletFilter的调用也是通过责任链模式,通过FilterChain作为链条的管理者

//FilterChain接口
public interface FilterChain {
    public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException;
}

//FilterChain实现类
public class MockFilterChain implements FilterChain {
	
    //......
	private final List<Filter> filters;
	//......
    
	@Override
	public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException {
		Assert.notNull(request, "Request must not be null");
		Assert.notNull(response, "Response must not be null");
		Assert.state(this.request == null, "This FilterChain has already been called!");

		if (this.iterator == null) {
			this.iterator = this.filters.iterator();
		}

		if (this.iterator.hasNext()) {
			Filter nextFilter = this.iterator.next();
			nextFilter.doFilter(request, response, this);
		}

		this.request = request;
		this.response = response;
	}
}

由上可知,FilterChain中管理的 List<Filter> filters即为所有实现了Filter的过滤器,调用过滤器的时候,通过FilterChain进行链条的调用。


//Filter接口
public interface Filter {

    default public void init(FilterConfig filterConfig) throws ServletException {}

    public void doFilter(ServletRequest request, ServletResponse response,
                         FilterChain chain)
            throws IOException, ServletException;

    default public void destroy() {}
}

//Filter实现类
public class AuthFilter extends AuthenticationFilter {
   //......
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest httpRequest = toLowerCase((HttpServletRequest)request);
        String tokenString = httpRequest.getParameter("delegation");
        if (tokenString != null) {
            filterChain.doFilter(httpRequest, response);
        } else {
            super.doFilter(httpRequest, response, filterChain);
        }
    }
    //......
}

FilterdoFilter方法最后调用filterChain.doFilter(httpRequest, response);即传递至下一个Filter进行处理

Netty

Netty中的handler使用了责任链模式,但是其中回调过多,责任链模式的体现不清晰,参考该文章**Spring中如何使用责任链模式**,将责任链抽离出来,完成在spring中的调用


在这里插入图片描述

该模型中,具有多条链,每条链属于不同层级,链中节点为HandlerContextHandlerContext包含相邻接点的引用,还有Handler的引用

Pipeline:为链条的管理者,通过Pipeline来调用责任链

HandlerContext:为链条中节点的上下文,它里面有链条的前一个节点和后一个节点的HandlerContext引用

Handler: 具体的处理程序,与HandlerContext一一对应


我们先仅看Filter事件这一条链,整个结构由Pipeline管理整条链

在这里插入图片描述

//Pipelie接口
public interface Pipeline {
    Pipeline fireTaskFiltered();
}

//Pipeline实现类
Component("pipeline")
@Scope("prototype")
public class DefaultPipeline implements Pipeline, ApplicationContextAware, InitializingBean {
    // 创建一个默认的handler,将其注入到首尾两个节点的HandlerContext中,其作用只是将链往下传递
    private static final Handler DEFAULT_HANDLER = new Handler() {};

    // 将ApplicationContext注入进来的主要原因在于,HandlerContext是prototype类型的,因而需要
    // 通过ApplicationContext.getBean()方法来获取其实例
    private ApplicationContext context;

    // 创建一个头结点和尾节点,这两个节点内部没有做任何处理,只是默认的将每一层级的链往下传递,
    // 这里头结点和尾节点的主要作用就是用于标志整个链的首尾,所有的业务节点都在这两个节点中间
    private HandlerContext head;
    private HandlerContext tail;

    // 用于业务调用的request对象,其内部封装了业务数据
    private Request request;
    // 用于执行任务的task对象
    private Task task;

    // 最初始的业务数据需要通过构造函数传入,因为这是驱动整个pipeline所需要的数据,
    // 一般通过外部调用方的参数进行封装即可
    public DefaultPipeline(Request request) {
        this.request = request;
    }

    // 这里我们可以看到,每一层级的调用都是通过HandlerContext.invokeXXX(head)的方式进行的,
    // 也就是说我们每一层级链的入口都是从头结点开始的,当然在某些情况下,我们也需要从尾节点开始链
    // 的调用,这个时候传入tail即可。

    // 触发任务过滤的链调用
    @Override
    public Pipeline fireTaskFiltered() {
        HandlerContext.invokeTaskFiltered(head, task);
        return this;
    }

    // 用于往Pipeline中添加节点的方法,读者朋友也可以实现其他的方法用于进行链的维护
    void addLast(Handler handler) {
        HandlerContext handlerContext = newContext(handler);
        tail.prev.next = handlerContext;
        handlerContext.prev = tail.prev;
        handlerContext.next = tail;
        tail.prev = handlerContext;
    }

    // 这里通过实现InitializingBean接口来达到初始化Pipeline的目的,可以看到,这里初始的时候
    // 我们通过ApplicationContext实例化了两个HandlerContext对象,然后将head.next指向tail节点,
    // 将tail.prev指向head节点。也就是说,初始时,整个链只有头结点和尾节点。
    @Override
    public void afterPropertiesSet() throws Exception {
        head = newContext(DEFAULT_HANDLER);
        tail = newContext(DEFAULT_HANDLER);
        head.next = tail;
        tail.prev = head;
    }

    // 使用默认的Handler初始化一个HandlerContext
    private HandlerContext newContext(Handler handler) {
        HandlerContext context = this.context.getBean(HandlerContext.class);
        context.handler = handler;
        return context;
    }

    // 注入ApplicationContext对象
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) {
        this.context = applicationContext;
    }
}

Pipeline的实现类内部除了实现接口的方法,其他方法均为初始化

  • DEFAULT_HANDLER: Pipeline管理一条链表,该链表的每个节点包含HandlerContextHandler两个对象, 而链表的首和尾两个节点由Pipeline自己指定(其他自定义的节点放在首尾两节点之间),DEFAULT_HANDLER用来作为首尾节点的Handler,不起任何作用
  • context: 由于这些类的作用域均不是单例,所以要使用ApplicationContext.getBean()方法获取,所以类实现了ApplicationContextAware接口的setApplicationContext方法,用于注入ApplicationContext对象
  • private HandlerContext head, tail: 为一条链的首尾两个节点,从这儿也可以看出,链条的每个节点都是通过HandlerContext来引用的,HandlerContext再引用一个Handler

@Component
@Scope("prototype")
public class HandlerContext {

    HandlerContext prev;
    HandlerContext next;
    Handler handler;

    private Task task;

    public void fireTaskFiltered(Task task) {
        invokeTaskFiltered(next(), task);
    }

    /**
     * 处理任务过滤事件
     */
    static void invokeTaskFiltered(HandlerContext ctx, Task task) {
        if (null != ctx) {
            try {
                ctx.handler().filterTask(ctx, task);
            } catch (Throwable e) {
                ctx.handler().exceptionCaught(ctx, e);
            }
        }
    }

    private HandlerContext next() {
        return next;
    }

    private Handler handler() {
        return handler;
    }
}

HandlerContext作为节点,应有前后两个节点的引用pre next,还有具体处理任务的Handler

fireTaskFiltered方法供Hanndler调用,将请求传递给下一个节点处理(方法实现中区下一个HandlerContext去执行)

invokeTaskFiltered静态方法供Pipeline 和 上一个节点的fireTaskFiltered方法调用,去执行Handler的方法


public interface Handler {
    /**
     * 查询到task之后,进行task过滤的逻辑
     */
    default void filterTask(HandlerContext ctx, Task task) {
        ctx.fireTaskFiltered(task);
    }
}

Handler定义了感兴趣的事件(暂时只看过滤事件)

Handler的实现类由我们根据自己的需要去编写,实现Handler接口即可

@Component
public class DurationHandler implements Handler {
    @Override
    public void filterTask(HandlerContext ctx, Task task) {
        System.out.println("时效性检验");
        ctx.fireTaskFiltered(task);
    }
}

@Component
public class RiskHandler implements Handler {
    @Override
    public void filterTask(HandlerContext ctx, Task task) {
        System.out.println("风控拦截");
        ctx.fireTaskFiltered(task);
    }
}

@Component
public class TimesHandler implements Handler {
    @Override
    public void filterTask(HandlerContext ctx, Task task) {
        System.out.println("次数限制检验");
        ctx.fireTaskFiltered(task);
    }
}

这里我们已经实现了PipelineHandlerContextHandler,知道这些bean都是被Spring所管理的bean,那么我们接下来的问题主要在于如何进行整个链的组装。这里的组装方式比较简单,其主要需要解决两个问题:

  • 对于后续写业务代码的人而言,其只需要实现一个Handler接口即可,而无需处理与链相关的所有逻辑,因而我们需要获取到所有实现了Handler接口的bean;
  • 将实现了Handler接口的bean通过HandlerContext进行封装,然后将其添加到Pipeline中。

以上可以由spring完成,通过生命实现接口BeanPostProcessor的类,实现postProcessAfterInitialization方法,可以在初始化完Pipeline后,获取所有实现了Handlerbean,并添加到pipeline中,spring自动调用该方法

@Component
public class HandlerBeanProcessor implements BeanPostProcessor, ApplicationContextAware {

    private ApplicationContext context;

    // 该方法会在一个bean初始化完成后调用,这里主要是在Pipeline初始化完成之后获取所有实现了
    // Handler接口的bean,然后通过调用Pipeline.addLast()方法将其添加到pipeline中
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) {
        if (bean instanceof DefaultPipeline) {
            DefaultPipeline pipeline = (DefaultPipeline) bean;
            Map<String, Handler> handlerMap = context.getBeansOfType(Handler.class);
            handlerMap.forEach((name, handler) -> pipeline.addLast(handler));
        }

        return bean;
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) {
        this.context = applicationContext;
    }
}
  • postProcessAfterInitialization(Object bean, String beanName): 实现了BeanPostProcessor的该方法,在spring启动之后没初始化完成一个Bean之后,都会调用该方法

如此,当初始化完pipeline之后,获取实现了Handler接口的所有实现类,在addLast()方法中为每一个Handler初始化一个HandlerContext,并添加到Pipeline


如此下来,整个过程调用如下

@Service
public class ApplicationService {

    @Autowired
    private ApplicationContext context;

    public void mockedClient(Request request) {
        Pipeline pipeline = newPipeline(request);
        pipeline.fireTaskFiltered();
    }

    private Pipeline newPipeline(Request request) {
        return context.getBean(DefaultPipeline.class, request);
    }
}

1、spring项目启动,加载和初始化Bean,当加载到DefaultPipeline的时候,由于实现了InitializingBean接口,所以会调用初始化方法afterPropertiesSet(),为DefaultPipeline添加两个首尾节点HandlerContext

2、当初始化完成Pipeline之后,调用postProcessAfterInitialization(Object bean, String beanName);

3、加载所有实现了Hnadler接口的Bean,并通过pipeline.addLast(Handler handler方法为每一个handler创建一个HandlerContext,并添加到链条中;

至此形成了一条链

4、service想执行该链的时候,通过有参构造方法,将请求request传递给pipeline,调用pipeline.fireTaskFiltered();

5、pipeline.fireTaskFiltered()中,会调用HandlerContext的静态方法HandlerContext.invokeTaskFiltered(HandlerContext ctx, Task task)将第一个HandlerContext传入去执行,其handlerfilterTask(HandlerContext ctx, Task task)方法执行具体逻辑

6、handlerfilterTask(HandlerContext ctx, Task task)方法最后会调用ctxinvokeTaskFiltered(HandlerContext ctx, Task task)方法,该方法会使用invokeTaskFiltered(next(), task)去执行下一个节点ctx.handler().filterTask(ctx, task)

7、直至最后到节点tail没有下一个节点会停止执行;


至此单条链的责任链模式已完成

在netty中,并不是一条链,每一个handler有很多针对不同的事件的处理

在pipeline中有所有的事件,我们相对某一个事件处理是,实现handler的对应方法的处理逻辑,就会在对应层级的链中加入该handler,netty多层级代码责任链参考:Spring中如何使用责任链模式

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/414783.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

基于springboot+vue的大学城水电管理系统(前后端分离)

博主主页&#xff1a;猫头鹰源码 博主简介&#xff1a;Java领域优质创作者、CSDN博客专家、阿里云专家博主、公司架构师、全网粉丝5万、专注Java技术领域和毕业设计项目实战&#xff0c;欢迎高校老师\讲师\同行交流合作 ​主要内容&#xff1a;毕业设计(Javaweb项目|小程序|Pyt…

特征融合篇 | YOLOv8 引入通用高效层聚合网络 GELAN | YOLOv9 新模块

今天的深度学习方法专注于如何设计最合适的目标函数,以使模型的预测结果最接近真实情况。同时,必须设计一个合适的架构,以便为预测提供足够的信息。现有方法忽视了一个事实,即当输入数据经过逐层特征提取和空间转换时,会丢失大量信息。本文将深入探讨数据通过深度网络传输…

UE 贴地绘制/日历/鼠标光标滚轮位置缩放图片/UMG滚动数据从前后添加新UI/多图片批量下载 收费项目源码资源

基本里面的内容本人CSDN发的都有现成代码.里面大部分是功能实现思路.这里面是把这几个功能合成了一个完整5.1项目源码.拿到即用.收费项目源码资源. 1.贴地绘制 2.日历 3.鼠标光标滚轮位置缩放图片 \ 4.UMG滚动数据从前后添加新UI思路 5.多图片批量下载 这是整合的懒人源码包收…

Rocky Linux 运维工具yum

一、yum的简介 ​​yum​是用于在基于RPM包管理系统的包管理工具。用户可以通过 ​yum​来搜索、安装、更新和删除软件包&#xff0c;自动处理依赖关系&#xff0c;方便快捷地管理系统上的软件。 二、yum的参数说明 1、install 用于在系统的上安装一个或多个软件包 2、seach 用…

【HarmonyOS】鸿蒙开发之Video组件——第3.7章

Video组件内VideoOptions属性简介 src&#xff1a;设置视频地址。currentProgressRate&#xff1a;设置视频播放倍速&#xff0c;参数说明如下&#xff1a; number|string&#xff1a;只支持 0.75 &#xff0c; 1.0 &#xff0c; 1.25 &#xff0c; 1.75 &#xff0c; 2.0 。P…

JavaEE进阶(7)Spring Boot 日志(概述、用途、使用:打印日志,框架介绍,SLF4J 框架介绍、更简单的日志输出)

接上次博客&#xff1a;JavaEE进阶&#xff08;6&#xff09;SpringBoot 配置文件&#xff08;作用、格式、properties配置文件说明、yml配置文件说明、验证码案例&#xff09;-CSDN博客 目录 日志概述 日志的用途 日志使用 打印日志 在程序中获取日志对象 使用日志对象…

androidble蓝牙开发,12道Android高级面试题

Java面试题 1.GC机制 垃圾回收需要完成两件事&#xff1a;找到垃圾&#xff0c;回收垃圾。 找到垃圾一般的话有两种方法&#xff1a; 引用计数法&#xff1a; 当一个对象被引用时&#xff0c;它的引用计数器会加一&#xff0c;垃圾回收时会清理掉引用计数为0的对象。但这种方…

论文笔记:A survey on zero knowledge range proofs and applications

https://link.springer.com/article/10.1007/s42452-019-0989-z 描述了构建零知识区间证明&#xff08;ZKRP&#xff09;的不同策略&#xff0c;例如2001年Boudot提出的方案&#xff1b;2008年Camenisch等人提出的方案&#xff1b;以及2017年提出的Bulletproofs。 Introducti…

SpringBoot案例(黑马学习笔记)

这个案例呢&#xff0c;就是Tlias智能学习辅助系统。 参考接口文档完成后端功能的开 发&#xff0c;然后结合前端工程进行联调测试即可。 完成后的成品效果展示&#xff1a; 准备工作 需求&环境搭建 需求说明 部门管理 部门管理功能开发包括&#xff1a; ● 查询部门列…

【JavaSE】实用类——枚举类型、包装类、数学类

目录 Java API枚举优势代码示例 包装类作用包装类和基本数据类型的对应关系包装类的构造方法包装类的常用方法装箱和拆箱 留一个问题大家猜猜看包装类的特点 Math类Random类代码示例 Java API Java API(Java Application Programming Interface) 即Java应用程序编程接口&#…

设备管理系统解决方案

软件资料获取&#xff1a;软件项目开发全套文档下载_软件项目文档-CSDN博客 1.系统概述 1.1.需求描述 建立设备信息库&#xff0c;对设备相关档案的登录、整理。通过建立完善的设备档案&#xff0c;将设备的各类原始信息进行信息化管理&#xff0c;使设备档案查询工作方便快…

NoReturn与None的区别

问题来源&#xff1a; class xx:def __init__(self) -> NoReturn:passpycharm编译器提示如下问题&#xff1a; init should return None 根因探索&#xff1a; None 是Python的一个特殊的数据类型&#xff0c;用于表示空值或者没有值。当一个函数没有显式返回值时&#x…

模型预测控制MPC算法的讲解-案例(C语言代码)

目录 一、模型预测控制MPC的基本原理 1.1 建立模型 1.2 设定目标和约束条件 1.3 求解优化问题 1.4 应用控制输入 1.5 重复优化 二、模型预测控制MPC的特点 三、应用场景 四、应用案例 一个MPC算法的简化版框架&#xff1a; 4.1 案例系统模型 4.2 控制目标和当前状态…

【JavaWeb】

Javaweb 数据库相关概念MySQL数据库MySQL数据模型SQLDDL--操作数据库图形化客户端工具DML--操作数据DQL数据库约束 数据库设计多表查询事务 数据库相关概念 数据库 存储数据的仓库&#xff0c;数据是有组织的进行存储 英文&#xff1a;DataBase&#xff0c;简称DB 数据库管理系…

自然语言:信息抽取技术在CRM系统中的应用与成效

一、引言 在当今快速变化的商业环境中&#xff0c;客户关系管理&#xff08;CRM&#xff09;已成为企业成功的关键因素。CRM系统的核心在于有效地管理客户信息&#xff0c;跟踪与客户的每一次互动&#xff0c;以及深入分析这些数据以提升客户满意度和忠诚度。在我最近参与的一个…

SpringBoot+Vue全栈开发-刘老师教编程(b站)(一)

课堂内容 Java EE企业级框架&#xff1a;SpringBootMyBatisPlusWeb前端核心框架&#xff1a;VueElementUI公共云部署&#xff1a;前后端项目集成打包与部署 B/S(Browser/Server):浏览器/服务器架构模式 C/S(Client/Server):客户端/服务器架构模式 Maven是一个项目管理工具&…

【Java多线程】面试常考——锁策略、synchronized的锁升级优化过程以及CAS(Compare and swap)

目录 1、锁的策略 1.1、乐观锁和悲观锁 1.2、轻量级锁和重量级锁 1.3、自旋锁和挂起等待锁 1.4、普通互斥锁和读写锁 1.5、公平锁和非公平锁 1.6、可重入锁和不可重入锁 2、synchronized 内部的升级与优化过程 2.1、锁的升级/膨胀 2.1.1、偏向锁阶段 2.1.2、轻量级锁…

为什么会造成服务器丢包?

随着云服务器市场的发展和网络安全问题&#xff0c;服务器丢包问题成为了一个普遍存在的现象。服务器丢包是指在网络传输过程中&#xff0c;数据包由于各种原因未能到达目标服务器&#xff0c;导致数据传输中断或延迟。那么&#xff0c;为什么会造成服务器丢包呢&#xff1f;下…

naive-ui-admin 表格去掉工具栏toolbar

使用naive-ui-admin的时候&#xff0c;有时候不需要显示工具栏&#xff0c;工具栏太占地方了。 1.在src/components/Table/src/props.ts 里面添加属性 showToolbar 默认显示&#xff0c;在不需要的地方传false。也可以默认不显示 &#xff0c;这个根据需求来。 2.在src/compo…

在SAP HANA中使用OData(二)

通常有两种方式通过OData来暴露SAP HANA中的数据库对象&#xff0c;一是直接使用Database Object&#xff0c;比如前一篇和本篇文章介绍的例子&#xff0c;这种方式针对于数据已经存在于SAP HANA中&#xff0c;在Repository中没有对应的设计时对象(Design-time Object)&#xf…