Java SPI机制源码

news2024/11/15 18:54:46

文章目录

    • SPI简介
    • 使用案例
    • SPI的应用
    • SPI机制源码
    • SPI与类加载器
    • 双亲委派机制

SPI简介

Java的SPI(Service Provider Interface)机制允许第三方为应用程序提供插件式的扩展,而不需要修改应用程序本身的代码,从而实现了解耦。Java标准库本身就提供了SPI机制,通常是通过在META-INF/services目录下放置文件来实现的。

在这里插入图片描述
SPI机制的核心组件包括:

  • 服务接口:这是一个Java接口,定义了服务提供者需要实现的方法,应用程序将使用这个接口与具体的服务实现进行交互。

  • 服务实现:这是实现了服务接口的具体类,第三方可以为服务接口提供多个实现。

  • 服务提供者配置文件:这是一个位于META-INF/services目录下的文件,文件名与服务接口的全限定名相同,该文件包含了服务实现类的全限定名,每行一个接口的具体实现类,在运行时就可以加载这些实现类。

  • ServiceLoader:这是Java标准库中的一个类,用于加载服务实现,应用程序可以使用ServiceLoader来获取服务接口的所有具体实现类。

SPI的工作流程如下:

  1. 定义服务接口。

  2. 实现服务接口,创建具体的服务实现类。

  3. 在META-INF/services目录下创建服务提供者配置文件,列出所有服务实现类的全限定名。

  4. 使用ServiceLoader加载服务具体实现类,并根据需要使用它们。

总结就是说SPI机制使得应用程序可以在运行时动态地选择和加载服务实现,从而提高了应用程序的可扩展性和灵活性。

使用案例

首先定义一个服务的接口

public interface Service {
    void execute();
}

接着创建两个两个服务实现类去实现接口,并重写接口中的方法

public class Implementation1 implements Service {
    @Override
    public void execute() {
        System.out.println("服务实现类1");
    }
}

// Implementation2.java
public class Implementation2 implements Service {
    @Override
    public void execute() {
        System.out.println("服务实现类2");
    }
}

然后在META-INF/services目录下创建一个名为com.xydp.SPI.Service(全限定名要与接口的名称对应)的文件,用于存储服务实现类的全限定名。文件内容如下:
在这里插入图片描述

com.xydp.SPI.Implementation1
com.xydp.SPI.Implementation2

编写对应的测试类进行验证

public class SPITest {
    public static void main(String[] args) {
     ServiceLoader<Service> loader = ServiceLoader.load(Service.class);
        Iterator<Service> iterator = loader.iterator();
        while(iterator.hasNext()){
            System.out.println(iterator.next().getClass());
        }
    }
}

输出结果

class com.xydp.SPI.Implementation1
class com.xydp.SPI.Implementation2

SPI的应用

SPI 机制被用于加载和注册 JDBC 驱动程序,JDBC的使用方法如下
首先,导入依赖

 <dependency>
     <groupId>mysql</groupId>
     <artifactId>mysql-connector-java</artifactId>
     <version>8.0.18</version>
</dependency>

public class JDBCTest {
    @SneakyThrows
    public static void main(String[] args) {
        String url = "jdbc:mysql://localhost:3306/hmdp?useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2B8";
        String username = "root";
        String password = "1445413748";

            // 1. 加载JDBC驱动(Driver的静态代码块里面会注册JDBC驱动)
            //	实际上这行代码是可以省略的
            Class.forName("com.mysql.cj.jdbc.Driver");

            // 2. 建立数据库连接
            Connection connection = DriverManager.getConnection(url, username, password);

            // 3. 创建Statement对象
            Statement statement = connection.createStatement();

            // 4. 执行SQL查询
            String sql = "SELECT * FROM test";
            ResultSet resultSet = statement.executeQuery(sql);

            // 5. 处理查询结果
            while (resultSet.next()) {
                int id = resultSet.getInt("id");
                String name = resultSet.getString("value");
                System.out.println("ID: " + id + ", value: " + name);
            }
            //6. 关闭资源
                    connection.close();
        }

}

运行结果
在这里插入图片描述

可以知道JDBC在加载驱动后,节省了注册驱动这一步骤,这是因为在Driver类的静态代码块中已经注册了。

