当前位置: 首页 > news >正文

十一、GoF之代理模式

1 对代理模式的理解

在这里插入图片描述
【在程序中,对象A和对象B无法直接交互时。】
【在程序中,功能需要增强时。】
【在程序中,目标需要被保护时】

业务场景:系统中有A、B、C三个模块,使用这些模块的前提是需要用户登录,也就是说在A模块中要编写判断登录的代码,B模块中也要编写,C模块中还要编写,这些判断登录的代码反复出现,显然代码没有得到复用,可以为A、B、C三个模块提供一个代理,在代理当中写一次登录判断即可。代理的逻辑是:请求来了之后,判断用户是否登录了,如果已经登录了,则执行对应的目标,如果没有登录则跳转到登录页面。【在程序中,目标不但受到保护,并且代码也得到了复用。】

代理模式是GoF23种设计模式之一。属于结构型设计模式。

代理模式的作用是:为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个客户不想或者不能直接引用一个对象,此时可以通过一个称之为“代理”的第三者来实现间接引用。代理对象可以在客户端和目标对象之间起到中介的作用,并且可以通过代理对象去掉客户不应该看到的内容和服务或者添加客户需要的额外服务。 通过引入一个新的对象来实现对真实对象的操作或者将新的对象作为真实对象的一个替身,这种实现机制即为代理模式,通过引入代理对象来间接访问一个对象,这就是代理模式的模式动机。


代理模式中的角色:

  • 代理类(代理主题)
  • 目标类(真实主题)
  • 代理类和目标类的公共接口(抽象主题):客户端在使用代理类时就像在使用目标类,不被客户端所察觉,所以代理类和目标类要有共同的行为,也就是实现共同的接口。

代理模式的类图:
在这里插入图片描述


代理模式在代码实现上,包括两种形式:

  • 静态代理
  • 动态代理

2 静态代理

有一个接口和实现类:

package com.powernode.proxy.service;/*** 订单业务接口* 代理对象和目标对象的公共接口*/
public interface OrderService {/*** 生成订单*/void generate();/*** 修改订单信息*/void modify();/*** 查看订单详情*/void detail();
}
package com.powernode.proxy.service.impl;import com.powernode.proxy.service.OrderService;/*** 目标对象*/
public class OrderServiceImpl implements OrderService {@Override// 目标方法public void generate() {// 模拟生成订单的耗时try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("订单已生成");}@Overridepublic void modify() {// 模拟修改订单的耗时try {Thread.sleep(400);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("订单已修改");}@Overridepublic void detail() {// 模拟查看订单的耗时try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("订单详情");}
}

