畅购商城(一):环境搭建
好好学习,天天向上
本文已收录至我的Github仓库DayDayUP:
github.com/RobodLee/DayDayUP
,欢迎Star
畅购商城(一):环境搭建畅购商城(二):分布式文件系统FastDFS
畅购商城(三):商品管理畅购商城(四):Lua、OpenResty、Canal实现广告缓存与同步畅购商城(五):Elasticsearch实现商品搜索畅购商城(六):商品搜索
畅购商城(七):Thymeleaf实现静态页畅购商城(八):微服务网关和JWT令牌畅购商城(九):Spring Security Oauth2畅购商城(十):购物车畅购商城(十一):订单畅购商城(十二):接入微信支付
畅购商城(十三):秒杀系统「上」代码:
github.com/RobodLee/changgou
啰嗦几句
畅购商城是黑马的一个项目。要说这个项目有多难,跟着视频做肯定是没什么大问题了,但是可以让我知道一个项目的具体开发流程以及提高自己对于一系列框架使用的熟练度,这也是我做这个项目的目的。关于这个项目的资料我就不提供了,视频b站上面有,配套的资料也在视频下面的评论中,有需要的朋友直接到b站上面找就可以了。接下来我会用十几篇文章来记录一下整个项目的开发经过以及遇到的问题。
畅购商城项目介绍
畅购商城项目是一个B2C的电商网站,采用了微服务架构,并且使用了前后端分离的方式进行开发。
技术栈
上面这张图就是畅购商城使用到的技术栈,从图中可以看出,整个微服务的开发是基于SpringBoot的,OAuth2.0是用来进行授权操作的,JWT用来封装用户的授权信息,Spring AMQP是消息队列协议。然后就是一套Spring Cloud的微服务框架。
持久化技术栈选用了MyBatis+通用Mapper,但我不准备用通用Mapper,因为我想锻炼一下写SQL语句,平时SQL也没怎么写,就借此机会练习练习。还用到了SpringDataEs用来操作ElasticSearch,SpringDataRedis用来操作Redis。
数据库采用了MySQL,消息队列选用了RabbitMQ,还实现了MySQL读写分离。
支付接口就选择了微信支付。
技术架构
这张图是畅购商城的技术架构图,可以看到,先是使用了Nginx做负载均衡以及限流;紧跟着的就是微服务网关,是用来将请求路由到不同的微服务,网关也集成了限流和权限校验的功能。后面就是具体的微服务了,业务方面一共分为了7个微服务,微服务之间也可能会相互调用,采用了Feign来进行不同微服务之间的调用,一些JavaBean及工具类也被单独抽取了出来。一些公共组件微服务也被单独抽取了出来,比如Oauth2.0微服务,RabbitMQ微服务等。
Hystrix Dashboard作为监控中心,Eureka是微服务的注册中心。
数据支撑方面,搜索功能用的是ElasticSearch,文件系统采用的是FastDFS,数据库选用的当然是MySQL了,缓存用的是Redis。
这里面有很多我也没用过,具体是干什么的我也不是很清楚,介绍的就简单了点。
环境搭建
项目介绍得差不多了就开始搭建项目吧。
安装虚拟机,准备数据库
我们用到的MySQL数据库是安装在docker中的,docker安装在了CentOS上,这些都已经安装好了,我们只需要把黑马提供的虚拟机安装一下就可以了。安装过程很简单,但是安装完成后一定要记得改IP,因为虚拟机的静态IP和我们自己的电脑可能不在一个网段,改到一个网段。还有一个问题就是用Navicat连不上数据库,可能的原因是数据库密码不对,视频上说的是123456,可我试了是root;要么就是没有允许远程访问,开启就好;最后一个原因就是防火墙不允许我们访问3306端口,把防火墙关闭就OK了。
项目框架搭建
1. 创建父工程
在changgou目录下创建一个新的Module名为changou-parent作为整个项目的父工程:
在父工程里面不需要写代码,所以把src目录删掉。因为每个微服务工程都是SpringBoot的,所以在changgou-parent的pom文件中添加SpringBoot的起步依赖。然后再添加一些需要用到的依赖包,视频里还添加了swagger的依赖,但是暂时不准备用,等用的时候再添加。整个changgou-parent的pom文件的内容我贴在了下面:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.robod</groupId>
<artifactId>changgou-parent</artifactId>
<version>1.0-SNAPSHOT</version>
<description>畅购商城项目的父工程</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.4.RELEASE</version>
</parent>
<properties>
<!-- 跳过测试 -->
<skipTests>true</skipTests>
</properties>
<!--依赖包-->
<dependencies>
<!--测试包-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
<!--fastjson-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.51</version>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Greenwich.SR1</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
</project>
2. 其它几个公共模块搭建
在changgou-parent下面创建changgou-gateway、changgou-service、changgou-service-api、changgou-web四个Module,因为这几个是各个模块的父工程,所以也不用写代码,删除src目录,并且打pom包。
<packaging>pom</packaging>
3. Eureka微服务搭建
微服务工程都搭建完毕了,现在就需要有个注册中心去启动微服务,所以接下来就在changgou-parent下创建一个名为changgou-eureka的Module,要想开启eureka的服务,就需要添加相应的依赖包。
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
</dependencies>
接下来在resource目录下添加配置文件application.yml:
server:
port: 7001 #端口号
eureka:
instance:
hostname: 127.0.0.1 #ip
client:
register-with-eureka: false #是否将自己注册到eureka中
fetch-registry: false #是否从eureka中获取信息
service-url:
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
spring:
application:
name: eureka
最后在java包下添加一个启动类:com.robod.EurekaApplication:
@SpringBootApplication
@EnableEurekaServer //开启Eureka服务
public class EurekaApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaApplication.class,args);
}
}
现在就来测试一下Eureka能不能启动成功,运行上面的代码,等项目启动起来,访问http://127.0.0.1:7001
成功出现了上面的界面,说明我们的注册中心已经搭建成功了
4. 创建common工程
不同的微服务里面都会使用到一些相同的工具类,我们将这些工具类抽取到一个单独的子工程里面,在changgou-parent下面新建一个Module叫做changgou-common,在pom文件中添加所需的依赖。
<dependencies>
<!--web起步依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- redis 使用-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!--eureka-client-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<!--openfeign-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<!--微信支付-->
<dependency>
<groupId>com.github.wxpay</groupId>
<artifactId>wxpay-sdk</artifactId>
<version>0.0.3</version>
</dependency>
<!--httpclient支持-->
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
</dependency>
</dependencies>
最后在com.robod.entity包下添加几个工具类(这些类在配套资料里面都有):
5. 数据库工程搭建
这个工程是将需要访问数据库的一些依赖进行一个汇总,没有代码,在chnaggou-parent下新建一个Module叫changgou-common-db,然后删除src目录,最后添加所需的依赖:
<!--依赖-->
<dependencies>
<!--对changgou-common的依赖-->
<dependency>
<groupId>com.robod</groupId>
<artifactId>changgou-common</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<!--通用mapper起步依赖,我不需要,所以没有添加这个-->
<!-- <dependency>-->
<!-- <groupId>tk.mybatis</groupId>-->
<!-- <artifactId>mapper-spring-boot-starter</artifactId>-->
<!-- <version>2.0.4</version>-->
<!-- </dependency>-->
<!--MySQL数据库驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!--mybatis分页插件-->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper-spring-boot-starter</artifactId>
<version>1.2.3</version>
</dependency>
</dependencies>
商品微服务工程
1. 商品微服务的JavaBean工程
前面我们创建了一个changgou-service-api用来管理所有的微服务工程的API抽取,现在我们就在changgou-service-api下创建一个changogu-service-goods-api工程用来管理商品微服务的JavaBean,为了简化代码,我们需要用到Lombok,将Lombok的依赖添加到changgou-service-api的pom文件中:
<dependencies>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.10</version>
<scope>provided</scope>
</dependency>
</dependencies>
然后将我们需要的一些JavaBean放到com.robod.goods.pojo包下。
2. 创建商品微服务工程changgou-service-goods
在changgou-service下面创建一个changgou-service-goods作为商品微服务工程,因为要使用连接数据库的一些东西,所以我们将changgou-common-db引入到changgou-service中:
<dependency>
<groupId>com.robod</groupId>
<artifactId>changgou-common-db</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
微服务工程自然少不了配置文件,在resources目录下创建application.yml:
server:
port: 18081
spring:
application:
name: goods
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://192.168.31.200:3306/changgou_goods?useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC
username: root
password: root
eureka:
client:
service-url:
defaultZone: http://127.0.0.1:7001/eureka
instance:
prefer-ip-address: true
feign:
hystrix:
enabled: true
最后为这个工程创建一个启动类com.robod.GoodsApplication:
@SpringBootApplication
@EnableEurekaClient //开启Eureka客户端
@MapperScan("com.robod.mapper") //开启包扫描
@EnableTransactionManagement //事务管理
public class GoodsApplication {
public static void main(String[] args) {
SpringApplication.run(GoodsApplication.class, args);
}
}
将项目启动起来,访问http://127.0.0.1:7001。
可以看到,商品微服务工程已经成功启动并且注册到Eureka中了。
3. 查询所有品牌功能实现
分别创建出Controller层,Service层,Dao层对应的类:
写出对应的代码:
@Repository("brandMapper")
public interface BrandMapper {
/**
* 查询所有的品牌信息
* @return
*/
@Select("select * from tb_brand")
public List<Brand> findAll();
}
-------------------------------------------------------
@Service("brandService")
@Transactional(rollbackFor = Exception.class) //异常回滚
public class BrandServiceImpl implements BrandService {
private final BrandMapper brandMapper;
public BrandServiceImpl(BrandMapper brandMapper) {
this.brandMapper = brandMapper;
}
@Override
public List<Brand> findAll() {
return brandMapper.findAll();
}
}
---------------------------------------------------------
@RestController
@RequestMapping("/brand")
@CrossOrigin
public class BrandController {
private final BrandService brandService;
public BrandController(BrandService brandService) {
this.brandService = brandService;
}
@GetMapping
public Result<List<Brand>> findAll() {
List<Brand> brands = brandService.findAll();
return new Result<>(true, StatusCode.OK,"查询成功",brands);
}
}
然后将项目运行起来,访问http://localhost:18081/brand
成功查询出所有的品牌信息,说明我们的环境搭建成功了,剩下的修改品牌,删除品牌等功能直接写就可以了,如果不行很有可能是代码有问题,不是环境问题。
4. 分页 + 条件查询
分页查询我们需要用到PageHelper,这个依赖我们前面已经添加过了。直接上代码:
public PageInfo<Brand> findPage(Brand brand, int page, int size) {
PageHelper.startPage(page,size);
List<Brand> brands = brandMapper.findList(brand);
return new PageInfo<>(brands);
}
很简单,先是PageHelper.startPage(page,size),page是需要获取第几页的数据,size是每页的数据条数;再调用方法拿到所有数据;最后使用一个PageInfo将数据封装起来即可。
条件查询就需要根据条件动态构建SQL语句:
@SelectProvider(type = BrandMapperProvider.class, method = "findList")
public List<Brand> findList(Brand brand);
class BrandMapperProvider {
public String findList(Brand brand) {
StringBuilder builder = new StringBuilder("select * from tb_brand where ");
if (!StringUtils.isEmpty(brand.getName())) {
builder.append(" name like ").append("\"%").append(brand.getName()).append("%\" ");
}
if (!StringUtils.isEmpty(brand.getImage())) {
builder.append(" and image like ").append("\"%").append(brand.getImage()).append("%\" ");
}
if (!StringUtils.isEmpty(brand.getLetter())) {
builder.append(" and letter = ").append(" \"").append(brand.getLetter()).append("\" ");
}
if (brand.getSeq() != null) {
builder.append(" and seq = ").append(brand.getSeq());
}
System.out.println(builder.toString());
return builder.toString();
}
}
在BrandMapper中添加一个内部类BrandMapperProvider,并提供一个构建SQL语句的方法,最后在findList方法上面添加注解@SelectProvider即可。
测试一下:
成功得到了我们想要的结果。
全局异常处理
为每个方法都写异常处理代码实在是太麻烦了,可以用一个全局异常处理类去处理所有的异常。因为所有的微服务工程都依赖了changgou-common,所以我们可以直接在changgou-common工程下的com.robod.exception包下创建一个类BaseExceptionHandler。
@ControllerAdvice
public class BaseExceptionHandler {
/***
* 异常处理
* @param e
* @return
*/
@ExceptionHandler(value = Exception.class)
@ResponseBody
public Result error(Exception e) {
e.printStackTrace();
return new Result(false, StatusCode.ERROR, e.getMessage());
}
}
在类的上面添加@ControllerAdvice注解,在这里的作用是应用到所有@RequestMapping中,实现全局异常处理,该注解还可以实现全局数据绑定以及全局数据预处理的功能。在error方法上面添加@ExceptionHandler(value=Exception.class)注解,用于声明所需要捕获的异常的类型。最后在error方法中编写异常处理的代码。
小结
这篇文章主要介绍了畅购商城的架构以及环境的搭建,然后写了个商品微服务工程,并实现了对品牌表的增删查改功能。最后我们还实现了全局异常处理功能,下一篇文章就写一写分布式文件系统fastDFS环境的搭建过程以及遇到的一些问题。
如果我的文章对你有些帮助,不要忘了点赞,收藏,转发,关注。要是有什么好的意见欢迎在下方留言。让我们下期再见!