Java 集成测试详解及示例

news2024/9/21 3:14:40

通过综合指南探索 Java 集成测试的世界。了解工具、流程和最佳实践,并辅以实际示例。

随着软件系统变得越来越大、越来越复杂,组件和服务以错综复杂的方式交互,集成测试已变得不可或缺。通过验证所有组件和模块在组合时是否正常工作,Java 集成测试可确保整个系统按预期运行。

随着模块化架构、微服务和自动化部署的兴起,通过集成测试尽早验证这些复杂的交互现在已成为一项核心原则。强大的集成测试可以识别由组件交互引起的缺陷,而单元测试本身无法检测到这些缺陷。利用 Java 集成测试框架可以帮助简化流程,确保所有模块和组件都经过彻底审查。

在当今的持续交付和 DevOps世界中,快速迭代和频繁升级已成为常态,可靠的集成测试对于保证质量和减少技术债务至关重要。

本文探讨了 Java 中有效集成测试的工具和技术。为了有效地管理多个集成测试,通常将它们分组到测试套件中。每个测试套件通常由多个测试类组成,其中每个类可以代表正在测试的特定功能或组件。因此,无论您是在顶级 Java 开发服务公司工作,还是希望提高测试技能的学生,我们都会涵盖所有内容。

什么是单元测试?

单元测试是一种软件测试过程,其中测试各个代码单元(例如方法、类和模块)以查看它们是否按预期工作。这是软件测试生命周期的第一步。Java 中的单元测试通常使用JUnit完成。单元测试的目的是隔离和验证代码中各个单元的正确性。失败的单元测试可以提前预示潜在问题,但集成测试将进一步确保整个系统的凝聚力和功能性。

想象一下组装汽车的过程。在将零部件组装在一起之前,每个零部件都要经过严格的测试。从长远来看,这样做效率更高,耗时更少。现在想象一下,如果所有零部件都没有经过适当的测试就组装在一起,那么汽车就无法正常工作。甚至要找出哪个部件有故障都需要花费大量的时间和精力,而实际修复它则需要花费更多的时间和精力。

这就是我们需要单元测试的原因。它使我们更容易及早发现错误并节省开发时间。

什么是 Java 集成测试?

集成测试是一种软件测试方法,其中将不同的模块耦合在一起并进行测试。目标是查看模块耦合在一起时是否按预期工作。它通常在单元测试之后和系统测试之前执行。

集成测试对于由多个层和组件组成的、彼此通信并与外部系统或服务通信的应用程序尤其重要。

想象一下,您正在从头开始构建一台个人计算机 (PC)。PC 由各种组件组成,例如主板、处理器、内存、存储设备、显卡等。您之前已经测试过所有组件。但是当您将它们集成到系统中时,它们无法工作。原因不是因为各个组件存在某些缺陷,而是它们彼此不兼容。集成测试可帮助我们识别这些类型的错误。

集成测试和单元测试之间的区别

范围:单元测试旨在测试最小的可测试代码单元,而集成测试则侧重于测试系统中多个组件的交互。

复杂性:单元测试往往更简单、更集中,因为它们单独处理各个组件。它们可以相对轻松地编写和执行。另一方面,集成测试通常更复杂,因为需要协调和验证多个组件之间的交互。它们需要更高级别的设置和配置才能准确模拟真实场景。

执行顺序:通常,单元测试在集成测试之前进行。我们首先需要验证各个单元是否功能齐全。然后我们才能将它们集成到更大的模块中并测试它们之间的关系。

集成测试的不同方法

进行集成测试有不同的策略。其中最常见的是大爆炸方法和增量(自上而下和自下而上)方法。

大爆炸

在这种方法中,大多数软件模块都耦合在一起并进行测试。它适用于模块较少的小型系统。在某些情况下,系统的组件可能紧密耦合或高度相互依赖,使得增量集成变得困难或不切实际。

大爆炸的优势
  1. 适用于较小的系统。
  2. 与其他方法相比,它耗费的时间更少。
大爆炸的缺点
  1. 然而,使用这种方法很难找到测试中发现的缺陷的根本原因。
  2. 由于您一次要测试大多数模块,因此很容易错过一些集成。
  3. 随着项目复杂性的增加,维护变得更加困难。
  4. 您必须等待大多数模块的开发。

