使用 ChatGPT 帮助开发人员进行日常工作:一起编写 Spring Boot 应用程序 – 迷途通
使用 ChatGPT 帮助开发人员进行日常工作:一起编写 Spring Boot 应用程序

ChatGPT 越来越像一个不可或缺的工具,可以帮助开发人员在日常生活中自动执行一些操作。他成为同伴,专注于顶层和架构任务。计算器或 Excel 曾经如何显着简化会计师和办公室工作人员的工作。

在本文中,我想通过示例来展示 ChatGPT 现在如何帮助开发人员。我们将提出申请,然后对其进行改进。所有这些都将在与电子助手的对话中进行。

对 ChatGPT 的请求结构可能不仅包括问题本身,还包括三个附加参数。这是一个角色、附加信息和说明。它们不是强制性的,但它们可以为我们提供更多相关信息并获得准确的答案,而不是模糊的表述。就好像我们正在与一位真正的专家交流,深深地沉浸在问题中,而不是文案。

想象一下,我们的任务是创建一个 API,用于从外部来源获取加密货币报价。应用程序必须使用现代技术编写,具有高质量的代码,并且可靠且没有瓶颈。

这里有一点需要考虑。ChatGPT 数据库的信息仅截至 2021 年 <> 月。如果我们要求在之后出现的技术上编写代码,我们将收到类似的消息:

我们将形成一个请求,其中我们还将指定角色和其他信息。

您是专业的软件工程师

给我写一个应用程序,它将从外部来源接收比特币报价

使用 Spring Boot、java 17 和 gradle

ChatGPT一步一步地告诉我们需要做什么:

  • 通过 Spring Initializer 或 IDE 创建 Spring Boot 项目
  • 项目结构
  • 带有代码的类
  • 主 Spring Boot 应用程序类
  • 发布具有所需依赖项的 build.gradle 文件
  • 我们可以用来启动应用程序的命令
  • 我们可以通过 HTTP 访问它的端点地址
    根据最佳实践,它将逻辑划分为模型层、服务和控制器层。它根据 Java 样式的约定给出类和变量的名称。
public class Quote {
    private String symbol;
    private double price;
    // Constructors, getters, setters 
}
@Service
public class QuoteService {
    private static final String QUOTE_API_URL = "https://api.example.com/bitcoin-quotes"; // Replace with actual API URL 

    public Quote getBitcoinQuote() {
        RestTemplate restTemplate = new RestTemplate();
        Quote quote = restTemplate.getForObject(QUOTE_API_URL, Quote.class);
        return quote;
    }
}
@RestController
public class QuoteController {
    @Autowired
    private QuoteService quoteService;

    @GetMapping("/bitcoin-quote")
    public Quote getBitcoinQuote() {
        return quoteService.getBitcoinQuote();
    }
}

由于我们需要可靠性,因此一个数据源可能还不够。让我们让 ChatGPT 为我们提供来自两个数据源的实际信息。不要忘记,自 2021 年数据库填满以来,地址可能已经更改。

实现 getBitcoinQuote 方法。它应该从两个实际数据源接收信息

@Service
public class QuoteService {
    private static final String SOURCE_1_API_URL = "https://api.coindesk.com/v1/bpi/currentprice/BTC.json";
    private static final String SOURCE_2_API_URL = "https://api.blockchain.info/ticker";

    public Quote getBitcoinQuote() {
        RestTemplate restTemplate = new RestTemplate();
        Quote quoteFromSource1 = restTemplate.getForObject(SOURCE_1_API_URL, Quote.class);
        Quote quoteFromSource2 = restTemplate.getForObject(SOURCE_2_API_URL, Quote.class);
        Quote selectedQuote = chooseQuote(quoteFromSource1, quoteFromSource2);
        return selectedQuote;
    }

    private Quote chooseQuote(Quote quote1, Quote quote2) {
        // Example logic: Choose the quote with the higher price 
        if (quote1.getPrice() > quote2.getPrice()) {
            return quote1;
        } else {
            return quote2;
        }
    }
}

ChatGPT 添加了 chooseQuote 方法,其逻辑是选择报价。它使它私有化,因为它不是从其他方法调用的。稍后,我们将能够在我们意义上更改它,例如,如果其中一个数据源突然变得不可用,则仅取非 null 值。