static {
    try {
    //注册驱动
        DriverManager.registerDriver(new Driver());
    } catch (SQLException var1) {
        throw new RuntimeException("Can't register driver!");
    }
}

除此之外,上面代码Class.forName(“com.mysql.cj.jdbc.Driver”)与SPI机制存在关联,会显示去加载Driver类,实际上这行代码是可以省略的,不写的话,下面的DriverManager的静态代码块就会通过ServiceLoader去加载配置文件下的Driver类。
DriverManager类的静态代码块

static {
        loadInitialDrivers();
        println("JDBC DriverManager initialized");
}

静态代码块就会执行loadInitialDrivers()方法,

private static void loadInitialDrivers() {
        String drivers;
        try {
            drivers = AccessController.doPrivileged(new PrivilegedAction<String>() {
                public String run() {
                    return System.getProperty("jdbc.drivers");
                }
            });
        } catch (Exception ex) {
            drivers = null;
        }
      
        AccessController.doPrivileged(new PrivilegedAction<Void>() {
            public Void run() {
							//通过SPI机制加载Driver类
                ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class);
                Iterator<Driver> driversIterator = loadedDrivers.iterator();
							//获取对应的实现类
                try{
                    while(driversIterator.hasNext()) {
                        driversIterator.next();
                    }
                } catch(Throwable t) {
                }
                return null;
            }
        });
    }

在不显示加载Driver类的情况下,ServiceLoader 会扫描类路径中的 META-INF/services 目录,查找 java.sql.Driver的配置文件,该配置文件中存在com.mysql.cj.jdbc.Driver这一行字符串,找到之后ServiceLoader通过反射的方式去加载Driver类。
在这里插入图片描述
对此我们可以进行验证JDBC的SPI机制,测试类如下

    public static void main(String[] args) {
        ServiceLoader<Driver> serviceLoader = ServiceLoader.load(Driver.class);
        Iterator<Driver> iterator = serviceLoader.iterator();
        while(iterator.hasNext()){ ;
            Driver driver = (Driver) iterator.next();
            System.out.println("驱动类的包:"+driver.getClass().getPackage()+"=======加载驱动类"+driver.getClass().getName());
        }
    }

运行结果
在这里插入图片描述由此可以得出结论JDBC确实是通过SPI机制去加载com.mysql.cj.jdbc.Driver类。

SPI机制源码

SPI主要是通过 ServiceLoader 类去解析配置文件,然后通过反射的方式创建对应的接口实现类。
ServiceLoader的主要成员

public final class ServiceLoader<S> implements Iterable<S>  
{  
	// 加载的默认配置文件目录
    private static final String PREFIX = "META-INF/services/";  
  
    // 需要被加载的 SPI 服务实现类
    private final Class<S> service;  
  
    // 该类加载器用于加载服务
    private final ClassLoader loader;  
  
    // 访问控制上下文,用于安全控制
    private final AccessControlContext acc;  
  
    // 按照实例化的顺序缓存服务实例
    private LinkedHashMap<String,S> providers = new LinkedHashMap<>();  
  
    // 懒查询迭代器
    private LazyIterator lookupIterator;

}

  1. 调用ServiceLoader.load 方法时,会根据接口和类加载器创建懒加载迭代器。
ServiceLoader<Service> loader = ServiceLoader.load(Service.class);
//源码如下
public static <S> ServiceLoader<S> load(Class<S> service, ClassLoader loader)  
{  
    return new ServiceLoader<>(service, loader);  
}


// 构造方法重新加载SPI服务
private ServiceLoader(Class<S> svc, ClassLoader cl) {  
    service = Objects.requireNonNull(svc, "Service interface cannot be null");
    // 获取系统类加载器
    loader = (cl == null) ? ClassLoader.getSystemClassLoader() : cl;  
    //安全访问控制
    acc = (System.getSecurityManager() != null) ? AccessController.getContext() : null;
    // 重新加载SPI的服务
    reload();  
}

public void reload() {
	// 清空服务提供者的缓存列表
    providers.clear();
	// 根据接口类型和类加载器创建懒加载迭代器,达到懒加载的目的,防止资源被浪费
    lookupIterator = new LazyIterator(service, loader);
}

private LazyIterator(Class<S> service, ClassLoader loader) {
    this.service = service;
    this.loader = loader;
}
  1. 获取ServiceLoader 的 iterator