项目经理提出一个新的需求:要统计所有业务接口中每一个业务方法的耗时。

  • 解决方案一:硬编码,在每一个业务接口中的每一个业务方法中直接添加统计耗时的程序。

    • 这种方案的缺点:
      • 缺点一:违背OCP开闭原则。
      • 缺点二:代码没有得到复用。(相同的代码写了很多遍。)
  • 解决方案二:编写业务类的子类,让子类继承业务类,对每个业务方法进行重写。

    • 缺点一:虽然解决了OCP开闭原则。但是这种方式会导致耦合度很高,因为采用了继承关系。继承关系是一种耦合度非常高的关系,不建议使用。
    • 缺点二:代码没有得到复用。(相同的代码写了很多遍。)
  • 解决方案三:代理模式。(静态代理)

    为OrderService接口提供一个代理类。

    package com.powernode.proxy.service.impl.static_proxy;import com.powernode.proxy.service.OrderService;/*** 代理对象(代理对象 和目标对象要具有相同的行为,就要实现公共接口)* 客户端在使用代理对象的时候就像在使用目标对象一样*/
    public class OrderServiceProxy implements OrderService {// 将目标对象作为代理对象的一个属性.关联关系,比继承关系耦合度低// 这里写一个公共接口。因为公共接口耦合度低// 目标对象 目标对象一定实现了OrderService接口private OrderService target;// 创建代理对象的时候,传一个目标对象给代理对象public OrderServiceProxy(OrderService target) {this.target = target;}@Override// 代理方法public void generate() {// 增强long begin = System.currentTimeMillis();// 调用目标对象的目标方法target.generate();long end = System.currentTimeMillis();System.out.println("耗时"+(end - begin)+"毫秒");}@Overridepublic void modify() {// 增强long begin = System.currentTimeMillis();// 调用目标对象的目标方法target.modify();long end = System.currentTimeMillis();System.out.println("耗时"+(end - begin)+"毫秒");}@Overridepublic void detail() {// 增强long begin = System.currentTimeMillis();// 调用目标对象的目标方法target.detail();long end = System.currentTimeMillis();System.out.println("耗时"+(end - begin)+"毫秒");}
    }
    

    编写客户端程序:

    package com.powernode.proxy.client;import com.powernode.proxy.service.OrderService;
    import com.powernode.proxy.service.impl.OrderServiceImpl;
    import com.powernode.proxy.service.impl.static_proxy.OrderServiceProxy;public class Test {public static void main(String[] args) {// 创建目标对象OrderService target = new OrderServiceImpl();// 创建代理对象OrderService proxy = new OrderServiceProxy(target);// 调用代理对象的代理方法proxy.generate();proxy.modify();proxy.detail();}
    }
    

    以上就是代理模式中的静态代理,其中OrderService接口是代理类和目标类的共同接口。OrderServiceImpl是目标类。OrderServiceProxy是代理类。

    优点1:解决了OCP问题。
    优点2:采用代理模式的has a,可以降低耦合度。

目前我们使用的是静态代理,这个静态代理的缺点是什么?
        类爆炸。假设系统中有1000个接口,那么每个接口都需要对应代理类,这样类会急剧膨胀。不好维护。

怎么解决类爆炸问题?
        可以使用动态代理来解决这个问题。

动态代理还是代理模式,只不过添加了字节码生成技术,可以在内存中为我们动态的生成一个class字节码,这个字节码就是代理类。
在内存中动态的生成字节码代理类的技术,叫做:动态代理。

3 动态代理

在程序运行阶段,在内存中动态生成代理类,被称为动态代理,目的是为了减少代理类的数量。解决代码复用的问题。

在内存当中动态生成类的技术常见的包括:

  • JDK动态代理技术:只能代理接口。
  • CGLIB动态代理技术:CGLIB(Code Generation Library)是一个开源项目。是一个强大的,高性能,高质量的Code生成类库,它可以在运行期扩展Java类与实现Java接口。它既可以代理接口,又可以代理类,底层是通过继承的方式实现的。性能比JDK动态代理要好。(底层有一个小而快的字节码处理框架ASM。
  • Javassist动态代理技术:Javassist是一个开源的分析、编辑和创建Java字节码的类库。是由东京工业大学的数学和计算机科学系的 Shigeru Chiba (千叶 滋)所创建的。它已加入了开放源代码JBoss 应用服务器项目,通过使用Javassist对字节码操作为JBoss实现动态"AOP"框架。

3.1 JDK动态代理

在静态代理的时候,除了接口和实现类之外,还要写一个代理类OrderServiceProxy
在动态代理中OrderServiceProxy 代理类是可以动态生成的。这个类不需要写。我们直接写客户端程序即可:

package com.powernode.proxy.client;import com.powernode.proxy.service.OrderService;
import com.powernode.proxy.service.impl.OrderServiceImpl;
import com.powernode.proxy.service.impl.TimerInvocationHandler;import java.lang.reflect.Proxy;public class DynamicProxyJDKTest {public static void main(String[] args) {// 创建目标对象OrderService target = new OrderServiceImpl();// 创建代理对象// 参数:类加载器,代理类要实现的接口,调用处理器OrderService proxyObj = (OrderService)Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),new TimerInvocationHandler(target));// 调用代理对象的代理方法proxyObj.generate();proxyObj.modify();proxyObj.detail();}
}

Proxy类全名:java.lang.reflect.Proxy。这是JDK提供的一个类(所以称为JDK动态代理)。主要是通过这个类在内存中生成代理类的字节码。

static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)

  1. newProxyInstance : 新建代理对象
    也就是说,通过调用这个方法可以创建代理对象。
    本质上,这个Proxy.newProxyInstance()方法的执行,做了两件事:
    第一件事:在内存中动态的生成了一个代理类的字节码class。
    第二件事:new对象了。通过内存中生成的代理类这个代码,实例化了代理对象。
  2. 关于newProxyInstance()方法的三个重要的参数,每一个什么含义,有什么用?
    1. 第一个参数:ClassLoader loader
      类加载器。这个类加载器有什么用呢?
      在内存当中生成的字节码也是class文件,要执行也得先加载到内存当中。加载类就需要类加载器。所以这里需要指定类加载器。
      并且JDK要求,目标类的类加载器必须和代理类的类加载器使用同一个。

    2. 第二个参数:Class<?>[] interfaces
      接口类型
      代理类和目标类要实现同一个接口或同一些接口。
      在内存中生成代理类的时候,这个代理类是需要你告诉它实现哪些接口的。

    3. 第三个参数:InvocationHandler h
      InvocationHandler 被翻译为:调用处理器。是一个接口。
      在调用处理器接口中编写的就是:增强代码。
      既然是接口,就要写接口的实现类。
      这是一个JDK动态代理规定的接口,接口全名:java.lang.reflect.InvocationHandler。显然这是一个回调接口,也就是说调用这个接口中方法的程序已经写好了,就差这个接口的实现类了。

java.lang.reflect.InvocationHandler接口的实现类,并且实现接口中的方法,代码如下:

package com.powernode.proxy.service.impl;import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;/*** 专门负责计时的调用处理器对象* 在这个调用处理器当中编写计时相关的增强代码*/
public class TimerInvocationHandler implements InvocationHandler {// 目标对象private Object target;// 通过构造方法来传目标对象// 赋值给成员变量public TimerInvocationHandler(Object target) {this.target = target;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {// 这个接口的目的就是写增强代码// 目标执行之前增强。long begin = System.currentTimeMillis();// 调用目标对象上的目标方法// 方法四要素:哪个对象,哪个方法,传什么参数,返回什么类型// invoke方法执行过程中,使用method来调用目标对象的目标方法。Object retValue = method.invoke(target, args);// 目标执行之后增强。long end = System.currentTimeMillis();System.out.println("耗时" + (end - begin) + "毫秒");// 注意这个invoke方法的返回值,如果代理对象调用代理方法之后,需要返回结果的话,invoke方法必须将目标对象的目标方法执行结果继续返回。return retValue;}
}
  1. invoke方法不是我们负责调用的,JDK负责调用

  2. invoke方法什么时候调用

    当代理对象调用代理方法的时候,注册在InvocationHandler调用处理器当中的invoke()方法被调用

  3. invoke方法的三个参数:

    invoke方法是JDK负责调用的,所以JDK调用这个方法的时候会自动给我们传过来这三个参数。

    我们可以在invoke方法的大括号中直接使用。

    第一个参数:Object proxy 代理对象的引用。设计这个参数只是为了后期的方便,如果想在invoke方法中使用代理对象的话,尽管通过这个参数来使用。

    第二个参数:Method method 目标对象上的目标方法。(要执行的目标方法就是它。)

    第三个参数:Object[] args 目标方法上的实参。

3.2 CGLIB动态代理

CGLIB既可以代理接口,又可以代理类。底层采用继承的方式实现。所以被代理的目标类不能使用final修饰。
使用CGLIB,需要引入依赖

<dependency><groupId>cglib</groupId><artifactId>cglib</artifactId><version>3.3.0</version>
</dependency>

准备一个没有实现接口的类

package com.dynamic.proxy.cglib.service;/*** 目标类*/
public class UserService {// 目标方法public boolean login(String username, String password){System.out.println("正在登录");if ("admin".equals(username) && "123".equals(password)) {return true;}return false;}// 目标方法public void logout(){System.out.println("退出系统");}
}

使用CGLIB在内存中为UserService类生成代理类,并创建对象:

package com.dynamic.proxy.cglib.client;import com.dynamic.proxy.cglib.service.TimerMethodInterceptor;
import com.dynamic.proxy.cglib.service.UserService;
import net.sf.cglib.proxy.Enhancer;public class Client {public static void main(String[] args) {// 创建字节码增强器对象// 这个对象是CGLIB库中的核心对象,依靠它来生成代理类Enhancer enhancer = new Enhancer();// 告诉CGLIB父类(目标类)enhancer.setSuperclass(UserService.class);// 设置回调接口(等同于JDK动态代理中的调用处理器,InvocationHandler)// 在CGLIB中是方法拦截器接口:MethodInterceptorenhancer.setCallback(new TimerMethodInterceptor());// 创建代理对象// 这一步做两件事// 1. 在内存中生成UserService类的子类(代理类的字节码)// 2. 创建代理对象 new// 生成源码,编译class,加载到JVM,并创建代理对象// 父类是UserService,子类一定也是UserServiceUserService userServiceProxy = (UserService) enhancer.create();// CGLIB动态代理生成的代理对象的名字格式// com.dynamic.proxy.cglib.service.UserService$$EnhancerByCGLIB$$65b0e71c@5d3411dSystem.out.println(userServiceProxy);// 调用代理对象的代理方法boolean success = userServiceProxy.login("admin", "123");System.out.println(success ? "登录成功" : "登录失败");userServiceProxy.logout();}
}

底层本质
class UserService$$EnhancerByCGLIB$$65b0e71c extends UserService{}

net.sf.cglib.proxy.MethodInterceptor
编写MethodInterceptor接口实现类:

package com.dynamic.proxy.cglib.service;import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;import java.lang.reflect.Method;public class TimerMethodInterceptor implements MethodInterceptor {@Overridepublic Object intercept(Object target, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {// 前面增强long begin = System.currentTimeMillis();// 调用目标对象的目标方法Object retValue = methodProxy.invokeSuper(target, objects);// 后面增强long end = System.currentTimeMillis();System.out.println("耗时"+(end - begin)+"毫秒");// 返回方法的结果return retValue;}
}

MethodInterceptor接口中有一个方法intercept(),该方法有4个参数:

第一个参数:目标对象
第二个参数:目标方法
第三个参数:目标方法调用时的实参
第四个参数:代理方法

对于高版本的JDK,如果使用CGLIB,需要在启动项中添加两个启动参数:
在这里插入图片描述

  • –add-opens java.base/java.lang=ALL-UNNAMED
  • –add-opens java.base/sun.net.util=ALL-UNNAMED






JDK动态代理和CGLIB动态代理的区别?

1、JDK动态代理只能代理接口,CGLIB动态代理既可以代理接口,也可以代理类。
2、JDK动态代理的底层是采用实现接口的方式实现的,而CGLIB动态代理底层是使用继承实现的。
3、CGLIB动态代理的效率比JDK动态代理的高。

相关文章:

十一、GoF之代理模式

1 对代理模式的理解 【在程序中&#xff0c;对象A和对象B无法直接交互时。】 【在程序中&#xff0c;功能需要增强时。】 【在程序中&#xff0c;目标需要被保护时】 业务场景&#xff1a;系统中有A、B、C三个模块&#xff0c;使用这些模块的前提是需要用户登录&#xff0c;也…...

MySQL5.6和JVM(1.6)调优

MySQL5.6调优 目标了解什么是优化掌握优化查询的方法掌握优化数据库结构的方法掌握优化MySQL服务器的方法什么是优化?合理安排资源、调整系统参数使MySQL运行更快、更节省资源。<...

【汇编】三、寄存器(一只 Assember 的成长史)

嗨~你好呀&#xff01; 我是一名初二学生&#xff0c;热爱计算机&#xff0c;码龄两年。最近开始学习汇编&#xff0c;希望通过 Blog 的形式记录下自己的学习过程&#xff0c;也和更多人分享。 上篇系列文章链接&#xff1a;【汇编】二、预备知识&#xff08;一只 Assember 的…...

TFT通信协议解析与应用

TFTP&#xff08;Trivial File Transfer Protocol&#xff09;是一种简单的文件传输协议&#xff0c;常用于在本地网络上的设备之间传输小型文件。 通信大致过程 TFTP通信过程如下&#xff1a; TFTP通信双方建立连接&#xff1a;TFTP客户端与TFTP服务器建立连接。TFTP服务器监…...

python 操作word库docx 增强接口

前言用python 的docx 库操作word完成一些自动化的文档生成工作&#xff0c;但有时候会遇到docx库提供的操作无法直接满足业务上的需求&#xff0c;需要对其进行一些扩展。接口完善实现在指定的文字后面插入指定的文字任务&#xff1a;以下示例需要在文档中的所有 "人生苦短…...

ARM uboot 源码分析9 - uboot的硬件驱动部分

一、uboot 与 linux 驱动 1、uboot 本身是裸机程序 (1) 裸机本来是没有驱动的概念的&#xff08;狭义的驱动的概念就是&#xff0c;操作系统中用来具体操控硬件的那部分代码叫驱动&#xff09; (2) 裸机程序中是直接操控硬件的&#xff0c;操作系统中必须通过驱动来操控硬件…...

Mybatis动态sql语句foreach中拼接正则表达式字符串注意事项

今天要说到的查询情况&#xff0c;平时项目里边其实用到的并不是很多&#xff0c;使用正则表达式无非是为了匹配结果比较灵活&#xff0c;最常见的&#xff0c;我们的查询条件一般一个参数仅仅只是一种情况的筛选&#xff0c;对于如何选择查询方式&#xff0c;主要还是要看前端…...

JVM内置锁synchronized关键字详解

目录 JVM内置锁synchronized关键字详解 设计同步器的意义 如何解决线程并发安全问题&#xff1f; synchronized原理详解 synchronized底层原理 synchronized在jdk1.6前后的变化【重点】 jdk小于1.6时 jdk>1.6时 轻量级锁何时升级为重量级锁&#xff1f;&#xff1f;…...

【2021.12.25】xv6系统入门学习

【2021.12.28】为xv6系统添加一个开机密码 文章目录【2021.12.28】为xv6系统添加一个开机密码0、说明1、Ubuntu20上安装xv62、测试指令3、修改系统代码4、添加自己的程序命令0、说明 xv6 是 MIT 设计的一个教学型操纵系统。 记录Ubuntu上安装x86版本的xv6系统&#xff0c;为其…...

Linux内核4.14版本——drm框架分析(4)——crtc分析

目录 1. struct drm_crtc结构体 2. crtc相关的API 2.1 drm_crtc_init_with_planes 2.5 drm_mode_setcrtc 3. func的一些介绍 3.1 struct drm_crtc_helper_funcs 3.2 struct drm_crtc_funcs 4. 应用层的调用 4.1 drmModeSetCrtc &#xff08;drmlib库里&#xff09;---…...

用原生js手写分页功能

分页功能如下&#xff1a; 数据分页显示&#xff0c;每页显示若干条数据&#xff0c;默认当前页码为第一页。例如&#xff1a;每页5条数据&#xff0c;则第一页显示 1-5 条&#xff0c;第二页显示 6-10 条&#xff0c;依此类推。当页码为第一页时&#xff0c;上一页为禁用状态…...

CornerNet介绍

CornerNet: Detecting Objects as Paired Keypoints ECCV 2018 Paper&#xff1a;https://arxiv.org/pdf/1808.01244v2.pdf Code&#xff1a;GitHub - princeton-vl/CornerNet 摘要&#xff1a; 提出了一种single-stage的目标检测算法CornerNet&#xff0c;它把每个目标检…...

【SpringBoot】日志使用

默认配置 Spring Boot默认帮我们配置好了日志 //记录器Logger logger LoggerFactory.getLogger(getClass());Testpublic void contextLoads() {//System.out.println();//日志的级别&#xff1b;//由低到高 trace<debug<info<warn<error//可以调整输出的日志级…...

关于slice扩容性能损耗的探究

背景 ​ 如果让我评选最伟大的数据结构&#xff0c;在我心中答案只有两个&#xff0c;数组和哈希表&#xff0c;这两个是我的程序的重要组成部分&#xff0c;同时也是我饭碗的重要组成部分。slice和map简洁明了的API很容易让我们有一种他们提供了无限大的空间&#xff0c;可以…...

Java实现单向链表

✅作者简介&#xff1a;热爱Java后端开发的一名学习者&#xff0c;大家可以跟我一起讨论各种问题喔。 &#x1f34e;个人主页&#xff1a;Hhzzy99 &#x1f34a;个人信条&#xff1a;坚持就是胜利&#xff01; &#x1f49e;当前专栏&#xff1a;Java数据结构与算法 &#x1f9…...

3月4日,30秒知全网,精选7个热点

///印度最大供电商罕见于现货市场购煤&#xff0c;能源供应短缺成忧 据知情人士透露&#xff0c;这家印度国有发电公司计划在下周左右发布300万吨的招标 ///QQ音乐推出AIGC黑胶播放器 这是国内音乐行业首个运用AI技术&#xff0c;通过文字、图片指令快速生成不同风格的播放器…...

EXCEL-职业版本(2)

Excel-职业版本&#xff08;2&#xff09; 定位 1.如何快速定位到不连续的空值&#xff0c;填充为0 1.在任意空单元格里复制0 2.选中数据区域CtrlA 3.CtrlG 4.选择【定位条件】 5.选择【空值】 6.ctrlV 粘贴 即可 2.怎么一次性计算每个小组的数量 单价和金额的和? 1.选中…...

java中延时队列的实现

大家好&#xff0c;我是一名CRUD工程师&#xff0c;最近我朋友突然来问我如何实现延时队列&#xff0c;我脱口而出就是MQ。不过突然想到公司的项目好像用的是java的一个原生类。于是我就想着趁周末的时间好好的去探究一下各方法实现延时队列的优缺点。 延迟消息 延迟消息就是字…...

基于java的circle buffer的实现

总目录链接==>> AutoSAR入门和实战系列总目录 文章目录 缓冲区示例什么是循环缓冲区?方法 1:使用数组插入元素删除元素方法 2:使用链表插入元素:删除元素:当数据经常从一个地方移动到另一个地方或从一个进程移动到另一个进程或被频繁访问时,它不能存储在永久性内存…...

通用方法——为什么重写equals还要重写hashcode

本文介绍java.lang.Object类中的两个方法&#xff1a;equals和hashCode。这两个方法大家应该都知道&#xff0c;但是这两个方法的作用是什么、为什么重写equals还要重写hashCode、它们之间有什么关系和约定等&#xff0c;今天就来带大家了解一下。 1、hashCode hashCode即散列…...

JavaSE学习进阶day2_01 包和权限修饰符

第一章 包 1.1 包 包在操作系统中其实就是一个文件夹。包是用来分门别类的管理技术&#xff0c;不同的技术类放在不同的包下&#xff0c;方便管理和维护。 在IDEA项目中&#xff0c;建包的操作如下&#xff1a; 这个咱们在基础班就谈到过。 包名的命名规范&#xff1a; 路径…...

Android性能调优 - 省电优化

省电&#xff1a;通过工具Battery Historian查看到:耗电大头: 屏幕、网络、cpuled/oled屏幕显示:降低亮度&#xff0c;开深色模式&#xff1b;锁屏间隔缩短到 &#xff1b;亮屏需要一直持有唤醒锁&#xff0c;还有gps定位也需要用到唤醒锁;网络&#xff1a; 常用的网络优化措施…...

ElasticSearch - SpringBoot整合ES之全文搜索匹配查询 match

文章目录1. 数据准备2. match 匹配查询1. 全文检索2. 简化查询DSL语句3. match 匹配查询原理官方文档地址&#xff1a;https://www.elastic.co/guide/en/elasticsearch/reference/index.html权威指南&#xff1a;https://www.elastic.co/guide/cn/elasticsearch/guide/current/…...

句子的改写和扩写

目录 1.句子改写 2.句子扩写 &#xff08;不低于15个句子算是长句子&#xff0c;不能太多长句子&#xff09; 1.句子改写 我绝不会嫁给你的。 如果你是世界上最后一个男人&#xff0c;我就去寺庙。 If you married me,I would jump into the well. 如果你嫁给我&#xff0c;我…...

DockerFile创建及案例

DockerFile dockerfile是用来构建docker镜像的文件&#xff0c;命令脚本参数脚本&#xff01; 构建步骤 编写一个dockerfile文件docker build 构建成为一个对象docker run 运行镜像docker push 发布镜像&#xff08;DockerHub、阿里云镜像仓库&#xff09; 去官网Docker-Hub…...

第十四届蓝桥杯三月真题刷题训练——第 1 天

目录 题目1&#xff1a;数列求值 代码&#xff1a; 题目2&#xff1a;质数 代码&#xff1a; 题目3&#xff1a;饮料换购 代码&#xff1a; 题目1&#xff1a;数列求值 题目描述 本题为填空题&#xff0c;只需要算出结果后&#xff0c;在代码中使用输出语句将所填结果输出…...

基于容器云提交spark job任务

容器云提交spark job任务 容器云提交KindJob类型的spark任务&#xff0c;首先需要申请具有Job任务提交权限的rbac&#xff0c;然后编写对应的yaml文件&#xff0c;通过spark内置的spark-submit命令&#xff0c;提交用户程序(jar包)到集群执行。 1、创建任务job提交权限rbac …...

Linux系统调用之目录操作函数

前言 如果&#xff0c;想要深入的学习Linux系统调用中mkdir&#xff0c;rmdir&#xff0c;rename&#xff0c;chdir&#xff0c;getcwd等这些有关于目录操作函数&#xff0c;还是需要去自己阅读Linux系统中的帮助文档。 具体输入命令&#xff1a; man 2 mkdir/rmdir/rename/ch…...

设计模式-策略模式

前言 作为一名合格的前端开发工程师&#xff0c;全面的掌握面向对象的设计思想非常重要&#xff0c;而“设计模式”是众多软件开发人员经过相当长的一段时间的试验和错误总结出来的&#xff0c;代表了面向对象设计思想的最佳实践。正如《HeadFirst设计模式》中说的一句话&…...

面试+算法:罗马数字及Excel列名与数字互相转换

概述 算法是一个程序员的核心竞争力&#xff0c;也是面试最重要的考查环节。 试题 判断一个罗马数字是否有效 罗马数字包含七种字符&#xff1a;I&#xff0c;V&#xff0c;X&#xff0c;L&#xff0c;C&#xff0c;D和M&#xff0c;如下 字符数值I1V5X10L50C100D500M1000…...