最近在技术社区看到一个有趣的数据:超过90%的.NET开发者在学习Java时,第一选择就是Spring Boot框架。为什么?因为Spring Boot的设计理念与.NET Core高度相似——约定优于配置、开箱即用、快速开发。
当然还有一点,现在国内基本spring==java了。。。哎,这也是个尴尬的事。
作为一名从.NET转向Java的开发者,我深知这个转换过程中的困惑和挑战。今天就来聊聊Spring Boot到底是什么,以及如何快速上手,让你的Java开发之路更加顺畅。
本文将通过实战代码示例,帮你快速理解Spring Boot的核心概念,并对比.NET开发经验,让技术转换变得轻松自然。
传统Java Web开发就像搭积木需要先做木工——配置繁琐、依赖复杂、启动缓慢。这让习惯了.NET Core简洁开发体验的我们感到困扰:
而Spring Boot的出现,就是为了解决这些问题,让Java开发也能像.NET Core一样丝滑。
Spring Boot = Spring框架 + 自动配置 + 内嵌服务器 + 生产就绪特性
如果用.NET来类比:
特性 | Spring Boot | .NET Core |
---|---|---|
启动方式 | @SpringBootApplication | Program.cs |
依赖注入 | @Autowired | 构造函数注入 |
配置管理 | application.properties | appsettings.json |
Web API | @RestController | [ApiController] |
Bash# 检查Java版本(Spring Boot 3.x需要JDK 17+)
java -version
# 使用Maven创建项目(类似dotnet new webapi),其实一般没人这么创建项目
# 这么创建后,还得手动一步步的手动创建支持spring boot 项目
mvn archetype:generate -DgroupId=com.rick -DartifactId=spring-rick-demo -DarchetypeArtifactId=maven-archetype-quickstart
.NET Core项目结构:
C#MyApp/ ├── Controllers/ ├── Models/ ├── Program.cs ├── Startup.cs └── appsettings.json
Spring Boot项目结构:
对于第一次接触这个目录结构的可能有点痛苦,其实可以想成.net目录结构的命名空间。
Javademo/ ├── src/main/java/com/example/ │ ├── controller/ │ ├── model/ │ ├── service/ │ ├── mapper/ │ └── DemoApplication.java ├── src/main/resources/ │ ├── application.properties │ └── mapper/ └── pom.xml
Spring Boot启动类:
Javapackage com.example.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.mybatis.spring.annotation.MapperScan;
/**
* Spring Boot应用程序入口点
* @SpringBootApplication注解相当于.NET Core的Program.cs + Startup.cs的组合
* 包含了@Configuration, @EnableAutoConfiguration, @ComponentScan三个注解
* @MapperScan 用于扫描MyBatis的Mapper接口
*/
@SpringBootApplication
@MapperScan("com.example.demo.mapper") // 扫描MyBatis Mapper接口
public class DemoApplication {
/**
* 主方法 - 应用程序启动入口
* 类似于.NET Core的Program.Main()方法
*/
public static void main(String[] args) {
// 启动Spring Boot应用
// 相当于.NET Core的CreateHostBuilder(args).Build().Run()
SpringApplication.run(DemoApplication.class, args);
}
}
对应的.NET** Core启动代码:**
C#// Program.cs (.NET 6+)
var builder = WebApplication.CreateBuilder(args);
// 添加服务到容器
builder.Services.AddControllers();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
var app = builder.Build();
// 配置请求管道
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
app.Run();
MyBatis Mapper接口:
这块比.net 差的不是一星半点了
Javapackage com.example.demo.mapper;
import com.example.demo.model.User;
import org.apache.ibatis.annotations.*;
import java.util.List;
/**
* 用户数据访问接口
* 相当于.NET Core中的Repository接口
* MyBatis会根据注解或XML文件自动实现这个接口
*/
@Mapper
public interface UserMapper {
/**
* 查询所有用户
* @Select注解定义SQL查询语句
* 相当于.NET Core中Entity Framework的查询方法
*/
@Select("SELECT id, name, email FROM users")
List<User> findAll();
/**
* 根据ID查询用户
* #{id}是MyBatis的参数占位符,相当于.NET Core中的@id参数
*/
@Select("SELECT id, name, email FROM users WHERE id = #{id}")
User findById(@Param("id") Long id);
/**
* 插入新用户
* @Insert注解定义插入语句
* @Options用于获取自增主键
*/
@Insert("INSERT INTO users(name, email) VALUES(#{name}, #{email})")
@Options(useGeneratedKeys = true, keyProperty = "id")
int insert(User user);
/**
* 更新用户信息
* @Update注解定义更新语句
*/
@Update("UPDATE users SET name = #{name}, email = #{email} WHERE id = #{id}")
int update(User user);
/**
* 删除用户
* @Delete注解定义删除语句
*/
@Delete("DELETE FROM users WHERE id = #{id}")
int deleteById(@Param("id") Long id);
/**
* 复杂查询示例:根据邮箱模糊查询
* 演示MyBatis的动态SQL能力
*/
@Select("SELECT id, name, email FROM users WHERE email LIKE CONCAT('%', #{email}, '%')")
List<User> findByEmailContaining(@Param("email") String email);
}
UserService服务类:
Javapackage com.example.demo.service;
import com.example.demo.mapper.UserMapper;
import com.example.demo.model.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
/**
* 用户业务逻辑服务
* 相当于.NET Core中的Service层
* @Service注解标记这是一个业务服务组件
*/
@Service
@Transactional // 声明式事务管理,相当于.NET Core的TransactionScope
public class UserService {
/**
* 注入UserMapper
* @Autowired相当于.NET Core的依赖注入
*/
@Autowired
private UserMapper userMapper;
/**
* 获取所有用户
* 相当于.NET Core中的GetAllAsync方法
*/
public List<User> getAllUsers() {
return userMapper.findAll();
}
/**
* 根据ID获取用户
* 相当于.NET Core中的GetByIdAsync方法
*/
public User getUserById(Long id) {
if (id == null || id <= 0) {
throw new IllegalArgumentException("用户ID不能为空或小于等于0");
}
return userMapper.findById(id);
}
/**
* 创建新用户
* @Transactional确保数据一致性
*/
@Transactional
public User createUser(User user) {
// 参数验证
if (user == null) {
throw new IllegalArgumentException("用户信息不能为空");
}
if (user.getName() == null || user.getName().trim().isEmpty()) {
throw new IllegalArgumentException("用户姓名不能为空");
}
if (user.getEmail() == null || !isValidEmail(user.getEmail())) {
throw new IllegalArgumentException("邮箱格式不正确");
}
// 执行插入操作
int result = userMapper.insert(user);
if (result > 0) {
return user; // MyBatis会自动设置生成的ID
} else {
throw new RuntimeException("用户创建失败");
}
}
/**
* 更新用户信息
*/
@Transactional
public User updateUser(Long id, User user) {
User existingUser = userMapper.findById(id);
if (existingUser == null) {
throw new RuntimeException("用户不存在,ID: " + id);
}
// 更新用户信息
user.setId(id);
int result = userMapper.update(user);
if (result > 0) {
return userMapper.findById(id);
} else {
throw new RuntimeException("用户更新失败");
}
}
/**
* 删除用户
*/
@Transactional
public boolean deleteUser(Long id) {
User existingUser = userMapper.findById(id);
if (existingUser == null) {
throw new RuntimeException("用户不存在,ID: " + id);
}
int result = userMapper.deleteById(id);
return result > 0;
}
/**
* 根据邮箱搜索用户
*/
public List<User> searchUsersByEmail(String email) {
if (email == null || email.trim().isEmpty()) {
return getAllUsers();
}
return userMapper.findByEmailContaining(email.trim());
}
/**
* 简单的邮箱格式验证
*/
private boolean isValidEmail(String email) {
return email != null && email.matches("^[A-Za-z0-9+_.-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}$");
}
}
**Spring Boot控制器:**这块不.net 基本一样的,最大区别就是注入方式,原生.net 只支持构造注入,说实话构造注入更符合程序设计,但灵活性比spring boot差不少。
Javapackage com.example.demo.controller;
import com.example.demo.model.User;
import com.example.demo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import jakarta.validation.Valid;
import java.util.*;
/**
* 用户API控制器
* @RestController相当于.NET Core的[ApiController]
* 自动将返回值序列化为JSON
*/
@RestController
@RequestMapping("/api/users") // 基础路由,类似.NET Core的[Route("api/[controller]")]
public class UserController {
/**
* 注入用户服务
* 相当于.NET Core控制器的构造函数注入
*/
@Autowired
private UserService userService;
/**
* 获取所有用户
* @GetMapping相当于.NET Core的[HttpGet]
*/
@GetMapping
public ResponseEntity<List<User>> getAllUsers() {
try {
List<User> users = userService.getAllUsers();
return ResponseEntity.ok(users);
} catch (Exception e) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
}
}
/**
* 根据ID获取用户
* @PathVariable相当于.NET Core的路由参数
*/
@GetMapping("/{id}")
public ResponseEntity<User> getUserById(@PathVariable Long id) {
try {
User user = userService.getUserById(id);
if (user != null) {
return ResponseEntity.ok(user);
} else {
return ResponseEntity.notFound().build();
}
} catch (IllegalArgumentException e) {
return ResponseEntity.badRequest().build();
} catch (Exception e) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
}
}
/**
* 根据邮箱搜索用户
* @RequestParam相当于.NET Core的查询参数
*/
@GetMapping("/search")
public ResponseEntity<List<User>> searchUsers(@RequestParam(required = false) String email) {
try {
List<User> users = userService.searchUsersByEmail(email);
return ResponseEntity.ok(users);
} catch (Exception e) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
}
}
/**
* 创建新用户
* @PostMapping + @RequestBody相当于.NET Core的[HttpPost]
* Spring Boot会自动将JSON反序列化为Java对象
* @Valid注解用于参数验证,类似.NET Core的模型验证
*/
@PostMapping
public ResponseEntity<User> createUser(@Valid @RequestBody User user) {
try {
User createdUser = userService.createUser(user);
return ResponseEntity.status(HttpStatus.CREATED).body(createdUser);
} catch (IllegalArgumentException e) {
return ResponseEntity.badRequest().build();
} catch (Exception e) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
}
}
/**
* 更新用户信息
* @PutMapping相当于.NET Core的[HttpPut]
*/
@PutMapping("/{id}")
public ResponseEntity<User> updateUser(@PathVariable Long id, @Valid @RequestBody User user) {
try {
User updatedUser = userService.updateUser(id, user);
return ResponseEntity.ok(updatedUser);
} catch (IllegalArgumentException e) {
return ResponseEntity.badRequest().build();
} catch (RuntimeException e) {
return ResponseEntity.notFound().build();
} catch (Exception e) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
}
}
/**
* 删除用户
* @DeleteMapping相当于.NET Core的[HttpDelete]
*/
@DeleteMapping("/{id}")
public ResponseEntity<Map<String, String>> deleteUser(@PathVariable Long id) {
try {
boolean deleted = userService.deleteUser(id);
Map<String, String> response = new HashMap<>();
if (deleted) {
response.put("message", "用户删除成功");
response.put("userId", id.toString());
return ResponseEntity.ok(response);
} else {
response.put("message", "用户删除失败");
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(response);
}
} catch (RuntimeException e) {
Map<String, String> errorResponse = new HashMap<>();
errorResponse.put("error", e.getMessage());
return ResponseEntity.notFound().body(errorResponse);
} catch (Exception e) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
}
}
}
Java实体类:
这块基本一回事,在.net下不管是EF还是Sqlsugar,但肯定比.net麻烦。
Javapackage com.example.demo.model;
import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
/**
* 用户实体类
* 相当于.NET Core中的Model或Entity
* 使用Jakarta Validation注解进行参数验证(Spring Boot 3.x使用Jakarta EE)
*/
public class User {
private Long id;
@NotBlank(message = "用户姓名不能为空")
private String name;
@NotBlank(message = "邮箱不能为空")
@Email(message = "邮箱格式不正确")
private String email;
// 无参构造函数(Spring Boot JSON反序列化需要)
public User() {}
// 全参构造函数
public User(Long id, String name, String email) {
this.id = id;
this.name = name;
this.email = email;
}
// Getter和Setter方法(相当于.NET的属性)
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 String getEmail() { return email; }
public void setEmail(String email) { this.email = email; }
@Override
public String toString() {
return String.format("User{id=%d, name='%s', email='%s'}", id, name, email);
}
}
application.properties配置:
.properties# 服务器配置(相当于.NET Core的appsettings.json中的Kestrel配置) server.port=8080 server.servlet.context-path=/ # 应用配置 spring.application.name=demo-app # 数据库配置示例(类似.NET Core的ConnectionStrings) spring.datasource.url=jdbc:mysql://localhost:3306/demo_db?useSSL=false&serverTimezone=UTC&allowPublicKeyRetrieval=true spring.datasource.username=root spring.datasource.password=password spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver # HikariCP连接池配置(Spring Boot 3.x默认连接池) spring.datasource.hikari.maximum-pool-size=20 spring.datasource.hikari.minimum-idle=5 spring.datasource.hikari.connection-timeout=30000 spring.datasource.hikari.idle-timeout=600000 spring.datasource.hikari.max-lifetime=1800000 # MyBatis配置(相当于.NET Core的Entity Framework配置) mybatis.mapper-locations=classpath:mapper/*.xml mybatis.type-aliases-package=com.example.demo.model mybatis.configuration.map-underscore-to-camel-case=true mybatis.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl mybatis.configuration.cache-enabled=true mybatis.configuration.lazy-loading-enabled=true # Jackson JSON配置(Spring Boot 3.x默认JSON处理器) spring.jackson.default-property-inclusion=NON_NULL spring.jackson.serialization.write-dates-as-timestamps=false spring.jackson.time-zone=GMT+8 # 日志配置 logging.level.root=INFO logging.level.com.example.demo=DEBUG logging.level.com.example.demo.mapper=DEBUG logging.pattern.console=%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n logging.pattern.file=%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n # 生产就绪特性配置(Actuator) management.endpoints.web.exposure.include=health,info,metrics management.endpoint.health.show-details=when-authorized management.info.env.enabled=true # 开发环境配置 spring.profiles.active=dev
application-dev.properties(开发环境配置):
.properties# 开发环境特定配置 logging.level.org.springframework.web=DEBUG spring.jackson.serialization.indent-output=true # 开发环境数据库配置 spring.datasource.url=jdbc:h2:mem:testdb spring.datasource.driver-class-name=org.h2.Driver spring.datasource.username=sa spring.datasource.password= spring.h2.console.enabled=true
**pom.xml文件:**这个类似.net依赖
XML<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<!-- Spring Boot父级依赖(类似.NET Core的TargetFramework) -->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.2.0</version>
<relativePath/>
</parent>
<groupId>com.example</groupId>
<artifactId>demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>demo</name>
<description>Spring Boot 3.x Demo Project with MyBatis</description>
<properties>
<java.version>17</java.version>
<mybatis.spring.boot.version>3.0.3</mybatis.spring.boot.version>
<mysql.connector.version>8.2.0</mysql.connector.version>
</properties>
<dependencies>
<!-- Web开发启动器(相当于.NET Core的Microsoft.AspNetCore.App) -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- MyBatis Spring Boot启动器(相当于Entity Framework Core) -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>${mybatis.spring.boot.version}</version>
</dependency>
<!-- MySQL驱动 -->
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<version>${mysql.connector.version}</version>
<scope>runtime</scope>
</dependency>
<!-- H2数据库(开发测试用) -->
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<!-- 参数验证(Spring Boot 3.x使用Jakarta Validation) -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<!-- 生产就绪特性(监控、健康检查等) -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!-- JSON处理(Spring Boot 3.x默认使用Jackson) -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-json</artifactId>
</dependency>
<!-- 测试启动器(相当于.NET Core的Microsoft.AspNetCore.Mvc.Testing) -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- MyBatis测试支持 -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter-test</artifactId>
<version>${mybatis.spring.boot.version}</version>
<scope>test</scope>
</dependency>
<!-- Testcontainers用于集成测试 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-testcontainers</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>mysql</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<!-- Spring Boot Maven插件(用于打包和运行) -->
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
<!-- Maven编译插件 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.11.0</version>
<configuration>
<source>17</source>
<target>17</target>
</configuration>
</plugin>
</plugins>
</build>
<!-- Spring Boot官方仓库 -->
<repositories>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</pluginRepository>
</pluginRepositories>
</project>
src/main/resources/mapper/UserMapper.xml:
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">
<mapper namespace="com.example.demo.mapper.UserMapper">
<!-- 结果映射,处理数据库字段和Java属性的映射 -->
<resultMap id="userResultMap" type="com.example.demo.model.User">
<id property="id" column="id"/>
<result property="name" column="name"/>
<result property="email" column="email"/>
</resultMap>
<!-- 复杂查询示例:分页查询用户 -->
<select id="findUsersWithPagination" resultMap="userResultMap">
SELECT id, name, email
FROM users
<where>
<if test="name != null and name != ''">
AND name LIKE CONCAT('%', #{name}, '%')
</if>
<if test="email != null and email != ''">
AND email LIKE CONCAT('%', #{email}, '%')
</if>
</where>
ORDER BY id DESC
LIMIT #{offset}, #{limit}
</select>
<!-- 批量插入示例 -->
<insert id="batchInsert" parameterType="java.util.List">
INSERT INTO users (name, email) VALUES
<foreach collection="list" item="user" separator=",">
(#{user.name}, #{user.email})
</foreach>
</insert>
</mapper>
**UserControllerTest.java:**说实话我觉得这块java比.net强,省事。
Javapackage com.example.demo.controller;
import com.example.demo.model.User;
import com.example.demo.service.UserService;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MockMvc;
import java.util.Arrays;
import java.util.List;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.when;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
/**
* 用户控制器测试
* 相当于.NET Core的ControllerTest
*/
@WebMvcTest(UserController.class)
class UserControllerTest {
@Autowired
private MockMvc mockMvc;
@MockBean
private UserService userService;
@Autowired
private ObjectMapper objectMapper;
@Test
void getAllUsers_ShouldReturnUserList() throws Exception {
// 准备测试数据
List<User> users = Arrays.asList(
new User(1L, "张三", "zhangsan@example.com"),
new User(2L, "李四", "lisi@example.com")
);
when(userService.getAllUsers()).thenReturn(users);
// 执行测试
mockMvc.perform(get("/api/users"))
.andExpected(status().isOk())
.andExpected(content().contentType(MediaType.APPLICATION_JSON))
.andExpected(jsonPath("$").isArray())
.andExpected(jsonPath("$.length()").value(2))
.andExpected(jsonPath("$[0].name").value("张三"));
}
@Test
void createUser_ShouldReturnCreatedUser() throws Exception {
// 准备测试数据
User newUser = new User(null, "王五", "wangwu@example.com");
User createdUser = new User(3L, "王五", "wangwu@example.com");
when(userService.createUser(any(User.class))).thenReturn(createdUser);
// 执行测试
mockMvc.perform(post("/api/users")
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(newUser)))
.andExpected(status().isCreated())
.andExpected(jsonPath("$.id").value(3))
.andExpected(jsonPath("$.name").value("王五"));
}
}
Bash# ❌ 错误:使用Java 8运行Spring Boot 3.x
java -version # openjdk version "1.8.0_XXX"
# ✅ 正确:Spring Boot 3.x需要Java 17+
java -version # openjdk version "17.0.XXX"
Java// ❌ 错误:使用旧的javax命名空间
import javax.validation.constraints.NotBlank;
import javax.servlet.http.HttpServletRequest;
// ✅ 正确:Spring Boot 3.x使用jakarta命名空间
import jakarta.validation.constraints.NotBlank;
import jakarta.servlet.http.HttpServletRequest;
XML<!-- ❌ 错误:使用旧版本MyBatis -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.3.1</version>
</dependency>
<!-- ✅ 正确:使用Spring Boot 3.x兼容版本 -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>3.0.3</version>
</dependency>
.properties# ❌ 错误:使用旧的配置属性名 spring.datasource.hikari.maximum-pool-size=20 # ✅ 正确:Spring Boot 3.x的配置属性 spring.datasource.hikari.maximum-pool-size=20 # 注意:大部分配置保持不变,但某些配置可能有调整
Spring Boot 3.x + MyBatis天生适合微服务开发,支持原生镜像编译:
Bash# 打包应用(相当于dotnet publish)
mvn clean package
# 运行应用(相当于dotnet run)
java -jar target/demo-0.0.1-SNAPSHOT.jar
# 构建原生镜像(GraalVM)
mvn -Pnative native:compile
Spring Boot 3.x对云原生支持更好,启动速度更快,内存占用更小,就像.NET Core 6+的性能提升。
通过本文的学习,你应该掌握了以下关键点:
Spring Boot 3.x不仅延续了框架的优秀传统,更在性能、标准化和云原生方面有了质的飞跃。它让Java开发变得像.NET Core一样现代化,而MyBatis则提供了类似Dapper的灵活性,这个组合是企业级Java开发的黄金搭配。
💬 互动时间:
觉得这篇文章对你的技术升级有帮助吗?请转发给更多正在学习现代Java开发的同行,让我们一起拥抱技术的新时代!
#SpringBoot3 #Java开发 #NET转Java #MyBatis #现代化开发 #云原生
本文作者:技术老小子
本文链接:
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!