ServiceLoader 类的主要成员包含有LinkedHashMap实现的 providers,providers 用于用于缓存被成功加载的服务实例,key 是接口实现类的全限定名,value 是对应实现类的实例对象。

Iterator<Service> iterator = loader.iterator();
//源码如下
public Iterator<S> iterator() {
		//返回自定义的迭代器
        return new Iterator<S>() {
            
            // 创建缓存
            Iterator<Map.Entry<String,S>> knownProviders = providers.entrySet().iterator();

            
            public boolean hasNext() {
   
                if (knownProviders.hasNext())
                    return true;
                  
                return lookupIterator.hasNext();
            }

            public S next() {
                if (knownProviders.hasNext())
                    return knownProviders.next().getValue();
                return lookupIterator.next();
            }

            public void remove() {
                throw new UnsupportedOperationException();
            }

        };
    }

3. 迭代器判断是否有数据

iterator.hasNext()
//源码如下
//判断是否还有数据
public boolean hasNext() {
    // 一开始缓存为空
    //(在调用next方法之后才会将服务对象实例放入该缓存,之后调用hasNext就会直接返回true)
    if (knownProviders.hasNext())
        return true;
    //一开始代码会执行到这
    return lookupIterator.hasNext();
}

public boolean hasNext() {
  	//acc访问控制默认为null,代码逻辑执行到这
    if (acc == null) {
        return hasNextService();
    } else {
        PrivilegedAction<Boolean> action = new PrivilegedAction<Boolean>() {
            public Boolean run() { return hasNextService(); }
        };
        return AccessController.doPrivileged(action, acc);
    }
}

//判断是否还有服务
private boolean hasNextService() {
    // 判断是否还有服务实现类,有则返回true
    if (nextName != null) {
        return true;
    }
    // 配置刚开始为空
    if (configs == null) {
        try {
            //配置文件目录拼接接口名称 META-INF/services + 接口名称
            // META-INF/services/com.xydp.SPI.Service
            String fullName = PREFIX + service.getName();
         		//根据服务实现类的绝对路径加载配置
            if (loader == null)
                configs = ClassLoader.getSystemResources(fullName);
            else
                configs = loader.getResources(fullName);
        } catch (IOException x) {
            fail(service, "Error locating configuration files", x);
        }
    }
    // 判断集合是否为空或者已经遍历完毕
    while ((pending == null) || !pending.hasNext()) {
        if (!configs.hasMoreElements()) {
            return false;
        }
        // 从配置文件解析以下的服务实现类
        //class com.xydp.SPI.Implementation1
        //class com.xydp.SPI.Implementation2
        pending = parse(service, configs.nextElement());
    }
    // 遍历集合的服务实现类信息
    //第一次返回class com.xydp.SPI.Implementation1
    nextName = pending.next();
    return true;
}

  1. 获取服务实现类的信息
System.out.println(iterator.next().getClass());
//源码如下
public S next() {
    // 刚开始缓存为空,所以为false1不执行
    if (knownProviders.hasNext())
        return knownProviders.next().getValue();
    //第一次执行到这
    return lookupIterator.next();
}

public S next() {
 		//控制权限默认为空,执行这段代码
    if (acc == null) {
        return nextService();
    } else {
        PrivilegedAction<S> action = new PrivilegedAction<S>() {
            public S run() { return nextService(); }
        };
        return AccessController.doPrivileged(action, acc);
    }
}
//通过反射获取服务实例
private S nextService() {
		//此时 cn = com.xydp.SPI.Implementation1
    String cn = nextName;
    nextName = null;
    Class<?> c = null;
    // 使用反射,通过类的全限定名和类加载器得到类
    c = Class.forName(cn, false, loader);
	
    // 创建类的实例对象 
    S p = service.cast(c.newInstance());
    // 将实例对象放入缓存
    // key:com.xydp.SPI.Implementation1
    // value:Implementation1@28d25987
    providers.put(cn, p);
    // 返回实例对象
    return p;
}

SPI弊端:从源码可以知道SPI不能按需加载,需要通过 Iterator 形式遍历所有的服务实现类,无法根据参数名称来获取具体的服务实现类。

SPI与类加载器

