初始搭建案例演示
Spring Boot 是一个基于 Spring 框架的快速应用开发框架,它简化了新 Spring 应用的初始搭建以及开发过程。下面是一个简单的 Spring Boot 应用开发案例,用于创建一个 RESTful API 服务。
步骤 1: 创建项目
-
使用 Spring Initializr 创建项目:
- 访问 https://start.spring.io/
- 选择你喜欢的项目类型(如 Maven 或 Gradle),Java 作为语言,JDK 版本,以及其他依赖项。
- 添加依赖项,比如 “Web” 依赖来支持 Spring MVC。
-
生成并解压项目:
- 下载项目 ZIP 文件后解压到本地文件系统中。
步骤 2: 创建实体类
- 定义实体类:
- 在
src/main/java
目录下创建一个包,例如com.example.demo
。 - 创建一个 Java 类,比如
User.java
,定义一些基本属性如 id, name, email。
- 在
package com.example.demo;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
@Entity
public class User {
@Id
@GeneratedValue
private Long id;
private String name;
private String email;
// getters and setters
}
步骤 3: 创建 Repository
- 创建 Repository 接口:
- 使用 Spring Data JPA 创建一个简单的 CRUD 操作接口。
package com.example.demo;
import org.springframework.data.repository.CrudRepository;
public interface UserRepository extends CrudRepository<User, Long> {
}
步骤 4: 创建 Controller
- 创建 Controller 类:
- 创建一个 Controller 类,比如
UserController.java
,用来处理 HTTP 请求。
- 创建一个 Controller 类,比如
package com.example.demo;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@RequestMapping("/api/users")
public class UserController {
private final UserRepository userRepository;
public UserController(UserRepository userRepository) {
this.userRepository = userRepository;
}
@GetMapping
public List<User> getAllUsers() {
return userRepository.findAll();
}
@PostMapping
public User createUser(@RequestBody User user) {
return userRepository.save(user);
}
}
步骤 5: 启动应用程序
- 修改主类:
- 在主类中添加
@EnableAutoConfiguration
或@SpringBootApplication
注解。
- 在主类中添加
package com.example.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
步骤 6: 运行应用程序
-
运行程序:
- 通过 IDE 或者命令行工具运行
DemoApplication
类。
- 通过 IDE 或者命令行工具运行
-
测试 API:
- 使用 Postman 或者浏览器访问
http://localhost:8080/api/users
测试 GET 请求。 - 发送 POST 请求到
http://localhost:8080/api/users
创建新用户。
- 使用 Postman 或者浏览器访问
以上就是创建一个简单的 Spring Boot RESTful API 的步骤。在基础的 Spring Boot RESTful API 上,我们可以进一步增强其功能。接下来,我们可以添加以下特性:
步骤 7. 配置数据库连接
为了使我们的应用能够持久化存储数据,我们需要配置数据库连接。假设我们使用的是 MySQL 数据库。
添加 MySQL 数据库驱动依赖
在 pom.xml
中添加 MySQL 数据库驱动依赖:
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
配置 application.properties
在 resources
目录下编辑 application.properties
文件,添加数据库连接信息:
spring.datasource.url=jdbc:mysql://localhost:3306/yourdbname
spring.datasource.username=yourusername
spring.datasource.password=yourpassword
spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true
这里,spring.jpa.hibernate.ddl-auto=update
表示 Hibernate 会在启动时自动创建或更新表结构。
步骤8. 异常处理
为提高用户体验,我们可以在控制器中添加异常处理逻辑。
package com.example.demo;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(value = { Exception.class })
public ResponseEntity<Object> handleException(Exception ex) {
return new ResponseEntity<>("Error: " + ex.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR);
}
}
步骤9. 安全性与身份验证
为保护我们的 API 不被未经授权的访问,可以使用 Spring Security 模块来实现安全性控制。
添加 Spring Security 依赖
在 pom.xml
中添加 Spring Security 依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
配置 Spring Security
创建一个配置类来配置 Spring Security 规则:
package com.example.demo;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.antMatchers("/api/users").permitAll()
.anyRequest().authenticated()
.and()
.formLogin().permitAll()
.and()
.logout().permitAll();
}
}
这将允许任何人访问 /api/users
路径,而其他所有路径都需要经过身份验证。
步骤10. 单元测试
为了确保代码质量,编写单元测试是必不可少的。Spring Boot 支持 JUnit 和 TestNG 测试框架。
创建测试类
package com.example.demo;
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.test.web.servlet.MockMvc;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
@WebMvcTest(UserController.class)
public class UserControllerTests {
@Autowired
private MockMvc mockMvc;
@Test
public void shouldReturnDefaultMessage() throws Exception {
this.mockMvc.perform(get("/api/users"))
.andExpect(status().isOk())
.andExpect(content().string("..."));
}
}
以上步骤会帮助你构建一个更加健壮且安全的 Spring Boot 应用程序。你可以根据项目的具体需求进一步扩展功能。
综合应用案例
让我们设计一个更复杂的 Spring Boot 综合应用案例。我们将创建一个具有用户管理、权限控制、RESTful API、集成测试等功能的在线图书管理系统。这个系统将包括用户注册登录、书籍管理、评论功能,并且会使用 JWT(JSON Web Token)进行身份验证。
技术栈
- Spring Boot: 主要框架
- Spring Data JPA: 数据访问层
- MySQL: 数据库
- JWT (JSON Web Tokens): 身份验证
- Spring Security: 安全模块
- Thymeleaf: 前端模板引擎(可选)
- Lombok: 减少 getter/setter 代码量
- JUnit / TestNG: 测试框架
应用结构
1. 创建项目
使用 Spring Initializr 创建项目,包含如下依赖:
- Spring Web
- Spring Data JPA
- Spring Security
- Thymeleaf
- Lombok
- MySQL Driver
- Spring Boot DevTools
- Spring Boot Starter Test
2. 实体类定义
用户实体 (User.java
)
package com.example.bookstore.model;
import javax.persistence.*;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
@Entity
@Table(name = "users")
public class User implements UserDetails {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String username;
private String password;
// 更多属性,如 email 等
@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
// 返回用户的权限集合
}
@Override
public boolean isAccountNonExpired() {
return true;
}
@Override
public boolean isAccountNonLocked() {
return true;
}
@Override
public boolean isCredentialsNonExpired() {
return true;
}
@Override
public boolean isEnabled() {
return true;
}
// Getters and Setters
}
书籍实体 (Book.java
)
package com.example.bookstore.model;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
@Entity
public class Book {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String title;
private String author;
private String isbn;
// Getters and Setters
}
3. Repository 接口
创建对应的 JPA Repository 接口。
package com.example.bookstore.repository;
import com.example.bookstore.model.Book;
import org.springframework.data.jpa.repository.JpaRepository;
public interface BookRepository extends JpaRepository<Book, Long> {
}
package com.example.bookstore.repository;
import com.example.bookstore.model.User;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface UserRepository extends JpaRepository<User, Long> {
User findByUsername(String username);
}
4. Service 层
创建服务层处理业务逻辑。
package com.example.bookstore.service;
import com.example.bookstore.model.User;
import com.example.bookstore.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Service;
@Service
public class UserService implements UserDetailsService {
private final UserRepository userRepository;
@Autowired
public UserService(UserRepository userRepository) {
this.userRepository = userRepository;
}
@Override
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
User user = userRepository.findByUsername(username);
if (user == null) {
throw new UsernameNotFoundException(username);
}
return user;
}
}
5. Controller 层
创建控制器处理 HTTP 请求。
package com.example.bookstore.controller;
import com.example.bookstore.model.Book;
import com.example.bookstore.repository.BookRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@RequestMapping("/api/books")
public class BookController {
private final BookRepository bookRepository;
@Autowired
public BookController(BookRepository bookRepository) {
this.bookRepository = bookRepository;
}
@GetMapping
public List<Book> getAllBooks() {
return bookRepository.findAll();
}
@PostMapping
public Book createBook(@RequestBody Book book) {
return bookRepository.save(book);
}
}
6. 配置 Spring Security
使用 JWT 进行身份验证。
package com.example.bookstore.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.antMatchers("/api/auth/**").permitAll()
.anyRequest().authenticated()
.and()
.addFilterBefore(new JwtTokenFilter(), UsernamePasswordAuthenticationFilter.class);
}
}
7. JWT Token Filter
创建 JWT token 处理器。
package com.example.bookstore.security;
import com.example.bookstore.model.User;
import com.example.bookstore.service.UserService;
import io.jsonwebtoken.Jwts;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class JwtTokenFilter extends OncePerRequestFilter {
private final UserService userService;
private final String jwtSecret = "your_jwt_secret";
public JwtTokenFilter(UserService userService) {
this.userService = userService;
}
@Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response,
FilterChain filterChain) throws ServletException, IOException {
try {
String jwt = parseJwt(request);
if (jwt != null && validateJwtToken(jwt)) {
String username = getUsernameFromJwtToken(jwt);
UserDetails userDetails = userService.loadUserByUsername(username);
UsernamePasswordAuthenticationToken authentication =
new UsernamePasswordAuthenticationToken(userDetails, null, userDetails.getAuthorities());
authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
SecurityContextHolder.getContext().setAuthentication(authentication);
}
} catch (Exception e) {
logger.error("Cannot set user authentication: {}", e);
}
filterChain.doFilter(request, response);
}
private String parseJwt(HttpServletRequest request) {
String headerAuth = request.getHeader("Authorization");
if (StringUtils.hasText(headerAuth) && headerAuth.startsWith("Bearer ")) {
return headerAuth.substring(7);
}
return null;
}
private boolean validateJwtToken(String authToken) {
return !authToken.isEmpty() && Jwts.parser().setSigningKey(jwtSecret).parseClaimsJws(authToken) != null;
}
private String getUsernameFromJwtToken(String token) {
return Jwts.parser().setSigningKey(jwtSecret).parseClaimsJws(token).getBody().getSubject();
}
}
8. 编写测试
创建单元测试以确保所有功能正确无误。
package com.example.bookstore;
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.test.web.servlet.MockMvc;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@WebMvcTest(BookController.class)
class BookControllerTest {
@Autowired
private MockMvc mockMvc;
@Test
void shouldReturnDefaultMessage() throws Exception {
mockMvc.perform(get("/api/books"))
.andExpect(status().isOk());
}
}
这个案例展示了如何构建一个较为复杂的 Spring Boot 应用程序,包括用户认证、权限控制、数据库操作等核心功能。我们继续完善这个图书管理系统的案例。接下来,我们将添加更多的功能,如用户注册和登录流程、书籍的增删改查功能、前端界面展示、以及集成测试。
9. 用户注册和登录功能
用户注册
首先,我们需要创建一个用户注册的功能,允许新用户注册账户。
package com.example.bookstore.controller;
import com.example.bookstore.model.User;
import com.example.bookstore.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/api/auth")
public class AuthController {
private final UserService userService;
@Autowired
public AuthController(UserService userService) {
this.userService = userService;
}
@PostMapping("/register")
public ResponseEntity<String> registerUser(@RequestBody User user) {
userService.registerUser(user);
return ResponseEntity.ok("User registered successfully.");
}
}
用户登录
然后,我们需要实现用户登录功能,并返回 JWT Token。
package com.example.bookstore.controller;
import com.example.bookstore.model.User;
import com.example.bookstore.security.JwtTokenProvider;
import com.example.bookstore.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/api/auth")
public class AuthController {
private final AuthenticationManager authenticationManager;
private final JwtTokenProvider jwtTokenProvider;
private final UserService userService;
@Autowired
public AuthController(AuthenticationManager authenticationManager, JwtTokenProvider jwtTokenProvider, UserService userService) {
this.authenticationManager = authenticationManager;
this.jwtTokenProvider = jwtTokenProvider;
this.userService = userService;
}
@PostMapping("/login")
public ResponseEntity<String> loginUser(@RequestBody User user) {
Authentication auth = authenticationManager.authenticate(
new UsernamePasswordAuthenticationToken(user.getUsername(), user.getPassword())
);
String token = jwtTokenProvider.generateToken(user);
return ResponseEntity.ok(token);
}
}
10. 前端展示
假设我们使用 Thymeleaf 作为前端模板引擎,创建一些基本的 HTML 页面来展示书籍列表和用户界面。
HTML 页面
在 src/main/resources/templates
目录下创建 HTML 文件。
<!-- templates/bookList.html -->
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>Book List</title>
</head>
<body>
<h1>Book List</h1>
<table>
<thead>
<tr>
<th>Title</th>
<th>Author</th>
<th>ISBN</th>
</tr>
</thead>
<tbody>
<tr th:each="book : ${books}">
<td th:text="${book.title}"></td>
<td th:text="${book.author}"></td>
<td th:text="${book.isbn}"></td>
</tr>
</tbody>
</table>
</body>
</html>
控制器中的 Thymeleaf 视图
package com.example.bookstore.controller;
import com.example.bookstore.model.Book;
import com.example.bookstore.repository.BookRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
@Controller
public class BookViewController {
private final BookRepository bookRepository;
@Autowired
public BookViewController(BookRepository bookRepository) {
this.bookRepository = bookRepository;
}
@GetMapping("/")
public String listBooks(Model model) {
model.addAttribute("books", bookRepository.findAll());
return "bookList";
}
}
11. CRUD 功能
创建书籍
package com.example.bookstore.controller;
import com.example.bookstore.model.Book;
import com.example.bookstore.repository.BookRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/api/books")
public class BookController {
private final BookRepository bookRepository;
@Autowired
public BookController(BookRepository bookRepository) {
this.bookRepository = bookRepository;
}
@PostMapping
public ResponseEntity<Book> createBook(@RequestBody Book book) {
Book createdBook = bookRepository.save(book);
return ResponseEntity.ok(createdBook);
}
// 更新、删除等方法...
}
更新书籍
@PutMapping("/{id}")
public ResponseEntity<Book> updateBook(@PathVariable Long id, @RequestBody Book bookDetails) {
Book updatedBook = bookRepository.findById(id)
.map(book -> {
book.setTitle(bookDetails.getTitle());
book.setAuthor(bookDetails.getAuthor());
book.setIsbn(bookDetails.getIsbn());
return bookRepository.save(book);
})
.orElseThrow(() -> new ResourceNotFoundException("Book not found with id " + id));
return ResponseEntity.ok(updatedBook);
}
删除书籍
@DeleteMapping("/{id}")
public ResponseEntity<Void> deleteBook(@PathVariable Long id) {
bookRepository.deleteById(id);
return ResponseEntity.noContent().build();
}
12. 集成测试
最后,我们可以添加集成测试来确保整个应用正常工作。
package com.example.bookstore;
import com.example.bookstore.model.Book;
import com.example.bookstore.repository.BookRepository;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.web.servlet.MockMvc;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
@SpringBootTest
@AutoConfigureMockMvc
public class BookIntegrationTest {
@Autowired
private MockMvc mockMvc;
@Autowired
private BookRepository bookRepository;
@Test
public void shouldCreateAndRetrieveBook() throws Exception {
// Given
Book book = new Book();
book.setTitle("Test Book");
book.setAuthor("Test Author");
book.setIsbn("1234567890");
// When
mockMvc.perform(post("/api/books").contentType("application/json").content("{\"title\":\"Test Book\",\"author\":\"Test Author\",\"isbn\":\"1234567890\"}"))
.andExpect(status().isCreated());
// Then
mockMvc.perform(get("/api/books/{id}", book.getId()))
.andExpect(status().isOk());
}
}
以上就是一个相对完整的 Spring Boot 图书管理系统案例,涵盖了用户注册登录、书籍管理、前端展示、以及集成测试等多个方面。
————————————————
最后我们放松一下眼睛