SpringBoot实现Java高并发秒杀系统之DAO层开发(一)

本文涉及的产品
Redis 开源版,标准版 2GB
推荐场景:
搭建游戏排行榜
云数据库 Tair(兼容Redis),内存型 2GB
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
简介: SpringBoot实现Java高并发秒杀系统之DAO层开发(一) 秒杀系统在如今电商项目中是很常见的,最近在学习电商项目时讲到了秒杀系统的实现,于是打算使用SpringBoot框架学习一下秒杀系统(本项目基于慕课网的一套免费视频教程:Java高并发秒杀API,视频教程中讲解的很详细,非常感谢这位讲师)。

SpringBoot实现Java高并发秒杀系统之DAO层开发(一)

秒杀系统在如今电商项目中是很常见的,最近在学习电商项目时讲到了秒杀系统的实现,于是打算使用SpringBoot框架学习一下秒杀系统(本项目基于慕课网的一套免费视频教程:Java高并发秒杀API,视频教程中讲解的很详细,非常感谢这位讲师)。也是因为最近学习了SpringBoot框架(GitHub教程:SpringBoot入门之CRUD ),觉得SpringBoot框架确实比传统SSM框架方便了很多,于是更深层次练习使用SpringBoot框架,注意:SpringBoot不是对Spring功能上的增强,而是提供了一种快速使用Spring的方式。 如果你熟悉了SSM框架,学习SpringBoot框架也是很Easy的。