自下而上的方法

自下而上的方法侧重于开发和测试系统中最低级别的独立模块,然后再将它们集成到更大的模块中。这些模块经过测试后,再集成到更大的模块中,直到整个系统都集成并测试完毕。

在这种方法中,测试从最简单的组件开始,然后向上移动,添加和测试更高级别的组件,直到构建和测试整个系统。

这种测试的最大优点是你不必等待所有模块的开发。相反,你可以为已经构建的模块编写测试。最大的缺点是你对关键模块的行为不太清楚。

优点
  1. 自下而上强调编码和早期测试,可以在指定第一个模块后立即开始。
  2. 由于测试过程是从低级模块开始的,因此非常清晰,并且易于编写测试。
  3. 不需要了解结构设计的细节。
  4. 由于您从最低级别开始,因此开发测试条件通常更容易。
缺点
  1. 如果系统由大量子模块组成,这种方法就会变得耗时且复杂。
  2. Java 开发人员对关键模块的行为没有清晰的认识。

自上而下的方法

自上而下的Java 集成测试是一种软件测试方法,其中测试过程从软件系统最顶层最关键的模块开始,并逐渐向较低级别的模块发展。它涉及以分层方式测试软件系统不同组件之间的集成和交互。

在自上而下的集成测试中,首先测试较高级别的模块,同时用存根或模拟版本替换较低级别的模块,这些存根或模拟版本可提供较低级别的模块的预期行为。随着测试的进展,较低级别的模块逐渐与较高级别的模块合并并一起进行测试。

优点
  1. 首先测试关键模块。
  2. 开发人员对应用程序关键功能的行为有清晰的认识。
  3. 轻松检测顶层的问题。
  4. 由于测试过程的向下增量特性,隔离接口和数据传输错误更加容易。
缺点
  1. 它需要使用模拟、存根和间谍。
  2. 您仍然需要等待关键模块的开发。

混合(三明治)方法

混合或夹层方法是自下而上方法和自上而下方法的结合。通常,这种方法有多个层,每个层都使用自上而下或自下而上的方法构建。

集成测试涉及的步骤

选择正确的工具和框架

Java 是一种流行的高级编程语言。它拥有庞大的测试框架和库生态系统。以下是一些最常用的集成测试工具:

  1. JUnit5:一种广泛使用的Java 测试框架,可用于编写单元测试和集成测试。
  2. TestNG:另一个流行的测试框架,提供并行测试执行和测试配置灵活性等功能。
  3. Spring Boot 测试:如果您使用Spring Boot,此模块为 Java 集成测试提供了广泛的支持,包括 @SpringBootTest 注释。
  4. Mockito:一个强大的模拟框架,允许您模拟依赖关系并专注于单独测试特定组件。
  5. Testcontainers:一个 Java 库,允许您在测试期间定义和运行依赖项的 Docker 容器。

在本文中,我们将使用 JUnit5 作为主要测试框架。我们将使用 Spring Boot Test 框架提供一两个示例。这些框架的语法非常直观,因此即使您不使用 JUnit5,也很容易理解。

设置测试环境

设置测试环境是 Java 集成测试的第一步。理想情况下,您需要设置数据库、模拟一些依赖项并添加测试数据。

添加测试数据

在开始测试之前,您可以使用 Junit5 @BeforeEach 注释填充数据库。

@DataJpaTest
public class UserRepositoryIntegrationTest {

    @Autowired
    private UserRepository userRepository;

    @BeforeEach
    public void setUpTestData() {
        User user1 = new User("John Doe", "johndoe@gmail.com");
        User user2 = new User("Jane Smith", "janesmith@gmail.com");

        userRepository.save(user1);        userRepository.save(user2);
    }
}
模拟和存根

模拟和存根可帮助您隔离依赖项或模拟尚未实现的依赖项。在下面给出的示例中,我们正在为 UserRepository 类创建一个模拟。

import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.Mockito.*;

public class UserServiceTest {

    @Test
    public void testCreateUser() {
        UserRepository userRepository = mock(UserRepository.class);
        UserService userService = new UserService(userRepository);
        User user = new User("John Doe", "john.doe@example.com");

        when(userRepository.save(user)).thenReturn(true);
        boolean result = userService.createUser(user);

        verify(userRepository, times(1)).save(user);
        assertEquals(true, result);
    }
}
设置H2数据库