在加载 SPI 服务时,需要指定类加载器 ClassLoader,否则无法找到具体的服务实现类,这是受限于双亲委派机制,该机制规定子类加载器可以使用父类已经加载的类,但是父类加载器无法使用子类已经加载的类。
(1) SPI 接口属于 Java 的核心库,是由顶层父类启动类加载器 BootstrapClassLoader所加载的;
(2 )SPI 的具体实现类是由系统类加载器AppClassLoader 加载的,顶层的父类启动类加载器 BootstrapClassLoader 是无法找到具体实现类的,所以需要指定类加载器 ClassLoader来加载。
主要有以下四种类加载器:

1 启动类加载器(Bootstrap ClassLoader):启动类加载器是最顶层的类加载器,它负责加载Java核心API和核心类库。启动类加载器是由JVM实现的,不是由Java类实现的。用来加载java核心类库,无法被java程序直接引用。

2 扩展类加载器(extensions class loader):扩展类加载器是启动类加载器的子类加载器,它用来加载 Java 的扩展库。Java 虚拟机的实现会提供 一个扩展库目录。该类加载器在此目录里面查找并加载 Java 类。

3 系统类加载器(system class loader):系统类加载器是扩展类加载器的子类加载器,加载路径主要包括应用程序的CLASSPATH环境变量指定的路径。可以通过ClassLoader.getSystemClassLoader()来获取它。

4 用户自定义类加载器,通过继承 java.lang.ClassLoader类重写findClass()方法的方式实现。

双亲委派机制

1.什么是双亲委派机制?

(1)当加载一个类时,当前类加载器先判断此类是否已经被加载,如果类已经被加载则返回;

(2)如果类没有被加载,则先委托父类加载(父类加载时会判断该类有没有被自己加载过),如果父类加载过则返回;如果没被加载过则继续向上委托;

(3)如果一直委托都无法加载,当前类加载器才会尝试自己加载。

2.双亲委派机制作用/优点

双亲委派机制是Java类加载器中的一个重要机制。它的主要作用有以下几点:

  1. 避免类的重复加载:Java中的类是由类加载器加载的,如果没有双亲委派机制,那么可能会出现多个类加载器加载同一个类的情况,造成资源的浪费。

  2. 保证Java核心API的类型安全:双亲委派机制保证了所有的Java应用都至少会使用java.* 开头的类库,而这些由系统类加载器所加载的类库在程序中有着至关重要的地位,比如java.lang.Object类,没有哪个类可以不使用Object类,所以为了防止用户自定义类篡改这些核心类。

    例如:实现了自定义的String时, 当应用程序通过系统类记载器加载核心类String时,它会首先委托给拓展类加载器,再委托给启动类加载器,启动类加载器就会加载核心String类,由于启动类加载器已经加载了正确的核心String类,所以应用程序不会加载到被篡改的String类。

3.为什么要打破双亲委派机制?

打破双亲委派机制的原因主要有以下几点:

  1. 灵活性:双亲委派机制虽然保证了类的唯一性和安全性,但也限制了Java类加载器的灵活性。有时候,我们需要自定义类加载器来实现一些特殊的功能,比如实现代码的热部署、动态加载第三方插件等。在这些场景下,打破双亲委派机制可以让我们更加灵活地控制类的加载过程。
  2. 隔离性:在某些场景下,**我们可能需要在同一个JVM中运行多个相互隔离的应用。这时候,我们可以使用自定义类加载器来实现类的隔离,从而避免类的冲突和安全问题。**打破双亲委派机制可以让我们更加灵活地控制类的加载过程,从而实现应用的隔离。
  3. 增加了类加载时间:在类加载的过程中,需要不断地查询并委托父类加载器,这意味着类加载所需要的时间可能会增加。在类数量庞大或类加载器层次比较深的情况下,这种时间延迟可能会变得更加明显。

4.如何打破双亲委派机制?

继承java.lang.ClassLoader类重写loadClass()方法来打破双亲委派机制,然后直接从自己的类路径中加载类,而不需要委托给父类加载器进行加载。

5.Tomcat是如何打破双亲委派机制的?

Tomcat是一个Java Web应用服务器,它可以运行多个Web应用。为了实现Web应用的隔离和独立部署,Tomcat使用了自定义类加载器来打破双亲委派机制。