我们不满足于按顺序接收报价,因为这会减慢应用程序的速度。让我们让 ChatGPT 将呼叫转换为并行呼叫,并使用我们需要的技术——反应式。

从外部来源获取报价不是按顺序进行的,而是在反应式的帮助下并行获取

为此,我们需要替换依赖项。机器人会立即警告我们什么。

我们在服务中的 getBitcoinQuote 方法也发生了变化。

    public Mono<Quote> getBitcoinQuote() {
        RestTemplate restTemplate = new RestTemplate();
        Mono<Quote> quoteFromSource1 = Mono.fromCallable(() -> restTemplate.getForObject(SOURCE_1_API_URL, Quote.class));
        Mono<Quote> quoteFromSource2 = Mono.fromCallable(() -> restTemplate.getForObject(SOURCE_2_API_URL, Quote.class));
        return Mono.zip(quoteFromSource1, quoteFromSource2, this::chooseQuote);
    }

我们将以 Mono 的形式给出响应。

@RestController
public class QuoteController {
    @Autowired
    private QuoteService quoteService;

    @GetMapping("/bitcoin-quote")
    public Mono<Quote> getBitcoinQuote() {
        return quoteService.getBitcoinQuote();
    }
}

我们还得到了有关所做工作的详细说明。这对开发人员非常有用,尤其是当他们正在使用新技术并且不完全了解使用它的所有细微差别时。

要了解我们已经正确地编写了应用程序并且逻辑按照我们预期的方式工作,有必要编写测试。我们将要求您使用框架执行此操作。

使用 SpringBootTest 为我们的应用程序编写测试

结果,我们得到了两个测试 – 一个是涵盖控制器的集成,另一个是针对服务的集成。ChatGPT 自行生成测试数据,并使用 MockMvc 的正确方法来测试控制器。它还提供了有关@SpringBootTest注释和模拟如何工作的简要参考@MockBean。一切都方便而清晰——如果您忘记了什么,无需再次谷歌。

@SpringBootTest
public class QuoteServiceIntegrationTest {
    @Autowired
    private QuoteService quoteService;
    @MockBean
    private RestTemplate restTemplate;

    @Test
    public void testGetBitcoinQuote() {
        Quote quote = new Quote("BTC", 52000.0);
        when(restTemplate.getForObject(QuoteService.SOURCE_1_API_URL, Quote.class)).thenReturn(quote);
        when(restTemplate.getForObject(QuoteService.SOURCE_2_API_URL, Quote.class)).thenReturn(quote);
        Mono<Quote> result = quoteService.getBitcoinQuote();
        StepVerifier.create(result).expectNextMatches(q -> q.getPrice() == 52000.0).verifyComplete();
    }
}
@SpringBootTest
@AutoConfigureMockMvc
public class QuoteControllerIntegrationTest {
    @Autowired
    private MockMvc mockMvc;
    @MockBean
    private QuoteService quoteService;

    @Test
    public void testGetBitcoinQuote() throws Exception {
        Quote quote = new Quote("BTC", 52000.0);
        when(quoteService.getBitcoinQuote()).thenReturn(Mono.just(quote));
        mockMvc.perform(get("/bitcoin-quote")
                .accept(MediaType.APPLICATION_JSON))
                .andExpect(status().isOk())
                .andExpect(content().contentType(MediaType.APPLICATION_JSON))
                .andExpect(jsonPath("$.symbol").value("BTC"))
                .andExpect(jsonPath("$.price").value(52000.0));
    }
}

We see that all test data is generated correctly.

It also provides information about which dependencies we need to add to the project.

        dependencies {
            // ... other dependencies 
            
            // Testing dependencies 
            testImplementation 'org.springframework.boot:spring-boot-starter-test' 
            testImplementation 'io.projectreactor:reactor-test' 
            testImplementation 'org.mockito:mockito-core' 
            testImplementation 'org.springframework.boot:spring-boot-starter-webflux' 
            testImplementation 'org.springframework.boot:spring-boot-starter-data-jpa' 
            testImplementation 'org.springframework.boot:spring-boot-starter-data-redis' 
            testImplementation 'org.springframework.boot:spring-boot-starter-security' 
            testImplementation 'org.junit.jupiter:junit-jupiter-api:5.7.0' 
            testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.7.0'
        }