H2 是一个内存数据库,由于其速度快且轻量,因此经常用于测试。要配置 H2 进行集成测试,您可以使用 Spring Boot 提供的 @DataJpaTest 注释。此注释设置了一个内存数据库,您可以使用 JPA 存储库与其交互。

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import static org.junit.jupiter.api.Assertions.assertEquals;

@DataJpaTest
public class UserRepositoryIntegrationTest {

    @Autowired
    private UserRepository userRepository;

    @Test
    public void testSaveUser() {
        User user = new User("John", "johndoe@example.com");
        userRepository.save(user);
        User savedUser = userRepository.findByEmail("johndoe@example.com");
        assertEquals(user.getName(), savedUser.getName());
        assertEquals(user.getEmail(), savedUser.getEmail());
    }
}

需要注意的一点是,您的应用程序可能没有使用 H2 作为主数据库。在这种情况下,您没有模仿生产环境。如果您想在测试环境中使用数据库 PostgreSQL 或 MongoDB,则应该使用容器(稍后讨论)。

记录和报告

记录测试最简单的方法之一是通过日志记录。日志可以帮助您快速识别或复制应用程序中任何缺陷的根本原因。

这是使用 Logback 和 SLF4J 的简单日志设置。Logback 帮助我们自定义记录的消息。我们可以提供日期、时间、线程、日志级别、跟踪等详细信息。首先创建一个 logback.xml 文件并添加如下所示的配置。

<configuration> 
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender"> 
        <encoder> 
            <pattern>%d{HH:mm:ss.SSS} [%thread] %level - %msg%n</pattern> 
        </encoder> 
    </appender> 

    <root level="debug"> 
        <appender-ref ref="STDOUT" /> 
    </root> 
</configuration>

这里,%d{HH:mm:ss.SSS} 打印时间(H 表示小时,m 表示分钟,s 表示秒,S 表示毫秒)。%thread、%level、%msg 和 %n 分别打印线程名称、日志级别、消息和换行符。上面创建的附加程序在标准输出中显示信息。但您可以执行诸如将日志存储在文件中之类的操作。

现在我们可以在我们的 Java 代码中使用来自 SLF4J 的 Logger 外观。

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class ExampleTest {
    private static final Logger logger
            = LoggerFactory.getLogger(ExampleTest.class);

    public static void main(String[] args) {
        logger.info("Hello from {}", ExampleTest.class.getSimpleName());
    }
}

如果你运行它,输出将会是这样的:

14:45:01.260 [main] INFO - Hello from ExampleTest

运行容器化测试

Docker 容器是模拟生产环境的最佳方式之一,因为在许多情况下,您的应用程序本身就运行在某些远程容器中。为了进行这些演示,我们将编写一个简单的测试,然后在 docker 容器中运行它。

// SampleController.java
@RestController
public class SampleController {
    @GetMapping("/hello")
    public String sayHello() {
        return "Hello world";
    }
}

// SampleApplication.java
@SpringBootApplication
public class SampleApplication {
    public static void main(String[] args) {
        SpringApplication.run(SampleApplication.class, args);
    }
}

上面的代码创建了一个返回“Hello world”的简单 REST api。我们可以使用 Spring Boot Test 来测试这个 api。在这里,我们只是检查 api 是否返回响应,并且响应主体必须包含“Hello world”。

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class SampleApplicationTests {
    
    @LocalServerPort
    private int port;
    
    @Autowired
    private TestRestTemplate restTemplate;
    
    @Test
    public void testHelloEndpoint() {
        ResponseEntity<String> response = restTemplate.getForEntity("http://localhost:" + port + "/hello", String.class);
        assertEquals(HttpStatus.OK, response.getStatusCode());
        assertEquals("Hello, World!", response.getBody());
    }
}

现在,您可以创建一个 Dockerfile 并添加以下配置。Dockerfile 包含有关如何构建应用程序的说明。

FROM openjdk:17-jre-slim
WORKDIR /app
COPY target/sample-application.jar .
CMD ["java", "-jar", "sample-application.jar"]

运行以下命令来构建并启动您的docker容器。

docker build -t sample-application .
docker run -d -p 8080:8080 sample-application

