Java Springboot 快速入门

Java Springboot 快速入门

在IDEA里面选择Spring Initializr 创建一个全新的Spring项目

image-20250309201424465

安装依赖,Spring Web必选,用来实现Web服务,其他根据开发需求加,后面也能选

image-20250309201810246

初始化完之后的项目结构如下

image-20250309201322926

好的,上面的SpringDemoApplication就是我们的主启动类,我们要依照分层架构来创建各种层.

一个典型的Java Springboot应用大概会有Controller Service Model Repository Config 等层级.

一个典型的请求处理流程是:

  1. Controller层接收HTTP请求
  2. Controller调用Service层进行业务处理
  3. Service层使用Repository层访问数据
  4. Repository层操作Entity/Model对象
  5. 处理结果沿着调用链返回给客户端

我们来写一个简单的Controller:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package com.demo.spring_demo.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class HelloController{
@GetMapping("/hello")
public String hello(){
return "Hello World";
}
@GetMapping("/api/version")
public String getVersion(){
return "1.0.0";
}
}

image-20250309203542294

你可能会看到很多@,它们在Java应用程序中被称为注解。Springboot是一个框架,它通过扫描你的注解和逻辑来知道应用程序应该如何工作.

架构包初探

通常的架构大概会分成这些软件包来做:

  • Controller

Controller是一个控制器,它负责根据我们的路由来分发请求,接受用户输入并返回响应.

  • Service

Service用于具体的实现我们的业务逻辑.

  • Model

Model层往往是一些数据结构层,定义了应用中的数据结构.

  • Repository

Repository提供对数据源的访问.

  • Config

Config用于管理应用程序的配置.

根据架构,我们设计一个更能体现Spring特色的应用程序:

image-20250309214706655

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
com.demo.spring_demo
├── SpringDemoApplication.java (主启动类)
├── controller
│ ├── HelloController.java
│ └── UserController.java (用户API控制器)
├── service
│ ├── UserService.java (服务接口)
│ └── UserServiceImpl.java (服务实现)
├── repository
│ ├── UserRepository.java (数据访问接口)
│ └── UserRepositoryImpl.java (数据访问实现)
├── model
│ └── User.java (用户模型类)
└── config
└── AppConfig.java (应用配置类)

SpringDemoApplication

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package com.demo.spring_demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class SpringDemoApplication {

public static void main(String[] args) {
SpringApplication.run(SpringDemoApplication.class, args);
}

}

@SpringBootApplication 是主应用类上的注解,它标志了这是一个主类,从此处开始整个应用程序.它会支持所有注解的扫描和自动配置的实现.

数据结构的确定

这个应用主要实现的是对用户的CRUD增删改查,所以我们先创建一个model包,在里面定义一个User类.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
package com.demo.spring_demo.model;

import java.util.Date;

public class User {
private Long id;
private String username;
private String email;
private Date createdAt;

// 构造函数
public User() {
}

public User(Long id, String username, String email) {
this.id = id;
this.username = username;
this.email = email;
this.createdAt = new Date();
}

// Getter和Setter方法
public Long getId() {
return id;
}

public void setId(Long id) {
this.id = id;
}

public String getUsername() {
return username;
}

public void setUsername(String username) {
this.username = username;
}

public String getEmail() {
return email;
}

public void setEmail(String email) {
this.email = email;
}

public Date getCreatedAt() {
return createdAt;
}

public void setCreatedAt(Date createdAt) {
this.createdAt = createdAt;
}

@Override
public String toString() {
return "User{" +
"id=" + id +
", username='" + username + '\'' +
", email='" + email + '\'' +
", createdAt=" + createdAt +
'}';
}
}

设置了id username email createdAt等几个字段,设置了getter和setter方法来访问类的私有字段,并编写了toString的实现.这样我们有了最小的操作单元,我们可以基于User来进行操作.这个系统的目标就是做一个关于User的CRUD.

数据操作的定义

repository包中确定一些数据操作,包括简单的

获取所有用户findAll

Id为键获取单个User的findById

更新用户的方法save

删除用户的方法deleteById

UserRepository.java

1
2
3
4
5
6
7
8
9
10
11
12
package com.demo.spring_demo.repository;