在Tomcat中,每个Web应用都有一个对应的WebappClassLoader,它继承了java.lang.ClassLoader类。WebappClassLoader的主要作用是加载Web应用的类和资源。为了实现类的隔离,WebappClassLoader重写了loadClass方法,而不是findClass方法。在loadClass方法中,WebappClassLoader首先尝试从自己的类路径中加载类,如果找不到,再委托给父类加载器进行加载。这样,每个Web应用都可以使用自己的类加载器来加载类,从而实现了类的隔离

6.如何自定义类加载器?

继承java.lang.ClassLoader类重写findClass()方法实现自定义类加载器。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

HUAWEI华为MateBook B5-420 i5 集显(KLCZ-WXX9,KLCZ-WDH9)原装出厂Windows10系统文件下载

适用型号&#xff1a;KLCZ-WXX9、KLCZ-WDH9 链接&#xff1a;https://pan.baidu.com/s/12xnaLtcPjZoyfCcJUHynVQ?pwdelul 提取码&#xff1a;elul 华为原装系统自带所有驱动、出厂主题壁纸、系统属性联机支持标志、系统属性专属LOGO标志、华为浏览器、Office办公软件、华为…

[数据集][目标检测]石油泄漏检测数据集VOC+YOLO格式6633张1类别

数据集格式&#xff1a;Pascal VOC格式YOLO格式(不包含分割路径的txt文件&#xff0c;仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数)&#xff1a;6633 标注数量(xml文件个数)&#xff1a;6633 标注数量(txt文件个数)&#xff1a;6633 标注…

网络安全硬件

传统防火墙 传统防火墙 技术&#xff1a;访问控制、代理技术、会话机制 工作层次&#xff1a;应用层一下 防御模式&#xff1a;通过防御设备划分边界&#xff0c;基于IP/端口和特征进行判断&#xff1b;以隔离为基础&#xff0c;基于信任原则构建安全框架&#xff1b;以防护为…

【Rust】007-包管理与模块管理

【Rust】007-包管理与模块管理 文章目录 【Rust】007-包管理与模块管理一、包管理器&#xff1a;Cargo1、简介Cargo 官方文档仓库 2、项目初始化3、写一个小程序任务目标寻找合适的库添加库到我们的项目中代码实现cargo run运行 二、模块管理1、概述2、文件作为模块第一步&…

可靠性定义

一、广义可靠性 包含以下三个方面 1、可靠性&#xff08;Reliability&#xff09; 定义&#xff1a;产品、系统或组件在预定条件下和规定时间内&#xff0c;完成既定功能的能力。 平均故障间隔时间&#xff08;Mean Time Between Failures, MTBF&#xff09;&#xff1a; …

每天五分钟深度学习:广播机制(以python语言为例)

本文重点 因为向量化的计算方式导致效率的提升,所以现在很多时候,我们都是用向量化的计算,但是向量化计算有一个问题让人头痛就是维度的问题,本节课程我们将讲解python中的广播机制,你会发现这个机制的优秀之处。 代码实例 import numpy as np a=np.random.randn(3,4) …

使用 Milvus 和 Streamlit 搭建多模态产品推荐系统

我们可以使用 Milvus 搭建多模态 RAG 应用&#xff0c;用于产品推荐系统。用户只需简单上传一张图片并输入文字描述&#xff0c;Google 的 MagicLens 多模态 Embedding 模型就会将图像和文本编码成一个多模态向量。然后&#xff0c;使用这个向量从 Milvus 向量数据库中找到最相…

负载均衡 Ribbon 与 Fegin 远程调用原理

文章目录 一、什么是负载均衡二、Ribbon 负载均衡2.1 Ribbon 使用2.2 Ribbon 实现原理 (★)2.3 Ribbon 负载均衡算法 三、Feign 远程调用3.1 Feign 简述3.2 Feign 的集成3.3 Feign 实现原理 (★) 一、什么是负载均衡 《服务治理&#xff1a;Nacos 注册中心》 末尾提到了负载均…

简单的Linux Ftp服务搭建

简单的Linux FTP服务搭建 1.需求 公司有一个esb文件传输代理&#xff0c;其中我们程序有文件传输功能&#xff0c;需要将本地文件传输到esb文件代理服务器上&#xff0c;传输成功之后发送http请求&#xff0c;告知esb将固定文件进行传输到对应外围其他服务的文件目录中&#…

【高阶数据结构】秘法(二)——图(一):图的基本概念和存储结构

