邪修实战系列(3)


1、第一阶段邪修实战总览(9.1-9.30)

把第一阶段(基础夯实期)的学习计划拆解成极具操作性的每日行动方案。这个计划充分利用我"在职学习"的特殊优势,强调"用输出倒逼输入",确保每一分钟的学习都直接服务于面试和实战。

  • 核心目标:构建起Java后端开发的知识树主干,并能通过一个小型项目串联起所有知识点。
  • 核心策略:每天3小时雷打不动的高效学习(工作日可分散,周末集中攻坚)。

2、周目标(9.1-9.14)

Java核心+Sprig Boot破冰,能独立使用Spring搭建Web后端并提供RESTful接口。


3、分日目标与邪修技巧

3.1、Day 9-10: Spring Boot开发RESTful API

  • 行动:设计/api/ads相关的接口(GET、POST),用@PostMapping接收前端传过来的JSON数据并解析。
  • 邪修技巧:使用Postman软件测试自己写的接口,确保能通。这个过程和前端联调一模一样,这就是经验。

3.1.1、学习分享

接下来我会讲解一下我在学习这部分知识的时候编写与测试GET接口的详细步骤:

第一步:设计我们的数据模型

在编写接收数据的API之前,需要先定义数据长什么样。我们可以直接在我们7-8天测试的那个demo中进行修改。

  • 创建实体类:在scr/main/java/com/adcampaign下新建一个entity包,并在该包下创建一个新的Java类,名为AdAdvertisement
  • 编写类代码:
java 复制代码
package com.adcampaign.entity;

/**
 * 广告活动数据模型类
 * 用于存储广告活动的核心数据,并提供数据计算和分析功能
 * 每个实例代表一天的广告活动数据
 */
public class AdAdvertisement {
    // 日期(例如:"2023-10-01")
    private String date;
    // 广告花费(单位:元)
    private double cost;
    // 线索提交个数(用户提交的潜在客户信息数量)
    private int leadCount;
    // 私信消息数(收到的用户私信数量)
    private int messageCount;

    /**
     * 构造方法:创建广告活动数据对象
     * @param date 日期字符串
     * @param cost 广告花费金额
     * @param leadCount 线索数量
     * @param messageCount 私信消息数量
     */
    public AdAdvertisement(String date, double cost, int leadCount, int messageCount) {
        this.date = date;
        this.cost = cost;
        this.leadCount = leadCount;
        this.messageCount = messageCount;
    }

    // Getter方法:获取私有属性的值
    public String getDate() { return date; }
    public double getCost() { return cost; }
    public int getLeadCount() { return leadCount; }
    public int getMessageCount() { return messageCount; }

    // Setter方法:修改私有属性的值
    public void setDate(String date) { this.date = date; }
    public void setCost(double cost) { this.cost = cost; }
    public void setLeadCount(int leadCount) { this.leadCount = leadCount; }
    public void setMessageCount(int messageCount) { this.messageCount = messageCount; }

    /**
     * 计算单个线索成本
     * 公式:单个线索成本 = 广告花费 ÷ 线索数量
     * @return 单个线索成本(元/个),如果线索数量为0则返回0
     */
    public double calculateCostPerLead() {
        // 防止除以零错误:如果线索数为0,返回0
        return (leadCount > 0) ? cost / leadCount : 0;
    }

    /**
     * 计算私信消息转化成本
     * 公式:私信转化成本 = 广告花费 ÷ 私信消息数
     * @return 私信转化成本(元/条),如果私信数为0则返回0
     */
    public double calculateCostPerMessage() {
        // 防止除以零错误:如果私信数为0,返回0
        return (messageCount > 0) ? cost / messageCount : 0;
    }

    /**
     * 分析广告花费随日期的变动情况
     * @param previousCampaign 上一个日期的广告活动数据(用于比较)
     * @return 变动情况描述,包含变动金额和百分比
     */
    public String analyzeCostChange(AdAdvertisement previousCampaign) {
        // 检查是否有历史数据可供比较
        if (previousCampaign == null) return "无历史数据可供比较";

        // 计算变动金额:当前花费 - 上一期花费
        double changeAmount = this.cost - previousCampaign.getCost();
        // 计算变动百分比:(变动金额 ÷ 上一期花费) × 100%
        double changeRate = (changeAmount / previousCampaign.getCost()) * 100;

        // 格式化输出:保留两位小数,显示变动金额和百分比
        return String.format("花费变动: %.2f元 (%.2f%%)", changeAmount, changeRate);
    }