您可以使用 docker-compose 来编排多个容器。docker compose 的一个挑战是当您运行集成测试时;您无法在运行时更改配置。此外,即使测试失败,您的容器仍将继续运行。相反,我们将使用一个名为 testcontainers 的库来动态启动和停止容器。

测试容器

Testcontainers 是一个 Java 库,可简化运行隔离的一次性容器以进行集成测试的过程。它为流行的数据库(例如 PostgreSQL、MySQL、Oracle、MongoDB)、消息代理(例如 Kafka、RabbitMQ)、Web 服务器(例如 Tomcat、Jetty)等提供轻量级、预配置的容器。

使用 Testcontainers,您可以在 Java 集成测试中定义和管理容器,这样您就可以针对依赖项的真实实例进行测试,而无需外部基础架构。Testcontainers 负责容器生命周期管理、自动配置以及与 JUnit 或 TestNG 等测试框架的集成。

import org.junit.jupiter.api.Test;
import org.testcontainers.containers.PostgreSQLContainer;
import org.testcontainers.junit.jupiter.Container;
import org.testcontainers.junit.jupiter.Testcontainers;
import static org.junit.Assert.*;

@Testcontainers
public class PostgreSQLIntegrationTest {

    @Container
    private static final PostgreSQLContainer<?> postgresContainer = new PostgreSQLContainer<>("postgres:latest")
            .withDatabaseName("db_name")
            .withUsername("johndoe")
            .withPassword("random_password");

    @Test
    public void testPostgreSQLContainer() {
           assertTrue(postgresContainer.isRunning())
    }
}

编写集成测试的最佳实践

尽早开始编写单元测试和集成测试

在传统的瀑布式方法中,任务是按顺序执行的。测试通常在开发周期的后期进行。由于您的应用程序稍后进行测试,因此错误被忽视并进入生产环境的可能性相当高。

相比之下,采用敏捷方法,您可以尽早开始编写测试。这可以确保每次对代码库进行小幅更改时,您都会立即收到反馈,了解更改是否对现有代码库产生影响。如果单元测试失败并且您意识到存在问题,您可以立即解决它,以免它在后期变成大问题。这是敏捷方法的主要优势,尽早编写测试可以提供持续的反馈,从而更难在任何阶段引入错误。

确定测试优先级

集成测试可能很慢,在需要大量时间和资源的情况下,重复运行它们变得不切实际。在这种情况下,对测试进行优先级排序可以节省大量宝贵的时间。您可以根据与故障相关的风险级别、所测试功能的复杂性以及对最终用户或整个系统的潜在影响等因素对测试进行优先级排序。

在 Junit5 中,使用测试类,您可以为测试添加标签并设置优先级。以下是示例。

// SampleTests.java
public class SampleTests {
 @Test
 @Tag("HIGH")
 public void testCriticalFunctionality() {}

 @Test
 @Tag("LOW")
 public void testLessCriticalFunctionality() {}
}

// HighPriorityTestSuite.java
// only testing the high priority integrations
import org.junit.platform.suite.api.IncludeTags;
import org.junit.platform.suite.api.SelectPackages;
import org.junit.platform.suite.api.Suite;

@Suite
@IncludeTags("HIGH")
public class HighPriorityTestSuite {}

尽可能模拟生产环境

创建尽可能与生产环境相似的测试环境。这包括设置类似的配置、数据库、网络条件和任何外部依赖项。

为所有场景设计测试用例

设计测试用例并确定适合所有场景的正确测试方法。创建涵盖各种场景和边缘情况的测试用例和测试方法,以确保最大覆盖范围。测试正面场景并进行负面集成测试,以验证系统在不同情况下的行为。

记录并报告您的测试

当集成测试失败时,尤其是在大型软件项目中,找出原因可能非常耗时。集成测试失败后,记录测试运行情况会很有帮助,这样您就可以轻松找出根本原因或重现问题。

结论

在本文中,我们探讨了 Java 集成测试的概念、优势和各种 Java 集成测试框架。我们还介绍了如何使用 JUnit5、Spring Boot Test、TestContainers 和 Logback 等框架编写有效的集成测试。集成测试至关重要,因此集成测试在确保Java 应用程序的性能、质量和功能方面起着至关重要的作用。它还允许我们验证不同组件和层之间的交互和依赖关系。

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

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

