二、前端Java后端对比指南

前端转Java后端完全指南

📖 写给前端同学的话

你好!欢迎从前端世界来到Java后端开发的世界。这是一份专门为前端开发者准备的Java后端学习指南。

👋 为什么前端同学学后端更容易?

作为前端开发者,你已经具备了很多优势:

技术基础

  • 了解HTTP协议(请求和响应)
  • 熟悉JSON数据格式
  • 掌握JavaScript编程思想
  • 理解前后端交互原理
  • 有开发经验和解决问题的能力

思维优势

  • 熟悉MVC模式(前端框架也用)
  • 理解组件化开发思想
  • 掌握调试技巧
  • 有良好的代码习惯

🎯 你将学到

  • ✅ 如何搭建一个完整的Java后端框架
  • ✅ 各种后端技术的作用和原理
  • ✅ 如何从零开始配置项目
  • ✅ 如何开发和测试API接口
  • ✅ 前后端技术的对比和联系
  • ✅ 前端开发者学Java的注意事项

🤝 第一章:前后端技术对比

1.1 技术栈对比表

对比维度 前端技术 Java后端技术 说明
编程语言 JavaScript/TypeScript Java 服务端开发语言
运行环境 Node.js JVM(Java虚拟机) 代码运行环境
Web框架 Express/Koa/Nest.js Spring Boot Web开发框架
数据库 MongoDB(文档型) MySQL(关系型) 数据存储
ORM框架 Mongoose/Prisma MyBatis/Hibernate 数据库操作
包管理 npm/yarn Maven/Gradle 依赖管理工具
构建工具 Webpack/Vite Maven/Gradle 项目构建工具
开发工具 VS Code IntelliJ IDEA 代码编辑器
API测试 Postman/Axios Postman/Curl 接口测试工具
部署方式 Docker/PM2 Docker/Tomcat 应用部署

1.2 前后端思维差异

🧠 前端思维 vs 后端思维
复制代码
前端思维:
├── 用户体验优先
├── 界面交互流畅
├── 响应式设计
├── 性能优化(首屏加载、渲染)
└── 浏览器兼容性

后端思维:
├── 数据安全优先
├── 业务逻辑正确
├── 性能优化(并发、数据库)
├── 数据一致性
└── 系统稳定性
🎯 关注点差异
关注点 前端 后端
核心 用户界面和交互 数据处理和业务逻辑
数据 展示和交互数据 存储和管理数据
安全 XSS、CSRF防护 身份认证、权限控制、SQL注入防护
性能 页面加载速度、渲染性能 并发处理、数据库查询优化
部署 静态资源部署 应用服务器部署

1.3 前后端交互流程

复制代码
用户操作
   ↓
前端页面(React/Vue)
   ↓
HTTP请求(Axios/Fetch)
   ↓
后端Controller(Spring Boot)
   ↓
Service层(业务逻辑)
   ↓
Mapper层(数据库操作)
   ↓
数据库(MySQL)
   ↓
返回数据
   ↓
后端响应(JSON)
   ↓
前端接收并渲染
   ↓
用户看到结果

🚀 第二章:前端开发者快速上手Java

2.1 JavaScript vs Java对比

🔤 语法对比

变量声明

javascript 复制代码
// JavaScript
let name = "张三";
const age = 25;
var isActive = true;
java 复制代码
// Java
String name = "张三";
final int age = 25;
boolean isActive = true;

函数/方法

javascript 复制代码
// JavaScript
function add(a, b) {
    return a + b;
}

// 箭头函数
const add = (a, b) => a + b;
java 复制代码
// Java
public int add(int a, int b) {
    return a + b;
}

// Lambda表达式(Java 8+)
IntBinaryOperator add = (a, b) -> a + b;

类和对象

javascript 复制代码
// JavaScript(ES6)
class Person {
    constructor(name, age) {
        this.name = name;
        this.age = age;
    }
    
    sayHello() {
        console.log(`Hello, I'm ${this.name}`);
    }
}

const person = new Person("张三", 25);
java 复制代码
// Java
public class Person {
    private String name;
    private int age;
    
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
    
    public void sayHello() {
        System.out.println("Hello, I'm " + name);
    }
    
    // Getter和Setter
    public String getName() {
        return name;
    }
    
    public void setName(String name) {
        this.name = name;
    }
}

Person person = new Person("张三", 25);

数组/集合

javascript 复制代码
// JavaScript
const arr = [1, 2, 3, 4, 5];
arr.push(6);
const filtered = arr.filter(item => item > 3);
java 复制代码
// Java
List<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);
list.add(3);

List<Integer> filtered = list.stream()
    .filter(item -> item > 3)
    .collect(Collectors.toList());

条件判断

javascript 复制代码
// JavaScript
if (score >= 90) {
    console.log("优秀");
} else if (score >= 80) {
    console.log("良好");
} else {
    console.log("继续努力");
}
java 复制代码
// Java
if (score >= 90) {
    System.out.println("优秀");
} else if (score >= 80) {
    System.out.println("良好");
} else {
    System.out.println("继续努力");
}
🎯 关键差异
特性 JavaScript Java
类型系统 动态类型 静态类型
编译 解释执行 编译为字节码
面向对象 基于原型 基于类
并发 单线程(异步) 多线程
内存管理 自动GC 自动GC
运行环境 浏览器/Node.js JVM

2.2 前端开发者容易犯的错误

❌ 常见错误1:类型转换
java 复制代码
// 错误示例
String str = "123";
int num = str; // 编译错误!Java不会自动转换

// 正确做法
int num = Integer.parseInt(str);
❌ 常见错误2:空指针异常
java 复制代码
// 错误示例
String str = null;
int length = str.length(); // NullPointerException

// 正确做法
if (str != null) {
    int length = str.length();
}

// 或者使用Optional
Optional<String> optional = Optional.ofNullable(str);
optional.ifPresent(s -> System.out.println(s.length()));
❌ 常见错误3:数组越界
java 复制代码
// 错误示例
int[] arr = {1, 2, 3};
int value = arr[3]; // ArrayIndexOutOfBoundsException

// 正确做法
if (index >= 0 && index < arr.length) {
    int value = arr[index];
}
❌ 常见错误4:忘记分号
java 复制代码
// JavaScript可以省略
const name = "张三"