import com.demo.spring_demo.model.User;
import java.util.List;
import java.util.Optional;

public interface UserRepository {
List<User> findAll();
Optional<User> findById(Long id);
User save(User user);
void deleteById(Long id);
}

这并不是一个类.这是一个接口,它定义了我们要实现什么东西来帮助我们完成我们的业务逻辑.

UserRepositoyImpl.java中,我们实现它.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
package com.demo.spring_demo.repository;

import com.demo.spring_demo.model.User;
import org.springframework.stereotype.Repository;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicLong;

@Repository
public class UserRepositoryImpl implements UserRepository {

// 模拟数据库
private final Map<Long, User> userMap = new HashMap<>();
private final AtomicLong idGenerator = new AtomicLong(1L);

public UserRepositoryImpl() {
// 初始化一些测试数据
User user1 = new User(idGenerator.getAndIncrement(), "zhangsan", "[email protected]");
User user2 = new User(idGenerator.getAndIncrement(), "lisi", "[email protected]");

userMap.put(user1.getId(), user1);
userMap.put(user2.getId(), user2);
}

@Override
public List<User> findAll() {
return new ArrayList<>(userMap.values());
}

@Override
public Optional<User> findById(Long id) {
return Optional.ofNullable(userMap.get(id));
}

@Override
public User save(User user) {
if (user.getId() == null) {
// 新用户,分配ID
user.setId(idGenerator.getAndIncrement());
}
userMap.put(user.getId(), user);
return user;
}

@Override
public void deleteById(Long id) {
userMap.remove(id);
}
}

Java新手可能看不懂HashMap是什么,没关系。

你可以把它当成Python中的字典(键值存储数据结构).

在这里我们把它用来模拟从数据库获取数据的实现.

@Repository表明被注解的类是一个”仓库”(Repository),用于封装存储、检索和查询数据的逻辑。

@Override是Java语言的标准注解,用于表明被注解的方法是覆盖(重写)父类或实现接口的方法。

构造方法会新定义一些User对象,并把它们添加到数据源,在此之后实现了应用程序的逻辑.

业务逻辑的实现

UserService.java

1
2
3
4
5
6
7
8
9
10
11
12
13
package com.demo.spring_demo.service;

import com.demo.spring_demo.model.User;
import java.util.List;
import java.util.Optional;

public interface UserService {
List<User> getAllUsers();
Optional<User> getUserById(Long id);
User createUser(User user);
Optional<User> updateUser(Long id, User user);
boolean deleteUser(Long id);
}

UserService中确定了业务逻辑的接口,在之后的UserServiceImpl里完成实现.

UserServiceImpl.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
package com.demo.spring_demo.service;

import com.demo.spring_demo.model.User;
import com.demo.spring_demo.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.Optional;

@Service
public class UserServiceImpl implements UserService {

private final UserRepository userRepository;

@Autowired
public UserServiceImpl(UserRepository userRepository) {
this.userRepository = userRepository;
}

@Override
public List<User> getAllUsers() {
return userRepository.findAll();
}

@Override
public Optional<User> getUserById(Long id) {
return userRepository.findById(id);
}

@Override
public User createUser(User user) {
// 可以在这里添加业务逻辑,比如验证
return userRepository.save(user);
}

@Override
public Optional<User> updateUser(Long id, User userDetails) {
return userRepository.findById(id)
.map(existingUser -> {
// 更新用户信息
existingUser.setUsername(userDetails.getUsername());
existingUser.setEmail(userDetails.getEmail());
// 保存更新后的用户
return userRepository.save(existingUser);
});
}

@Override
public boolean deleteUser(Long id) {
if (userRepository.findById(id).isPresent()) {
userRepository.deleteById(id);
return true;
}
return false;
}
}

这里定义了一些常见的CRUD逻辑,调用了之前提到的Repository里面的数据操作方法来获取具体的内容.

路由函数的映射

回到我们亲爱的Controller层,也是我们直接和路由交互的函数入口:

UserController.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
package com.demo.spring_demo.controller;

import com.demo.spring_demo.model.User;
import com.demo.spring_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 java.util.List;