相关文章

三级_网络技术_27_计算机网络环境及应用系统的安装与调试

1.对于频繁改变位置并使用DHCP获取PP地址的DNS客户端&#xff0c;为减少对其资源记录的手动管理&#xff0c;可采取的措施是()。 允许动态更新 使用反向查找区域 增加别名记录 设置较小的生存时间 2.下列Windows 2003系统命令中&#xff0c;可以清空DNS缓存(DNScache)的是…

在Ubuntu上有什么命令,或者是系统文件能告诉我链接nvme ssd的pcie槽位是不是支持热插拔功能?

&#x1f3c6;本文收录于《CSDN问答解惑-专业版》专栏&#xff0c;主要记录项目实战过程中的Bug之前因后果及提供真实有效的解决方案&#xff0c;希望能够助你一臂之力&#xff0c;帮你早日登顶实现财富自由&#x1f680;&#xff1b;同时&#xff0c;欢迎大家关注&&收…

打靶记录11——Billu_b0x

靶机&#xff1a; https://download.vulnhub.com/billu/Billu_b0x.zip难度&#xff1a; 中&#xff08;两种攻击路线&#xff09; 目标&#xff1a; 取得root权限 涉及的攻击方法&#xff1a; 主机发现端口扫描Web信息收集SQL注入&#xff08;Sqlmap跑不出来&#xff09;…

Spring Boot 3.3 【四】Spring Boot 整合JPA

&#x1f31f; 技术人聊管理 请关注 【技术管理修行】 一、JPA 简介 Spring Data JPA 是 Spring Data 项目的一部分&#xff0c;它为使用 Java Persistence API (JPA) 进行数据库访问提供了一种非常简便的方式。Spring Data JPA 的主要目的是简化基于 JPA 的数据访问层的开发工…

leetCode - - - 栈和队列

目录 1.有效的括号&#xff08; LeetCode 20 &#xff09; 2.最小栈&#xff08; LeetCode 155 &#xff09; 3.接雨水&#xff08; LeetCode 42 &#xff09; 4.逆波兰表达式求值&#xff08;LeetCode 150&#xff09; 5.柱状图中最大的矩形&#xff08;LeetCode 84&…

SAP LE学习笔记02 - WM和库存管理(IM)之间的关系,保管Lot(Quant)

上一章学习了LE的基础知识。 1&#xff0c;LE的概述&#xff0c;LE里面包含下面3个大的模块 - LE-WM 仓库管理 / - LE-SHP 发货/ - LE-TRA 运输 2&#xff0c;仓库的结构 - 仓库番号 / -保管域Type(存储区域)/ - 保管区画(存储区)/ - 棚番&#xff08;Storage Bin 仓位&…

IDEA快捷键(Ctrl + tab)非常好用 切换最近使用的编辑器选项卡

文章目录 1、为什么要使用 ctrl tab 快捷键&#xff1f;2、使用 ctrl tab 快捷键 1、为什么要使用 ctrl tab 快捷键&#xff1f; 当我们点击 ctrl alt 鼠标左键点击 进入方法的实现时&#xff0c;这个时候我们会在这个实现类中不断的点击&#xff0c;查看源码&#xff0c…

【安全工具推荐-Search_Viewer资产测绘】

目录 一、工具介绍 二、工具配置 三、传送门 一、工具介绍 Search_Viewer&#xff0c;集Fofa、Hunter鹰图、Shodan、360 quake、Zoomeye 钟馗之眼、censys 为一体的空间测绘gui图形界面化工具&#xff0c;支持一键采集爬取和导出fofa、shodan等数据&#xff0c;方便快捷查看…

竞争与冒险/亚稳态/跨时钟域

竞争与冒险/亚稳态/跨时钟域 文章目录 竞争与冒险/亚稳态/跨时钟域1.亚稳态1.1 好文章1.2 什么是亚稳态1.3亚稳态的解决办法1.3.1 跨时钟域的亚稳态——采用同步机制1.3.1.1 单比特(脉冲和单比特流)的跨时钟域同步1.3.1.1.1 单比特流的跨时钟域同步1.3.1.1.2 脉冲的跨时钟域同步…

.NET辅助角色服务入门简介