本项目的源码请参看:springboot-seckill 如果觉得不错可以star一下哦(#^.^#)

本项目一共分为四个模块来讲解,具体的开发教程请看我的博客文章:

  • SpringBoot实现Java高并发秒杀系统之DAO层开发(一)

  • SpringBoot实现Java高并发秒杀系统之Service层开发(二)

  • SpringBoot实现Java高并发秒杀系统之Web层开发(三)

  • SpringBoot实现Java高并发秒杀系统之并发优化(四)

起步

首先我们需要搭建SpringBoot项目开发环境,IDEA搭建SpringBoot项目的具体教程请看我的:博文

如果你对SpringBoot框架或是SSM框架不熟悉,我想推荐一下我的几个小项目帮助你更好的理解:

  • SpringBoot起步之环境搭建

  • SpringBoot-Mybatis入门之CRUD

  • 手把手教你整合SSM框架

  • SSM框架入门之环境搭建


项目设计

.
├── README -- Doc文档
├── db -- 数据库约束文件
├── mvnw
├── mvnw.cmd
├── pom.xml -- 项目依赖
└── src
├── main
│   ├── java
│   │   └── cn
│   │   └── tycoding
│   │   ├── SpringbootSeckillApplication.java -- SpringBoot启动器
│   │   ├── controller -- MVC的web层
│   │   ├── dto -- 统一封装的一些结果属性,和entity类似
│   │   ├── entity -- 实体类
│   │   ├── enums -- 手动定义的字典枚举参数
│   │   ├── exception -- 统一的异常结果
│   │   ├── mapper -- Mybatis-Mapper层映射接口,或称为DAO层
│   │   ├── redis -- redis,jedis 相关配置
│   │   └── service -- 业务层
│   └── resources
│   ├── application.yml -- SpringBoot核心配置
│   ├── mapper -- Mybatis-Mapper层XML映射文件
│   ├── static -- 存放页面静态资源,可通过浏览器直接访问
│   │   ├── css
│   │   ├── js
│   │   └── lib
│   └── templates -- 存放Thymeleaf模板引擎所需的HTML,不能在浏览器直接访问
│   ├── page
│   └── public -- HTML页面公共组件(头部、尾部)
└── test -- 测试文件

SpringBoot

之前我们在SpringBoot-Mybatis入门之CRUD中已经详细讲解了SpringBoot框架的开发流程,还是觉得一句话说的特别好:SpringBoot不是对对Spring功能上的增强,而是提供了一种快速使用Spring的方式。所以用SSM阶段的知识足够了SpringBoot阶段的开发,下面我们强调一下小技巧:

  • SpringBoot不需要配置注解扫描,之前我们配置<context:component-scan>扫描可能使用注解(@Service,@Component,@Controller等)的包路径。默认创建SpringBoot项目自动生成的Application.java启动器类会自动扫描其下的所有注解。

  • SpringBoot项目中静态资源都放在resources目录下,其中static目录中的数据可以直接通过浏览器访问,多用来放CSS、JS、img,但是不用来放html页面;其中templates用来存放HTML页面,但是需要在SpringBoot的配置文件(application.yml)中配置spring.thymeleaf.prefix标识Thymeleaf模板引擎渲染的页面位置。

  • HTML页面通过Thymeleaf的加持,为HTML页面赋予了很多功能,此时的HTML页面类似于JSP页面。访问后端存入域对象(session,request…)中的数据,可以通过th:text="${key}"获得,在JS中也可以通过[[${key}]]获得。

  • Thymeleaf提供了类似JSP页面<include>的功能:public-component:<div th:fragment="header">,main-component:<div th:replace="path/header :: header">(其中path表示public-component相对于templates的路径,/header表示component文件名,最后的header表示th:fragment中定义的名称)。

pom依赖

<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.5.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>

<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>

<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.2</version>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>

<!-- alibaba的druid数据库连接池 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.9</version>
</dependency>

<!-- redis客户端 -->
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
</dependency>

<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
</dependencies>

JavaBean实体类配置

此处源码请看:GitHub

Seckill.java

public class Seckill implements Serializable {

private long seckillId; //商品ID
private String title; //商品标题
private String image; //商品图片
private BigDecimal price; //商品原价格
private BigDecimal costPrice; //商品秒杀价格

@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private Date createTime; //创建时间

@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private Date startTime; //秒杀开始时间

@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private Date endTime; //秒杀结束时间

private long stockCount; //剩余库存数量
}

SeckillOrder.java

public class SeckillOrder implements Serializable {

private long seckillId; //秒杀到的商品ID
private BigDecimal money; //支付金额

private long userPhone; //秒杀用户的手机号

@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
private Date createTime; //创建时间

private boolean status; //订单状态, -1:无效 0:成功 1:已付款

private Seckill seckill; //秒杀商品,和订单是一对多的关系
}

注意实体类中Date类型数据都用了@DateTimeFormat()(来自springframework)和@JsonFormat()(来自jackson)标识可以实现Controller在返回JSON数据(用@ResponseBody标识的方法或@RestController标识的类)的时候能将Date类型的参数值(经Mybatis查询得到的数据是英文格式的日期,因为实体类中是Date类型)转换为注解中指定的格式返回给页面(相当于经过了一层SimpleDateFormate)。

其次要注意在编写实体类的时候尽量养成习惯继承Serializable接口。在SeckillOrder中我们注入了Seckill类作为一个属性,目的是为了可以使用多表查询的方式从seckill_order表中查询出来对应的seckill表数据。

表设计

创建完成了SpringBoot项目,首先我们需要初始化数据库,秒杀系统的建表SQL如下:

/*
* mysql-v: 5.7.22
*/

-- 创建数据库
-- CREATE DATABASE seckill DEFAULT CHARACTER SET utf8;

DROP TABLE IF EXISTS `seckill`;
DROP TABLE IF EXISTS `seckill_order`;

-- 创建秒杀商品表
CREATE TABLE `seckill`(
`seckill_id` bigint NOT NULL AUTO_INCREMENT COMMENT '商品ID',
`title` varchar (1000) DEFAULT NULL COMMENT '商品标题',
`image` varchar (1000) DEFAULT NULL COMMENT '商品图片',
`price` decimal (10,2) DEFAULT NULL COMMENT '商品原价格',
`cost_price` decimal (10,2) DEFAULT NULL COMMENT '商品秒杀价格',
`stock_count` bigint DEFAULT NULL COMMENT '剩余库存数量',
`start_time` timestamp NOT NULL DEFAULT '1970-02-01 00:00:01' COMMENT '秒杀开始时间',
`end_time` timestamp NOT NULL DEFAULT '1970-02-01 00:00:01' COMMENT '秒杀结束时间',
`create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
PRIMARY KEY (`seckill_id`),
KEY `idx_start_time` (`start_time`),
KEY `idx_end_time` (`end_time`),
KEY `idx_create_time` (`end_time`)
) CHARSET=utf8 ENGINE=InnoDB COMMENT '秒杀商品表';

-- 创建秒杀订单表
CREATE TABLE `seckill_order`(
`seckill_id` bigint NOT NULL COMMENT '秒杀商品ID',
`money` decimal (10, 2) DEFAULT NULL COMMENT '支付金额',
`user_phone` bigint NOT NULL COMMENT '用户手机号',
`create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '创建时间',
`state` tinyint NOT NULL DEFAULT -1 COMMENT '状态:-1无效 0成功 1已付款',
PRIMARY KEY (`seckill_id`, `user_phone`) /*联合主键,保证一个用户只能秒杀一件商品*/
) CHARSET=utf8 ENGINE=InnoDB COMMENT '秒杀订单表';

解释

秒杀系统的表设计还是相对简单清晰的,这里我们只考虑秒杀系统的业务表,不涉及其他的表,所以整个系统主要涉及两张表:秒杀商品表、订单表。当然实际情况肯定不止这两张表(比如付款相关表,但是我们并未实现这个功能),也不止表中的这些字段。这里我们需要特别注意以下几点:

注意

  • 1.我这里使用的Mysql版本是5.7.22,在Mysql5.7之后timestamp默认值不能再是0000 00-00 00:00:00,具体的介绍请看:mysql官方文档。即 TIMESTAMP has a range of ‘1970-01-01 00:00:01’ UTC to ‘2038-01-19 03:14:07’ UTC.

  • 2.timestamp类型用来实现自动为新增行字段设置当前系统时间;且使用timestamp的字段必须给timestamp设置默认值,而在Mysql中date, datetime等类型都是无法实现默认设置当前系统时间值的功能(DEFAULT CURRENT_TIMESTAMP)的,所以我们必须使用timestamp类型,否则你要给字段传进来系统时间。

  • 3.decimal类型用于在数据库中设置精确的数值,比如decimal(10,2)表示可以存储10位且有2位小数的数值。

  • 4.tinyint类型用于存放int类型的数值,但是若用Mybatis作为DAO层框架,Mybatis会自动为tinyint类型的数据转换成true或false(0:false; 1 or 1+:true)。

  • 5.在订单表seckill_order中我们设计了联合主键:PRIMARY KEY (seckill_id, user_phone),目的是为了避免单个用户重复购买同一件商品(一个用户只能秒杀到一次同一件商品)。

  • 6.无论是创建数据库还是创建表我们都应该养成一个习惯就是指定character=utf-8,避免中文数据乱码;其次还应该指定表的储存引擎是InnoDB,MySQL提供了两种储存引擎:InnoDB, MyISAM。但是只有InnoDB是支持事务的,且InnoDB相比MyISAM在并发上更具有高性能的优点。

DAO层开发

DAO层是我们常说的三层架构(Web层-业务层-持久层)中与数据库交互的持久层,但是实际而言,架构是这样设计的,但是并不代表着实际项目中就一定存在一个dao文件夹,特别是现阶段我们使用的Spring-Mybatis框架。Mybatis提供了一种接口代理开发模式,也就是我们需要提供一个interface接口,其他和数据库交互的SQL编写放到对应的XML文件中(但是需要进行相关的数据库参数配置,并且Mybatis规定了使用这种开发模式必须保持接口和XML文件名称对应)。于是在本项目中就没有出现dao整个文件夹,取而代之的是mapper这个文件夹,我感觉更易识别出为Mybatis的映射接口文件。其实在实际项目中考虑到项目的大小和复杂程度,daomapper可能是同时存在的,因为service可能并不满足项目的设计,即为dao接口创建实现类,在实现类中再调用mapper接口来实现功能模块的扩展。


DAO层开发,即DAO层接口开发,主要设计需要和数据库交互的数据有哪些?应该用什么返回值类型接收查询到的数据?所以包含的方法有哪些?带着这些问题,我们先看一下秒杀系统的业务流程:

由上图可以看出,相对与本项目而言和数据库打交道的主要涉及两个操作:1.减库存(秒杀商品表);2.记录购买明细(订单表)。

  • 减库存,顾名思义就是减少当前被秒杀到的商品的库存数量,这也是秒杀系统中一个处理难点的地方。实现减库存即count-1,但是我们需要考虑Mysql的事务特性引发的种种问题、需要考虑如何避免同一用户重复秒杀的行为。

  • 如果减库存的业务解决了那么记录购买明细的业务就相对简单很多了,我们需要记录购买用户的姓名、手机号、购买的商品ID等。因为本项目中不涉及支付功能,所以记录用户的购买订单的业务并不复杂。

分析了上面的功能,下面我们开始DAO层接口的编写(源码请看:GitHub):

/**
* 减库存。
* 对于Mapper映射接口方法中存在多个参数的要加@Param()注解标识字段名称,不然Mybatis不能识别出来哪个字段相互对应
*
* @param seckillId 秒杀商品ID
* @param killTime 秒杀时间
* @return 返回此SQL更新的记录数,如果>=1表示更新成功
*/
int reduceStock(@Param("seckillId") long seckillId, @Param("killTime") Date killTime);

/**
* 插入购买订单明细
*
* @param seckillId 秒杀到的商品ID
* @param money 秒杀的金额
* @param userPhone 秒杀的用户
* @return 返回该SQL更新的记录数,如果>=1则更新成功
*/
int insertOrder(@Param("seckillId") long seckillId, @Param("money") BigDecimal money, @Param("userPhone") long userPhone);

但从接口设计上我们无非关注的就是这两个方法:1.减库存;2.插入购买明细。此处需要注意的是:

  • 对于SpringBoot系统,DAO(Mapper)层的接口需要使用@Mapper注解标识。因为SpringBoot系统中接口的XML文件不在/java目录下而是在/resources目录下。

  • 对于Mapper接口方法中存在传递多个参数的情况需要使用@Param()标识这个参数的名称,目的是为了帮助Mybatis识别传递的参数,不然Mybatis的XML中用的#{}不能识别出来你传递的参数名称是谁和谁对应的,类似于Controller层中常用的@RequestParam()注解。

  • 小技巧: 之前我们做insert和update操作时直接用void作为方法返回值,实际上虽然Mybatis的<update><select>语句并没有resultType属性,但是并不代表其没有返回值,默认返回0或1,表示执行该SQL影响的行数。为此我们可以这样写SQL,如:insert ignore into xxx用来避免Mybatis报错,而是直接返回0表示当前SQL执行失败。

  • 小技巧:因为我们必须要避免同一个用户多次抢购同一件商品,在SQL中必须限制这一点(因为即使前端怎么控制都无法避免用户多次请求同一个接口,所谓接口防刷)。所以在设计订单表的时候用了联合主键且不自增的方式,以用户ID和用户电话组成联合主键,这样当同一个用户(电话相同)多次抢购同一件商品时插入的SQL就会产生主键冲突的问题,这样就会报错。

XML映射

<update id="reduceStock">
UPDATE seckill
SET stock_count = stock_count - 1
WHERE seckill_id = #{seckillId}
AND start_time &lt;= #{killTime}
AND end_time &gt;= #{killTime}
AND stock_count &gt; 0
</update>

<insert id="insertOrder">
INSERT ignore INTO seckill_order(seckill_id, money, user_phone)
VALUES (#{seckillId}, #{money}, #{userPhone})
</insert>

SQL语句相对不是很复杂。减库存:执行update语句,令stock_count字段依次减一,并且当前要在一系列where条件的限制下;新增订单信息:保存订单数据,这里为接口防刷用联合主键seckillId, userPhone,如果同一个用户多次抢购同一件商品导致主键冲突会直接报错,为了避免系统不直接报错设计了ignore实现主键冲突就直接返回0表示该条SQL执行失败。

拓展

上面我使用了&lt;&gt;的语法其实代表的是>= <=这种符号,因为在Mybatis中编写的SQL语句如果直接使用>=<=这种判断条件可能会报错,我这里提供一种简单的解决方案就是用这种英文符号代替:

原符号 替换符号
< <
<= <=
> >
>= >=
& &
'
"

order表中findById方法

之前在SeckillOrder.java实体类中我们注入了Seckill属性,用于可以根据查询seckill_order表的同时查询到其对应的seckill表数据,对应的接口定义如下:

/**
* 根据秒杀商品ID查询订单明细数据并得到对应秒杀商品的数据,因为我们再SeckillOrder中已经定义了一个Seckill的属性
*
* @param seckillId
* @return
*/
SeckillOrder findById(long seckillId);

对应的SQL如下:

<select id="findById" resultType="SeckillOrder">
SELECT
so.seckill_id,
so.user_phone,
so.money,
so.create_time,
so.state,
s.seckill_id "seckill.seckill_id",
s.title "seckill.title",
s.cost_price "seckill.cost_price",
s.create_time "seckill.create_time",
s.start_time "seckill.start_time",
s.end_time "seckill.end_time",
s.stock_count "seckill.stock_count"
FROM seckill_order so
INNER JOIN seckill s ON so.seckill_id = s.seckill_id
WHERE so.seckill_id = #{seckillId}
</select>

这个SQL看似复杂些,但是就是仅仅的多表(两张表)查询语句:根据seckill_order表中的seckill_id字段查询seckill表中seckill_id字段值对应的数据(也就是说:对于多表查询,其实两张表之间必然存在一定的字段关联关系,不一定是外键关联,当然我们也不建议用外键关联两张表)。

其中findById的SQL中类似s.seckill_id "seckill.seckill_id"语句其实是s.seckill_id as "seckill.seckill_id",这里省略了as(别名);而INNER JOIN语句正是查询若两张表中中又相同字段的匹配值就根据两张表关联字段查询两张表的数据。这也可以使用<resultMap>中的<association>标签来实现,用于查询两张关联表的数据,如:

<resultMap id="findById" type="SeckillOrder">
<id column="seckill_id" property="seckillId"/>
<result column="user_phone" property="userPhone"/>
...
<association property="seckill" javaType="Seckill">
<id column="seckill_id" property="seckillId"/>
<result column="title" property="title"/>
...
</association>
</resultMap>

如以上也是一种映射另外一张表数据的方式(当然使用这种方式在写SQL的时候需要指定限制条件where s.seckill_id = so.seckill_id强调两张表中的seckill_id字段值相同)。

测试

在编写了Mybatis的映射接口和XML映射文件,我们可以编写一个测试类来测试一下接口和XML配置是否正确。由于我们使用IDEA开发工具,打开接口文件用快捷键Alt + Enter(我这里用的Mac系统)显示一个面板,选择Create Test快速创建本文件的测试类。

由于使用的SpringBoot框架,新创建的测试类位于/src/test/java/目录下,我们举例说明,比如创建SeckillMapper接口的测试文件:SeckillMapperTest.java

public class SeckillMapperTest {

@Autowired
private SeckillMapper seckillMapper;

@Test
public void findAll() {
}

@Test
public void findById() {
}

@Test
public void reduceStock() {
}
}

以上就是使用IDEA快捷键创建的测试类,我们仅以findAll()方法举例说明一下如何使用SpringBoot的测试类。如下:

此处的源码请参看:Github

@RunWith(SpringJUnit4ClassRunner.class)
//@ContextConfiguration("classpath:application.yml")
@SpringBootTest
public class SeckillMapperTest {

@Autowired
private SeckillMapper seckillMapper;

@Test
public void findAll() {
List<Seckill> all = seckillMapper.findAll();
for (Seckill seckill : all) {
System.out.println(seckill.getTitle());
}
}

@Test
public void findById() {
}

@Test
public void reduceStock() {
}
}

SpringBoot的测试类和传统Spring框架测试类的最大区别就是不再使用@ContextConfiguration()注解去加载配置文件,取而代之的是使用@SpringBootTest注解。因为SpringBoot已经严格规定了配置文件放在resources目录下,且一般是.properties.yml结尾。如果你再使用@ContextConfiguration()注解加载配置文件反而会报错。

相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore &nbsp; &nbsp; ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库&nbsp;ECS 实例和一台目标数据库&nbsp;RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&amp;RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
相关文章
|
29天前
|
监控 Java API
如何使用Java语言快速开发一套智慧工地系统
使用Java开发智慧工地系统,采用Spring Cloud微服务架构和前后端分离设计,结合MySQL、MongoDB数据库及RESTful API,集成人脸识别、视频监控、设备与环境监测等功能模块,运用Spark/Flink处理大数据,ECharts/AntV G2实现数据可视化,确保系统安全与性能,采用敏捷开发模式,提供详尽文档与用户培训,支持云部署与容器化管理,快速构建高效、灵活的智慧工地解决方案。
|
19天前
|
Java 开发者 微服务
Spring Boot 入门:简化 Java Web 开发的强大工具
Spring Boot 是一个开源的 Java 基础框架,用于创建独立、生产级别的基于Spring框架的应用程序。它旨在简化Spring应用的初始搭建以及开发过程。
38 6
Spring Boot 入门:简化 Java Web 开发的强大工具
|
6天前
|
存储 JavaScript 前端开发
基于 SpringBoot 和 Vue 开发校园点餐订餐外卖跑腿Java源码
一个非常实用的校园外卖系统,基于 SpringBoot 和 Vue 的开发。这一系统源于黑马的外卖案例项目 经过站长的进一步改进和优化,提供了更丰富的功能和更高的可用性。 这个项目的架构设计非常有趣。虽然它采用了SpringBoot和Vue的组合,但并不是一个完全分离的项目。 前端视图通过JS的方式引入了Vue和Element UI,既能利用Vue的快速开发优势,
52 13
|
11天前
|
算法 Java API
如何使用Java开发获得淘宝商品描述API接口?
本文详细介绍如何使用Java开发调用淘宝商品描述API接口,涵盖从注册淘宝开放平台账号、阅读平台规则、创建应用并申请接口权限,到安装开发工具、配置开发环境、获取访问令牌,以及具体的Java代码实现和注意事项。通过遵循这些步骤,开发者可以高效地获取商品详情、描述及图片等信息,为项目和业务增添价值。
44 10
|
5天前
|
前端开发 Java 测试技术
java日常开发中如何写出优雅的好维护的代码
代码可读性太差,实际是给团队后续开发中埋坑,优化在平时,没有那个团队会说我专门给你一个月来优化之前的代码,所以在日常开发中就要多注意可读性问题,不要写出几天之后自己都看不懂的代码。
40 2
|
14天前
|
JavaScript 安全 Java
java版药品不良反应智能监测系统源码,采用SpringBoot、Vue、MySQL技术开发
基于B/S架构,采用Java、SpringBoot、Vue、MySQL等技术自主研发的ADR智能监测系统,适用于三甲医院,支持二次开发。该系统能自动监测全院患者药物不良反应,通过移动端和PC端实时反馈,提升用药安全。系统涵盖规则管理、监测报告、系统管理三大模块,确保精准、高效地处理ADR事件。
|
20天前
|
设计模式 消息中间件 搜索推荐
Java 设计模式——观察者模式:从优衣库不使用新疆棉事件看系统的动态响应
【11月更文挑战第17天】观察者模式是一种行为设计模式,定义了一对多的依赖关系,使多个观察者对象能直接监听并响应某一主题对象的状态变化。本文介绍了观察者模式的基本概念、商业系统中的应用实例,如优衣库事件中各相关方的动态响应,以及模式的优势和实际系统设计中的应用建议,包括事件驱动架构和消息队列的使用。
|
2月前
|
JavaScript 安全 Java
如何使用 Spring Boot 和 Ant Design Pro Vue 实现动态路由和菜单功能,快速搭建前后端分离的应用框架
本文介绍了如何使用 Spring Boot 和 Ant Design Pro Vue 实现动态路由和菜单功能,快速搭建前后端分离的应用框架。首先,确保开发环境已安装必要的工具,然后创建并配置 Spring Boot 项目,包括添加依赖和配置 Spring Security。接着,创建后端 API 和前端项目,配置动态路由和菜单。最后,运行项目并分享实践心得,包括版本兼容性、安全性、性能调优等方面。
176 1
|
1月前
|
JavaScript 安全 Java
如何使用 Spring Boot 和 Ant Design Pro Vue 构建一个具有动态路由和菜单功能的前后端分离应用。
本文介绍了如何使用 Spring Boot 和 Ant Design Pro Vue 构建一个具有动态路由和菜单功能的前后端分离应用。首先,创建并配置 Spring Boot 项目,实现后端 API;然后,使用 Ant Design Pro Vue 创建前端项目,配置动态路由和菜单。通过具体案例,展示了如何快速搭建高效、易维护的项目框架。
114 62
|
1月前
|
JavaScript 安全 Java
如何使用 Spring Boot 和 Ant Design Pro Vue 构建一个前后端分离的应用框架,实现动态路由和菜单功能
本文介绍了如何使用 Spring Boot 和 Ant Design Pro Vue 构建一个前后端分离的应用框架,实现动态路由和菜单功能。首先,确保开发环境已安装必要的工具,然后创建并配置 Spring Boot 项目,包括添加依赖和配置 Spring Security。接着,创建后端 API 和前端项目,配置动态路由和菜单。最后,运行项目并分享实践心得,帮助开发者提高开发效率和应用的可维护性。
80 2