// Java必须加
String name = "张三"; // 分号不能省略
❌ 常见错误5:方法参数传递
java 复制代码
// JavaScript按值传递(对象是引用)
function change(obj) {
    obj.name = "李四";
}

// Java按值传递
public void change(Person person) {
    person.setName("李四"); // 会改变原对象
    person = new Person(); // 不会改变原引用
}

2.3 前端开发者的优势

✅ 1. 理解HTTP协议

作为前端开发者,你每天都在使用HTTP:

javascript 复制代码
// 前端发送请求
axios.get('/api/users', {
    params: { page: 1, size: 10 }
});

// 后端接收请求
@GetMapping("/api/users")
public List<User> getUsers(
    @RequestParam(defaultValue = "1") int page,
    @RequestParam(defaultValue = "10") int size
) {
    // 处理逻辑
}
✅ 2. 熟悉JSON格式
javascript 复制代码
// 前端发送JSON
axios.post('/api/users', {
    name: '张三',
    age: 25
});

// 后端接收JSON
@PostMapping("/api/users")
public User createUser(@RequestBody User user) {
    // user对象已经包含name和age
    return userService.create(user);
}
✅ 3. 理解MVC模式
复制代码
前端MVC:
View(视图)→ Controller(控制器)→ Model(数据)

后端MVC:
Controller(控制器)→ Service(业务)→ Mapper(数据访问)
✅ 4. 组件化思想
javascript 复制代码
// 前端组件
<UserForm user={user} onSave={handleSave} />

// 后端Service
@Service
public class UserService {
    public User save(User user) {
        // 保存逻辑
    }
}

🛠️ 第三章:开发环境准备(前端视角)

3.1 安装JDK(对应Node.js)

什么是JDK?

  • 相当于Java的Node.js运行环境
  • 包含Java编译器、运行时、类库

安装步骤

  1. 下载https://www.oracle.com/java/technologies/downloads/
  2. 版本:Java 8或11(LTS版本,稳定)
  3. 配置环境变量
bash 复制代码
# Windows
set JAVA_HOME=C:\Program Files\Java\jdk1.8.0_301
set PATH=%JAVA_HOME%\bin;%PATH%

# Mac/Linux
export JAVA_HOME=/Library/Java/JavaVirtualMachines/jdk1.8.0_301.jdk/Contents/Home
export PATH=$JAVA_HOME/bin:$PATH

验证安装

bash 复制代码
java -version
# 类似node -v

3.2 安装Maven(对应npm)

什么是Maven?

  • 相当于Java的npm
  • 管理依赖、构建项目、打包部署

安装步骤

  1. 下载https://maven.apache.org/download.cgi
  2. 配置环境变量
bash 复制代码
# Windows
set MAVEN_HOME=D:\maven\apache-maven-3.6.3
set PATH=%MAVEN_HOME%\bin;%PATH%

# Mac/Linux
export MAVEN_HOME=/Users/yourname/maven/apache-maven-3.6.3
export PATH=$MAVEN_HOME/bin:$PATH

配置国内镜像(对应npm淘宝源):

编辑 conf/settings.xml

xml 复制代码
<mirrors>
    <mirror>
        <id>aliyunmaven</id>
        <mirrorOf>*</mirrorOf>
        <url>https://maven.aliyun.com/repository/public</url>
    </mirror>
</mirrors>

常用命令

bash 复制代码
mvn clean          # 清理(类似rm -rf node_modules)
mvn compile        # 编译(类似npm run build)
mvn package        # 打包(类似npm run build)
mvn install        # 安装到本地仓库(类似npm install)
mvn spring-boot:run # 运行项目(类似npm start)

3.3 安装MySQL(对应MongoDB)

什么是MySQL?

  • 关系型数据库(MongoDB是文档型)
  • 表格结构,支持SQL查询

安装步骤

  1. 下载https://dev.mysql.com/downloads/mysql/
  2. 安装:按照向导安装,设置root密码
  3. 验证
bash 复制代码
mysql -u root -p
# 类似mongo命令

SQL vs MongoDB查询

sql 复制代码
-- MySQL查询
SELECT * FROM users WHERE age > 18 LIMIT 10;
javascript 复制代码
// MongoDB查询
db.users.find({ age: { $gt: 18 } }).limit(10);

3.4 安装IntelliJ IDEA(对应VS Code)

什么是IntelliJ IDEA?

  • Java开发的VS Code
  • 功能更强大,智能提示更好

安装步骤

  1. 下载https://www.jetbrains.com/idea/
  2. 版本:Community版(免费)足够学习使用
  3. 配置
    • 配置JDK路径
    • 配置Maven路径
    • 安装中文插件(可选)

常用快捷键

功能 Windows Mac VS Code对应
运行 Shift+F10 Control+R F5
调试 Shift+F9 Control+D F5
格式化 Ctrl+Alt+L Option+Command+L Shift+Alt+F
查找 Double Shift Double Shift Ctrl+P
重构 Ctrl+Alt+Shift+T Control+T F2

3.5:Maven vs npm 详细对比

❓ 常见疑问:会像npm那样安装吗?

答案:不会! Maven的依赖管理方式与npm有本质区别。

🎯 核心区别

特性 npm Maven
安装方式 全局或项目本地 只在项目中声明
依赖存储 node_modules目录 本地仓库(~/.m2/repository)
命令 npm install mvn compile 自动下载
配置文件 package.json pom.xml
版本管理 package-lock.json 自动解析
全局安装 支持(-g) 不支持

✅ Maven安装依赖的正确方式

步骤1:在pom.xml中声明依赖

xml 复制代码
<!-- pom.xml -->
<dependencies>
    <!-- MyBatis Spring Boot Starter -->
    <dependency>
        <groupId>org.mybatis.spring.boot</groupId>
        <artifactId>mybatis-spring-boot-starter</artifactId>
        <version>2.2.2</version>
    </dependency>
    
    <!-- MySQL驱动 -->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>8.0.30</version>
    </dependency>
    
    <!-- Spring Boot Web -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
</dependencies>

步骤2:执行Maven命令

bash 复制代码
# 编译项目(自动下载依赖)
mvn clean compile