在日常开发中&#xff0c;并不是所有的功能都是用户可见的&#xff0c;还在一些背后默默支持的程序&#xff0c;这些程序通常以服务的形式出现&#xff0c;统称为辅助角色服务。今天以一个简单的小例子&#xff0c;简述基于.NET开发辅助角色服务的相关内容&#xff0c;仅供学习…

旅行商问题变体:欧几里德平面中线段最小连接算法

问题描述 假设在欧几里德平面上有有限多条线段&#xff0c;如何将它们连接起来&#xff0c;形成一条最小长度的线段链&#xff1f; 首先&#xff0c;自然可以穷举所有情况&#xff0c;找到最优解。还可以采用动态规划、贪心算法找到局部最优解。 另外&#xff0c;则将其作为T…

定时器延时us(hal库)

目录 定时器延时us 配置cubemx ​编辑​编辑新建模块 代码实现 测试代码 定时器延时us 复制工程模板 配置cubemx 新建模块 新建文件HardWare 添加文件HardWare 添加文件路径 添加HardWare 或者直接输入/HardWare 添加.c和.h文件 .h文件 添加防重复定义代码 #ifnd…

麒麟v10(ky10.x86_64)升级——openssl-3.2.2、openssh-9.8p1

系统版本: ky10.x86_64 下载安装包并上传 openssh下载地址 https://cdn.openbsd.org/pub/OpenBSD/OpenSSH/portable openssl下载地址 https://openssl-library.org/source/index.html zlib下载地址 https://zlib.net/fossils/ 上传安装包 备份配置文件 cp -r /etc/ssh /et…

DePT: Decoupled Prompt Tuning

当前的问题:Base-New Tradeoff(BNT)困境 现有的提示调优方法通常无法摆脱Base-New Tradeoff(BNT)困境&#xff0c;即调优/调整的模型对基本任务的泛化效果越好&#xff0c;对新任务的泛化效果就越差(包含不可见的类)&#xff0c;反之新任务的泛化效果越好&#xff0c;所需要的…

北京城市图书馆-非遗文献馆:OLED透明拼接屏的璀璨应用

在数字化与传统文化深度融合的今天&#xff0c;北京城市图书馆的非遗文献馆以一场前所未有的视觉盛宴&#xff0c;向世人展示了OLED透明拼接屏的非凡魅力与无限可能。这座集阅读、展示、体验于一体的非遗文献馆&#xff0c;通过2*7布局的OLED透明拼接屏&#xff0c;不仅为传统非…

2024.8.15(python管理mysql、Mycat实现读写分离)

一、python管理mysql 1、搭建主mysql [rootmysql57 ~]# tar -xf mysql-5.7.44-linux-glibc2.12-x86_64.tar.gz [rootmysql57 ~]# cp -r mysql-5.7.44-linux-glibc2.12-x86_64 /usr/local/mysql [rootmysql57 ~]# rm -rf /etc/my.cnf [rootmysql57 ~]# mkdir /usr/local/mysql…

高数3.4 函数单调性和曲线的凹凸性

目录 1. 定义 2. 判断方法 3. 证明 4. 例子 1. 定义 2. 判断方法 3. 证明 4. 例子

高频焊机系统介绍及工作原理

一、高频焊机生产的工艺流程 将带钢卷成圆筒形&#xff0c;然后将接缝焊接起来这就形成了焊管。近些年来&#xff0c;随着焊接技术的进步以及社会工业化进程的加快&#xff0c;钢管的焊管技术也得到了较快的发展。其中直缝高频焊机应用广泛&#xff0c;其原理是利用高频电流的邻…

基于Java+SpringBoot+Vue的网上点餐系统

基于JavaSpringBootVue的网上点餐系统 前言 ✌全网粉丝20W,csdn特邀作者、博客专家、CSDN[新星计划]导师、java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取项目下载方式&#x1f345; 哈喽…

大模型学习方法之——大模型技术学习路线

“ 技术学习无非涵盖三个方面&#xff0c;理论&#xff0c;实践和应用**”** 大模型技术爆火至今已经有两年的时间了&#xff0c;而且大模型技术的发展潜力也不言而喻。因此&#xff0c;很多人打算学习大模型&#xff0c;但又不知道该怎么入手&#xff0c;因此今天就来了解一下…