我们有一个现成的 API 来获取报价。我们需要描述和记录它,以便最终确定项目的外部用户或开发人员可以获得所有必要的信息。

为 api 制作文档

ChatGPT 建议通过行业标准 Swagger 来做到这一点。此外,它建议在我们的 build.gradle 文件中添加依赖项,注册 Bean 进行配置。

@Configuration
@EnableSwagger2
public class SwaggerConfig {
    @Bean
    public Docket api() {
        return new Docket(DocumentationType.SWAGGER_2)
                .select()
                .apis(RequestHandlerSelectors.basePackage("com.yourpackage.controller"))
                .paths(PathSelectors.any())
                .build();
    }
}

它向控制器添加必要的注释。

@RestController
@RequestMapping("/api")
@Api(tags = "Bitcoin Quote API")
public class QuoteController {
    @Autowired
    private QuoteService quoteService;

    @GetMapping("/bitcoin-quote")
    @ApiOperation("Get a Bitcoin quote")
    public Mono<Quote> getBitcoinQuote() {
        return quoteService.getBitcoinQuote();
    }
}

它说在启动Spring Boot应用程序时,我们将能够在哪个地址访问Swagger UI。

http://localhost:8080/swagger-ui.html

如果我们想将我们的应用程序重写为另一种编程语言,该怎么办。许多公司现在正在将他们的微服务从 Java 重写为 Kotlin,因为它们在同一个平台上工作。此外,Kotlin 对开发人员来说更简洁易懂,并且具有一些优势,例如使用异步代码的便利性。让我们让 ChatGPT 在 Kotlin 上重写我们的应用程序。

重写 Kotlin 和协程

我们得到了一个新的项目结构,其中包含更新的依赖项和我们的 Kotlin 代码。ChatGPT 还提供了有关协程的简要信息。

@Service
class QuoteService {
    private val SOURCE_1_API_URL = "https://api.coindesk.com/v1/bpi/currentprice/BTC.json"
    private val SOURCE_2_API_URL = "https://api.blockchain.info/ticker"

    suspend fun getBitcoinQuote(): Quote {
        val restTemplate = RestTemplate()
        val quoteFromSource1 = getQuoteAsync(restTemplate, SOURCE_1_API_URL)
        val quoteFromSource2 = getQuoteAsync(restTemplate, SOURCE_2_API_URL)
        val (quote1, quote2) = listOf(
            quoteFromSource1,
            quoteFromSource2
        ).awaitAll()
        return chooseQuote(quote1, quote2)
    }

    private suspend fun getQuoteAsync(restTemplate: RestTemplate, url: String): Quote {
        return kotlin.coroutines.suspendCoroutine { continuation ->
            val quote = restTemplate.getForObject(url, Quote::class.java)
            continuation.resume(quote!!)
        }
    }

    private fun chooseQuote(quote1: Quote, quote2: Quote): Quote {
        // Example logic: Choose the quote with the higher price
        return if (quote1.price > quote2.price) {
            quote1
        } else {
            quote2
        }
    }
}
@RestController
class QuoteController @Autowired constructor(private val quoteService: QuoteService) {
    @GetMapping("/bitcoin-quote")
    suspend fun getBitcoinQuote(): Quote {
        return quoteService.getBitcoinQuote()
    }
}

今天,ChatGPT 可以成为开发人员的成熟助手。它可以:

  • 创建应用程序框架
  • 根据职权范围编写业务逻辑
  • 使用必要的技术
  • 编写测试
  • 生成测试数据
  • 将项目从一种编程语言迁移到另一种编程语言

通过学习如何与 ChatGPT 交互,您可以减少执行日常任务的时间,专注于更有趣的任务。当然,他仍然无法取代程序员;他的代码需要重构和调整以适应他自己。例如,它可以考虑所有极端情况。此外,初学者不应该盲目地进行复制粘贴(就像 Stackoverflow 一样)。

发表回复

您的电子邮箱地址不会被公开。