# 或者打包
mvn clean package

# 或者直接运行
mvn spring-boot:run

步骤3:Maven自动处理

Maven会:

  1. 从中央仓库下载依赖
  2. 存储到本地仓库(~/.m2/repository
  3. 编译时自动引用
  4. 打包时包含到JAR/WAR中

📁 依赖存储位置对比

复制代码
# npm 依赖存储(项目本地)
项目目录/node_modules/
├── express/
├── mongoose/
├── lodash/
└── ...

# Maven 依赖存储(用户仓库)
用户目录/.m2/repository/
├── org/
│   └── springframework/
│       └── boot/
│           └── spring-boot-starter-web/
├── mysql/
│   └── mysql-connector-java/
│       └── 8.0.30/
└── ...

🎯 工作流程对比

复制代码
# npm 流程
1. 编辑 package.json
2. 执行 npm install
3. 下载到 node_modules
4. 项目中 require() 使用

# Maven 流程
1. 编辑 pom.xml
2. 执行 mvn compile
3. 自动下载到本地仓库
4. 编译时自动引入

💡 Maven关键概念

1. 坐标(Coordinate)

Maven使用坐标唯一标识依赖:

xml 复制代码
<dependency>
    <groupId>org.mybatis</groupId>      <!-- 组织ID(类似npm的@scope) -->
    <artifactId>mybatis</artifactId>    <!-- 项目ID(类似npm的包名) -->
    <version>3.5.9</version>           <!-- 版本号 -->
    <scope>compile</scope>              <!-- 作用域(可选) -->
    <optional>false</optional>          <!-- 是否可选(可选) -->
</dependency>

2. 作用域(Scope)

作用域 说明 示例
compile 编译、测试、运行都需要(默认) Spring Boot
test 只在测试时需要 JUnit
provided 编译测试需要,运行时由容器提供 Servlet API
runtime 运行时需要,编译不需要 JDBC驱动
system 本地JAR,不从仓库下载 内部SDK

3. 仓库(Repository)

  • 中央仓库:Maven官方仓库(https://repo.maven.apache.org/maven2/)
  • 本地仓库 :用户本地存储(~/.m2/repository
  • 私服:公司内部仓库(如Nexus)
  • 镜像仓库:中央仓库的镜像(如阿里云)

4. 依赖传递

Maven会自动下载依赖的依赖:

复制代码
你的项目 → Spring Boot Web → Spring Core → Spring Context → ...

🚀 实际操作示例

场景:在项目中使用MyBatis

步骤1:添加依赖到pom.xml

xml 复制代码
<dependencies>
    <!-- MyBatis Spring Boot Starter -->
    <dependency>
        <groupId>org.mybatis.spring.boot</groupId>
        <artifactId>mybatis-spring-boot-starter</artifactId>
        <version>2.2.2</version>
    </dependency>
    
    <!-- MySQL驱动 -->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>8.0.30</version>
    </dependency>
    
    <!-- Spring Boot Web -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
</dependencies>

步骤2:配置数据源

yaml 复制代码
# application.yml
spring:
  datasource:
    url: jdbc:mysql://localhost:3306/test
    username: root
    password: your_password
    driver-class-name: com.mysql.cj.jdbc.Driver

步骤3:创建Mapper接口

java 复制代码
package com.example.mapper;

import com.example.domain.User;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;

@Mapper
public interface UserMapper {
    
    @Select("SELECT * FROM user WHERE id = #{id}")
    User selectById(Long id);
    
    @Select("SELECT * FROM user")
    List<User> selectAll();
}

步骤4:创建Service

java 复制代码
package com.example.service;

import com.example.domain.User;
import com.example.mapper.UserMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;

@Service
public class UserService {
    
    @Autowired
    private UserMapper userMapper;
    
    public User getUserById(Long id) {
        return userMapper.selectById(id);
    }
    
    public List<User> getAllUsers() {
        return userMapper.selectAll();
    }
}

步骤5:创建Controller

java 复制代码
package com.example.controller;

import com.example.domain.User;
import com.example.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@RestController
@RequestMapping("/api/users")
public class UserController {
    
    @Autowired
    private UserService userService;
    
    @GetMapping("/{id}")
    public User getUserById(@PathVariable Long id) {
        return userService.getUserById(id);
    }
    
    @GetMapping
    public List<User> getAllUsers() {
        return userService.getAllUsers();
    }
}

步骤6:运行项目

bash 复制代码
# 方式1:Maven命令
mvn spring-boot:run

# 方式2:IDE中运行
# 找到启动类,点击运行按钮

# 方式3:打包后运行
mvn clean package -DskipTests
java -jar target/demo-0.0.1-SNAPSHOT.jar

步骤7:测试接口

bash 复制代码
# 使用curl测试
curl http://localhost:8080/api/users
curl http://localhost:8080/api/users/1

# 使用Postman测试
GET http://localhost:8080/api/users

⚠️ 常见问题及解决

问题1:依赖下载慢

原因:默认从中央仓库下载,速度慢

解决:配置国内镜像(阿里云)

编辑 ~/.m2/settings.xml

xml 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<settings>
    <mirrors>
        <mirror>
            <id>aliyunmaven</id>
            <mirrorOf>*</mirrorOf>
            <url>https://maven.aliyun.com/repository/public</url>
        </mirror>
    </mirrors>
</settings>

问题2:依赖冲突

原因:多个依赖引用了同一个库的不同版本

解决:查看依赖树并排除冲突

bash 复制代码
# 查看依赖树
mvn dependency:tree -Dverbose

# 示例输出
# [INFO] com.example:demo:jar:0.0.1-SNAPSHOT
# [INFO] +- org.springframework.boot:spring-boot-starter-web:jar:2.6.7:compile
# [INFO] |  +- org.springframework.boot:spring-boot-starter:jar:2.6.7:compile
# [INFO] |  |  +- org.springframework:spring-core:jar:5.3.23:compile

# 在pom.xml中排除冲突
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    <exclusions>
        <exclusion>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
        </exclusion>
    </exclusions>
</dependency>

问题3:找不到依赖

原因

  1. groupId、artifactId或version错误
  2. 网络连接问题
  3. 中央仓库没有该依赖

解决

bash 复制代码
# 1. 检查坐标是否正确
# 2. 检查网络连接
# 3. 手动下载并安装到本地仓库
mvn install:install-file \
    -Dfile=mybatis-3.5.9.jar \
    -DgroupId=org.mybatis \
    -DartifactId=mybatis \
    -Dversion=3.5.9 \
    -Dpackaging=jar

问题4:编译错误

原因

  1. 依赖缺失
  2. 版本不兼容
  3. 代码错误

解决

bash 复制代码
# 1. 清理并重新编译
mvn clean compile

# 2. 查看详细错误信息
mvn compile -X

# 3. 检查IDE提示

📊 常用命令对比表

操作 npm Maven
安装依赖 npm install pkg 编辑pom.xml,执行mvn
卸载依赖 npm uninstall pkg 从pom.xml删除
更新依赖 npm update pkg 修改pom.xml版本号
查看依赖 npm list mvn dependency:tree
全局安装 npm install -g pkg 不支持(使用本地仓库)
版本锁定 package-lock.json 自动解析
缓存清理 rm -rf node_modules mvn clean
编译项目 npm run build mvn compile
运行项目 npm start mvn spring-boot:run
打包项目 npm run build mvn package
安装到全局 npm link mvn install

💡 给前端开发者的建议

  1. 理解思想差异:npm是项目本地安装,Maven是仓库管理
  2. 学习坐标概念:groupId:artifactId:version
  3. 掌握常用命令:clean, compile, package, install
  4. 学会排除冲突:dependency:tree 和 exclusions
  5. 配置国内镜像:加速依赖下载
  6. 使用IDE自动提示:IntelliJ IDEA会自动补全依赖

记住:Maven不需要像npm那样手动安装依赖,只需在pom.xml中声明,Maven会自动处理一切! 🎉


🎯 第四章:核心技术对比

4.1 Spring Boot vs Express/Koa

📦 框架对比
javascript 复制代码
// Express应用
const express = require('express');
const app = express();

app.get('/api/users', (req, res) => {
    res.json([{ name: '张三' }, { name: '李四' }]);
});

app.listen(3000, () => {
    console.log('Server running on port 3000');
});
java 复制代码
// Spring Boot应用
@SpringBootApplication // 启动类注解 作用:启动Spring Boot应用
@RestController // 控制器注解 作用:定义控制器
public class Application {
    
    @GetMapping("/api/users")
    public List<User> getUsers() {
        return Arrays.asList(
            new User("张三"),
            new User("李四")
        );
    }
    
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
        System.out.println("Server running on port 8080");
    }
}
🎯 特性对比
特性 Express Spring Boot
启动方式 代码启动 主类启动
路由定义 函数式 注解式
中间件 app.use() Filter/Interceptor
配置 代码/环境变量 配置文件/注解
依赖注入 手动 @Autowired
生态 丰富 非常丰富

4.2 MyBatis vs Mongoose

📦 ORM对比
javascript 复制代码
// Mongoose查询
const User = require('./models/User');

async function getUsers() {
    const users = await User.find({ age: { $gt: 18 } })
        .sort({ createTime: -1 })
        .limit(10);
    return users;
}
java 复制代码
// MyBatis查询
@Mapper
public interface UserMapper {
    
    @Select("SELECT * FROM users WHERE age > #{age} ORDER BY create_time DESC LIMIT #{limit}")
    List<User> selectUsersByAge(@Param("age") int age, @Param("limit") int limit);
}

// 使用
List<User> users = userMapper.selectUsersByAge(18, 10);
🎯 关键差异
特性 Mongoose MyBatis
查询方式 链式调用 SQL语句
类型安全 运行时检查 编译时检查
性能 中等 优秀
灵活性 非常高
学习曲线 平缓 稍陡
🔍 MyBatis Mapper接口与XML交互原理

核心机制 :MyBatis使用JDK动态代理机制,让你只需要定义接口,不需要实现类,MyBatis会自动创建实现类。

🎯 完整交互流程
复制代码
1. 定义Mapper接口
   ↓
2. 编写Mapper XML
   ↓
3. MyBatis扫描接口
   ↓
4. 创建动态代理对象
   ↓
5. 调用接口方法
   ↓
6. 动态代理拦截调用
   ↓
7. 查找XML中的SQL
   ↓
8. 执行SQL并返回结果
📝 详细步骤解析

步骤1:定义Mapper接口

java 复制代码
// UserMapper.java
@Mapper
public interface UserMapper {
    
    // 方法名要与XML中的id对应
    User selectById(Long id);
    
    List<User> selectAll();
    
    int insert(User user);
    
    int update(User user);
    
    int delete(Long id);
}

步骤2:编写Mapper XML

xml 复制代码
<!-- UserMapper.xml -->
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" 
    "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<!-- namespace必须与接口全类名一致 -->
<mapper namespace="com.example.mapper.UserMapper">
    
    <!-- id必须与接口方法名一致 -->
    <select id="selectById" resultType="com.example.domain.User">
        SELECT * FROM user WHERE id = #{id}
    </select>
    
    <select id="selectAll" resultType="com.example.domain.User">
        SELECT * FROM user
    </select>
    
    <insert id="insert" parameterType="com.example.domain.User"
            useGeneratedKeys="true" keyProperty="id">
        INSERT INTO user(name, age) VALUES(#{name}, #{age})
    </insert>
    
    <update id="update" parameterType="com.example.domain.User">
        UPDATE user SET name=#{name}, age=#{age} WHERE id=#{id}
    </update>
    
    <delete id="delete">
        DELETE FROM user WHERE id = #{id}
    </delete>
</mapper>

步骤3:MyBatis配置

yaml 复制代码
# application.yml
mybatis:
  # 告诉MyBatis去哪里找XML文件
  mapper-locations: classpath*:mapper/**/*Mapper.xml
  # 类型别名包
  type-aliases-package: com.example.domain

步骤4:调用Mapper方法

java 复制代码
@Service
public class UserService {
    
    @Autowired
    private UserMapper userMapper; // 实际注入的是代理对象
    
    public User getUser(Long id) {
        // 调用接口方法
        // 实际执行的是动态代理的invoke()方法
        return userMapper.selectById(id); 
    }
}

步骤5:动态代理执行

复制代码
调用userMapper.selectById(id)
   ↓
JDK动态代理拦截调用
   ↓
进入InvocationHandler的invoke()方法
   ↓
解析方法名和参数
   ↓
在XML中查找对应的SQL语句
   ↓
创建PreparedStatement
   ↓
设置参数(#{id})
   ↓
执行SQL
   ↓
处理结果集(映射为User对象)
   ↓
返回结果
🔍 关键匹配规则
元素 Mapper接口 Mapper XML 匹配规则
命名空间 全类名 namespace属性 必须完全一致
方法名 方法名 SQL标签的id 必须完全一致
参数 方法参数 #{param} 按名称或索引匹配
返回值 返回类型 resultType 自动映射
✅ 正确示例
java 复制代码
// 接口
public interface UserMapper {
    User selectById(Long id); // 方法名
}
xml 复制代码
<!-- XML -->
<mapper namespace="com.example.mapper.UserMapper">
    <!-- id必须与方法名一致 -->
    <select id="selectById" resultType="User">
        SELECT * FROM user WHERE id = #{id} <!-- 参数名与方法参数一致 -->
    </select>
</mapper>
❌ 错误示例
java 复制代码
// 错误1:方法名不匹配
public interface UserMapper {
    User getById(Long id); // 方法名是getById
}
xml 复制代码
<!-- XML中的id是selectById,不匹配 -->
<select id="selectById" resultType="User">
    SELECT * FROM user WHERE id = #{id}
</select>
🧠 动态代理实现原理
java 复制代码
// JDK动态代理核心类(简化版)
public class MapperProxy<T> implements InvocationHandler {
    
    private final SqlSession sqlSession;
    private final Class<T> mapperInterface;
    
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) 
            throws Throwable {
        
        // 1. 判断是否是Object方法(toString, hashCode等)
        if (Object.class.equals(method.getDeclaringClass())) {
            return method.invoke(this, args);
        }
        
        // 2. 获取方法对应的MappedStatement(包含SQL等信息)
        String methodName = method.getName();
        
        // 3. 执行SQL
        if (methodName.startsWith("select")) {
            // 查询操作
            return sqlSession.selectOne(methodName, args);
        } else if (methodName.startsWith("insert")) {
            // 插入操作
            return sqlSession.insert(methodName, args);
        }
        // ... 其他操作
    }
}
📊 参数传递机制

1. 单个参数

java 复制代码
// 接口
User selectById(Long id);
xml 复制代码
<!-- XML -->
<select id="selectById" resultType="User">
    SELECT * FROM user WHERE id = #{id}
</select>

2. 多个参数(使用@Param)

java 复制代码
// 接口
User selectByCondition(
    @Param("name") String name, 
    @Param("age") Integer age
);
xml 复制代码
<!-- XML -->
<select id="selectByCondition" resultType="User">
    SELECT * FROM user 
    WHERE name = #{name} AND age = #{age}
</select>

3. 对象参数

java 复制代码
// 接口
int insert(User user);
xml 复制代码
<!-- XML -->
<insert id="insert" parameterType="User">
    INSERT INTO user(name, age, email) 
    VALUES(#{name}, #{age}, #{email})
</insert>
⚠️ 常见问题及解决

问题1:BindingException

错误信息

复制代码
org.apache.ibatis.binding.BindingException: 
Invalid bound statement (not found): 
com.example.mapper.UserMapper.selectById

原因

  1. Mapper接口和XML文件不在同一个包
  2. XML的namespace与接口全类名不匹配
  3. XML中的SQL id与接口方法名不匹配
  4. XML文件没有被正确扫描

解决

xml 复制代码
<!-- 检查namespace -->
<mapper namespace="com.example.mapper.UserMapper">
    <!-- 检查id -->
    <select id="selectById" resultType="User">
        SELECT * FROM user WHERE id = #{id}
    </select>
</mapper>
yaml 复制代码
# 检查配置
mybatis:
  mapper-locations: classpath*:mapper/**/*Mapper.xml

问题2:TypeException

错误信息

复制代码
org.apache.ibatis.type.TypeException: 
Could not set parameters for mapping

原因

  1. 参数类型不匹配
  2. 参数名错误
  3. 数据库列不存在

解决

java 复制代码
// 检查方法参数
User selectById(@Param("id") Long id);
xml 复制代码
<!-- 检查SQL中的参数名 -->
<select id="selectById" resultType="User">
    SELECT * FROM user WHERE id = #{id}
</select>
💡 给前端开发者的建议
  1. 对比理解

    • Mapper接口 ≈ Mongoose的Model
    • XML文件 ≈ 自定义查询方法
    • 动态代理 ≈ JavaScript的Proxy
  2. 关键规则

    • namespace必须与接口全类名一致
    • SQL标签的id必须与方法名一致
    • 参数名要与方法参数一致
  3. 推荐做法

    • 简单查询用注解(@Select, @Insert等)
    • 复杂查询用XML(支持动态SQL)
    • 使用@Param明确参数名
    • 配置下划线转驼峰
  4. 常见错误

    • ❌ 忘记写@Mapper注解
    • ❌ namespace写错
    • ❌ SQL id与方法名不匹配
    • ❌ 参数名拼写错误

记住:MyBatis的魔法就在于动态代理,让你只需要定义接口,剩下的交给MyBatis! 🎉

4.3 数据库对比(MySQL vs MongoDB)

📊 数据模型
sql 复制代码
-- MySQL(关系型)
CREATE TABLE users (
    id INT PRIMARY KEY AUTO_INCREMENT,
    name VARCHAR(50) NOT NULL,
    age INT,
    email VARCHAR(100) UNIQUE,
    create_time DATETIME
);
javascript 复制代码
// MongoDB(文档型)
db.users.insertOne({
    name: "张三",
    age: 25,
    email: "zhangsan@example.com",
    createTime: new Date(),
    hobbies: ["篮球", "游泳"] // 嵌套数组
});
🎯 适用场景
场景 MySQL MongoDB
结构化数据 ✅ 适合 ⚠️ 可以
复杂查询 ✅ 适合 ⚠️ 有限
事务支持 ✅ 强 ⚠️ 弱
大数据量 ⚠️ 分片 ✅ 适合
快速开发 ⚠️ 稍慢 ✅ 快速

💡 第五章:前端开发者的学习建议

5.1 学习路径

📚 第一阶段:Java基础(1-2周)

学习内容

  • Java语法(变量、类型、运算符)
  • 流程控制(if、for、while)
  • 面向对象(类、对象、继承、多态)
  • 集合框架(List、Map、Set)
  • 异常处理

学习方法

  • 对比JavaScript学习
  • 每天写100行代码
  • 做简单的算法题

推荐资源

  • 《Java核心技术 卷I》
  • B站Java基础教程
📚 第二阶段:Web开发(2-3周)

学习内容

  • Spring Boot基础
  • MVC模式
  • RESTful API设计
  • MyBatis使用
  • 数据库操作

学习方法

  • 开发简单的CRUD接口
  • 连接数据库
  • 测试API接口

实战项目

  • 用户管理系统
  • 博客系统
📚 第三阶段:进阶技术(3-4周)

学习内容

  • Spring Security/Shiro安全框架
  • Redis缓存
  • 消息队列
  • 分布式基础

学习方法

  • 集成安全框架
  • 优化系统性能
  • 学习分布式思想

5.2 学习技巧

✅ 技巧1:对比学习

将Java技术与前端技术对比理解:

复制代码
前端:npm install express
后端:<dependency>express</dependency>

前端:app.get('/', handler)
后端:@GetMapping("/") public String handler()

前端:res.json(data)
后端:return AjaxResult.success(data)
✅ 技巧2:从简单开始
java 复制代码
// 先写简单的main方法
public class HelloWorld {
    public static void main(String[] args) {
        System.out.println("Hello World!");
    }
}

// 再写Web接口
@RestController
public class HelloController {
    @GetMapping("/hello")
    public String hello() {
        return "Hello World!";
    }
}
✅ 技巧3:多调试
java 复制代码
// 使用System.out.println
System.out.println("debug: " + variable);

// 使用IDE调试器
// 设置断点 → 启动调试 → 单步执行
✅ 技巧4:阅读源码
复制代码
1. 先看简单的开源项目
2. 理解设计思想
3. 模仿优秀代码
4. 尝试改进

5.3 开发注意事项

⚠️ 注意事项1:线程安全
java 复制代码
// 错误示例(非线程安全)
@Controller
public class UserController {
    private int count = 0; // 多个线程共享
    
    @GetMapping("/count")
    public int getCount() {
        return ++count; // 竞态条件
    }
}

// 正确做法
@Controller
public class UserController {
    private AtomicInteger count = new AtomicInteger(0);
    
    @GetMapping("/count")
    public int getCount() {
        return count.incrementAndGet();
    }
}
⚠️ 注意事项2:资源释放
java 复制代码
// 错误示例(资源未关闭)
public void readFile() throws IOException {
    FileInputStream fis = new FileInputStream("file.txt");
    // 读取文件...
    // 忘记关闭,可能导致内存泄漏
}

// 正确做法(try-with-resources)
public void readFile() throws IOException {
    try (FileInputStream fis = new FileInputStream("file.txt")) {
        // 读取文件...
        // 自动关闭资源
    }
}

5.4 IDEA代码生成技巧

🎯 方法1:使用MyBatisX插件(推荐)

MyBatisX 是一个专门为MyBatis设计的IDEA插件,可以一键生成Mapper接口、XML、实体类等。

✅ 安装插件
  1. 打开IDEA → File → Settings → Plugins
  2. 搜索 MyBatisX
  3. 点击Install安装
  4. 重启IDEA
🚀 使用步骤

步骤1:连接数据库

复制代码
1. 打开Database工具窗口(右侧边栏)
2. 点击「+」→ Data Source → MySQL
3. 填写连接信息:
   - Host: localhost
   - Port: 3306
   - User: root
   - Password: 你的密码
   - Database: 数据库名
4. 点击「Test Connection」测试连接
5. 点击「OK」连接成功

步骤2:生成代码

复制代码
1. 在Database工具窗口中找到要生成的表
2. 右键点击表名 → MyBatisX → Generator
3. 配置生成选项:
   - Generator: 选择生成器(推荐FreeMarker)
   - Package: 目标包名(如com.example)
   - Module: 目标模块
   - Table: 表名
   - Entity: 实体类名
   - Mapper: Mapper接口名
   - Service: Service接口名
   - Controller: 控制器名
4. 点击「Generate」生成代码

步骤3:查看生成的文件

复制代码
生成的文件结构:
├── domain/
│   └── User.java          # 实体类
├── mapper/
│   ├── UserMapper.java    # Mapper接口
│   └── UserMapper.xml     # Mapper XML
├── service/
│   ├── IUserService.java  # Service接口
│   └── impl/
│       └── UserServiceImpl.java
└── controller/
    └── UserController.java

步骤4:使用快捷键

功能 Windows Mac
生成Mapper接口 Alt+Insert → MyBatisX Option+Insert → MyBatisX
跳转到XML Ctrl+B Command+B
跳转到接口 Ctrl+B Command+B
生成SQL语句 Alt+Enter Option+Enter
🎯 方法2:使用IDEA Live Templates(代码模板)

Live Templates 可以自定义代码模板,快速生成常用代码。

✅ 创建模板
复制代码
1. 打开IDEA → File → Settings → Editor → Live Templates
2. 点击「+」→ Template Group
3. 输入组名(如MyBatis)
4. 点击「OK」
5. 选择刚创建的组 → 点击「+」→ Live Template
6. 配置模板:
   - Abbreviation: 模板缩写(如mapper)
   - Description: 模板描述
   - Template text: 模板代码
   - Applicable contexts: 选择Java
7. 点击「Define」→ 选择Java
8. 点击「OK」保存
📝 常用模板示例

模板1:Mapper接口

复制代码
Abbreviation: mapper
Template text:
@Mapper
public interface $NAME$Mapper {
    
    $NAME$ selectById(Long id);
    
    List<$NAME$> selectAll();
    
    int insert($NAME$ entity);
    
    int update($NAME$ entity);
    
    int delete(Long id);
}

模板2:Service接口

复制代码
Abbreviation: service
Template text:
public interface I$NAME$Service {
    
    $NAME$ getById(Long id);
    
    List<$NAME$> list();
    
    boolean save($NAME$ entity);
    
    boolean update($NAME$ entity);
    
    boolean removeById(Long id);
}

模板3:Service实现

复制代码
Abbreviation: serviceimpl
Template text:
@Service
public class $NAME$ServiceImpl implements I$NAME$Service {
    
    @Autowired
    private $NAME$Mapper $name$Mapper;
    
    @Override
    public $NAME$ getById(Long id) {
        return $name$Mapper.selectById(id);
    }
    
    @Override
    public List<$NAME$> list() {
        return $name$Mapper.selectAll();
    }
    
    @Override
    public boolean save($NAME$ entity) {
        return $name$Mapper.insert(entity) > 0;
    }
    
    @Override
    public boolean update($NAME$ entity) {
        return $name$Mapper.update(entity) > 0;
    }
    
    @Override
    public boolean removeById(Long id) {
        return $name$Mapper.delete(id) > 0;
    }
}

模板4:Controller

复制代码
Abbreviation: controller
Template text:
@RestController
@RequestMapping("/$path$")
public class $NAME$Controller {
    
    @Autowired
    private I$NAME$Service $name$Service;
    
    @GetMapping("/list")
    public AjaxResult list() {
        return AjaxResult.success($name$Service.list());
    }
    
    @GetMapping("/{id}")
    public AjaxResult getById(@PathVariable Long id) {
        return AjaxResult.success($name$Service.getById(id));
    }
    
    @PostMapping
    public AjaxResult save(@RequestBody $NAME$ entity) {
        return toAjax($name$Service.save(entity) ? 1 : 0);
    }
    
    @PutMapping
    public AjaxResult update(@RequestBody $NAME$ entity) {
        return toAjax($name$Service.update(entity) ? 1 : 0);
    }
    
    @DeleteMapping("/{id}")
    public AjaxResult removeById(@PathVariable Long id) {
        return toAjax($name$Service.removeById(id) ? 1 : 0);
    }
}
🚀 使用模板
复制代码
1. 在Java文件中输入模板缩写(如mapper)
2. 按Tab键展开模板
3. 按Tab键切换变量,填写内容
4. 按Enter键完成
🎯 方法3:使用Lombok简化代码

Lombok 可以通过注解自动生成getter、setter、构造方法等代码。

✅ 添加依赖
xml 复制代码
<!-- pom.xml -->
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.24</version>
    <scope>provided</scope>
</dependency>
✅ 安装插件
复制代码
1. 打开IDEA → File → Settings → Plugins
2. 搜索 Lombok
3. 点击Install安装
4. 重启IDEA
📝 常用注解

注解1:@Data

java 复制代码
// 使用Lombok前
public class User {
    private Long id;
    private String name;
    private Integer age;
    
    public Long getId() { return id; }
    public void setId(Long id) { this.id = id; }
    public String getName() { return name; }
    public void setName(String name) { this.name = name; }
    public Integer getAge() { return age; }
    public void setAge(Integer age) { this.age = age; }
    
    @Override
    public boolean equals(Object o) { ... }
    @Override
    public int hashCode() { ... }
    @Override
    public String toString() { ... }
}

// 使用Lombok后
@Data
public class User {
    private Long id;
    private String name;
    private Integer age;
}

注解2:@NoArgsConstructor和@AllArgsConstructor

java 复制代码
@Data
@NoArgsConstructor           // 无参构造方法
@AllArgsConstructor          // 全参构造方法
public class User {
    private Long id;
    private String name;
    private Integer age;
}

注解3:@Builder

java 复制代码
@Data
@Builder
public class User {
    private Long id;
    private String name;
    private Integer age;
}

// 使用
User user = User.builder()
    .id(1L)
    .name("张三")
    .age(25)
    .build();

注解4:@Slf4j

java 复制代码
// 使用Lombok前
public class UserService {
    private static final Logger log = LoggerFactory.getLogger(UserService.class);
    
    public void save(User user) {
        log.info("保存用户: {}", user);
    }
}

// 使用Lombok后
@Slf4j
public class UserService {
    
    public void save(User user) {
        log.info("保存用户: {}", user);
    }
}
💡 给前端开发者的建议
  1. 推荐使用MyBatisX插件

    • 类似VS Code的代码片段扩展
    • 一键生成,节省时间
  2. 掌握Live Templates

    • 类似VS Code的User Snippets
    • 自定义常用代码模板
  3. 使用Lombok

    • 类似TypeScript的class语法
    • 减少样板代码
  4. 常用快捷键

    • Alt+Insert(Windows)/ Option+Insert(Mac):生成代码
    • Ctrl+Space(Windows)/ Command+Space(Mac):代码补全
    • Ctrl+B(Windows)/ Command+B(Mac):跳转到定义

记住:工欲善其事,必先利其器!掌握这些技巧可以大大提高开发效率! 🎉

复制代码
#### ⚠️ 注意事项3:数据库连接

```java
// 错误示例(每次创建连接)
public User getUser(Long id) {
    Connection conn = DriverManager.getConnection(url, user, password);
    // 查询...
    conn.close();
}

// 正确做法(使用连接池)
@Autowired
private DataSource dataSource;

public User getUser(Long id) {
    try (Connection conn = dataSource.getConnection()) {
        // 查询...
    }
}
⚠️ 注意事项4:SQL注入
java 复制代码
// 错误示例(SQL注入风险)
public User getUser(String username) {
    String sql = "SELECT * FROM users WHERE username = '" + username + "'";
    // 如果username是 ' OR '1'='1,会查询所有用户
}

// 正确做法(使用参数化查询)
public User getUser(String username) {
    String sql = "SELECT * FROM users WHERE username = ?";
    try (PreparedStatement ps = conn.prepareStatement(sql)) {
        ps.setString(1, username);
        ResultSet rs = ps.executeQuery();
    }
}

🎉 第六章:实战项目建议

6.1 项目1:用户管理系统

功能需求

  • 用户列表展示
  • 用户新增
  • 用户编辑
  • 用户删除
  • 用户搜索

技术要点

  • Spring Boot + MyBatis
  • MySQL数据库
  • RESTful API
  • 前端页面(可选)

学习收获

  • 掌握CRUD开发
  • 理解MVC模式
  • 熟悉数据库操作

6.2 项目2:博客系统

功能需求

  • 文章列表
  • 文章详情
  • 文章发布
  • 文章编辑
  • 分类管理
  • 标签管理

技术要点

  • 一对多关系(文章-分类)
  • 多对多关系(文章-标签)
  • 分页查询
  • 富文本编辑器

学习收获

  • 掌握复杂查询
  • 理解数据库设计
  • 学习关联关系

6.3 项目3:电商系统(进阶)

功能需求

  • 商品管理
  • 购物车
  • 订单管理
  • 支付集成
  • 物流跟踪

技术要点

  • 分布式事务
  • 缓存优化
  • 消息队列
  • 微服务架构

学习收获

  • 掌握高级技术
  • 理解分布式系统
  • 学习性能优化

📚 第七章:推荐学习资源

7.1 官方文档

7.2 在线教程

7.3 视频教程

7.4 书籍推荐

📖 入门书籍
  • 《Java核心技术 卷I》
  • 《Head First Java》
  • 《Java编程思想》
📖 进阶书籍
  • 《Spring实战》
  • 《深入浅出MyBatis》
  • 《Java并发编程实战》
  • 《深入理解Java虚拟机》
📖 前端转后端
  • 《Node.js设计模式》(理解后端思想)
  • 《RESTful Web APIs》(API设计)

💪 第八章:心态调整

8.1 遇到困难怎么办?

🧘 正确心态
  • 遇到问题是正常的:每个开发者都会遇到问题
  • 问题是成长的机会:解决问题就是提升能力
  • 搜索引擎是朋友:90%的问题都能在网上找到答案
  • 社区求助:Stack Overflow、掘金、CSDN
🛠️ 解决问题的步骤
  1. 理解问题:明确错误信息
  2. 搜索答案:Google/Baidu搜索错误信息
  3. 尝试解决:按照答案尝试
  4. 记录总结:记录问题和解决方案
  5. 分享经验:帮助别人解决问题

8.2 坚持的重要性

🎯 学习曲线
复制代码
第1周:兴奋 → 学习Java基础
第2周:困惑 → 理解面向对象
第3周:迷茫 → 学习Spring Boot
第4周:突破 → 写出第一个接口
第8周:熟练 → 开发完整功能
第12周:自信 → 成为Java开发者
💡 坚持的方法
  • 每天学习1-2小时
  • 每周完成一个小项目
  • 加入学习群,互相鼓励
  • 记录学习进度
  • 定期复习

🎊 第九章:总结

9.1 你已经具备的优势

作为前端开发者,你已经:

  • ✅ 了解前后端交互
  • ✅ 熟悉HTTP和JSON
  • ✅ 有编程思维
  • ✅ 会调试代码
  • ✅ 解决问题的能力

9.2 需要学习的内容

  • 📚 Java语法和特性
  • 📚 Spring Boot框架
  • 📚 数据库设计和SQL
  • 📚 后端开发思维

9.3 未来展望

成为全栈开发者后,你可以:

  • 👨‍💻 独立开发完整项目
  • 💰 获得更高的薪资
  • 🎯 有更多的职业选择
  • 🚀 参与更大的项目

📞 常见问题

Q1:前端转后端需要多久?

回答

  • 基础学习:1-2个月
  • 独立开发:3-6个月
  • 成为熟练开发者:1-2年

关键是每天坚持学习和实践。

Q2:Java和Node.js哪个更好?

回答

  • Java:企业级应用、大型系统、高性能
  • Node.js:轻量级、快速开发、前后端统一

没有好坏之分,适合的场景不同。

Q3:需要放弃前端吗?

回答:不需要!

  • 前端经验是你的优势
  • 可以成为全栈开发者
  • 理解前后端有助于更好的协作

Q4:找不到工作怎么办?

回答

  • 完善简历(突出全栈优势)
  • 做几个实战项目
  • 学习面试技巧
  • 降低期望,从实习开始

Q5:如何平衡工作和学习?

回答

  • 每天早起1小时学习
  • 利用周末时间
  • 减少娱乐时间
  • 制定学习计划

📝 文档信息

文档版本 :v1.0
适用人群 :前端转Java后端开发者
维护人员::yuppie

反馈与建议

  • 如果发现错误,请及时反馈
  • 如果有更好的建议,欢迎提出
  • 文档会持续更新和完善

💌 最后的话

恭喜你完成了这份指南!

学习编程是一个持续的过程,遇到困难不要放弃。记住:

  • ✨ 每一个大神都是从小白开始的
  • ✨ 遇到问题是成长的机会
  • ✨ 多写代码,多实践
  • ✨ 阅读优秀的开源代码
  • ✨ 参与技术社区,分享经验

作为前端开发者,你有天然的优势。相信自己,你一定能成功!

祝你学习愉快,早日成为全栈开发者! 🎉


「成功的路上并不拥挤,因为坚持的人不多。」

相关推荐
糠帅傅蓝烧牛肉面2 小时前
单实例多MCP聚合服务:两种实现方案深度对比
前端·docker·ai
钟离墨笺2 小时前
Go语言--2go基础-->map
开发语言·后端·golang
JosieBook2 小时前
【Vue】12 Vue技术—— Vue 事件修饰符详解:掌握事件处理的高级技巧
前端·javascript·vue.js
lsx2024062 小时前
DOM CDATA
开发语言
Tony Bai2 小时前
Go 语言的“魔法”时刻:如何用 -toolexec 实现零侵入式自动插桩?
开发语言·后端·golang
未若君雅裁2 小时前
SpringAI基础入门
java·spring boot·ai
CC.GG2 小时前
【C++】用哈希表封装myunordered_map和 myunordered_set
java·c++·散列表
Coding茶水间3 小时前
基于深度学习的交通标志检测系统演示与介绍(YOLOv12/v11/v8/v5模型+Pyqt5界面+训练代码+数据集)
开发语言·人工智能·深度学习·yolo·目标检测·机器学习
a努力。3 小时前
字节Java面试被问:TCP的BBR拥塞控制算法原理
java·开发语言·python·tcp/ip·elasticsearch·面试·职场和发展