    /**
     * 分析单个线索成本随日期的变动情况
     * @param previousCampaign 上一个日期的广告活动数据(用于比较)
     * @return 变动情况描述,包含变动金额和百分比
     */
    public String analyzeCostPerLeadChange(AdAdvertisement previousCampaign) {
        if (previousCampaign == null) return "无历史数据可供比较";

        // 获取当前和上一期的单个线索成本(调用已有的计算方法)
        double currentCpl = this.calculateCostPerLead();
        double previousCpl = previousCampaign.calculateCostPerLead();

        // 防止除以零错误
        if (previousCpl <= 0) return "历史单个线索成本为0,无法计算变动率";

        double changeAmount = currentCpl - previousCpl;
        double changeRate = (changeAmount / previousCpl) * 100;

        return String.format("单个线索成本变动: %.2f元 (%.2f%%)", changeAmount, changeRate);
    }

    /**
     * 分析私信消息转化成本随日期的变动情况
     * @param previousCampaign 上一个日期的广告活动数据(用于比较)
     * @return 变动情况描述,包含变动金额和百分比
     */
    public String analyzeMessageCostChange(AdAdvertisement previousCampaign) {
        if (previousCampaign == null) return "无历史数据可供比较";

        // 获取当前和上一期的私信转化成本(调用已有的计算方法)
        double currentCpm = this.calculateCostPerMessage();
        double previousCpm = previousCampaign.calculateCostPerMessage();

        // 防止除以零错误
        if (previousCpm <= 0) return "历史私信转化成本为0,无法计算变动率";

        double changeAmount = currentCpm - previousCpm;
        double changeRate = (changeAmount / previousCpm) * 100;

        return String.format("私信转化成本变动: %.2f元 (%.2f%%)", changeAmount, changeRate);
    }
}

(其实要是大家仔细研究过这个系列的前面,并且看了Data_Board-README文档的话,不难发现,其实这个类是直接复制的,仅仅只是改了名字而已)

第二步:升级控制器

现在我们需要改造我们Day7-8创建的控制器或者来创建一个更专业的控制器,我们这里直接来创建吧

  • 创建控制器:在controller包下,创建AdController.java
  • 编写Get接口(返回数据列表):
java 复制代码
package com.adcampaign.controller;

import com.adcampaign.entity.AdAdvertisement; // 导入刚刚创建的实体类
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.Arrays;
import java.util.List;

@RestController
@RequestMapping("/api/ads") // 所有这个控制器下的接口路径都以 /api/ads 开头
public class AdController {

    // 模拟一些静态数据
    private List<AdAdvertisement> adList = Arrays.asList(
            new AdAdvertisement("2025-9-28",5000,10,50),
            new AdAdvertisement("2025-9-29",6000,15,60)
    );

    @GetMapping // 等价于 @GetMapping(""),访问路径是 /api/ads
    public List<AdAdvertisement> getAllAds() {
        return adList; // Spring Boot会自动将这个List序列化为JSON返回
    }
}

第三步:编写启动类

启动类通常位于项目最顶级的包下,之前7-8天的测试自动生成了一个启动类,我们这里直接删除即可,接下来在src/main/java/com/adcampaign包下新建一个启动类叫Data_Board_Application.java

  • 编写启动类代码:
java 复制代码
package com.adcampaign;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.RestController;

// 核心注解:标记这是一个Spring Boot应用的主配置类
@SpringBootApplication
@RestController
public class Data_Board_Appliction {
	public static void main(String[] args) {
		SpringApplication.run(Data_Board_Appliction.class, args);
	}
}
  • 测试GET接口
    • 启动项目,运行启动类Data_Board_Application.java,然后在在终端窗口如果出现下面的样式则代表运行成功:
    • 打开浏览器,访问网址http://localhost:8080/api/ads,如果在浏览器中显示如图所示的两条包含广告数据的JSON数组,就证明成功了。

第四步:编写POST接口(接收数据)

这是最关键的一步,可以学会如何接收前端发送的JOSN数据。

  • 在AdController中添加POST接口:
java 复制代码
// 新增POST接口
    @PostMapping
    public String createAd(@RequestBody AdAdvertisement newAd) {
        // @RequestBody 注解告诉Spring:"请把请求体里的JSON数据,转换成一个AdAdvertisement对象"

        // 模拟处理:打印接收到数据
        System.out.println("接收到新的广告数据:");
        System.out.println("日期: " + newAd.getDate());
        System.out.println("广告花费: " + newAd.getCost());
        System.out.println("线索提交个数: " + newAd.getLeadCount());
        System.out.println("私信消息数: " + newAd.getMessageCount());

        // 这里暂时不做实际存储,只是返回成功消息
        return "广告数据创建成功! " + newAd.getDate();
    }
  • AdController控制器的完整代码如下:
java 复制代码
package com.adcampaign.controller;

import com.adcampaign.entity.AdAdvertisement; // 导入刚刚创建的实体类
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;


import java.util.Arrays;
import java.util.List;

@RestController
@RequestMapping("/api/ads") // 所有这个控制器下的接口路径都以 /api/ads 开头
public class AdController {

