1. 项目概述与技术选型
作为一个长期从事Java全栈开发的工程师,我最近完成了一个基于Spring Boot+Vue的博客管理系统。这个项目让我深刻体会到现代前后端分离架构带来的开发效率提升。下面我将从技术选型、架构设计到具体实现,全面分享这个项目的实战经验。
博客系统作为内容创作和分享的基础平台,其核心需求可以归纳为:
- 用户端:文章浏览、评论互动、个人中心
- 管理端:内容管理、用户管理、系统配置
- 技术需求:高性能、易维护、良好的扩展性
1.1 技术栈决策过程
在技术选型阶段,我对比了多种方案后最终确定:
- 后端:Spring Boot 2.7 + MyBatis Plus
- 前端:Vue 3 + Element Plus
- 数据库:MySQL 8.0
- 构建工具:Maven + Webpack
选择这套技术栈主要基于以下考量:
- 开发效率:Spring Boot的自动配置和Vue的组件化开发能显著提升开发速度
- 社区支持:两者都有庞大的社区和丰富的插件生态
- 性能表现:Spring的响应式编程和Vue的虚拟DOM都能提供良好的性能
- 学习曲线:团队成员对这两项技术都有一定基础
提示:对于中小型项目,这套技术组合已经足够成熟。如果是超大型项目,可以考虑引入Spring Cloud微服务架构。
2. 系统架构设计
2.1 前后端分离架构
系统采用经典的前后端分离架构:
code复制前端(Vue) <-- HTTP/HTTPS --> 后端(Spring Boot) <--> 数据库(MySQL)
这种架构的优势在于:
- 前后端可以并行开发
- 前端可以独立部署,减轻服务器压力
- 更清晰的职责划分
2.2 后端模块划分
后端采用标准的三层架构,包结构如下:
code复制com.example.blog
├── config # 配置类
├── controller # 控制器层
├── service # 业务逻辑层
│ └── impl # 实现类
├── dao # 数据访问层
├── entity # 实体类
├── dto # 数据传输对象
├── util # 工具类
└── exception # 异常处理
2.3 数据库设计
核心表结构设计:
sql复制CREATE TABLE `article` (
`id` bigint NOT NULL AUTO_INCREMENT,
`title` varchar(100) NOT NULL,
`content` longtext NOT NULL,
`user_id` bigint NOT NULL,
`create_time` datetime NOT NULL,
`update_time` datetime NOT NULL,
PRIMARY KEY (`id`),
KEY `idx_user` (`user_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE TABLE `user` (
`id` bigint NOT NULL AUTO_INCREMENT,
`username` varchar(50) NOT NULL,
`password` varchar(100) NOT NULL,
`email` varchar(100) NOT NULL,
`avatar` varchar(255) DEFAULT NULL,
`create_time` datetime NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `uk_username` (`username`),
UNIQUE KEY `uk_email` (`email`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
3. 核心功能实现
3.1 用户认证模块
采用JWT实现无状态认证,关键代码如下:
java复制// JWT工具类
public class JwtUtil {
private static final String SECRET = "your-secret-key";
private static final long EXPIRATION = 86400000L; // 24小时
public static String generateToken(UserDetails userDetails) {
return Jwts.builder()
.setSubject(userDetails.getUsername())
.setIssuedAt(new Date())
.setExpiration(new Date(System.currentTimeMillis() + EXPIRATION))
.signWith(SignatureAlgorithm.HS512, SECRET)
.compact();
}
public static String getUsernameFromToken(String token) {
return Jwts.parser()
.setSigningKey(SECRET)
.parseClaimsJws(token)
.getBody()
.getSubject();
}
}
前端axios拦截器配置:
javascript复制// 请求拦截器
axios.interceptors.request.use(config => {
const token = localStorage.getItem('token');
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
return config;
}, error => {
return Promise.reject(error);
});
// 响应拦截器
axios.interceptors.response.use(response => {
return response;
}, error => {
if (error.response.status === 401) {
// 处理token过期
router.push('/login');
}
return Promise.reject(error);
});
3.2 文章管理模块
采用MyBatis Plus实现CRUD操作,示例代码:
java复制@Service
public class ArticleServiceImpl implements ArticleService {
@Autowired
private ArticleMapper articleMapper;
@Override
public Page<Article> listArticles(int pageNum, int pageSize) {
Page<Article> page = new Page<>(pageNum, pageSize);
return articleMapper.selectPage(page, null);
}
@Override
@Transactional
public void createArticle(Article article) {
article.setCreateTime(LocalDateTime.now());
article.setUpdateTime(LocalDateTime.now());
articleMapper.insert(article);
}
}
对应的Vue组件:
vue复制<template>
<div>
<el-table :data="articles" style="width: 100%">
<el-table-column prop="title" label="标题"></el-table-column>
<el-table-column prop="createTime" label="创建时间"></el-table-column>
<el-table-column label="操作">
<template #default="scope">
<el-button @click="editArticle(scope.row)">编辑</el-button>
</template>
</el-table-column>
</el-table>
<el-pagination
@current-change="handlePageChange"
:current-page="pagination.current"
:page-size="pagination.size"
:total="pagination.total">
</el-pagination>
</div>
</template>
<script>
export default {
data() {
return {
articles: [],
pagination: {
current: 1,
size: 10,
total: 0
}
}
},
methods: {
loadArticles() {
axios.get('/api/articles', {
params: {
pageNum: this.pagination.current,
pageSize: this.pagination.size
}
}).then(response => {
this.articles = response.data.records;
this.pagination.total = response.data.total;
});
},
handlePageChange(current) {
this.pagination.current = current;
this.loadArticles();
}
},
mounted() {
this.loadArticles();
}
}
</script>
4. 性能优化实践
4.1 缓存策略
采用Redis实现多级缓存:
- 热点文章缓存
- 用户信息缓存
- 系统配置缓存
配置示例:
java复制@Configuration
@EnableCaching
public class RedisConfig {
@Bean
public RedisCacheManager cacheManager(RedisConnectionFactory factory) {
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(Duration.ofHours(1))
.disableCachingNullValues();
return RedisCacheManager.builder(factory)
.cacheDefaults(config)
.build();
}
}
4.2 数据库优化
- 索引优化:为常用查询字段添加索引
- 查询优化:使用MyBatis Plus的QueryWrapper避免N+1问题
- 连接池配置:
yaml复制spring:
datasource:
hikari:
maximum-pool-size: 20
minimum-idle: 5
idle-timeout: 30000
max-lifetime: 1800000
connection-timeout: 30000
5. 部署方案
5.1 后端部署
采用Docker容器化部署:
dockerfile复制FROM openjdk:11-jre
WORKDIR /app
COPY target/blog-backend-0.0.1-SNAPSHOT.jar app.jar
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "app.jar"]
5.2 前端部署
Nginx配置示例:
nginx复制server {
listen 80;
server_name yourdomain.com;
location / {
root /var/www/blog-frontend;
try_files $uri $uri/ /index.html;
}
location /api {
proxy_pass http://backend:8080;
proxy_set_header Host $host;
}
}
6. 踩坑与解决方案
6.1 跨域问题
解决方案:
java复制@Configuration
public class CorsConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOrigins("*")
.allowedMethods("GET", "POST", "PUT", "DELETE")
.allowedHeaders("*")
.maxAge(3600);
}
}
6.2 文件上传
后端处理:
java复制@PostMapping("/upload")
public R upload(@RequestParam("file") MultipartFile file) {
if (file.isEmpty()) {
return R.error("请选择文件");
}
try {
String fileName = UUID.randomUUID() + "." +
StringUtils.getFilenameExtension(file.getOriginalFilename());
Path path = Paths.get("/uploads/" + fileName);
Files.copy(file.getInputStream(), path, StandardCopyOption.REPLACE_EXISTING);
return R.ok().put("url", "/uploads/" + fileName);
} catch (IOException e) {
return R.error("上传失败");
}
}
前端实现:
vue复制<template>
<el-upload
action="/api/upload"
:on-success="handleSuccess">
<el-button type="primary">点击上传</el-button>
</el-upload>
</template>
<script>
export default {
methods: {
handleSuccess(response) {
this.imageUrl = response.data.url;
}
}
}
</script>
7. 项目扩展方向
- SEO优化:实现服务端渲染(SSR)或预渲染
- 移动端适配:开发响应式布局或单独移动端应用
- 内容推荐:基于用户行为的智能推荐算法
- 多语言支持:国际化(i18n)实现
- 第三方登录:集成微信、GitHub等OAuth2登录
这个项目从技术选型到最终部署,每个环节都让我对现代Web开发有了更深的理解。特别是Spring Boot和Vue的配合使用,真正实现了前后端开发的解耦和高效协作。在实际开发中,合理设计API接口规范、统一异常处理机制以及良好的代码组织方式,都能显著提升项目的可维护性。