1. 项目概述
这个基于SpringBoot+Vue的图书管理系统是一个典型的前后端分离架构应用。后端采用SpringBoot框架构建RESTful API服务,前端使用Vue.js框架实现用户界面,数据库选用MySQL,并通过MyBatis实现数据持久化操作。
作为一个完整的全栈项目,它涵盖了从数据库设计、后端业务逻辑实现到前端界面交互的全流程开发。系统主要功能包括图书信息管理、借阅记录管理、用户权限控制等核心模块,适合作为学习Java全栈开发的实战案例。
2. 技术栈解析
2.1 SpringBoot后端架构
SpringBoot作为后端框架提供了以下核心优势:
- 自动配置:简化了Spring应用的初始搭建和开发过程
- 内嵌服务器:默认集成Tomcat,无需部署WAR文件
- Starter依赖:简化构建配置,如spring-boot-starter-web、spring-boot-starter-data-jpa等
- Actuator:提供生产级监控端点
典型项目结构:
code复制src/main/java
├── config/ # 配置类
├── controller/ # 控制器层
├── service/ # 业务逻辑层
├── dao/ # 数据访问层
├── entity/ # 实体类
└── Application.java # 启动类
2.2 Vue前端架构
Vue.js作为前端框架的主要特点:
- 组件化开发:提高代码复用性和可维护性
- 响应式数据绑定:简化DOM操作
- Vue Router:实现前端路由管理
- Vuex:集中式状态管理
- Axios:处理HTTP请求
典型项目结构:
code复制src/
├── api/ # 接口定义
├── assets/ # 静态资源
├── components/ # 公共组件
├── router/ # 路由配置
├── store/ # Vuex状态管理
├── views/ # 页面组件
└── App.vue # 根组件
2.3 数据库设计
MySQL数据库表设计示例:
sql复制CREATE TABLE `book` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`isbn` varchar(20) NOT NULL,
`title` varchar(100) NOT NULL,
`author` varchar(50) NOT NULL,
`publisher` varchar(50) DEFAULT NULL,
`publish_date` date DEFAULT NULL,
`status` tinyint(1) DEFAULT '1' COMMENT '1-可借 0-已借出',
PRIMARY KEY (`id`),
UNIQUE KEY `isbn` (`isbn`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE `user` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(50) NOT NULL,
`password` varchar(100) NOT NULL,
`role` varchar(20) DEFAULT 'USER',
PRIMARY KEY (`id`),
UNIQUE KEY `username` (`username`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE `borrow_record` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`book_id` int(11) NOT NULL,
`user_id` int(11) NOT NULL,
`borrow_date` datetime NOT NULL,
`return_date` datetime DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `book_id` (`book_id`),
KEY `user_id` (`user_id`),
CONSTRAINT `borrow_record_ibfk_1` FOREIGN KEY (`book_id`) REFERENCES `book` (`id`),
CONSTRAINT `borrow_record_ibfk_2` FOREIGN KEY (`user_id`) REFERENCES `user` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
3. 核心功能实现
3.1 图书管理模块
后端Controller示例:
java复制@RestController
@RequestMapping("/api/books")
public class BookController {
@Autowired
private BookService bookService;
@GetMapping
public ResponseEntity<List<Book>> getAllBooks() {
return ResponseEntity.ok(bookService.findAll());
}
@PostMapping
public ResponseEntity<Book> addBook(@RequestBody Book book) {
return ResponseEntity.ok(bookService.save(book));
}
@PutMapping("/{id}")
public ResponseEntity<Book> updateBook(
@PathVariable Long id,
@RequestBody Book book) {
return ResponseEntity.ok(bookService.update(id, book));
}
@DeleteMapping("/{id}")
public ResponseEntity<Void> deleteBook(@PathVariable Long id) {
bookService.delete(id);
return ResponseEntity.noContent().build();
}
}
前端Vue组件示例:
vue复制<template>
<div>
<el-table :data="books" style="width: 100%">
<el-table-column prop="isbn" label="ISBN"></el-table-column>
<el-table-column prop="title" label="书名"></el-table-column>
<el-table-column prop="author" label="作者"></el-table-column>
<el-table-column label="操作">
<template #default="scope">
<el-button size="mini" @click="handleEdit(scope.row)">编辑</el-button>
<el-button size="mini" type="danger" @click="handleDelete(scope.row)">删除</el-button>
</template>
</el-table-column>
</el-table>
</div>
</template>
<script>
import { getBooks } from '@/api/book'
export default {
data() {
return {
books: []
}
},
created() {
this.fetchBooks()
},
methods: {
async fetchBooks() {
try {
const { data } = await getBooks()
this.books = data
} catch (error) {
console.error(error)
}
},
handleEdit(row) {
// 编辑逻辑
},
handleDelete(row) {
// 删除逻辑
}
}
}
</script>
3.2 借阅管理模块
后端Service实现:
java复制@Service
public class BorrowServiceImpl implements BorrowService {
@Autowired
private BorrowRecordRepository borrowRecordRepository;
@Autowired
private BookRepository bookRepository;
@Transactional
public BorrowRecord borrowBook(Long bookId, Long userId) {
Book book = bookRepository.findById(bookId)
.orElseThrow(() -> new ResourceNotFoundException("图书不存在"));
if (book.getStatus() != 1) {
throw new BusinessException("该图书已被借出");
}
book.setStatus(0);
bookRepository.save(book);
BorrowRecord record = new BorrowRecord();
record.setBookId(bookId);
record.setUserId(userId);
record.setBorrowDate(new Date());
return borrowRecordRepository.save(record);
}
@Transactional
public BorrowRecord returnBook(Long recordId) {
BorrowRecord record = borrowRecordRepository.findById(recordId)
.orElseThrow(() -> new ResourceNotFoundException("借阅记录不存在"));
if (record.getReturnDate() != null) {
throw new BusinessException("该图书已归还");
}
Book book = bookRepository.findById(record.getBookId())
.orElseThrow(() -> new ResourceNotFoundException("图书不存在"));
book.setStatus(1);
bookRepository.save(book);
record.setReturnDate(new Date());
return borrowRecordRepository.save(record);
}
}
3.3 用户认证与授权
Spring Security配置:
java复制@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Autowired
private UserDetailsServiceImpl userDetailsService;
@Autowired
private JwtAuthenticationEntryPoint unauthorizedHandler;
@Bean
public JwtAuthenticationFilter jwtAuthenticationFilter() {
return new JwtAuthenticationFilter();
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http
.cors()
.and()
.csrf()
.disable()
.exceptionHandling()
.authenticationEntryPoint(unauthorizedHandler)
.and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.antMatchers("/api/auth/**").permitAll()
.antMatchers("/api/books").permitAll()
.antMatchers("/api/books/**").hasRole("ADMIN")
.anyRequest().authenticated();
http.addFilterBefore(jwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.userDetailsService(userDetailsService)
.passwordEncoder(passwordEncoder());
}
@Bean
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
}
JWT工具类:
java复制@Component
public class JwtTokenProvider {
@Value("${app.jwtSecret}")
private String jwtSecret;
@Value("${app.jwtExpirationInMs}")
private int jwtExpirationInMs;
public String generateToken(UserDetails userDetails) {
Date now = new Date();
Date expiryDate = new Date(now.getTime() + jwtExpirationInMs);
return Jwts.builder()
.setSubject(userDetails.getUsername())
.setIssuedAt(new Date())
.setExpiration(expiryDate)
.signWith(SignatureAlgorithm.HS512, jwtSecret)
.compact();
}
public String getUsernameFromJWT(String token) {
Claims claims = Jwts.parser()
.setSigningKey(jwtSecret)
.parseClaimsJws(token)
.getBody();
return claims.getSubject();
}
public boolean validateToken(String authToken) {
try {
Jwts.parser().setSigningKey(jwtSecret).parseClaimsJws(authToken);
return true;
} catch (SignatureException ex) {
// 签名无效
} catch (MalformedJwtException ex) {
// token格式错误
} catch (ExpiredJwtException ex) {
// token过期
} catch (UnsupportedJwtException ex) {
// 不支持的token
} catch (IllegalArgumentException ex) {
// claims为空
}
return false;
}
}
4. 前后端交互实现
4.1 API接口设计
RESTful API设计规范:
- GET /api/books - 获取图书列表
- POST /api/books - 添加新图书
- GET /api/books/{id} - 获取指定图书详情
- PUT /api/books/{id} - 更新图书信息
- DELETE /api/books/{id} - 删除图书
- POST /api/borrow - 借阅图书
- PUT /api/borrow/{id}/return - 归还图书
- POST /api/auth/login - 用户登录
- POST /api/auth/register - 用户注册
4.2 Axios封装
前端API请求封装:
javascript复制import axios from 'axios'
import router from '@/router'
import { Message } from 'element-ui'
// 创建axios实例
const service = axios.create({
baseURL: process.env.VUE_APP_BASE_API,
timeout: 5000
})
// 请求拦截器
service.interceptors.request.use(
config => {
const token = localStorage.getItem('token')
if (token) {
config.headers['Authorization'] = 'Bearer ' + token
}
return config
},
error => {
return Promise.reject(error)
}
)
// 响应拦截器
service.interceptors.response.use(
response => {
const res = response.data
if (res.code !== 200) {
Message({
message: res.message || 'Error',
type: 'error',
duration: 5 * 1000
})
if (res.code === 401) {
// 未授权,跳转登录
router.push('/login')
}
return Promise.reject(new Error(res.message || 'Error'))
} else {
return res
}
},
error => {
Message({
message: error.message,
type: 'error',
duration: 5 * 1000
})
return Promise.reject(error)
}
)
export default service
4.3 跨域解决方案
SpringBoot后端配置CORS:
java复制@Configuration
public class CorsConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("*")
.allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")
.allowedHeaders("*")
.allowCredentials(true)
.maxAge(3600);
}
}
5. 项目部署与优化
5.1 后端部署
SpringBoot应用打包:
bash复制mvn clean package -DskipTests
Docker部署示例:
dockerfile复制FROM openjdk:8-jdk-alpine
VOLUME /tmp
ARG JAR_FILE=target/*.jar
COPY ${JAR_FILE} app.jar
ENTRYPOINT ["java","-jar","/app.jar"]
5.2 前端部署
Vue项目打包:
bash复制npm run build
Nginx配置示例:
nginx复制server {
listen 80;
server_name library.example.com;
location / {
root /usr/share/nginx/html;
index index.html;
try_files $uri $uri/ /index.html;
}
location /api {
proxy_pass http://backend:8080;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
5.3 性能优化建议
-
数据库优化:
- 添加适当的索引
- 使用连接池配置
- 考虑读写分离
-
后端优化:
- 启用缓存(Redis)
- 异步处理耗时操作
- 合理使用分页查询
-
前端优化:
- 组件懒加载
- 路由懒加载
- 代码分割
- 图片压缩
6. 常见问题与解决方案
6.1 跨域问题
解决方案:
- 后端配置CORS(如前面所示)
- 使用Nginx反向代理
- 开发环境配置代理:
javascript复制// vue.config.js
module.exports = {
devServer: {
proxy: {
'/api': {
target: 'http://localhost:8080',
changeOrigin: true
}
}
}
}
6.2 接口调试问题
推荐工具:
- Postman
- Swagger UI集成:
java复制@Configuration
@EnableSwagger2
public class SwaggerConfig {
@Bean
public Docket api() {
return new Docket(DocumentationType.SWAGGER_2)
.select()
.apis(RequestHandlerSelectors.basePackage("com.example.library.controller"))
.paths(PathSelectors.any())
.build()
.apiInfo(apiInfo());
}
private ApiInfo apiInfo() {
return new ApiInfoBuilder()
.title("图书管理系统API文档")
.description("图书管理系统接口说明")
.version("1.0")
.build();
}
}
6.3 前端路由问题
Vue Router配置注意:
javascript复制const router = new VueRouter({
mode: 'history',
base: process.env.BASE_URL,
routes: [
{
path: '/',
name: 'Home',
component: Home
},
{
path: '/login',
name: 'Login',
component: () => import('../views/Login.vue')
}
// 其他路由...
]
})
6.4 权限控制问题
前端路由守卫示例:
javascript复制router.beforeEach((to, from, next) => {
const isAuthenticated = localStorage.getItem('token')
if (to.matched.some(record => record.meta.requiresAuth)) {
if (!isAuthenticated) {
next({
path: '/login',
query: { redirect: to.fullPath }
})
} else {
next()
}
} else {
next()
}
})
7. 项目扩展方向
- 增加图书分类管理
- 实现图书预约功能
- 添加图书评论系统
- 集成第三方登录(微信、QQ等)
- 开发移动端应用(使用Uniapp或Flutter)
- 实现数据分析报表功能
- 添加消息通知系统
- 集成支付功能(押金、罚款等)
这个图书管理系统项目涵盖了现代Web开发的完整技术栈,通过实际开发可以深入理解前后端分离架构的设计理念和实现细节。项目代码结构清晰,模块划分合理,既适合作为学习案例,也可以作为实际应用的起点进行二次开发。