    // 模拟一些静态数据
    private List<AdAdvertisement> adList = Arrays.asList(
            new AdAdvertisement("2025-9-28",5000,10,50),
            new AdAdvertisement("2025-9-29",6000,15,60)
    );

    @GetMapping // 等价于 @GetMapping(""),访问路径是 /api/ads
    public List<AdAdvertisement> getAllAds() {
        return adList; // Spring Boot会自动将这个List序列化为JSON返回
    }

    // 新增POST接口
    @PostMapping
    public String createAd(@RequestBody AdAdvertisement newAd) {
        // @RequestBody 注解告诉Spring:"请把请求体里的JSON数据,转换成一个AdAdvertisement对象"

        // 模拟处理:打印接收到数据
        System.out.println("接收到新的广告数据:");
        System.out.println("日期: " + newAd.getDate());
        System.out.println("广告花费: " + newAd.getCost());
        System.out.println("线索提交个数: " + newAd.getLeadCount());
        System.out.println("私信消息数: " + newAd.getMessageCount());

        // 这里暂时不做实际存储,只是返回成功消息
        return "广告数据创建成功! " + newAd.getDate();
    }
}

第五步:测试POST接口(使用Postman)

由于我们不能用浏览器地址栏测试POST请求,必须使用Postman或类似的API测试工具,所以需要我们来下载并安装一个Postman来进行测试。

  • 大家可以跟着这篇文章来初步了解一下Postman,postman接口测试工具详解【全】,这篇文章中详细介绍了如何下载、安装与一些简单的使用。
  • 创建一个新请求:
    • 方法选择:POST
    • URL输入:localhost:8080/api/ads
  • 设置请求头:
    • 点击Headers标签
    • 添加一个Key:Content-Type,Value:application/json
  • 设置请求体:
    • 点击Body标签
    • 选择raw和JOSN
    • 在下方文本框中输入一段JOSN数据:
bash 复制代码
{
    "date": "2025-09-30",
    "cost": 100.5,
    "leadCount": 5,
    "messageCount": 7
}
  • 点击Send:

    • 成功标志:下方响应去会看到广告数据创建成功2025-9-30
    • 同时,回头看我们的IDE控制台(运行Spring Boot)的窗口,应该能看到打印出的接收到的数据信息。
  • 这个项目的源码我放到了我的GitHub里,点击Data_Board_Spring_Boot链接直接学习和使用即可~


总结

@RequestBody是灵魂:

  • 它完成了HTTP请求体 -> Java对象的魔法转换。这个过程叫"反序列化",由Spring内置的Jackson库自动完成。只需要定义一个Java类,字段名能和JSON的key对上就行。

为什么要有无参构造器和Getter/Setter?

  • Jackson库在创建对象和读取/设置属性时需要使用它们。没有他们,转换就会失败。这也是之前为什么要强调生成这些方法的原因。

Web开发经验

  • 使用Spring Boot的@RestController开发过RESTful接口,包括处理GET和POST请求。并且我知道如何使用@RequestBody注解来接收前端发送的JSON格式参数,完成反序列化。

@SpringBootApplication

是一个组合注解,它包含了三个核心功能:

  • @SpringBootConfiguration:标记该类为配置类
  • @EnableAutoConfiguration:开启自动配置,这是Spring Boot的魔法所在,它根据您引入的jar包依赖(如Spring-Web,MySQL Driver),自动配置应用程序锁需的组件(如Tomcat服务器、SpringMVC)。
  • @ComponentScan:自动扫描当前包及其子包下的组件(如@Controller,@Service,@RestController,@Component等),并注册为Spring Bean。

包路径的重要性

  • AdControlle控制器类必须放在启动类的同级或者子包下!例如,如果启动类在com.example.demo包下,那么您的控制器最好放在com.example.demo.controller包下。如果放在一个毫不相干的包路径下,@ComponentScan将无法发现它,导致404错误。

相关推荐
BD_Marathon1 天前
SpringBoot程序快速启动
java·spring boot·后端
stillaliveQEJ1 天前
【JavaEE】Spring IoC(二)
java·开发语言·spring
寻星探路1 天前
【Python 全栈测开之路】Python 基础语法精讲(一):常量、变量与运算符
java·开发语言·c++·python·http·ai·c#
stevenzqzq1 天前
Android Studio 断点调试核心技巧总结
android·ide·android studio
行百里er1 天前
代码跑得慢?让Spring的StopWatch告诉你真相!
java·后端·github
又是忙碌的一天1 天前
SpringMVC响应
java·服务器·数据库
万物皆字节1 天前
Spring Cloud Gateway 启动流程源码分析
java·开发语言·spring boot
W001hhh1 天前
260110
java·数据库
stillaliveQEJ1 天前
【JavaEE】Spring IoC(一)
java·spring·java-ee
a程序小傲1 天前
得物Java面试被问:方法句柄(MethodHandle)与反射的性能对比和底层区别
java·开发语言·spring boot·后端·python·面试·职场和发展