前言&#xff1a; 今天我们要讲解的是数据结构中图的部分&#xff0c;这部分在我们实际生活中也是经常会碰到的&#xff0c;同时这部分也是数据结构中比较有难度的部分&#xff0c;这部分内容我会把它分为多章来进行讲解&#xff0c;今天我们先来讲解一下图的基本概念和存储结构…

Codeforces Round 920 (Div. 3)(A,B,C,D)

A 在二维坐标轴上有一个正方形&#xff0c;给你一个正方形的四个顶点坐标&#xff0c;求面积 知道一个边长&#xff0c;平方即可 for(int i0;i<4;i)x[i]x1; Arrays.sort(x); //1122 kMath.abs(x[2]-x[1]); System.out.println(k*k); B 操作1、2是添加和修改&#xff0c;操…

Windows系统下的Spark环境配置

一&#xff1a;Spark的介绍 Apache Spark 是一个开源的分布式大数据处理引擎&#xff0c;它提供了一整套开发API&#xff0c;包括流计算和机器学习。Spark 支持批处理和流处理&#xff0c;其显著特点是能够在内存中进行迭代计算&#xff0c;从而加快数据处理速度。尽管 Spark …

【专题】2024年8月中国企业跨境、出海、国际化、全球化行业报告汇总PDF合集分享(附原数据表)

原文链接&#xff1a; https://tecdat.cn/?p37584 在全球化浪潮汹涌澎湃的当下&#xff0c;中国企业积极探索海外市场&#xff0c;开启了出海跨境的新征程。本报告合集旨在全面梳理出海跨境全球化行业的发展态势&#xff0c;涵盖多个领域的深度洞察。 从游戏、快消品、医疗器…

Python行结构(逻辑行、物理行、显式拼接行、隐式拼接行、空白行)

Python行结构 &#xff08;逻辑行、物理行、显式拼接行、隐式拼接行、空白行&#xff09; 本文目录&#xff1a; 零、时光宝盒 一、Python PEP8 编码行规范 1.1、Maximum Line Length 行的最大长度 1.2、在二元运算符之前应该换行吗&#xff1f; 二、Python行结构 2.1、物…

电子设计-基础3-电感与二极管

电子设计-基础3-电感与二极管 电感电感简介电感的发展历史电感的原理结构电感的性质&#xff1a; 电流惯性电感性质的演示 电感的分类常用的几种电感&#xff1a;一体成型电感一、定义与结构二、特点 三、工作原理四、应用领域 五、优缺点屏蔽电感 CD系列电感&#xff1a;多用于…

网站安全问题整改

网站安全、政务云、第三方安全检测机构等评测出来的网站web安全问题整改&#xff0c;如果你也正需要做这方面&#xff0c;请联系我吧

【代码随想录训练营第42期 Day50打卡 - dfs入门 - 卡码网 98. 所有可达路径

目录 一、dfs基础 二、模板题 题目&#xff1a;98. 所有可达路径 题目链接 题解&#xff1a;dfs邻接矩阵 三、小结 一、dfs基础 dfs是按照一个方向搜索到尽头再搜索其他方向。怎样实现对其他方向的搜索呢&#xff1f;我们可以通过回溯&#xff0c;撤销最后一步&#xff0c…

JUC-无锁之CAS

问题提出 (应用之互斥) package cn.itcast; import java.util.ArrayList; import java.util.List; interface Account {// 获取余额Integer getBalance();// 取款void withdraw(Integer amount);/*** 方法内会启动 1000 个线程&#xff0c;每个线程做 -10 元 的操作* 如果初始…

2024全国大学省数学建模竞赛C题-优秀论文分析(2023)

​某商超蔬菜类商品动态定价与补货决策研究 摘 要 随着生鲜市场规模的持续扩大&#xff0c;蔬菜零售行业的竞争也愈加激烈。为帮助某商超 改善经营模式&#xff0c;本文基于题目所给数据信息&#xff0c;建立数学模型进行分析&#xff0c;从而制定合理 的蔬菜类商品动态定价与…

ARM发布新一代高性能处理器N3

简介 就在2月21日&#xff0c;ARM发布了新一代面向服务器的高性能处理器N3和V3&#xff0c;N系列平衡性能和功耗&#xff0c;而V系列则注重更高的性能。此次发布的N3&#xff0c;单个die最高32核&#xff08;并加入到CCS&#xff0c;Compute Subsystems&#xff0c;包含Core&a…