简介:“智子商城”是一个采用SpringBoot与Vue实现的前后端分离电子商务平台,涵盖现代Web开发核心技术栈。项目通过SpringBoot构建稳定高效的后端服务,使用MySQL存储商品、用户和订单等核心数据,并引入Redis缓存提升系统性能。安全方面集成SpringSecurity实现认证与权限控制,前端采用Vue框架构建响应式、组件化的用户界面,提升交互体验。配套zut-shop.sql数据库脚本和Zut-Shop.zip完整源码包,便于快速部署与学习。本项目覆盖前后端协作、数据库设计、缓存优化与安全控制等关键环节,是掌握全栈开发的优质实践案例。
前后端分离架构设计与实现
在当今互联网应用飞速发展的背景下,传统的“前后端耦合”开发模式早已无法满足快速迭代、高并发、跨平台等现代业务需求。你是否也曾经历过这样的场景:前端改个按钮颜色,后端却要重新打包部署?或者因为一个接口字段命名不统一,导致联调整整浪费了一天时间?
这些痛点背后,正是单体架构中 HTML模板渲染 + Java服务逻辑深度绑定 所带来的顽疾。而随着 RESTful API 规范的普及和 Vue/React 等现代前端框架的成熟,一种更优雅、更高效的解决方案浮出水面—— 前后端分离架构 。
这种架构的核心思想其实非常朴素: 让前端专注用户体验,让后端专注数据服务 。前端通过独立构建的静态资源(HTML/CSS/JS)运行于浏览器或移动端 WebView 中,而后端则以纯粹的 JSON 数据接口形式提供能力支持。两者之间通过标准 HTTP 协议进行通信,彻底解耦。
graph LR
A[前端Vue应用] -->|Axios调用| B[SpringBoot REST API]
B --> C[MySQL数据库]
C --> B --> A
看这张图是不是特别熟悉?它描绘的就是典型的 SPA(单页应用)工作流。用户访问 https://shop.zut.com ,浏览器加载 Vue 打包后的 JS 文件,初始化页面;当点击“加入购物车”时,前端发起 Axios 请求到 /api/cart/add 接口;SpringBoot 后端处理逻辑并返回 {success: true} 或错误码;前端根据响应结果更新视图状态。
这个过程看似简单,但其背后隐藏着一场软件工程范式的变革:
- ✅ 职责清晰 :前端不再需要懂 JSP/Thymeleaf 模板语法,后端也不必关心 DOM 结构;
- ✅ 并行开发 :前后端可以基于接口文档同时开工,极大缩短项目周期;
- ✅ 技术栈自由 :前端可以用 React 替换 Vue,后端可以从 SpringBoot 迁移到 Go,只要 API 兼容即可;
- ✅ 易于扩展 :一套后端 API 可同时服务于 Web、App、小程序等多个客户端;
- ✅ 便于测试与监控 :接口可被 Postman/Fiddler 抓包调试,也能接入 SkyWalking/Apollo 做全链路追踪。
更重要的是,这种架构为后续引入微服务、API网关、CDN 加速、灰度发布等高级能力打下了坚实基础。可以说,前后端分离不仅是当下主流,更是未来系统演进的必经之路。
SpringBoot后端服务搭建与自动化配置
当我们决定采用前后端分离架构时,选择一个强大且高效的后端框架就变得至关重要。而在 Java 生态圈里, SpringBoot 已经成为构建轻量级、高可用后端服务的事实标准 。它不仅解决了传统 Spring 框架“配置地狱”的问题,还通过“约定优于配置”的哲学理念,实现了真正的“开箱即用”。
想象一下:以前我们要写十几行 XML 配置才能启动一个 MVC 项目,而现在只需要一个注解、一个主类就能跑起来。这背后的魔法从何而来?我们不妨深入到底层去一探究竟。
SpringBoot核心机制与自动装配原理
@SpringBootApplication 注解的秘密
先来看一段再普通不过的代码:
@SpringBootApplication
public class ZutShopApplication {
public static void main(String[] args) {
SpringApplication.run(ZutShopApplication.class, args);
}
}
短短三行代码,却承载了整个应用的生命起点。其中最关键的就是那个 @SpringBootApplication —— 它不是一个简单的标签,而是一个集大成者的复合注解。
拆开来看,它由三个关键部分组成:
| 注解 | 功能说明 |
|---|---|
@SpringBootConfiguration | 标识该类为Spring Boot的配置类,本质是 @Configuration 的变体 |
@EnableAutoConfiguration | 启用自动装配机制,触发所有符合条件的 AutoConfiguration 类加载 |
@ComponentScan | 扫描当前包及其子包下的组件(如@Service、@Controller)并注册为Bean |
也就是说,这一行注解干了三件事:
1. 告诉 Spring:“我是个配置源”;
2. 开启“自动发现+自动配置”模式;
3. 自动扫描本包及子包下的所有 Spring 组件。
💡 小贴士:如果你的某些组件不在主类所在包路径下,记得显式指定扫描范围:
java @ComponentScan(basePackages = {"com.zut.shop", "com.common.utils"})
启动流程全景解析
接下来我们来看看,当你按下运行键那一刻,JVM 到底经历了什么。
graph TD
A[调用SpringApplication.run()] --> B[初始化ApplicationContext]
B --> C[加载application.properties/yml]
C --> D[执行@EventListener(ApplicationStartingEvent)]
D --> E[推断Web应用类型(Servlet或Reactive)]
E --> F[加载BootstrapRegistry初始器]
F --> G[准备Environment环境对象]
G --> H[创建并刷新ApplicationContext]
H --> I[执行BeanFactoryPostProcessor]
I --> J[实例化所有非懒加载Bean]
J --> K[发布ApplicationReadyEvent事件]
K --> L[启动完成,监听端口]
这个流程图虽然看起来复杂,但它完整地展示了 SpringBoot 应用从进程启动到对外提供 HTTP 服务的全过程。我们可以把它分为四个阶段来理解:
第一阶段:准备期(A ~ G)
- 调用
run()方法后,首先会创建一个StopWatch来记录启动耗时; - 然后读取命令行参数、系统变量、
application.yml等外部配置,组装成统一的Environment对象; - 并根据 classpath 判断是 Web 应用还是普通 Java 应用(比如批处理任务);
- 如果是 Web 应用,则使用
AnnotationConfigServletWebServerApplicationContext上下文。
🧠 实践建议:你可以通过设置
spring.main.web-application-type=none来强制禁用 Web 支持,常用于后台定时任务模块。
第二阶段:上下文刷新(H ~ I)
这是整个启动过程中最核心的一环,对应的是 Spring 框架中的 refresh() 方法。它包含多个子步骤:
- prepareBeanFactory :设置类加载器、注册默认的 BeanPostProcessor;
- invokeBeanFactoryPostProcessors :执行
@Configuration类解析、@ComponentScan扫描、@Import导入等操作; - registerBeanPostProcessors :注册 AOP 相关处理器,为后续代理做准备;
- initMessageSource / initApplicationEventMulticaster :初始化国际化消息源和事件广播器;
- onRefresh :特定于 Web 容器的刷新动作,比如启动内嵌 Tomcat;
- registerListeners :注册事件监听器;
- finishBeanFactoryInitialization :实例化所有非懒加载的单例 Bean;
- finishRefresh :发布
ContextRefreshedEvent和ApplicationReadyEvent。
⚠️ 注意:只有当
refresh()成功执行完毕,你的@Service、@Repository才真正变成了容器中的 Bean!
第三阶段:Bean 初始化(J)
在这个阶段,Spring 开始逐个创建 Bean 实例。它的顺序大致如下:
- Configuration Class → 2. AutoConfiguration Classes → 3. 用户自定义 Bean
你会发现,很多 DataSource 、 RedisTemplate 是在你还未手动声明之前就已经存在了——这就是自动装配的力量。
第四阶段:启动完成(K ~ L)
最后一步是发布 ApplicationReadyEvent ,表示应用已准备好接收请求。此时你可以注册一些启动后要执行的任务:
@Component
public class StartupTask implements ApplicationListener<ApplicationReadyEvent> {
@Override
public void onApplicationEvent(ApplicationReadyEvent event) {
System.out.println("✅ 系统启动完成,开始加载缓存...");
}
}
或者使用更简洁的方式:
@Component
public class MyStartupRunner implements CommandLineRunner {
@Override
public void run(String... args) throws Exception {
// 启动后执行一次
}
}
源码级剖析:SpringApplication.run()
为了更直观地理解上述流程,我们直接进入 SpringBoot 的源码世界。
public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch(); // 性能计时器
stopWatch.start();
DefaultBootstrapContext bootstrapContext = createBootstrapContext();
ConfigurableApplicationContext context = null;
configureHeadlessProperty(); // 设置headless模式
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting(bootstrapContext, getMainApplicationClass());
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
configureIgnoreBeanInfo(environment);
Banner printedBanner = printBanner(environment); // 打印启动banner
context = createApplicationContext(); // 创建上下文(AnnotationConfigServletWebServerApplicationContext)
context.setApplicationStartup(applicationStartup);
prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
refreshContext(context); // 核心:刷新上下文,触发IOC容器初始化
afterRefresh(context, applicationArguments);
stopWatch.stop();
return context;
} catch (Throwable ex) {
handleRunFailure(context, ex, listeners);
throw new IllegalStateException(ex);
}
}
🔍 逐行解读:
-
createBootstrapContext():创建引导上下文,用于早期初始化逻辑,比如配置中心接入(Nacos/Consul); -
getRunListeners():获取所有SpringApplicationRunListener实现,支持在不同生命周期节点插入自定义行为; -
prepareEnvironment():加载application.yml、系统变量、命令行参数等,构建成统一的Environment对象; -
createApplicationContext():根据 classpath 判断是否为 Web 环境,决定使用哪种 ApplicationContext; -
refreshContext(context):调用父类AbstractApplicationContext.refresh()方法,这是 Spring IOC 容器初始化的核心入口; -
afterRefresh():在容器刷新后执行额外操作,如触发CommandLineRunner和ApplicationRunner接口实现。
整个流程就像一场精密编排的交响乐,每个方法都扮演着不可或缺的角色。
🎯 关键洞察:
很多开发者只关注业务逻辑,却忽略了refresh()这个核心方法的重要性。实际上, Spring 的一切魔法都发生在这里 。无论是依赖注入、AOP 代理、事务管理,还是事件机制,都是在这一步完成初始化的。
外部化配置优先级体系
SpringBoot 提供了极其灵活的配置方式,允许我们在不同环境下动态调整参数。这些配置按优先级从高到低排列如下:
- 命令行参数(
--server.port=8081) -
SPRING_APPLICATION_JSON环境变量 - ServletConfig 初始化参数
- JVM系统属性(
-Dserver.port=8082) -
application-{profile}.yml文件 -
application.yml文件 -
@PropertySource注解配置 - 默认属性(通过
SpringApplication.setDefaultProperties设置)
举个例子:
java -jar shop.jar --spring.profiles.active=prod --server.port=9000
这条命令将激活生产环境配置,并将端口设为 9000。即使你在 application-prod.yml 中写了 server.port=8080 ,也会被命令行覆盖。
💬 经验之谈:
在 CI/CD 流程中,推荐使用环境变量或命令行参数来控制关键配置(如数据库地址、日志级别),而不是硬编码在配置文件中。这样可以真正做到“一份代码,多环境部署”。
条件化配置与AutoConfiguration实现机制
如果说 SpringBoot 是一辆豪华轿车,那么 条件化配置(Conditional Configuration) 就是它的智能驾驶系统——只在合适的时候启用合适的功能。
你有没有想过:为什么只要你加了 spring-boot-starter-data-jpa ,SpringBoot 就自动给你配好了 DataSource 、 EntityManagerFactory 、 TransactionManager ?而当你没引入相关依赖时,这些组件又不会出现?
答案就在于 @ConditionalOnXXX 系列注解与 spring.factories 的完美配合。
自动装配是如何被触发的?
早在 SpringBoot 2.7 之前,自动配置信息存储在 META-INF/spring.factories 文件中:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.example.MyAutoConfiguration
但从 2.7 开始,官方推荐迁移到新的位置:
META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
内容更简洁:
com.example.MyAutoConfiguration
启动时,SpringBoot 会读取这个文件,拿到所有候选的 AutoConfiguration 类列表。然后逐个检查它们上面的 @ConditionalOnXXX 注解,只有满足条件才会真正加载。
常见的条件注解包括:
| 注解 | 触发条件 |
|---|---|
@ConditionalOnClass | 类路径存在指定类 |
@ConditionalOnMissingBean | 容器中不存在指定类型的Bean |
@ConditionalOnProperty | 配置文件中存在且值匹配某属性 |
@ConditionalOnWebApplication | 当前为Web应用程序 |
@ConditionalOnExpression | SpEL表达式计算结果为true |
让我们以数据库自动配置为例,看看它是如何工作的。
深入源码:DataSourceAutoConfiguration
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({ DataSource.class, EmbeddedDatabaseType.class })
@ConditionalOnMissingBean(type = "io.r2dbc.spi.ConnectionFactory")
@EnableConfigurationProperties(DataSourceProperties.class)
@Import({ DataSourcePoolMetadataProvidersConfiguration.class,
DataSourceInitializationConfiguration.class })
public class DataSourceAutoConfiguration {
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(TypeExcludeFilter.class)
@ConditionalOnProperty(name = "spring.datasource.type")
static class PooledDataSourceConfiguration {
@Bean
@ConditionalOnMissingBean // 仅当用户未自定义DataSource时才创建
public DataSource dataSource(DataSourceProperties properties) {
return properties.initializeDataSourceBuilder().build();
}
}
}
🔎 逻辑拆解:
- 整个类上有
@ConditionalOnClass(DataSource.class),意味着只有当项目中存在javax.sql.DataSource接口时才会尝试加载此配置; - 内部静态类
PooledDataSourceConfiguration添加了@ConditionalOnProperty(name="spring.datasource.type"),表示必须在配置文件中设置了数据源类型才生效; - 最终的
dataSource()方法上还有@ConditionalOnMissingBean,确保不会与用户自定义的数据源冲突; - 使用
@EnableConfigurationProperties(DataSourceProperties.class)绑定application.yml中的spring.datasource.*配置项。
这样一来,既做到了“智能开启”,又能“安全退出”,避免污染用户空间。
✅ 设计哲学总结:
- 存在即合理 :有类才配,无类不理;
- 宁缺毋滥 :已有 Bean 就不重复造轮子;
- 开关可控 :可通过配置关闭某项自动配置。
如何自定义自己的 AutoConfiguration?
假设你想开发一个通用的邮件发送模块,希望别人引入依赖后就能直接使用 MailSender ,该怎么实现呢?
步骤一:编写配置类
@Configuration
@ConditionalOnProperty(name = "mail.enabled", havingValue = "true")
@ConditionalOnMissingBean(MailSender.class)
@EnableConfigurationProperties(MailProperties.class)
public class MailAutoConfiguration {
@Bean
public MailSender mailSender(MailProperties properties) {
return new SmtpMailSender(properties.getHost(), properties.getPort());
}
}
步骤二:定义配置属性
@Data
@ConfigurationProperties(prefix = "mail")
public class MailProperties {
private String host = "smtp.example.com";
private int port = 587;
private String username;
private String password;
}
步骤三:注册自动配置类
在模块的资源目录下创建:
src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
添加一行:
com.zut.shop.autoconfigure.MailAutoConfiguration
步骤四:使用者只需引入依赖并配置
mail:
enabled: true
host: smtp.gmail.com
port: 587
username: user@gmail.com
password: secret
就这么简单!使用者连 @Bean 都不用写,就能直接 @Autowired 注入 MailSender 。
🌟 这就是 SpringBoot 插件化开发的魅力所在:高度封装、即插即用、零侵入。
内嵌Tomcat与Web环境初始化过程
还记得当年我们要把 WAR 包上传到服务器,再重启 Tomcat 的痛苦经历吗?如今,SpringBoot 让这一切成为了历史。
只需引入 spring-boot-starter-web ,你的应用就会自带一个内嵌的 Servlet 容器(默认是 Tomcat)。这意味着:
- 不再需要外部 Web 服务器;
- 打包成 jar 就能直接运行;
- 更适合 Docker 容器化部署;
- 启动更快,运维更简单。
内嵌容器启动流程
-
SpringApplication检测到Servlet和Tomcat类存在于 classpath; - 创建
AnnotationConfigServletWebServerApplicationContext作为上下文; - 在
refreshContext()过程中调用onRefresh()方法,触发WebServer工厂构建; -
TomcatServletWebServerFactory.getWebServer()创建并启动 Tomcat 实例; - 将 Spring MVC 的
DispatcherServlet注册为默认 Servlet,映射路径为/。
核心代码分析
public WebServer getWebServer(ServletContextInitializer... initializers) {
Tomcat tomcat = new Tomcat();
File baseDir = this.baseDirectory != null ? this.baseDirectory : createTempDir("tomcat");
tomcat.setBaseDir(baseDir.getAbsolutePath());
Connector connector = new Connector(this.protocol);
tomcat.getService().addConnector(connector);
customizeConnector(connector);
tomcat.setPort(getPort());
tomcat.getHost().setAppBase(baseDir.getAbsolutePath());
Context context = tomcat.addContext("", baseDir.getAbsolutePath());
context.setDisplayName(getDisplayName());
for (ServletContextInitializer initializer : initializers) {
initializer.onStartup(context.getServletContext()); // 注册DispatcherServlet
}
prepareContext(context, initializers);
return new TomcatWebServer(tomcat, getPort() >= 0);
}
🧩 参数说明:
-
baseDirectory:Tomcat 工作目录,默认为临时目录; -
Connector:表示 HTTP 连接器,支持配置 SSL、线程池、超时时间等; -
customizeConnector():允许通过application.yml中的server.tomcat.*进行定制; -
initializers:包含所有实现了ServletContextInitializer接口的组件,最重要的是DispatcherServletRegistrationBean,它负责注册 Spring MVC 前端控制器; -
TomcatWebServer:包装了 Tomcat 实例,并提供start()和stop()方法用于生命周期管理。
性能调优:Tomcat 线程池配置
server:
port: 8080
tomcat:
max-threads: 200
min-spare-threads: 10
accept-count: 100
connection-timeout: 5000ms
这些配置会被 TomcatServletWebServerFactory 读取并应用到 Connector 上,有效应对高并发请求场景。
🚀 高阶玩法:替换为 Undertow
若追求更高性能,特别是处理大量短连接时,可以考虑切换为 Undertow:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-undertow</artifactId>
</dependency>
Undertow 采用非阻塞 I/O 模型,在吞吐量和内存占用方面表现更优,特别适合 API 网关类服务。
📊 数据参考:在相同压测条件下,Undertow 的 QPS 通常比 Tomcat 高 15%~30%,尤其是在小包高频请求场景下优势明显。
综上所述,SpringBoot 通过深度融合 Spring 容器与内嵌 Web 服务器,真正实现了“微服务就绪”。理解其自动装配与容器初始化机制,有助于我们在复杂业务场景中精准控制组件行为,提升系统的稳定性与可维护性。
MySQL数据库设计与电商数据模型构建
电商平台的本质,是一场关于“人、货、场”的数据组织游戏。而数据库,就是这场游戏的裁判兼记分员。
一个好的数据库设计,不仅能保证交易过程的准确性和一致性,还能支撑起千万级用户的并发访问。反之,一个糟糕的设计可能导致慢查询频发、死锁不断、扩容困难,最终拖垮整个系统。
本章我们将围绕典型电商业务场景,深入探讨 MySQL 数据库的设计艺术。
电商平台业务逻辑分析与E-R模型设计
一个完整的电商业务流程通常是这样的:
用户登录 → 浏览商品 → 加入购物车 → 提交订单 → 支付成功 → 商家发货 → 用户收货 → 售后评价
这条链路上涉及多个核心实体:用户、商品、购物车、订单、订单明细、支付记录、物流信息等。我们需要对这些实体及其关系进行精确建模。
核心实体关系建模
erDiagram
USER ||--o{ CART : "1:N"
USER ||--o{ ORDER : "1:N"
PRODUCT ||--o{ CART : "1:N"
PRODUCT ||--o{ ORDER_DETAIL : "1:N"
ORDER ||--o{ ORDER_DETAIL : "1:M"
USER {
bigint id PK
varchar username
varchar password
varchar phone
datetime create_time
}
PRODUCT {
bigint id PK
varchar name
decimal price
int stock
tinyint status
datetime create_time
}
CART {
bigint id PK
bigint user_id FK
bigint product_id FK
int quantity
datetime create_time
}
ORDER {
bigint id PK
bigint user_id FK
decimal total_amount
varchar order_status
datetime create_time
datetime pay_time
}
ORDER_DETAIL {
bigint id PK
bigint order_id FK
bigint product_id FK
varchar product_name
decimal unit_price
int quantity
decimal total_price
}
这张 E-R 图揭示了几组关键关系:
- 一个用户可以拥有多个购物车条目(1:N)
- 一个商品可以被多个用户添加至购物车(N:M,通过中间表
CART实现) - 一个订单包含多个订单明细项(1:M)
特别注意 ORDER_DETAIL 表中保留了 product_name 和 unit_price 字段——这是一种典型的 反范式设计 。
为什么要这么做?因为商品价格可能会变,但历史订单的价格必须保持不变。否则今天买手机花了 5999,明天商家降价到 5499,你的订单显示也变成 5499,那岂不是乱套了?
所以,我们在下单时会做一个“快照”操作,把当时商品的名称、价格、图片等信息一起写入订单明细表。虽然造成了数据冗余,但换来了财务审计的准确性。
💬 工程权衡法则:
“写时求一致,读时求高效。”
—— 在写入阶段保证数据正确性,在读取阶段优化查询性能。
范式理论的应用与权衡
| 范式 | 定义 | 是否推荐 |
|---|---|---|
| 1NF | 原子性:每个字段不可再分 | ✅ 必须遵守 |
| 2NF | 消除部分函数依赖,非主属性完全依赖于主键 | ✅ 推荐 |
| 3NF | 消除非主属性对主键的传递依赖 | ✅ 推荐 |
| 反范式 | 故意引入冗余以提升查询性能 | ⚠️ 条件使用 |
例如,如果严格遵循 3NF, t_order_detail 表应只保留 order_id , product_id , quantity ,其他信息均从 t_product 查询。但在实际中,频繁 JOIN 会导致性能下降。
因此,实践中常采用“适度反范式”策略,在写入订单时将商品快照信息一并写入。
主键策略选择:自增ID vs 分布式ID
随着业务增长,单机 MySQL 难以承载海量数据,分库分表成为必然选择。此时,传统的自增主键面临严峻挑战。
自增 ID 的局限
CREATE TABLE t_product (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
...
);
优点:
- 实现简单
- 数值连续,利于 B+ 树索引组织
- 易于调试
缺点:
- 分库分表后无法保证全局唯一
- 容易暴露业务规模(如注册人数)
- 高并发下可能出现锁竞争(InnoDB 的 AUTO-INC 锁)
分布式 ID 方案对比
| 方案 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| UUID | 全局唯一 | 太长(36字符),无序 | 小规模系统 |
| Snowflake | 64位整数,趋势递增 | 依赖系统时钟 | 中大型分布式系统 |
| 号段模式 | 减少数据库访问 | 存在跳跃 | 高并发写入 |
| Redis INCR | 性能高 | 单点风险 | 低可靠性要求 |
推荐使用 Snowflake 算法,以下是 Java 实现:
public class SnowflakeIdGenerator {
// 省略具体实现...
public synchronized long nextId() { /* 返回唯一ID */ }
}
生成的 ID 具备 全局唯一、趋势递增、长度适中 三大优势,非常适合用于订单、商品等核心表。
zut-shop.sql 脚本深度解析
脚本执行流程:
mysql -u root -p < zut-shop.sql
核心表结构示例:
CREATE TABLE t_product (
id BIGINT NOT NULL AUTO_INCREMENT COMMENT '主键',
name VARCHAR(255) NOT NULL COMMENT '商品名称',
price DECIMAL(10,2) NOT NULL DEFAULT 0.00 COMMENT '单价',
stock INT NOT NULL DEFAULT 0 COMMENT '库存数量',
status TINYINT NOT NULL DEFAULT 1 COMMENT '状态:1-上架,0-下架',
create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
update_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (id),
KEY idx_status (status),
KEY idx_create_time (create_time)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='商品表';
✅ 最佳实践:
- 使用
utf8mb4支持 emoji;- 添加
update_time自动更新;- 为常用查询字段建立索引。
Vue前端框架应用与组件化开发
Vue3响应式系统与Composition API实战
Vue3 最大的革新之一就是基于 Proxy 的响应式系统。
import { reactive, ref, computed } from 'vue';
const product = reactive({
name: 'iPhone 15',
price: 999,
inStock: true
});
const count = ref(1);
const totalPrice = computed(() => count.value * product.price);
相比 Vue2 的 Object.defineProperty , Proxy 能监听属性增删、数组索引修改等操作,响应更全面。
setup() 函数结合 Composition API,让逻辑组织更加清晰:
export default {
setup() {
const state = reactive({ loading: false });
onMounted(async () => {
state.loading = true;
const res = await axios.get('/api/products');
state.products = res.data;
state.loading = false;
});
return { state };
}
};
响应式流程如下:
graph TD
A[数据变化] --> B{是否为响应式对象?}
B -- 是 --> C[触发 Proxy.set]
C --> D[查找依赖 deps]
D --> E[运行 effect scheduler]
E --> F[执行 component.update]
F --> G[生成新 VNode]
G --> H[Diff 算法对比]
H --> I[Patch 更新真实 DOM]
配合 shallowReactive 、 readonly 等工具函数,可以灵活控制响应式粒度,避免不必要的性能开销。
🌈 总结一句话:
前后端分离 + SpringBoot 自动装配 + 合理数据库建模 + Vue3 响应式开发 = 现代化电商系统的黄金三角 。
简介:“智子商城”是一个采用SpringBoot与Vue实现的前后端分离电子商务平台,涵盖现代Web开发核心技术栈。项目通过SpringBoot构建稳定高效的后端服务,使用MySQL存储商品、用户和订单等核心数据,并引入Redis缓存提升系统性能。安全方面集成SpringSecurity实现认证与权限控制,前端采用Vue框架构建响应式、组件化的用户界面,提升交互体验。配套zut-shop.sql数据库脚本和Zut-Shop.zip完整源码包,便于快速部署与学习。本项目覆盖前后端协作、数据库设计、缓存优化与安全控制等关键环节,是掌握全栈开发的优质实践案例。
2万+

被折叠的 条评论
为什么被折叠?