@RestController
@RequestMapping("/api/users")
public class UserController {

private final UserService userService;

@Autowired
public UserController(UserService userService) {
this.userService = userService;
}

// 获取所有用户
@GetMapping
public ResponseEntity<List<User>> getAllUsers() {
List<User> users = userService.getAllUsers();
return ResponseEntity.ok(users);
}

// 根据ID获取用户
@GetMapping("/{id}")
public ResponseEntity<User> getUserById(@PathVariable Long id) {
return userService.getUserById(id)
.map(ResponseEntity::ok)
.orElseGet(() -> ResponseEntity.notFound().build());
}

// 创建用户
@PostMapping
public ResponseEntity<User> createUser(@RequestBody User user) {
User createdUser = userService.createUser(user);
return ResponseEntity.status(HttpStatus.CREATED).body(createdUser);
}

// 更新用户
@PutMapping("/{id}")
public ResponseEntity<User> updateUser(@PathVariable Long id, @RequestBody User user) {
return userService.updateUser(id, user)
.map(ResponseEntity::ok)
.orElseGet(() -> ResponseEntity.notFound().build());
}

// 删除用户
@DeleteMapping("/{id}")
public ResponseEntity<Void> deleteUser(@PathVariable Long id) {
boolean deleted = userService.deleteUser(id);
return deleted ? ResponseEntity.noContent().build() : ResponseEntity.notFound().build();
}
}

这里介绍几个用的比较多的注解:

@RestController

  • 作用:用于创建 RESTful Web 服务的控制器,等同于 @Controller@ResponseBody 的组合。它使得类中的每个方法默认都返回响应体,且响应体会被自动转换为 JSON 或 XML 格式。
  • 用法:标记在控制器类上,自动将方法的返回值转化为 JSON 或 XML 格式的响应体。
1
2
3
4
5
6
7
8
9
@RestController
public class GreetingController {

@GetMapping("/greeting")
public String greet(@RequestParam(name = "name", defaultValue = "World") String name) {
return "Hello, " + name + "!";
}
}

@RequestMapping

作用:用于处理 HTTP 请求的映射。可以与 HTTP 方法(GET、POST、PUT 等)一起使用,也可以指定路径。它是 Spring 中最基本的请求映射注解,支持多种 HTTP 方法(如 GET、POST、PUT、DELETE 等)和路径匹配。

用法:可以用于类或方法上,指定请求的 URL 路径和请求方法。如果没有指定请求方法,它会匹配所有的 HTTP 方法(GET、POST 等)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@RestController
@RequestMapping("/api")
public class MyController {

@RequestMapping(value = "/greeting", method = RequestMethod.GET)
public String greeting(@RequestParam(name = "name", defaultValue = "World") String name) {
return "Hello, " + name + "!";
}

@RequestMapping(value = "/submit", method = RequestMethod.POST)
public String submit(@RequestBody String data) {
return "Received: " + data;
}
}

  • @GetMapping("/") @PostMapping("/") @PutMapping("/") @DeleteMapping("/")等Mapping方法会在@RestController提供的基础路径上做细化的方法映射,如果没有@RestController那么默认的路径就是根路由.

另外还有一些获取参数的注解,简单贴一下

  • @PathVariable 获取rest风格路径参数
  • @RequestParam 绑定请求参数到方法形参
  • @RequestHeader 获取请求中的请求头
  • @CookieValue 获取指定的 Cookie 的值
  • @RequestBody 获取请求体Body的值

之后就是调用Service层中的方法,完成我们的整个调用链即可.

整理一下我们的整个调用链逻辑:

HTTP请求->Controller->Service->Repoistory->Model

整个架构的逻辑大概就是这样,这也被称为典型的Springboot MVC架构.

打包成Jar包

用Idea打包

文件->项目结构->工件->Jar->打包并选择主类

之后打开侧边栏->Maven->clean->package 打包完成

image-20250310060540751

image-20250310140844565

image-20250310141101934

image-20250310141213517

image-20250310141314524

用Maven插件打包

SpringBoot自带一个更简单的spring-boot-maven-plugin插件可以用来打包,只需要在pom.xml中加入以下配置:

1
2
3
4
5
6
7
8
9
10
11
<project>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>

之后运行mvn clean package即可.

运行Jar包

java -jar 运行jar包,注意java版本号需要和开发版本相匹配

image-20250310141938168