【SpringBoot从初学者到专家的成长23】使用SpringBoot构建高效的Web应用-拥抱你的第一个SpringBoot项目
随着软件开发的不断发展,知识体系变得越来越庞大和复杂。它不仅包括编程技能,还涉及到数据库、网络、架构、设计模式等多个知识领域。对于初学者而言,面对浩如烟海的资料和庞大的知识体系,很容易感到迷茫,不知道该学什么、怎么学,也不清楚该从何入手。其实,有一句话说得好:“书山有路勤为径,学海无涯苦作舟。”越是看似复杂的事物,学习的方法往往越简单。关键在于:首先要做好规划,理清学习的内容;然后安排合理的学习顺序,做到循序渐进;最重要的是,不断地写代码,将所学知识融入到自己的知识体系中。只有通过持续产出学习成果——比如可运行的代码、自己的开源项目、技术文章等,才能真正实现知识的内化与提升。
一、创建SpringBoot Web应用项目
首先,确保你已经安装了JDK 8及以上版本,并且安装了开发工具(例如IDEA或Spring Tool Suite)。
-
通过Spring Initializr创建项目
访问 Spring Initializr 创建项目。
- Project: Maven Project
- Language: Java
- SpringBoot Version: 2.x(根据实际情况选择最新版本)
- Dependencies: Spring Web, SpringBoot DevTools, Spring Data JPA, H2 Database(选择适合你的项目的依赖)
-
导入项目
将生成的项目导入到IDE中。
-
创建数据库
选择一个自己熟悉的数据库,建立数据表,并构造初始化数据; -
编写测试代码
通过单元测试和接口测试,保证项目运行后能够正常执行 -
部署运行
将开发完成的项目打包构建成可部署运行的应用服务。
二、创建Web应用代码
创建项目前,基于SpringBoot先规划项目的基本结构
1. Application.java - 启动类
package com.example.registration;import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplication
public class Application {public static void main(String[] args) {SpringApplication.run(Application.class, args);}
}
2. UserController.java - 控制器
创建一个用户注册和查询的REST控制器,提供相关API接口。
package com.example.registration.controller;import com.example.registration.model.User;
import com.example.registration.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;@RestController
@RequestMapping("/users")
public class UserController {@Autowiredprivate UserService userService;@PostMapping("/register")public User registerUser(@RequestBody User user) {return userService.registerUser(user);}@GetMapping("/{id}")public User getUserById(@PathVariable Long id) {return userService.getUserById(id);}
}
3. User.java - 用户实体类
定义一个用户实体类,用于存储用户的基本信息。
package com.example.registration.model;import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;@Entity
public class User {@Id@GeneratedValue(strategy = GenerationType.IDENTITY)private Long id;private String username;private String password;private String email;// Constructorspublic User() {}public User(String username, String password, String email) {this.username = username;this.password = password;this.email = email;}// Getters and Setterspublic 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 getPassword() {return password;}public void setPassword(String password) {this.password = password;}public String getEmail() {return email;}public void setEmail(String email) {this.email = email;}
}
4. UserService.java - 服务层
创建服务层来处理用户注册逻辑和用户查询。
package com.example.registration.service;import com.example.registration.model.User;
import com.example.registration.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;@Service
public class UserService {@Autowiredprivate UserRepository userRepository;// 注册新用户public User registerUser(User user) {return userRepository.save(user);}// 根据ID查询用户public User getUserById(Long id) {return userRepository.findById(id).orElse(null);}
}
5. UserRepository.java - 用户仓库
使用Spring Data JPA来操作数据库中的用户表。
package com.example.registration.repository;import com.example.registration.model.User;
import org.springframework.data.jpa.repository.JpaRepository;public interface UserRepository extends JpaRepository<User, Long> {// 可以添加自定义查询方法
}
基于上述代码,下面是相应的数据库建表语句以及一些初始化数据。
三、数据库建表语句
假设我们使用的是MySQL数据库,下面是创建users表的SQL语句:
1. users表建表语句
CREATE TABLE users (id BIGINT AUTO_INCREMENT PRIMARY KEY, -- 用户ID,主键,自增username VARCHAR(255) NOT NULL, -- 用户名,不为空password VARCHAR(255) NOT NULL, -- 密码,不为空email VARCHAR(255) NOT NULL, -- 邮箱,不为空created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, -- 创建时间,默认当前时间updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP -- 更新时间,自动更新时间
);
2. users表字段解释
- id:自增的用户ID。
- username:用户的用户名,唯一且不为空。
- password:加密后的密码,存储的是加密结果。
- email:用户的邮箱,唯一且不为空。
- created_at:记录创建时间,默认为当前时间。
- updated_at:记录更新时间,默认和
created_at相同,但会在每次更新时自动更新。
四、初始化数据
为了测试应用,我们可以插入一些初始用户数据。
1. 插入示例用户数据
INSERT INTO users (username, password, email)
VALUES('johndoe', '$2a$10$K1r6vHk5Ez24T.9swI1J7eLIfJhbHMZtk9g2w3BzFejCsl1/J9UyC', 'johndoe@example.com'),('janedoe', '$2a$10$06FrX.QBkERl7BhaV45X0ZxZZg.wMi9E8AzMZVzO9b1c3OUfzywAm', 'janedoe@example.com'),('samsmith', '$2a$10$8dFddKjJ8rd6bmJZfdG5htmzoE0cn7Jz.M0VZZ7TpJdmZPlDpBL1i', 'samsmith@example.com');
2. 生成的密码(BCrypt加密)
上面的密码字段使用了BCrypt加密算法来存储加密后的密码。为了生成加密的密码,可以使用Spring Security中的BCryptPasswordEncoder工具:
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;public class PasswordEncoderExample {public static void main(String[] args) {BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();String encodedPassword = encoder.encode("password123"); // 输入原始密码System.out.println("Encoded Password: " + encodedPassword);}
}
运行后,你会得到一个加密的密码,可以将这个加密密码替换上面的$2a$10$...部分。
3.数据库初始化脚本
如果你使用的是SpringBoot 的自动化配置,可以使用data.sql文件来初始化数据。你可以将以下内容放入src/main/resources/data.sql中:
-- 创建用户表
CREATE TABLE IF NOT EXISTS users (id BIGINT AUTO_INCREMENT PRIMARY KEY,username VARCHAR(255) NOT NULL,password VARCHAR(255) NOT NULL,email VARCHAR(255) NOT NULL,created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);-- 插入初始用户数据
INSERT INTO users (username, password, email)
VALUES('johndoe', '$2a$10$K1r6vHk5Ez24T.9swI1J7eLIfJhbHMZtk9g2w3BzFejCsl1/J9UyC', 'johndoe@example.com'),('janedoe', '$2a$10$06FrX.QBkERl7BhaV45X0ZxZZg.wMi9E8AzMZVzO9b1c3OUfzywAm', 'janedoe@example.com'),('samsmith', '$2a$10$8dFddKjJ8rd6bmJZfdG5htmzoE0cn7Jz.M0VZZ7TpJdmZPlDpBL1i', 'samsmith@example.com');
三、配置application.properties
这里为了简单选择properties文件,当然也可以选择yml文件,具体配置方法和详细内容可参考:【SpringBoot从初学者到专家的成长11】SpringBoot 中的application.properties与application.yml详解
为你的应用配置数据库连接、端口、日志等信息。
# 设置SpringBoot 内嵌服务器端口
server.port=8080# 配置数据库(使用H2内存数据库)
spring.datasource.url=jdbc:h2:mem:testdb
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=password
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
spring.jpa.hibernate.ddl-auto=update# 启用SpringBoot DevTools
spring.devtools.restart.enabled=true
四、单元测试
使用SpringBoot 的单元测试功能来测试注册和查询功能是否正确。单元测试是开发质量的重要保证
涉及单元测试更多的内容可参考:【SpringBoot从初学者到专家的成长12】聊聊SpringBoot 中的测试:如何编写高效的单元与集成测试
1. UserControllerTest.java - 控制器单元测试
package com.example.registration;import com.example.registration.controller.UserController;
import com.example.registration.model.User;
import com.example.registration.service.UserService;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;import static org.hamcrest.Matchers.is;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;@SpringBootTest
public class UserControllerTest {@Autowiredprivate UserController userController;private MockMvc mockMvc;@MockBeanprivate UserService mockUserService;@Testpublic void testRegisterUser() throws Exception {User mockUser = new User("johndoe", "password123", "johndoe@example.com");// 模拟注册返回Mockito.when(mockUserService.registerUser(Mockito.any(User.class))).thenReturn(mockUser);mockMvc = MockMvcBuilders.standaloneSetup(userController).build();mockMvc.perform(post("/users/register").contentType("application/json").content("{\"username\":\"johndoe\", \"password\":\"password123\", \"email\":\"johndoe@example.com\"}")).andExpect(jsonPath("$.username", is("johndoe"))).andExpect(jsonPath("$.email", is("johndoe@example.com")));}
}
测试解释:
@SpringBootTest:启动SpringBoot 应用上下文。@MockBean:模拟UserService,避免依赖数据库。MockMvc:模拟POST请求并验证响应内容。
2. UserServiceTest.java - 服务层单元测试
package com.example.registration;import com.example.registration.model.User;
import com.example.registration.repository.UserRepository;
import com.example.registration.service.UserService;
import org.junit.jupiter.api.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.springframework.boot.test.context.SpringBootTest;import static org.junit.jupiter.api.Assertions.assertEquals;@SpringBootTest
public class UserServiceTest {@Mockprivate UserRepository userRepository;@InjectMocksprivate UserService userService;@Testpublic void testRegisterUser() {User mockUser = new User("johndoe", "password123", "johndoe@example.com");// 模拟保存用户Mockito.when(userRepository.save(Mockito.any(User.class))).thenReturn(mockUser);User result = userService.registerUser(mockUser);assertEquals("johndoe", result.getUsername());assertEquals("johndoe@example.com", result.getEmail());}
}
五、客户端调用服务示例代码
假设我们有一个客户端服务需要调用上面提供的注册API。
1. UserClient.java - 客户端服务
package com.example.registration.client;import com.example.registration.model.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;@Service
public class UserClient {@Autowiredprivate RestTemplate restTemplate;public User registerUser(User user) {return restTemplate.postForObject("http://localhost:8080/users/register", user, User.class);}
}
2. UserClientTest.java - 客户端服务单元测试
package com.example.registration;import com.example.registration.client.UserClient;
import com.example.registration.model.User;
import org.junit.jupiter.api.Test;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.web.client.RestTemplate;import static org.mockito.Mockito.when;@SpringBootTest
public class UserClientTest {@Autowiredprivate UserClient userClient;@Mockprivate RestTemplate restTemplate;@Testpublic void testRegisterUser() {User mockUser = new User("johndoe", "password123", "johndoe@example.com");// 模拟RestTemplate的返回when(restTemplate.postForObject(Mockito.anyString(), Mockito.any(), Mockito.eq(User.class))).thenReturn(mockUser);User result = userClient.registerUser(mockUser);assert(result.getUsername()).equals("johndoe");assert(result.getEmail()).equals("johndoe@example.com");}
}
六、整合前端
如果需要为该注册功能创建一个简单的前端界面,您可以使用HTML和JavaScript进行实现。下面是一个使用HTML和JavaScript的简单前端注册页面示例。
1. index.html - 用户注册页面
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>User Registration</title><script>async function registerUser() {const username = document.getElementById('username').value;const password = document.getElementById('password').value;const email = document.getElementById('email').value;const response = await fetch('http://localhost:8080/users/register', {method: 'POST',headers: {'Content-Type': 'application/json'},body: JSON.stringify({username: username,password: password,email: email})});const data = await response.json();if (response.ok) {alert('Registration successful! Welcome ' + data.username);} else {alert('Error: ' + data.message);}}</script>
</head>
<body><h1>User Registration</h1><form id="registrationForm" onsubmit="event.preventDefault(); registerUser();"><label for="username">Username:</label><br><input type="text" id="username" name="username" required><br><br><label for="password">Password:</label><br><input type="password" id="password" name="password" required><br><br><label for="email">Email:</label><br><input type="email" id="email" name="email" required><br><br><button type="submit">Register</button></form>
</body>
</html>
2. 启动应用
确保后端SpringBoot 应用正在运行,并且前端页面可以通过浏览器访问。如果后端服务运行在本地,确保前端页面正确配置了API请求地址。
通过浏览器访问index.html,并填写用户信息进行注册。用户信息会通过POST请求发送到后端,后端会处理并返回注册成功的响应。
七、部署和监控
1. 部署到生产环境
可以将该应用部署到云服务(如AWS、Heroku、Azure等)或本地服务器。确保数据库连接、服务器配置和安全设置(如SSL/TLS)都适配生产环境的需求。具体的部署方法超过了该文章的范围,大家可自行查询相关资料。
2. 监控和日志记录
为了确保应用在生产环境中的正常运行,可以集成一些日志记录和监控工具,如:
- Spring Boot Actuator:提供应用健康检查、监控和审计等功能。
- ELK Stack(Elasticsearch, Logstash, Kibana):用于日志收集和可视化。
- Prometheus + Grafana:用于应用的性能监控。
3. 配置日志记录
在application.properties中启用日志记录。
logging.level.org.springframework.web=INFO
logging.level.com.example.registration=DEBUG
4. 安全性
考虑使用Spring Security来保护应用,确保用户的敏感数据(如密码)安全地存储和传输。
例如,可以加密用户的密码:
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;public class UserService {private PasswordEncoder passwordEncoder = new BCryptPasswordEncoder();public User registerUser(User user) {// 对密码进行加密String encodedPassword = passwordEncoder.encode(user.getPassword());user.setPassword(encodedPassword);return userRepository.save(user);}
}
八、卸载最后
学海无涯苦作舟,开发之路也是如此,没有什么捷径,都是多学多写多练。
